kalendly 0.1.7 → 0.2.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.
Files changed (39) hide show
  1. package/README.md +377 -662
  2. package/dist/core/index.js +3 -3
  3. package/dist/index.d.mts +61 -602
  4. package/dist/index.d.ts +61 -602
  5. package/dist/index.js +963 -2375
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +937 -2364
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/{vanilla/index.umd.js → index.umd.js} +438 -244
  10. package/dist/index.umd.js.map +1 -0
  11. package/dist/styles/calendar.css +96 -0
  12. package/package.json +54 -118
  13. package/dist/react/index.d.mts +0 -251
  14. package/dist/react/index.d.ts +0 -251
  15. package/dist/react/index.js +0 -969
  16. package/dist/react/index.js.map +0 -1
  17. package/dist/react/index.mjs +0 -924
  18. package/dist/react/index.mjs.map +0 -1
  19. package/dist/react-native/index.d.mts +0 -642
  20. package/dist/react-native/index.d.ts +0 -642
  21. package/dist/react-native/index.js +0 -1649
  22. package/dist/react-native/index.js.map +0 -1
  23. package/dist/react-native/index.mjs +0 -1615
  24. package/dist/react-native/index.mjs.map +0 -1
  25. package/dist/vanilla/index.d.mts +0 -271
  26. package/dist/vanilla/index.d.ts +0 -271
  27. package/dist/vanilla/index.js +0 -1064
  28. package/dist/vanilla/index.js.map +0 -1
  29. package/dist/vanilla/index.mjs +0 -1013
  30. package/dist/vanilla/index.mjs.map +0 -1
  31. package/dist/vanilla/index.umd.js.map +0 -1
  32. package/dist/vue/components/Calendar.vue.d.ts +0 -43
  33. package/dist/vue/components/Calendar.vue.d.ts.map +0 -1
  34. package/dist/vue/index.d.ts +0 -134
  35. package/dist/vue/index.d.ts.map +0 -1
  36. package/dist/vue/index.js +0 -1
  37. package/dist/vue/index.mjs +0 -717
  38. package/dist/vue/types.d.ts +0 -21
  39. package/dist/vue/types.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -3,9 +3,12 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __esm = (fn, res) => function __init() {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ };
6
9
  var __export = (target, all) => {
7
- for (var name2 in all)
8
- __defProp(target, name2, { get: all[name2], enumerable: true });
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
9
12
  };
10
13
  var __copyProps = (to, from, except, desc) => {
11
14
  if (from && typeof from === "object" || typeof from === "function") {
@@ -17,81 +20,14 @@ var __copyProps = (to, from, except, desc) => {
17
20
  };
18
21
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
22
 
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- CalendarEngine: () => CalendarEngine,
24
- DAYS: () => DAYS,
25
- DEFAULT_CATEGORY_COLORS: () => DEFAULT_CATEGORY_COLORS,
26
- MONTHS: () => MONTHS,
27
- MONTHS_FULL: () => MONTHS_FULL,
28
- React: () => react_exports,
29
- ReactCalendar: () => Calendar,
30
- ReactNative: () => react_native_exports,
31
- ReactNativeCalendar: () => Calendar2,
32
- Vanilla: () => vanilla_exports,
33
- createVanillaCalendar: () => createCalendar,
34
- formatAttendees: () => formatAttendees,
35
- formatDateForDisplay: () => formatDateForDisplay,
36
- formatTimeRange: () => formatTimeRange,
37
- generateCalendarDates: () => generateCalendarDates,
38
- generateYears: () => generateYears,
39
- getCategoryColor: () => getCategoryColor,
40
- getCellClasses: () => getCellClasses,
41
- getDefaultEventColor: () => getDefaultEventColor,
42
- getEventsForDate: () => getEventsForDate,
43
- getMonthYearText: () => getMonthYearText,
44
- getPopupPositionClass: () => getPopupPositionClass,
45
- hasEvents: () => hasEvents,
46
- isSameDay: () => isSameDay,
47
- isToday: () => isToday,
48
- isValidHexColor: () => isValidHexColor,
49
- mergeCategoryColors: () => mergeCategoryColors,
50
- name: () => name,
51
- normalizeDate: () => normalizeDate,
52
- sortEventsByTime: () => sortEventsByTime,
53
- version: () => version
23
+ // src/core/types.ts
24
+ var init_types = __esm({
25
+ "src/core/types.ts"() {
26
+ "use strict";
27
+ }
54
28
  });
55
- module.exports = __toCommonJS(index_exports);
56
29
 
57
30
  // src/core/utils.ts
58
- var MONTHS = [
59
- "Jan",
60
- "Feb",
61
- "Mar",
62
- "Apr",
63
- "May",
64
- "Jun",
65
- "Jul",
66
- "Aug",
67
- "Sep",
68
- "Oct",
69
- "Nov",
70
- "Dec"
71
- ];
72
- var MONTHS_FULL = [
73
- "January",
74
- "February",
75
- "March",
76
- "April",
77
- "May",
78
- "June",
79
- "July",
80
- "August",
81
- "September",
82
- "October",
83
- "November",
84
- "December"
85
- ];
86
- var DAYS = [
87
- "Sunday",
88
- "Monday",
89
- "Tuesday",
90
- "Wednesday",
91
- "Thursday",
92
- "Friday",
93
- "Saturday"
94
- ];
95
31
  function normalizeDate(date) {
96
32
  return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
97
33
  }
@@ -228,14 +164,6 @@ function sortEventsByTime(events) {
228
164
  return a.startTime.localeCompare(b.startTime);
229
165
  });
230
166
  }
231
- var DEFAULT_CATEGORY_COLORS = {
232
- work: "#3b82f6",
233
- personal: "#8b5cf6",
234
- meeting: "#10b981",
235
- deadline: "#ef4444",
236
- appointment: "#f59e0b",
237
- other: "#6b7280"
238
- };
239
167
  function getDefaultEventColor(category, customColors) {
240
168
  const colorMap = customColors || DEFAULT_CATEGORY_COLORS;
241
169
  if (category && colorMap[category]) {
@@ -257,2035 +185,553 @@ function getCategoryColor(category, customColors) {
257
185
  const color = colorMap[category] || colorMap.other || "#fc8917";
258
186
  return isValidHexColor(color) ? color : "#fc8917";
259
187
  }
260
-
261
- // src/core/calendar-engine.ts
262
- var CalendarEngine = class {
263
- constructor(config) {
264
- this.listeners = /* @__PURE__ */ new Set();
265
- this.config = config;
266
- this.categoryColors = mergeCategoryColors(config.categoryColors);
267
- const initialDate = config.initialDate || /* @__PURE__ */ new Date();
268
- this.state = {
269
- currentYear: initialDate.getFullYear(),
270
- currentMonth: initialDate.getMonth(),
271
- currentDate: initialDate.getDate(),
272
- selectedDate: null,
273
- selectedDayIndex: null,
274
- tasks: []
188
+ var MONTHS, MONTHS_FULL, DAYS, DEFAULT_CATEGORY_COLORS;
189
+ var init_utils = __esm({
190
+ "src/core/utils.ts"() {
191
+ "use strict";
192
+ MONTHS = [
193
+ "Jan",
194
+ "Feb",
195
+ "Mar",
196
+ "Apr",
197
+ "May",
198
+ "Jun",
199
+ "Jul",
200
+ "Aug",
201
+ "Sep",
202
+ "Oct",
203
+ "Nov",
204
+ "Dec"
205
+ ];
206
+ MONTHS_FULL = [
207
+ "January",
208
+ "February",
209
+ "March",
210
+ "April",
211
+ "May",
212
+ "June",
213
+ "July",
214
+ "August",
215
+ "September",
216
+ "October",
217
+ "November",
218
+ "December"
219
+ ];
220
+ DAYS = [
221
+ "Sunday",
222
+ "Monday",
223
+ "Tuesday",
224
+ "Wednesday",
225
+ "Thursday",
226
+ "Friday",
227
+ "Saturday"
228
+ ];
229
+ DEFAULT_CATEGORY_COLORS = {
230
+ work: "#3b82f6",
231
+ personal: "#8b5cf6",
232
+ meeting: "#10b981",
233
+ deadline: "#ef4444",
234
+ appointment: "#f59e0b",
235
+ other: "#6b7280"
275
236
  };
276
237
  }
277
- /**
278
- * Subscribe to state changes
279
- */
280
- subscribe(listener) {
281
- this.listeners.add(listener);
282
- return () => this.listeners.delete(listener);
283
- }
284
- /**
285
- * Notify all listeners of state changes
286
- */
287
- notify() {
288
- this.listeners.forEach((listener) => listener());
289
- }
290
- /**
291
- * Get current state
292
- */
293
- getState() {
294
- return { ...this.state };
295
- }
296
- /**
297
- * Get view model with computed properties
298
- */
299
- getViewModel() {
300
- const calendarDates = generateCalendarDates(
301
- this.state.currentYear,
302
- this.state.currentMonth,
303
- this.config.events,
304
- this.config.weekStartsOn
305
- );
306
- return {
307
- ...this.state,
308
- months: MONTHS,
309
- days: DAYS,
310
- years: generateYears(this.config.minYear, this.config.maxYear),
311
- monthAndYearText: getMonthYearText(
312
- this.state.currentYear,
313
- this.state.currentMonth
314
- ),
315
- scheduleDay: this.state.selectedDate ? formatDateForDisplay(this.state.selectedDate) : "",
316
- calendarDates,
317
- popupPositionClass: getPopupPositionClass(this.state.selectedDayIndex)
318
- };
319
- }
320
- /**
321
- * Get actions object
322
- */
323
- getActions() {
324
- return {
325
- next: this.next.bind(this),
326
- previous: this.previous.bind(this),
327
- jump: this.jump.bind(this),
328
- goToToday: this.goToToday.bind(this),
329
- isCurrentMonth: this.isCurrentMonth.bind(this),
330
- selectDate: this.selectDate.bind(this),
331
- updateTasks: this.updateTasks.bind(this)
332
- };
333
- }
334
- /**
335
- * Navigate to next month
336
- */
337
- next() {
338
- if (this.state.currentMonth === 11) {
339
- this.state.currentMonth = 0;
340
- this.state.currentYear++;
341
- } else {
342
- this.state.currentMonth++;
343
- }
344
- this.state.selectedDate = null;
345
- this.state.selectedDayIndex = null;
346
- this.updateTasks();
347
- this.notify();
348
- }
349
- /**
350
- * Navigate to previous month
351
- */
352
- previous() {
353
- if (this.state.currentMonth === 0) {
354
- this.state.currentMonth = 11;
355
- this.state.currentYear--;
356
- } else {
357
- this.state.currentMonth--;
358
- }
359
- this.state.selectedDate = null;
360
- this.state.selectedDayIndex = null;
361
- this.updateTasks();
362
- this.notify();
363
- }
364
- /**
365
- * Jump to specific month and year
366
- */
367
- jump(year, month) {
368
- this.state.currentYear = year;
369
- this.state.currentMonth = month;
370
- this.state.selectedDate = null;
371
- this.state.selectedDayIndex = null;
372
- this.updateTasks();
373
- this.notify();
374
- }
375
- /**
376
- * Navigate to current month (today)
377
- */
378
- goToToday() {
379
- const today = /* @__PURE__ */ new Date();
380
- this.state.currentYear = today.getFullYear();
381
- this.state.currentMonth = today.getMonth();
382
- this.state.selectedDate = null;
383
- this.state.selectedDayIndex = null;
384
- this.updateTasks();
385
- this.notify();
386
- }
387
- /**
388
- * Check if currently viewing today's month
389
- */
390
- isCurrentMonth() {
391
- const today = /* @__PURE__ */ new Date();
392
- return this.state.currentYear === today.getFullYear() && this.state.currentMonth === today.getMonth();
393
- }
394
- /**
395
- * Select a specific date
396
- */
397
- selectDate(date, dayIndex) {
398
- this.state.selectedDate = date;
399
- this.state.selectedDayIndex = dayIndex ?? null;
400
- this.state.currentDate = date.getDate();
401
- this.state.currentMonth = date.getMonth();
402
- this.state.currentYear = date.getFullYear();
403
- this.updateTasks();
404
- this.notify();
405
- }
406
- /**
407
- * Update tasks for the currently selected date
408
- */
409
- updateTasks() {
410
- if (!this.state.selectedDate) {
411
- this.state.tasks = [];
412
- return;
413
- }
414
- this.state.tasks = getEventsForDate(
415
- this.config.events,
416
- this.state.selectedDate
417
- );
418
- }
419
- /**
420
- * Update events configuration
421
- */
422
- updateEvents(events) {
423
- this.config.events = events;
424
- this.updateTasks();
425
- this.notify();
426
- }
427
- /**
428
- * Handle date cell click
429
- */
430
- handleDateClick(date, dayIndex) {
431
- this.selectDate(date, dayIndex);
432
- }
433
- /**
434
- * Check if date has events
435
- */
436
- hasEventsForDate(date) {
437
- return getEventsForDate(this.config.events, date).length > 0;
438
- }
439
- /**
440
- * Get events for a specific date
441
- */
442
- getEventsForDate(date) {
443
- return getEventsForDate(this.config.events, date);
444
- }
445
- /**
446
- * Clear selected date
447
- */
448
- clearSelection() {
449
- this.state.selectedDate = null;
450
- this.state.selectedDayIndex = null;
451
- this.state.tasks = [];
452
- this.notify();
453
- }
454
- /**
455
- * Get category color (with custom colors support)
456
- */
457
- getCategoryColor(category) {
458
- if (!category) return "#fc8917";
459
- return getCategoryColor(category, this.categoryColors);
460
- }
461
- /**
462
- * Update category colors dynamically
463
- */
464
- updateCategoryColors(colors) {
465
- this.categoryColors = mergeCategoryColors(colors);
466
- this.notify();
467
- }
468
- /**
469
- * Get all category colors
470
- */
471
- getCategoryColors() {
472
- return { ...this.categoryColors };
473
- }
474
- /**
475
- * Register a new category with color
476
- */
477
- registerCategory(name2, color) {
478
- this.categoryColors[name2] = color;
479
- this.notify();
480
- }
481
- /**
482
- * Destroy the engine and cleanup listeners
483
- */
484
- destroy() {
485
- this.listeners.clear();
486
- }
487
- };
488
-
489
- // src/react/index.tsx
490
- var react_exports = {};
491
- __export(react_exports, {
492
- Calendar: () => Calendar,
493
- CalendarEngine: () => CalendarEngine,
494
- DAYS: () => DAYS,
495
- DEFAULT_CATEGORY_COLORS: () => DEFAULT_CATEGORY_COLORS,
496
- DatePopup: () => DatePopup,
497
- MONTHS: () => MONTHS,
498
- MONTHS_FULL: () => MONTHS_FULL,
499
- formatAttendees: () => formatAttendees,
500
- formatDateForDisplay: () => formatDateForDisplay,
501
- formatTimeRange: () => formatTimeRange,
502
- generateCalendarDates: () => generateCalendarDates,
503
- generateYears: () => generateYears,
504
- getCategoryColor: () => getCategoryColor,
505
- getCellClasses: () => getCellClasses,
506
- getDefaultEventColor: () => getDefaultEventColor,
507
- getEventsForDate: () => getEventsForDate,
508
- getMonthYearText: () => getMonthYearText,
509
- getPopupPositionClass: () => getPopupPositionClass,
510
- hasEvents: () => hasEvents,
511
- isSameDay: () => isSameDay,
512
- isToday: () => isToday,
513
- isValidHexColor: () => isValidHexColor,
514
- mergeCategoryColors: () => mergeCategoryColors,
515
- normalizeDate: () => normalizeDate,
516
- sortEventsByTime: () => sortEventsByTime
517
238
  });
518
239
 
519
- // src/react/components/Calendar.tsx
520
- var import_react = require("react");
521
-
522
- // src/react/components/DatePopup.tsx
523
- var import_jsx_runtime = require("react/jsx-runtime");
524
- var PriorityBadge = ({ priority }) => {
525
- if (!priority) return null;
526
- const priorityConfig = {
527
- high: { label: "HIGH", className: "badge priority-high" },
528
- medium: { label: "MEDIUM", className: "badge priority-medium" },
529
- low: { label: "LOW", className: "badge priority-low" }
530
- };
531
- const config = priorityConfig[priority];
532
- if (!config) return null;
533
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: config.className, children: config.label });
534
- };
535
- var StatusBadge = ({ status }) => {
536
- if (!status || status === "scheduled") return null;
537
- const statusConfig = {
538
- completed: { label: "COMPLETED", className: "badge status-completed" },
539
- cancelled: { label: "CANCELLED", className: "badge status-cancelled" },
540
- tentative: { label: "TENTATIVE", className: "badge status-tentative" }
541
- };
542
- const config = statusConfig[status];
543
- if (!config) return null;
544
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: config.className, children: config.label });
545
- };
546
- var CategoryBadge = ({ category }) => {
547
- if (!category) return null;
548
- const categoryConfig = {
549
- work: { label: "WORK", className: "badge category-work" },
550
- personal: { label: "PERSONAL", className: "badge category-personal" },
551
- meeting: { label: "MEETING", className: "badge category-meeting" },
552
- deadline: { label: "DEADLINE", className: "badge category-deadline" },
553
- appointment: {
554
- label: "APPOINTMENT",
555
- className: "badge category-appointment"
556
- },
557
- other: { label: "OTHER", className: "badge category-other" }
558
- };
559
- const config = categoryConfig[category];
560
- if (!config) return null;
561
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: config.className, children: config.label });
562
- };
563
- var DatePopup = ({
564
- isVisible,
565
- selectedDate,
566
- events,
567
- scheduleDay,
568
- popupPositionClass,
569
- onEventClick,
570
- onClose,
571
- renderEvent,
572
- renderNoEvents
573
- }) => {
574
- if (!isVisible || !selectedDate) return null;
575
- const handleEventClick = (event) => {
576
- onEventClick?.(event);
577
- };
578
- const showScrollHint = events.length > 3;
579
- const defaultRenderEvent = (event) => {
580
- const timeRange = formatTimeRange(event);
581
- const attendeesList = formatAttendees(event.attendees);
582
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
583
- "div",
584
- {
585
- className: `event-card${onEventClick ? " clickable" : ""}`,
586
- onClick: () => handleEventClick(event),
587
- style: {
588
- borderLeftColor: event.color || void 0
589
- },
590
- children: [
591
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "event-header", children: [
592
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "event-title", children: event.name }),
593
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "event-badges", children: [
594
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CategoryBadge, { category: event.category }),
595
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PriorityBadge, { priority: event.priority }),
596
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusBadge, { status: event.status })
597
- ] })
598
- ] }),
599
- timeRange && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "event-time", children: [
600
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "event-time-label", children: "Time:" }),
601
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "event-time-value", children: timeRange })
602
- ] }),
603
- event.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "event-description", children: event.description }),
604
- event.location && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "event-time", children: [
605
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "event-time-label", children: "Location:" }),
606
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "event-time-value", children: event.location })
607
- ] }),
608
- attendeesList && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "event-time", children: [
609
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "event-time-label", children: "Attendees:" }),
610
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "event-time-value", children: attendeesList })
611
- ] }),
612
- event.organizer && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "event-time", children: [
613
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "event-time-label", children: "Organizer:" }),
614
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "event-time-value", children: event.organizer })
615
- ] }),
616
- event.notes && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "event-time", children: [
617
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "event-time-label", children: "Notes:" }),
618
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "event-time-value", children: event.notes })
619
- ] }),
620
- event.url && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "event-time", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
621
- "a",
622
- {
623
- href: event.url,
624
- target: "_blank",
625
- rel: "noopener noreferrer",
626
- className: "event-link",
627
- onClick: (e) => e.stopPropagation(),
628
- children: "View Details \u2192"
629
- }
630
- ) }),
631
- event.tags && event.tags.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "event-tags", children: event.tags.map((tag, idx) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "event-tag", children: tag }, idx)) })
632
- ]
633
- },
634
- event.id || event.name
635
- );
636
- };
637
- const defaultRenderNoEvents = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "no-events-message", children: "No events scheduled for this day." });
638
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `date-popup ${popupPositionClass}`, children: [
639
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "popup-header", children: [
640
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: scheduleDay }),
641
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
642
- "button",
643
- {
644
- type: "button",
645
- className: "popup-close",
646
- onClick: onClose,
647
- "aria-label": "Close",
648
- children: "\u2715"
649
- }
650
- )
651
- ] }),
652
- showScrollHint && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scroll-hint", children: "\u2193 Scroll to see more events \u2193" }),
653
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "events-container", children: events.length > 0 ? events.map(
654
- (event) => renderEvent ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
655
- "div",
656
- {
657
- className: `event-card${onEventClick ? " clickable" : ""}`,
658
- onClick: () => handleEventClick(event),
659
- children: renderEvent(event)
660
- },
661
- event.id || event.name
662
- ) : defaultRenderEvent(event)
663
- ) : renderNoEvents ? renderNoEvents() : defaultRenderNoEvents() })
664
- ] });
665
- };
666
-
667
- // src/react/components/Calendar.tsx
668
- var import_jsx_runtime2 = require("react/jsx-runtime");
669
- var Calendar = ({
670
- events,
671
- initialDate,
672
- minYear,
673
- maxYear,
674
- weekStartsOn = 0,
675
- useShortMonthNames = false,
676
- onDateSelect,
677
- onEventClick,
678
- onMonthChange,
679
- className = "",
680
- style,
681
- renderEvent,
682
- renderNoEvents,
683
- title,
684
- theme
685
- }) => {
686
- const engine = (0, import_react.useMemo)(
687
- () => new CalendarEngine({
688
- events,
689
- initialDate,
690
- minYear,
691
- maxYear,
692
- weekStartsOn
693
- }),
694
- [events, initialDate, minYear, maxYear, weekStartsOn]
695
- );
696
- const [, forceUpdate] = (0, import_react.useState)({});
697
- const rerender = (0, import_react.useCallback)(() => forceUpdate({}), []);
698
- const [pickerOpen, setPickerOpen] = (0, import_react.useState)(false);
699
- const [yearInput, setYearInput] = (0, import_react.useState)("");
700
- const [yearInputValid, setYearInputValid] = (0, import_react.useState)(true);
701
- const pickerRef = (0, import_react.useRef)(null);
702
- (0, import_react.useEffect)(() => {
703
- const unsubscribe = engine.subscribe(rerender);
704
- return unsubscribe;
705
- }, [engine, rerender]);
706
- (0, import_react.useEffect)(() => {
707
- engine.updateEvents(events);
708
- }, [engine, events]);
709
- (0, import_react.useEffect)(() => {
710
- return () => {
711
- engine.destroy();
712
- };
713
- }, [engine]);
714
- (0, import_react.useEffect)(() => {
715
- if (theme) {
716
- const root = document.documentElement;
717
- if (theme.primary)
718
- root.style.setProperty("--calendar-primary-color", theme.primary);
719
- if (theme.secondary)
720
- root.style.setProperty("--calendar-secondary-color", theme.secondary);
721
- if (theme.tertiary)
722
- root.style.setProperty("--calendar-tertiary-color", theme.tertiary);
723
- if (theme.textColor)
724
- root.style.setProperty("--calendar-text-color", theme.textColor);
725
- if (theme.textLight)
726
- root.style.setProperty("--calendar-text-light", theme.textLight);
727
- if (theme.background)
728
- root.style.setProperty("--calendar-background", theme.background);
729
- if (theme.cellHover)
730
- root.style.setProperty("--calendar-cell-hover", theme.cellHover);
731
- if (theme.borderColor)
732
- root.style.setProperty("--calendar-border-color", theme.borderColor);
733
- if (theme.todayOutline)
734
- root.style.setProperty("--calendar-today-outline", theme.todayOutline);
735
- if (theme.selectedBg)
736
- root.style.setProperty("--calendar-selected-bg", theme.selectedBg);
737
- if (theme.eventIndicator)
738
- root.style.setProperty(
739
- "--calendar-event-indicator",
740
- theme.eventIndicator
741
- );
742
- }
743
- }, [theme]);
744
- const viewModel = engine.getViewModel();
745
- const actions = engine.getActions();
746
- const { selectedDate, tasks } = viewModel;
747
- const handleDateClick = (event) => {
748
- const td = event.target.closest("td");
749
- if (!td) return;
750
- const tr = td.parentElement;
751
- if (!tr) return;
752
- const weekIndex = tr.rowIndex - 1;
753
- const dayIndex = Array.from(tr.children).indexOf(td);
754
- const calendarDate = viewModel.calendarDates[weekIndex]?.[dayIndex];
755
- if (!calendarDate) return;
756
- engine.handleDateClick(calendarDate.date, dayIndex);
757
- onDateSelect?.(calendarDate.date);
758
- };
759
- const handleMonthChange = (0, import_react.useCallback)(() => {
760
- onMonthChange?.(viewModel.currentYear, viewModel.currentMonth);
761
- }, [onMonthChange, viewModel.currentYear, viewModel.currentMonth]);
762
- const handleNext = () => {
763
- actions.next();
764
- handleMonthChange();
765
- };
766
- const handlePrevious = () => {
767
- actions.previous();
768
- handleMonthChange();
769
- };
770
- const handleGoToToday = () => {
771
- actions.goToToday();
772
- setPickerOpen(false);
773
- const today2 = /* @__PURE__ */ new Date();
774
- onMonthChange?.(today2.getFullYear(), today2.getMonth());
775
- };
776
- const togglePicker = () => {
777
- const newOpen = !pickerOpen;
778
- setPickerOpen(newOpen);
779
- if (newOpen) {
780
- setYearInput(String(viewModel.currentYear));
781
- setYearInputValid(true);
782
- }
783
- };
784
- const today = /* @__PURE__ */ new Date();
785
- const computedMinYear = minYear ?? today.getFullYear() - 30;
786
- const computedMaxYear = maxYear ?? today.getFullYear() + 10;
787
- const handleYearInputChange = (e) => {
788
- const value = e.target.value;
789
- if (value === "" || /^\d+$/.test(value)) {
790
- setYearInput(value);
791
- const year = parseInt(value, 10);
792
- setYearInputValid(
793
- value === "" || year >= computedMinYear && year <= computedMaxYear
794
- );
795
- }
796
- };
797
- const handleYearInputBlur = () => {
798
- const year = parseInt(yearInput, 10);
799
- if (isNaN(year) || year < computedMinYear || year > computedMaxYear) {
800
- setYearInput(String(viewModel.currentYear));
801
- setYearInputValid(true);
802
- } else {
803
- actions.jump(year, viewModel.currentMonth);
804
- handleMonthChange();
805
- }
806
- };
807
- const handleYearPrev = () => {
808
- const newYear = viewModel.currentYear - 1;
809
- if (newYear >= computedMinYear) {
810
- actions.jump(newYear, viewModel.currentMonth);
811
- setYearInput(String(newYear));
812
- handleMonthChange();
813
- }
814
- };
815
- const handleYearNext = () => {
816
- const newYear = viewModel.currentYear + 1;
817
- if (newYear <= computedMaxYear) {
818
- actions.jump(newYear, viewModel.currentMonth);
819
- setYearInput(String(newYear));
820
- handleMonthChange();
821
- }
822
- };
823
- const handleMonthSelect = (month) => {
824
- actions.jump(viewModel.currentYear, month);
825
- setPickerOpen(false);
826
- handleMonthChange();
827
- };
828
- (0, import_react.useEffect)(() => {
829
- const handleClickOutside = (event) => {
830
- if (pickerRef.current && !pickerRef.current.contains(event.target)) {
831
- setPickerOpen(false);
240
+ // src/core/calendar-engine.ts
241
+ var CalendarEngine;
242
+ var init_calendar_engine = __esm({
243
+ "src/core/calendar-engine.ts"() {
244
+ "use strict";
245
+ init_utils();
246
+ CalendarEngine = class {
247
+ constructor(config) {
248
+ this.listeners = /* @__PURE__ */ new Set();
249
+ this.config = config;
250
+ this.categoryColors = mergeCategoryColors(config.categoryColors);
251
+ const initialDate = config.initialDate || /* @__PURE__ */ new Date();
252
+ this.state = {
253
+ currentYear: initialDate.getFullYear(),
254
+ currentMonth: initialDate.getMonth(),
255
+ currentDate: initialDate.getDate(),
256
+ selectedDate: null,
257
+ selectedDayIndex: null,
258
+ tasks: []
259
+ };
832
260
  }
833
- };
834
- if (pickerOpen) {
835
- document.addEventListener("mousedown", handleClickOutside);
836
- }
837
- return () => document.removeEventListener("mousedown", handleClickOutside);
838
- }, [pickerOpen]);
839
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `kalendly-calendar ${className}`, style, children: [
840
- title && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "page--title", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { children: title }) }),
841
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "calendar--content", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "calendar--card", children: [
842
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "calendar--nav-header", children: [
843
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
844
- "button",
845
- {
846
- type: "button",
847
- className: "calendar--nav-arrow",
848
- onClick: handlePrevious,
849
- "aria-label": "Previous month",
850
- children: "\u2039"
851
- }
852
- ),
853
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "calendar--picker-container", ref: pickerRef, children: [
854
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
855
- "button",
856
- {
857
- type: "button",
858
- className: "calendar--picker-btn",
859
- onClick: togglePicker,
860
- ...{ "aria-expanded": pickerOpen },
861
- "aria-haspopup": "true",
862
- children: [
863
- useShortMonthNames ? `${MONTHS[viewModel.currentMonth]} ${viewModel.currentYear}` : viewModel.monthAndYearText,
864
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "calendar--picker-chevron", children: "\u25BE" })
865
- ]
866
- }
867
- ),
868
- pickerOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "calendar--picker-dropdown", children: [
869
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "calendar--picker-year-row", children: [
870
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
871
- "button",
872
- {
873
- type: "button",
874
- className: "calendar--picker-year-arrow",
875
- onClick: handleYearPrev,
876
- disabled: viewModel.currentYear <= computedMinYear,
877
- "aria-label": "Previous year",
878
- children: "\u2039"
879
- }
880
- ),
881
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
882
- "input",
883
- {
884
- type: "text",
885
- className: `calendar--picker-year-input${!yearInputValid ? " invalid" : ""}`,
886
- value: yearInput,
887
- onChange: handleYearInputChange,
888
- onBlur: handleYearInputBlur,
889
- onKeyDown: (e) => {
890
- if (e.key === "Enter") handleYearInputBlur();
891
- },
892
- "aria-label": "Year"
893
- }
894
- ),
895
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
896
- "button",
897
- {
898
- type: "button",
899
- className: "calendar--picker-year-arrow",
900
- onClick: handleYearNext,
901
- disabled: viewModel.currentYear >= computedMaxYear,
902
- "aria-label": "Next year",
903
- children: "\u203A"
904
- }
905
- )
906
- ] }),
907
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "calendar--picker-months", children: (useShortMonthNames ? MONTHS : MONTHS_FULL).map(
908
- (month, index) => {
909
- const isSelected = index === viewModel.currentMonth;
910
- const isCurrentMonth = index === today.getMonth() && viewModel.currentYear === today.getFullYear();
911
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
912
- "button",
913
- {
914
- type: "button",
915
- className: `calendar--picker-month${isSelected ? " selected" : ""}${isCurrentMonth ? " current-month" : ""}`,
916
- onClick: () => handleMonthSelect(index),
917
- children: month
918
- },
919
- month
920
- );
921
- }
922
- ) })
923
- ] })
924
- ] }),
925
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
926
- "button",
927
- {
928
- type: "button",
929
- className: "calendar--today-btn",
930
- onClick: handleGoToToday,
931
- disabled: actions.isCurrentMonth(),
932
- children: "Today"
933
- }
934
- ),
935
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
936
- "button",
937
- {
938
- type: "button",
939
- className: "calendar--nav-arrow",
940
- onClick: handleNext,
941
- "aria-label": "Next month",
942
- children: "\u203A"
943
- }
944
- )
945
- ] }),
946
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("table", { className: "calendar--table calendar--table--bordered", children: [
947
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { children: viewModel.days.map((day) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("th", { children: day.slice(0, 3) }, day)) }) }),
948
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tbody", { onClick: handleDateClick, children: viewModel.calendarDates.map((week, weekIndex) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { children: week.map((calendarDate, dayIndex) => {
949
- const cellClasses = getCellClasses(calendarDate);
950
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
951
- "td",
952
- {
953
- className: cellClasses.join(" "),
954
- children: calendarDate.date.getDate()
955
- },
956
- `${weekIndex}-${dayIndex}`
957
- );
958
- }) }, weekIndex)) })
959
- ] }),
960
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
961
- DatePopup,
962
- {
963
- isVisible: !!selectedDate,
964
- selectedDate,
965
- events: tasks,
966
- scheduleDay: viewModel.scheduleDay,
967
- popupPositionClass: viewModel.popupPositionClass,
968
- onClose: () => engine.clearSelection(),
969
- onEventClick,
970
- renderEvent,
971
- renderNoEvents
972
- }
973
- )
974
- ] }) })
975
- ] });
976
- };
977
-
978
- // src/react-native/index.tsx
979
- var react_native_exports = {};
980
- __export(react_native_exports, {
981
- Calendar: () => Calendar2,
982
- CalendarEngine: () => CalendarEngine,
983
- DAYS: () => DAYS,
984
- DEFAULT_CATEGORY_COLORS: () => DEFAULT_CATEGORY_COLORS,
985
- DatePopup: () => DatePopup2,
986
- MONTHS: () => MONTHS,
987
- MONTHS_FULL: () => MONTHS_FULL,
988
- calendarStyles: () => calendarStyles,
989
- formatAttendees: () => formatAttendees,
990
- formatDateForDisplay: () => formatDateForDisplay,
991
- formatTimeRange: () => formatTimeRange,
992
- generateCalendarDates: () => generateCalendarDates,
993
- generateYears: () => generateYears,
994
- getCategoryColor: () => getCategoryColor,
995
- getCellClasses: () => getCellClasses,
996
- getDefaultEventColor: () => getDefaultEventColor,
997
- getEventsForDate: () => getEventsForDate,
998
- getMonthYearText: () => getMonthYearText,
999
- getPopupPositionClass: () => getPopupPositionClass,
1000
- hasEvents: () => hasEvents,
1001
- isSameDay: () => isSameDay,
1002
- isToday: () => isToday,
1003
- isValidHexColor: () => isValidHexColor,
1004
- mergeCategoryColors: () => mergeCategoryColors,
1005
- normalizeDate: () => normalizeDate,
1006
- sortEventsByTime: () => sortEventsByTime
1007
- });
1008
-
1009
- // src/react-native/components/Calendar.tsx
1010
- var import_react2 = require("react");
1011
- var import_react_native3 = require("react-native");
1012
-
1013
- // src/react-native/components/DatePopup.tsx
1014
- var import_react_native2 = require("react-native");
1015
-
1016
- // src/styles/react-native-styles.ts
1017
- var import_react_native = require("react-native");
1018
- var defaultColors = {
1019
- primary: "#fc8917",
1020
- secondary: "#fca045",
1021
- tertiary: "#fdb873",
1022
- text: "#2c3e50",
1023
- border: "#dee2e6",
1024
- todayOutline: "#f7db04",
1025
- eventIndicator: "#1890ff",
1026
- background: "#fff",
1027
- white: "#ffffff",
1028
- scrollHint: "#fff3e0",
1029
- scrollHintText: "#f59e0b"
1030
- };
1031
- var getResponsiveStyles = (width, height, customColors) => {
1032
- const colors = { ...defaultColors, ...customColors };
1033
- const isTablet = width >= 768;
1034
- const isSmallPhone = width < 375;
1035
- const isPhone = width < 768;
1036
- const cellSize = (width - 60) / 7;
1037
- const popupWidth = isTablet ? 500 : isPhone ? width * 0.95 : width * 0.9;
1038
- const popupMaxWidth = isTablet ? 500 : isPhone ? width : 400;
1039
- const popupMaxHeight = isTablet ? height * 0.8 : height * 0.85;
1040
- const headerFontSize = isTablet ? 18 : isSmallPhone ? 15 : 16;
1041
- const eventTitleSize = isTablet ? 16 : isSmallPhone ? 14 : 15;
1042
- const eventDetailSize = isTablet ? 13 : isSmallPhone ? 11 : 12;
1043
- const badgeFontSize = isTablet ? 10 : isSmallPhone ? 9 : 9;
1044
- const eventCardPadding = isTablet ? 14 : isSmallPhone ? 10 : 12;
1045
- const containerPadding = isTablet ? 15 : isSmallPhone ? 10 : 12;
1046
- return import_react_native.StyleSheet.create({
1047
- container: {
1048
- flex: 1,
1049
- backgroundColor: colors.background
1050
- },
1051
- titleContainer: {
1052
- paddingVertical: 20,
1053
- alignItems: "center"
1054
- },
1055
- title: {
1056
- fontSize: 24,
1057
- fontWeight: "600",
1058
- color: colors.text
1059
- },
1060
- contentContainer: {
1061
- marginHorizontal: 20,
1062
- marginTop: 20
1063
- },
1064
- card: {
1065
- backgroundColor: colors.background,
1066
- borderRadius: 8,
1067
- shadowColor: "#000",
1068
- shadowOffset: {
1069
- width: 0,
1070
- height: 2
1071
- },
1072
- shadowOpacity: 0.1,
1073
- shadowRadius: 3.84,
1074
- elevation: 5,
1075
- overflow: "hidden"
1076
- },
1077
- cardHeader: {
1078
- backgroundColor: colors.tertiary,
1079
- paddingVertical: 15,
1080
- paddingHorizontal: 20,
1081
- borderBottomWidth: 1,
1082
- borderBottomColor: colors.border
1083
- },
1084
- cardHeaderText: {
1085
- fontSize: 18,
1086
- fontWeight: "500",
1087
- color: colors.text,
1088
- textAlign: "center"
1089
- },
1090
- table: {
1091
- backgroundColor: colors.background
1092
- },
1093
- tableHeader: {
1094
- flexDirection: "row",
1095
- backgroundColor: "rgba(252, 137, 23, 0.1)",
1096
- borderBottomWidth: 2,
1097
- borderBottomColor: colors.border
1098
- },
1099
- tableHeaderCell: {
1100
- flex: 1,
1101
- paddingVertical: 12,
1102
- borderRightWidth: 1,
1103
- borderRightColor: colors.border,
1104
- alignItems: "center",
1105
- justifyContent: "center"
1106
- },
1107
- tableHeaderText: {
1108
- fontSize: 14,
1109
- fontWeight: "600",
1110
- color: colors.text
1111
- },
1112
- tableRow: {
1113
- flexDirection: "row",
1114
- borderBottomWidth: 1,
1115
- borderBottomColor: colors.border
1116
- },
1117
- tableCell: {
1118
- flex: 1,
1119
- height: cellSize,
1120
- borderRightWidth: 1,
1121
- borderRightColor: colors.border,
1122
- alignItems: "center",
1123
- justifyContent: "center",
1124
- position: "relative"
1125
- },
1126
- tableCellText: {
1127
- fontSize: 16,
1128
- color: colors.text
1129
- },
1130
- cellToday: {
1131
- borderWidth: 2,
1132
- borderColor: colors.todayOutline
1133
- },
1134
- cellTodayText: {
1135
- fontWeight: "bold"
1136
- },
1137
- cellWithEvents: {
1138
- backgroundColor: "rgba(252, 160, 69, 0.3)"
1139
- },
1140
- eventIndicator: {
1141
- position: "absolute",
1142
- bottom: 2,
1143
- width: 4,
1144
- height: 4,
1145
- backgroundColor: colors.eventIndicator,
1146
- borderRadius: 2
1147
- },
1148
- navigationContainer: {
1149
- flexDirection: "row",
1150
- justifyContent: "space-between",
1151
- paddingHorizontal: 20,
1152
- paddingVertical: 20,
1153
- gap: 15
1154
- },
1155
- navigationButton: {
1156
- flex: 1,
1157
- backgroundColor: "transparent",
1158
- borderWidth: 1,
1159
- borderColor: colors.primary,
1160
- borderRadius: 6,
1161
- paddingVertical: 12,
1162
- paddingHorizontal: 16,
1163
- alignItems: "center",
1164
- justifyContent: "center"
1165
- },
1166
- navigationButtonText: {
1167
- color: colors.primary,
1168
- fontSize: 16,
1169
- fontWeight: "500"
1170
- },
1171
- navigationButtonPressed: {
1172
- backgroundColor: colors.primary
1173
- },
1174
- navigationButtonTextPressed: {
1175
- color: colors.white
1176
- },
1177
- jumpForm: {
1178
- flexDirection: "row",
1179
- alignItems: "center",
1180
- justifyContent: "center",
1181
- paddingHorizontal: 20,
1182
- paddingBottom: 20,
1183
- gap: 10
1184
- },
1185
- jumpLabel: {
1186
- fontSize: 18,
1187
- fontWeight: "300",
1188
- color: colors.text
1189
- },
1190
- jumpSelect: {
1191
- borderWidth: 1,
1192
- borderColor: colors.border,
1193
- borderRadius: 6,
1194
- paddingHorizontal: 12,
1195
- paddingVertical: 8,
1196
- backgroundColor: colors.background,
1197
- minWidth: 80
1198
- },
1199
- jumpSelectText: {
1200
- fontSize: 16,
1201
- color: colors.text,
1202
- textAlign: "center"
1203
- },
1204
- // RESPONSIVE POPUP STYLES
1205
- popupOverlay: {
1206
- position: "absolute",
1207
- top: 0,
1208
- left: 0,
1209
- right: 0,
1210
- bottom: 0,
1211
- backgroundColor: "rgba(0, 0, 0, 0.5)",
1212
- justifyContent: "center",
1213
- alignItems: "center",
1214
- zIndex: 1e3
1215
- },
1216
- popup: {
1217
- backgroundColor: colors.white,
1218
- borderRadius: 8,
1219
- width: popupWidth,
1220
- maxWidth: popupMaxWidth,
1221
- maxHeight: popupMaxHeight,
1222
- shadowColor: "#000",
1223
- shadowOffset: {
1224
- width: 0,
1225
- height: 4
1226
- },
1227
- shadowOpacity: 0.3,
1228
- shadowRadius: 6,
1229
- elevation: 8,
1230
- overflow: "hidden"
1231
- },
1232
- popupHeader: {
1233
- backgroundColor: colors.primary,
1234
- paddingVertical: isTablet ? 15 : 12,
1235
- paddingHorizontal: isTablet ? 20 : 15,
1236
- flexDirection: "row",
1237
- justifyContent: "space-between",
1238
- alignItems: "center"
1239
- },
1240
- popupHeaderText: {
1241
- fontSize: headerFontSize,
1242
- fontWeight: "600",
1243
- color: colors.white,
1244
- flex: 1
1245
- },
1246
- popupCloseButton: {
1247
- width: isTablet ? 32 : isSmallPhone ? 26 : 28,
1248
- height: isTablet ? 32 : isSmallPhone ? 26 : 28,
1249
- backgroundColor: "rgba(255, 255, 255, 0.2)",
1250
- borderRadius: 16,
1251
- alignItems: "center",
1252
- justifyContent: "center"
1253
- },
1254
- popupCloseText: {
1255
- color: colors.white,
1256
- fontSize: isTablet ? 20 : isSmallPhone ? 16 : 18,
1257
- fontWeight: "bold"
1258
- },
1259
- scrollHint: {
1260
- backgroundColor: colors.scrollHint,
1261
- paddingVertical: isTablet ? 10 : 8,
1262
- paddingHorizontal: containerPadding,
1263
- alignItems: "center"
1264
- },
1265
- scrollHintText: {
1266
- color: colors.scrollHintText,
1267
- fontSize: isTablet ? 13 : isSmallPhone ? 11 : 12,
1268
- fontWeight: "500"
1269
- },
1270
- eventsContainer: {
1271
- maxHeight: popupMaxHeight - 150
1272
- },
1273
- eventsList: {
1274
- padding: containerPadding,
1275
- gap: isTablet ? 12 : 10
1276
- },
1277
- eventCard: {
1278
- backgroundColor: colors.white,
1279
- borderRadius: 6,
1280
- padding: eventCardPadding,
1281
- borderLeftWidth: 4,
1282
- borderLeftColor: colors.primary,
1283
- borderWidth: 1,
1284
- borderColor: "#e5e7eb",
1285
- shadowColor: "#000",
1286
- shadowOffset: {
1287
- width: 0,
1288
- height: 1
1289
- },
1290
- shadowOpacity: 0.05,
1291
- shadowRadius: 2,
1292
- elevation: 1
1293
- },
1294
- eventHeader: {
1295
- marginBottom: 10
1296
- },
1297
- eventTitle: {
1298
- fontSize: eventTitleSize,
1299
- color: "#1f2937",
1300
- fontWeight: "600",
1301
- marginBottom: 8
1302
- },
1303
- eventBadges: {
1304
- flexDirection: "row",
1305
- gap: 6,
1306
- flexWrap: "wrap"
1307
- },
1308
- badge: {
1309
- paddingHorizontal: isTablet ? 8 : 6,
1310
- paddingVertical: isTablet ? 3 : 2,
1311
- borderRadius: 10
1312
- },
1313
- badgeText: {
1314
- fontSize: badgeFontSize,
1315
- fontWeight: "600",
1316
- textTransform: "uppercase",
1317
- letterSpacing: 0.3
1318
- },
1319
- priorityBadgeHigh: {
1320
- backgroundColor: "#fee2e2"
1321
- },
1322
- priorityBadgeMedium: {
1323
- backgroundColor: "#fef3c7"
1324
- },
1325
- priorityBadgeLow: {
1326
- backgroundColor: "#dcfce7"
1327
- },
1328
- statusBadgeCompleted: {
1329
- backgroundColor: "#d1fae5"
1330
- },
1331
- statusBadgeCancelled: {
1332
- backgroundColor: "#fee2e2"
1333
- },
1334
- statusBadgeTentative: {
1335
- backgroundColor: "#e0e7ff"
1336
- },
1337
- categoryBadgeWork: {
1338
- backgroundColor: "#dbeafe"
1339
- },
1340
- categoryBadgePersonal: {
1341
- backgroundColor: "#fef3c7"
1342
- },
1343
- categoryBadgeMeeting: {
1344
- backgroundColor: "#d1fae5"
1345
- },
1346
- categoryBadgeDeadline: {
1347
- backgroundColor: "#fee2e2"
1348
- },
1349
- categoryBadgeAppointment: {
1350
- backgroundColor: "#fef3c7"
1351
- },
1352
- categoryBadgeOther: {
1353
- backgroundColor: "#f3f4f6"
1354
- },
1355
- eventTime: {
1356
- flexDirection: isSmallPhone ? "column" : "row",
1357
- gap: isSmallPhone ? 2 : 8,
1358
- marginBottom: 6
1359
- },
1360
- eventTimeLabel: {
1361
- fontSize: eventDetailSize,
1362
- fontWeight: "600",
1363
- color: "#6b7280",
1364
- minWidth: isSmallPhone ? void 0 : isTablet ? 80 : 70,
1365
- textAlign: "left"
1366
- },
1367
- eventTimeValue: {
1368
- fontSize: eventDetailSize,
1369
- color: "#374151",
1370
- flex: 1
1371
- },
1372
- eventDescription: {
1373
- color: "#4b5563",
1374
- fontSize: eventDetailSize,
1375
- lineHeight: isTablet ? 20 : 18,
1376
- marginTop: 10,
1377
- marginBottom: 10,
1378
- paddingVertical: 8,
1379
- paddingHorizontal: 12,
1380
- backgroundColor: "#f9fafb",
1381
- borderLeftWidth: 2,
1382
- borderLeftColor: "#e5e7eb",
1383
- borderRadius: 4,
1384
- textAlign: "left"
1385
- },
1386
- eventUrlContainer: {
1387
- marginTop: 8
1388
- },
1389
- eventLink: {
1390
- color: "#2563eb",
1391
- fontSize: eventDetailSize,
1392
- fontWeight: "500"
1393
- },
1394
- eventTags: {
1395
- flexDirection: "row",
1396
- flexWrap: "wrap",
1397
- gap: 6,
1398
- marginTop: 8
1399
- },
1400
- eventTag: {
1401
- paddingHorizontal: isTablet ? 10 : 8,
1402
- paddingVertical: isTablet ? 3 : 2,
1403
- backgroundColor: "#e0f2fe",
1404
- borderRadius: 12
1405
- },
1406
- eventTagText: {
1407
- fontSize: isTablet ? 11 : 10,
1408
- color: "#0369a1",
1409
- fontWeight: "500"
1410
- },
1411
- noEventsMessage: {
1412
- padding: containerPadding,
1413
- paddingVertical: isTablet ? 30 : 20,
1414
- alignItems: "center"
1415
- },
1416
- noEventsText: {
1417
- fontSize: isTablet ? 14 : 13,
1418
- color: "#6b7280",
1419
- textAlign: "center"
1420
- }
1421
- });
1422
- };
1423
- var calendarStyles = getResponsiveStyles(375, 667);
1424
-
1425
- // src/react-native/components/DatePopup.tsx
1426
- var import_jsx_runtime3 = require("react/jsx-runtime");
1427
- var PriorityBadge2 = ({
1428
- priority,
1429
- styles
1430
- }) => {
1431
- if (!priority) return null;
1432
- const priorityConfig = {
1433
- high: { label: "HIGH", style: styles.priorityBadgeHigh },
1434
- medium: { label: "MEDIUM", style: styles.priorityBadgeMedium },
1435
- low: { label: "LOW", style: styles.priorityBadgeLow }
1436
- };
1437
- const config = priorityConfig[priority];
1438
- if (!config) return null;
1439
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.View, { style: [styles.badge, config.style], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: styles.badgeText, children: config.label }) });
1440
- };
1441
- var StatusBadge2 = ({
1442
- status,
1443
- styles
1444
- }) => {
1445
- if (!status || status === "scheduled") return null;
1446
- const statusConfig = {
1447
- completed: { label: "COMPLETED", style: styles.statusBadgeCompleted },
1448
- cancelled: { label: "CANCELLED", style: styles.statusBadgeCancelled },
1449
- tentative: { label: "TENTATIVE", style: styles.statusBadgeTentative }
1450
- };
1451
- const config = statusConfig[status];
1452
- if (!config) return null;
1453
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.View, { style: [styles.badge, config.style], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: styles.badgeText, children: config.label }) });
1454
- };
1455
- var CategoryBadge2 = ({
1456
- category,
1457
- styles
1458
- }) => {
1459
- if (!category) return null;
1460
- const categoryConfig = {
1461
- work: { label: "WORK", style: styles.categoryBadgeWork },
1462
- personal: { label: "PERSONAL", style: styles.categoryBadgePersonal },
1463
- meeting: { label: "MEETING", style: styles.categoryBadgeMeeting },
1464
- deadline: { label: "DEADLINE", style: styles.categoryBadgeDeadline },
1465
- appointment: {
1466
- label: "APPOINTMENT",
1467
- style: styles.categoryBadgeAppointment
1468
- },
1469
- other: { label: "OTHER", style: styles.categoryBadgeOther }
1470
- };
1471
- const config = categoryConfig[category];
1472
- if (!config) return null;
1473
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.View, { style: [styles.badge, config.style], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: styles.badgeText, children: config.label }) });
1474
- };
1475
- var DatePopup2 = ({
1476
- visible,
1477
- selectedDate,
1478
- events,
1479
- scheduleDay,
1480
- onClose,
1481
- onEventClick,
1482
- renderEvent,
1483
- renderNoEvents,
1484
- showCloseButton = true
1485
- }) => {
1486
- const { width, height } = (0, import_react_native2.useWindowDimensions)();
1487
- const calendarStyles2 = getResponsiveStyles(width, height);
1488
- if (!selectedDate) return null;
1489
- const showScrollHint = events.length > 3;
1490
- const handleEventClick = (event) => {
1491
- onEventClick?.(event);
1492
- };
1493
- const defaultRenderEvent = (event) => {
1494
- const timeRange = formatTimeRange(event);
1495
- const attendeesList = formatAttendees(event.attendees);
1496
- const handleUrlPress = () => {
1497
- if (event.url) {
1498
- import_react_native2.Linking.openURL(event.url).catch(
1499
- (err) => console.error("Failed to open URL:", err)
261
+ /**
262
+ * Subscribe to state changes
263
+ */
264
+ subscribe(listener) {
265
+ this.listeners.add(listener);
266
+ return () => this.listeners.delete(listener);
267
+ }
268
+ /**
269
+ * Notify all listeners of state changes
270
+ */
271
+ notify() {
272
+ this.listeners.forEach((listener) => listener());
273
+ }
274
+ /**
275
+ * Get current state
276
+ */
277
+ getState() {
278
+ return { ...this.state };
279
+ }
280
+ /**
281
+ * Get view model with computed properties
282
+ */
283
+ getViewModel() {
284
+ const calendarDates = generateCalendarDates(
285
+ this.state.currentYear,
286
+ this.state.currentMonth,
287
+ this.config.events,
288
+ this.config.weekStartsOn
1500
289
  );
290
+ return {
291
+ ...this.state,
292
+ months: MONTHS,
293
+ days: DAYS,
294
+ years: generateYears(this.config.minYear, this.config.maxYear),
295
+ monthAndYearText: getMonthYearText(
296
+ this.state.currentYear,
297
+ this.state.currentMonth
298
+ ),
299
+ scheduleDay: this.state.selectedDate ? formatDateForDisplay(this.state.selectedDate) : "",
300
+ calendarDates,
301
+ popupPositionClass: getPopupPositionClass(this.state.selectedDayIndex)
302
+ };
1501
303
  }
1502
- };
1503
- const eventContent = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1504
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native2.View, { style: calendarStyles2.eventHeader, children: [
1505
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.eventTitle, children: event.name }),
1506
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native2.View, { style: calendarStyles2.eventBadges, children: [
1507
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CategoryBadge2, { category: event.category, styles: calendarStyles2 }),
1508
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PriorityBadge2, { priority: event.priority, styles: calendarStyles2 }),
1509
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatusBadge2, { status: event.status, styles: calendarStyles2 })
1510
- ] })
1511
- ] }),
1512
- timeRange && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native2.View, { style: calendarStyles2.eventTime, children: [
1513
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.eventTimeLabel, children: "Time:" }),
1514
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.eventTimeValue, children: timeRange })
1515
- ] }),
1516
- event.description && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.eventDescription, children: event.description }),
1517
- event.location && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native2.View, { style: calendarStyles2.eventTime, children: [
1518
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.eventTimeLabel, children: "Location:" }),
1519
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.eventTimeValue, children: event.location })
1520
- ] }),
1521
- attendeesList && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native2.View, { style: calendarStyles2.eventTime, children: [
1522
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.eventTimeLabel, children: "Attendees:" }),
1523
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.eventTimeValue, children: attendeesList })
1524
- ] }),
1525
- event.organizer && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native2.View, { style: calendarStyles2.eventTime, children: [
1526
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.eventTimeLabel, children: "Organizer:" }),
1527
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.eventTimeValue, children: event.organizer })
1528
- ] }),
1529
- event.notes && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native2.View, { style: calendarStyles2.eventTime, children: [
1530
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.eventTimeLabel, children: "Notes:" }),
1531
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.eventTimeValue, children: event.notes })
1532
- ] }),
1533
- event.url && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1534
- import_react_native2.TouchableOpacity,
1535
- {
1536
- onPress: handleUrlPress,
1537
- style: calendarStyles2.eventUrlContainer,
1538
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.eventLink, children: "View Details \u2192" })
304
+ /**
305
+ * Get actions object
306
+ */
307
+ getActions() {
308
+ return {
309
+ next: this.next.bind(this),
310
+ previous: this.previous.bind(this),
311
+ jump: this.jump.bind(this),
312
+ goToToday: this.goToToday.bind(this),
313
+ isCurrentMonth: this.isCurrentMonth.bind(this),
314
+ selectDate: this.selectDate.bind(this),
315
+ updateTasks: this.updateTasks.bind(this)
316
+ };
317
+ }
318
+ /**
319
+ * Navigate to next month
320
+ */
321
+ next() {
322
+ if (this.state.currentMonth === 11) {
323
+ this.state.currentMonth = 0;
324
+ this.state.currentYear++;
325
+ } else {
326
+ this.state.currentMonth++;
1539
327
  }
1540
- ),
1541
- event.tags && event.tags.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.View, { style: calendarStyles2.eventTags, children: event.tags.map((tag, idx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.View, { style: calendarStyles2.eventTag, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.eventTagText, children: tag }) }, idx)) })
1542
- ] });
1543
- if (onEventClick) {
1544
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1545
- import_react_native2.TouchableOpacity,
1546
- {
1547
- style: [
1548
- calendarStyles2.eventCard,
1549
- event.color && { borderLeftColor: event.color }
1550
- ],
1551
- onPress: () => handleEventClick(event),
1552
- activeOpacity: 0.7,
1553
- children: eventContent
1554
- },
1555
- event.id || event.name
1556
- );
1557
- }
1558
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1559
- import_react_native2.View,
1560
- {
1561
- style: [
1562
- calendarStyles2.eventCard,
1563
- event.color && { borderLeftColor: event.color }
1564
- ],
1565
- children: eventContent
1566
- },
1567
- event.id || event.name
1568
- );
1569
- };
1570
- const defaultRenderNoEvents = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.View, { style: calendarStyles2.noEventsMessage, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.noEventsText, children: "No events scheduled for this day." }) });
1571
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1572
- import_react_native2.Modal,
1573
- {
1574
- visible,
1575
- transparent: true,
1576
- animationType: "fade",
1577
- onRequestClose: onClose,
1578
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1579
- import_react_native2.TouchableOpacity,
1580
- {
1581
- style: calendarStyles2.popupOverlay,
1582
- activeOpacity: 1,
1583
- onPress: onClose,
1584
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1585
- import_react_native2.TouchableOpacity,
1586
- {
1587
- style: calendarStyles2.popup,
1588
- activeOpacity: 1,
1589
- onPress: (e) => e.stopPropagation(),
1590
- children: [
1591
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native2.View, { style: calendarStyles2.popupHeader, children: [
1592
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.popupHeaderText, children: scheduleDay }),
1593
- showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1594
- import_react_native2.TouchableOpacity,
1595
- {
1596
- style: calendarStyles2.popupCloseButton,
1597
- onPress: onClose,
1598
- accessibilityLabel: "Close",
1599
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.popupCloseText, children: "\u2715" })
1600
- }
1601
- )
1602
- ] }),
1603
- showScrollHint && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.View, { style: calendarStyles2.scrollHint, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { style: calendarStyles2.scrollHintText, children: "\u2193 Scroll to see more events \u2193" }) }),
1604
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1605
- import_react_native2.ScrollView,
1606
- {
1607
- style: calendarStyles2.eventsContainer,
1608
- showsVerticalScrollIndicator: true,
1609
- children: events.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.View, { style: calendarStyles2.eventsList, children: events.map(
1610
- (event) => renderEvent ? renderEvent(event) : defaultRenderEvent(event)
1611
- ) }) : renderNoEvents ? renderNoEvents() : defaultRenderNoEvents()
1612
- }
1613
- )
1614
- ]
1615
- }
1616
- )
328
+ this.state.selectedDate = null;
329
+ this.state.selectedDayIndex = null;
330
+ this.updateTasks();
331
+ this.notify();
332
+ }
333
+ /**
334
+ * Navigate to previous month
335
+ */
336
+ previous() {
337
+ if (this.state.currentMonth === 0) {
338
+ this.state.currentMonth = 11;
339
+ this.state.currentYear--;
340
+ } else {
341
+ this.state.currentMonth--;
1617
342
  }
1618
- )
1619
- }
1620
- );
1621
- };
1622
-
1623
- // src/react-native/components/Calendar.tsx
1624
- var import_jsx_runtime4 = require("react/jsx-runtime");
1625
- var Calendar2 = ({
1626
- events,
1627
- initialDate,
1628
- minYear,
1629
- maxYear,
1630
- weekStartsOn = 0,
1631
- useShortMonthNames = false,
1632
- onDateSelect,
1633
- onEventClick,
1634
- onMonthChange,
1635
- style,
1636
- containerStyle,
1637
- headerStyle,
1638
- headerTextStyle,
1639
- cellStyle,
1640
- cellTextStyle,
1641
- renderEvent,
1642
- renderNoEvents,
1643
- title,
1644
- showCloseButton = true,
1645
- theme
1646
- }) => {
1647
- const engine = (0, import_react2.useMemo)(
1648
- () => new CalendarEngine({
1649
- events,
1650
- initialDate,
1651
- minYear,
1652
- maxYear,
1653
- weekStartsOn
1654
- }),
1655
- [events, initialDate, minYear, maxYear, weekStartsOn]
1656
- );
1657
- const themeColors = (0, import_react2.useMemo)(() => {
1658
- if (!theme) return void 0;
1659
- return {
1660
- primary: theme.primary,
1661
- secondary: theme.secondary,
1662
- tertiary: theme.tertiary,
1663
- text: theme.textColor,
1664
- border: theme.borderColor,
1665
- todayOutline: theme.todayOutline,
1666
- eventIndicator: theme.eventIndicator,
1667
- background: theme.background
1668
- };
1669
- }, [theme]);
1670
- const calendarStyles2 = (0, import_react2.useMemo)(() => {
1671
- const { width, height } = import_react_native3.Dimensions.get("window");
1672
- return getResponsiveStyles(width, height, themeColors);
1673
- }, [themeColors]);
1674
- const [, forceUpdate] = (0, import_react2.useState)({});
1675
- const rerender = (0, import_react2.useCallback)(() => forceUpdate({}), []);
1676
- const [pickerOpen, setPickerOpen] = (0, import_react2.useState)(false);
1677
- const [yearInput, setYearInput] = (0, import_react2.useState)("");
1678
- const [yearInputValid, setYearInputValid] = (0, import_react2.useState)(true);
1679
- const today = /* @__PURE__ */ new Date();
1680
- const todayMonth = today.getMonth();
1681
- const todayYear = today.getFullYear();
1682
- const computedMinYear = minYear ?? todayYear - 30;
1683
- const computedMaxYear = maxYear ?? todayYear + 10;
1684
- (0, import_react2.useEffect)(() => {
1685
- const unsubscribe = engine.subscribe(rerender);
1686
- return unsubscribe;
1687
- }, [engine, rerender]);
1688
- (0, import_react2.useEffect)(() => {
1689
- engine.updateEvents(events);
1690
- }, [engine, events]);
1691
- (0, import_react2.useEffect)(() => {
1692
- return () => {
1693
- engine.destroy();
1694
- };
1695
- }, [engine]);
1696
- const viewModel = engine.getViewModel();
1697
- const actions = engine.getActions();
1698
- const { selectedDate, tasks } = viewModel;
1699
- const isCurrentMonth = viewModel.currentYear === todayYear && viewModel.currentMonth === todayMonth;
1700
- const handleDatePress = (date, dayIndex) => {
1701
- engine.handleDateClick(date, dayIndex);
1702
- onDateSelect?.(date);
1703
- };
1704
- const handleNext = () => {
1705
- actions.next();
1706
- onMonthChange?.(viewModel.currentYear, viewModel.currentMonth);
1707
- };
1708
- const handlePrevious = () => {
1709
- actions.previous();
1710
- onMonthChange?.(viewModel.currentYear, viewModel.currentMonth);
1711
- };
1712
- const handleGoToToday = () => {
1713
- actions.goToToday();
1714
- setPickerOpen(false);
1715
- onMonthChange?.(todayYear, todayMonth);
1716
- };
1717
- const togglePicker = () => {
1718
- const newOpen = !pickerOpen;
1719
- setPickerOpen(newOpen);
1720
- if (newOpen) {
1721
- setYearInput(String(viewModel.currentYear));
1722
- setYearInputValid(true);
1723
- }
1724
- };
1725
- const handleYearInputChange = (value) => {
1726
- if (value === "" || /^\d+$/.test(value)) {
1727
- setYearInput(value);
1728
- const year = parseInt(value, 10);
1729
- setYearInputValid(
1730
- value === "" || year >= computedMinYear && year <= computedMaxYear
1731
- );
1732
- }
1733
- };
1734
- const handleYearInputSubmit = () => {
1735
- const year = parseInt(yearInput, 10);
1736
- if (isNaN(year) || year < computedMinYear || year > computedMaxYear) {
1737
- setYearInput(String(viewModel.currentYear));
1738
- setYearInputValid(true);
1739
- } else {
1740
- actions.jump(year, viewModel.currentMonth);
1741
- onMonthChange?.(year, viewModel.currentMonth);
1742
- }
1743
- };
1744
- const handleYearPrev = () => {
1745
- const newYear = viewModel.currentYear - 1;
1746
- if (newYear >= computedMinYear) {
1747
- actions.jump(newYear, viewModel.currentMonth);
1748
- setYearInput(String(newYear));
1749
- onMonthChange?.(newYear, viewModel.currentMonth);
1750
- }
1751
- };
1752
- const handleYearNext = () => {
1753
- const newYear = viewModel.currentYear + 1;
1754
- if (newYear <= computedMaxYear) {
1755
- actions.jump(newYear, viewModel.currentMonth);
1756
- setYearInput(String(newYear));
1757
- onMonthChange?.(newYear, viewModel.currentMonth);
1758
- }
1759
- };
1760
- const handleMonthSelect = (month) => {
1761
- actions.jump(viewModel.currentYear, month);
1762
- setPickerOpen(false);
1763
- onMonthChange?.(viewModel.currentYear, month);
1764
- };
1765
- const closePopup = () => {
1766
- engine.clearSelection();
1767
- };
1768
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.ScrollView, { style: [calendarStyles2.container, containerStyle], children: [
1769
- title && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: calendarStyles2.titleContainer, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.Text, { style: calendarStyles2.title, children: title }) }),
1770
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: [calendarStyles2.contentContainer, style], children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: calendarStyles2.card, children: [
1771
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: [pickerStyles.navHeader, headerStyle], children: [
1772
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1773
- import_react_native3.TouchableOpacity,
1774
- {
1775
- style: pickerStyles.navArrow,
1776
- onPress: handlePrevious,
1777
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.Text, { style: pickerStyles.navArrowText, children: "\u2039" })
1778
- }
1779
- ),
1780
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1781
- import_react_native3.TouchableOpacity,
1782
- {
1783
- style: pickerStyles.pickerBtn,
1784
- onPress: togglePicker,
1785
- children: [
1786
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.Text, { style: [pickerStyles.pickerBtnText, headerTextStyle], children: useShortMonthNames ? `${MONTHS[viewModel.currentMonth]} ${viewModel.currentYear}` : viewModel.monthAndYearText }),
1787
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.Text, { style: pickerStyles.chevron, children: "\u25BC" })
1788
- ]
1789
- }
1790
- ),
1791
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1792
- import_react_native3.TouchableOpacity,
1793
- {
1794
- style: [
1795
- pickerStyles.todayBtn,
1796
- isCurrentMonth && pickerStyles.todayBtnDisabled
1797
- ],
1798
- onPress: handleGoToToday,
1799
- disabled: isCurrentMonth,
1800
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1801
- import_react_native3.Text,
1802
- {
1803
- style: [
1804
- pickerStyles.todayBtnText,
1805
- isCurrentMonth && pickerStyles.todayBtnTextDisabled
1806
- ],
1807
- children: "Today"
1808
- }
1809
- )
1810
- }
1811
- ),
1812
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1813
- import_react_native3.TouchableOpacity,
1814
- {
1815
- style: pickerStyles.navArrow,
1816
- onPress: handleNext,
1817
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.Text, { style: pickerStyles.navArrowText, children: "\u203A" })
1818
- }
1819
- )
1820
- ] }),
1821
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1822
- import_react_native3.Modal,
1823
- {
1824
- visible: pickerOpen,
1825
- transparent: true,
1826
- animationType: "fade",
1827
- onRequestClose: () => setPickerOpen(false),
1828
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1829
- import_react_native3.Pressable,
1830
- {
1831
- style: pickerStyles.modalOverlay,
1832
- onPress: () => setPickerOpen(false),
1833
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1834
- import_react_native3.Pressable,
1835
- {
1836
- style: pickerStyles.pickerDropdown,
1837
- onPress: (e) => e.stopPropagation(),
1838
- children: [
1839
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: pickerStyles.yearRow, children: [
1840
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1841
- import_react_native3.TouchableOpacity,
1842
- {
1843
- style: pickerStyles.yearArrow,
1844
- onPress: handleYearPrev,
1845
- disabled: viewModel.currentYear <= computedMinYear,
1846
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1847
- import_react_native3.Text,
1848
- {
1849
- style: [
1850
- pickerStyles.yearArrowText,
1851
- viewModel.currentYear <= computedMinYear && pickerStyles.yearArrowDisabled
1852
- ],
1853
- children: "\u2039"
1854
- }
1855
- )
1856
- }
1857
- ),
1858
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1859
- import_react_native3.TextInput,
1860
- {
1861
- style: [
1862
- pickerStyles.yearInput,
1863
- !yearInputValid && pickerStyles.yearInputInvalid
1864
- ],
1865
- value: yearInput,
1866
- onChangeText: handleYearInputChange,
1867
- onSubmitEditing: handleYearInputSubmit,
1868
- onBlur: handleYearInputSubmit,
1869
- keyboardType: "number-pad",
1870
- maxLength: 4
1871
- }
1872
- ),
1873
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1874
- import_react_native3.TouchableOpacity,
1875
- {
1876
- style: pickerStyles.yearArrow,
1877
- onPress: handleYearNext,
1878
- disabled: viewModel.currentYear >= computedMaxYear,
1879
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1880
- import_react_native3.Text,
1881
- {
1882
- style: [
1883
- pickerStyles.yearArrowText,
1884
- viewModel.currentYear >= computedMaxYear && pickerStyles.yearArrowDisabled
1885
- ],
1886
- children: "\u203A"
1887
- }
1888
- )
1889
- }
1890
- )
1891
- ] }),
1892
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: pickerStyles.monthGrid, children: (useShortMonthNames ? MONTHS : MONTHS_FULL).map(
1893
- (month, index) => {
1894
- const isSelected = index === viewModel.currentMonth;
1895
- const isCurrent = index === todayMonth && viewModel.currentYear === todayYear;
1896
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1897
- import_react_native3.TouchableOpacity,
1898
- {
1899
- style: [
1900
- pickerStyles.monthBtn,
1901
- isSelected && pickerStyles.monthBtnSelected,
1902
- isCurrent && pickerStyles.monthBtnCurrent
1903
- ],
1904
- onPress: () => handleMonthSelect(index),
1905
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1906
- import_react_native3.Text,
1907
- {
1908
- style: [
1909
- pickerStyles.monthBtnText,
1910
- isSelected && pickerStyles.monthBtnTextSelected
1911
- ],
1912
- children: month
1913
- }
1914
- )
1915
- },
1916
- month
1917
- );
1918
- }
1919
- ) })
1920
- ]
1921
- }
1922
- )
1923
- }
1924
- )
343
+ this.state.selectedDate = null;
344
+ this.state.selectedDayIndex = null;
345
+ this.updateTasks();
346
+ this.notify();
347
+ }
348
+ /**
349
+ * Jump to specific month and year
350
+ */
351
+ jump(year, month) {
352
+ this.state.currentYear = year;
353
+ this.state.currentMonth = month;
354
+ this.state.selectedDate = null;
355
+ this.state.selectedDayIndex = null;
356
+ this.updateTasks();
357
+ this.notify();
358
+ }
359
+ /**
360
+ * Navigate to current month (today)
361
+ */
362
+ goToToday() {
363
+ const today = /* @__PURE__ */ new Date();
364
+ this.state.currentYear = today.getFullYear();
365
+ this.state.currentMonth = today.getMonth();
366
+ this.state.selectedDate = null;
367
+ this.state.selectedDayIndex = null;
368
+ this.updateTasks();
369
+ this.notify();
370
+ }
371
+ /**
372
+ * Check if currently viewing today's month
373
+ */
374
+ isCurrentMonth() {
375
+ const today = /* @__PURE__ */ new Date();
376
+ return this.state.currentYear === today.getFullYear() && this.state.currentMonth === today.getMonth();
377
+ }
378
+ /**
379
+ * Select a specific date
380
+ */
381
+ selectDate(date, dayIndex) {
382
+ this.state.selectedDate = date;
383
+ this.state.selectedDayIndex = dayIndex ?? null;
384
+ this.state.currentDate = date.getDate();
385
+ this.state.currentMonth = date.getMonth();
386
+ this.state.currentYear = date.getFullYear();
387
+ this.updateTasks();
388
+ this.notify();
389
+ }
390
+ /**
391
+ * Update tasks for the currently selected date
392
+ */
393
+ updateTasks() {
394
+ if (!this.state.selectedDate) {
395
+ this.state.tasks = [];
396
+ return;
1925
397
  }
1926
- ),
1927
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: calendarStyles2.table, children: [
1928
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: calendarStyles2.tableHeader, children: viewModel.days.map((day) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: calendarStyles2.tableHeaderCell, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.Text, { style: calendarStyles2.tableHeaderText, children: day.slice(0, 3) }) }, day)) }),
1929
- viewModel.calendarDates.map((week, weekIndex) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: calendarStyles2.tableRow, children: week.map((calendarDate, dayIndex) => {
1930
- const cellClasses = getCellClasses(calendarDate);
1931
- const isToday2 = cellClasses.includes(
1932
- "schedule--current--exam"
1933
- );
1934
- const hasEvents2 = cellClasses.includes("has--event");
1935
- const isOtherMonth = cellClasses.includes("other-month");
1936
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1937
- import_react_native3.TouchableOpacity,
1938
- {
1939
- style: [
1940
- calendarStyles2.tableCell,
1941
- cellStyle,
1942
- isToday2 && calendarStyles2.cellToday,
1943
- hasEvents2 && calendarStyles2.cellWithEvents,
1944
- isOtherMonth && pickerStyles.cellOtherMonth
1945
- ],
1946
- onPress: () => {
1947
- handleDatePress(calendarDate.date, dayIndex);
1948
- },
1949
- children: [
1950
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1951
- import_react_native3.Text,
1952
- {
1953
- style: [
1954
- calendarStyles2.tableCellText,
1955
- cellTextStyle,
1956
- isToday2 && calendarStyles2.cellTodayText,
1957
- isOtherMonth && pickerStyles.cellOtherMonthText
1958
- ],
1959
- children: calendarDate.date.getDate()
1960
- }
1961
- ),
1962
- hasEvents2 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: calendarStyles2.eventIndicator })
1963
- ]
1964
- },
1965
- `${weekIndex}-${dayIndex}`
1966
- );
1967
- }) }, weekIndex))
1968
- ] })
1969
- ] }) }),
1970
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1971
- DatePopup2,
1972
- {
1973
- visible: !!selectedDate,
1974
- selectedDate,
1975
- events: tasks,
1976
- scheduleDay: viewModel.scheduleDay,
1977
- onClose: closePopup,
1978
- onEventClick,
1979
- renderEvent,
1980
- renderNoEvents,
1981
- showCloseButton
398
+ this.state.tasks = getEventsForDate(
399
+ this.config.events,
400
+ this.state.selectedDate
401
+ );
1982
402
  }
1983
- )
1984
- ] });
1985
- };
1986
- var pickerStyles = import_react_native3.StyleSheet.create({
1987
- navHeader: {
1988
- flexDirection: "row",
1989
- alignItems: "center",
1990
- justifyContent: "center",
1991
- paddingVertical: 12,
1992
- paddingHorizontal: 8,
1993
- backgroundColor: "#f8f9fa",
1994
- borderBottomWidth: 2,
1995
- borderBottomColor: "#dee2e6",
1996
- gap: 8
1997
- },
1998
- navArrow: {
1999
- width: 36,
2000
- height: 36,
2001
- borderRadius: 6,
2002
- borderWidth: 1,
2003
- borderColor: "#dee2e6",
2004
- backgroundColor: "#fff",
2005
- alignItems: "center",
2006
- justifyContent: "center"
2007
- },
2008
- navArrowText: {
2009
- fontSize: 20,
2010
- color: "#2c3e50"
2011
- },
2012
- pickerBtn: {
2013
- flexDirection: "row",
2014
- alignItems: "center",
2015
- paddingHorizontal: 16,
2016
- paddingVertical: 8,
2017
- borderRadius: 6,
2018
- borderWidth: 1,
2019
- borderColor: "#dee2e6",
2020
- backgroundColor: "#fff",
2021
- gap: 8
2022
- },
2023
- pickerBtnText: {
2024
- fontSize: 16,
2025
- fontWeight: "600",
2026
- color: "#2c3e50"
2027
- },
2028
- chevron: {
2029
- fontSize: 10,
2030
- color: "#2c3e50"
2031
- },
2032
- todayBtn: {
2033
- paddingHorizontal: 16,
2034
- paddingVertical: 8,
2035
- borderRadius: 6,
2036
- borderWidth: 1,
2037
- borderColor: "#dee2e6",
2038
- backgroundColor: "#fff"
2039
- },
2040
- todayBtnDisabled: {
2041
- opacity: 0.5
2042
- },
2043
- todayBtnText: {
2044
- fontSize: 14,
2045
- fontWeight: "500",
2046
- color: "#2c3e50"
2047
- },
2048
- todayBtnTextDisabled: {
2049
- color: "#9ca3af"
2050
- },
2051
- modalOverlay: {
2052
- flex: 1,
2053
- backgroundColor: "rgba(0, 0, 0, 0.3)",
2054
- justifyContent: "center",
2055
- alignItems: "center"
2056
- },
2057
- pickerDropdown: {
2058
- backgroundColor: "#fff",
2059
- borderRadius: 12,
2060
- padding: 16,
2061
- minWidth: 280,
2062
- shadowColor: "#000",
2063
- shadowOffset: { width: 0, height: 4 },
2064
- shadowOpacity: 0.15,
2065
- shadowRadius: 20,
2066
- elevation: 10
2067
- },
2068
- yearRow: {
2069
- flexDirection: "row",
2070
- alignItems: "center",
2071
- justifyContent: "center",
2072
- gap: 12,
2073
- marginBottom: 16,
2074
- paddingBottom: 12,
2075
- borderBottomWidth: 1,
2076
- borderBottomColor: "#dee2e6"
2077
- },
2078
- yearArrow: {
2079
- width: 32,
2080
- height: 32,
2081
- borderRadius: 6,
2082
- backgroundColor: "#f3f4f6",
2083
- alignItems: "center",
2084
- justifyContent: "center"
2085
- },
2086
- yearArrowText: {
2087
- fontSize: 18,
2088
- fontWeight: "600",
2089
- color: "#2c3e50"
2090
- },
2091
- yearArrowDisabled: {
2092
- opacity: 0.3
2093
- },
2094
- yearInput: {
2095
- width: 80,
2096
- paddingVertical: 6,
2097
- paddingHorizontal: 8,
2098
- borderRadius: 6,
2099
- borderWidth: 1,
2100
- borderColor: "#dee2e6",
2101
- textAlign: "center",
2102
- fontSize: 18,
2103
- fontWeight: "600",
2104
- color: "#2c3e50"
2105
- },
2106
- yearInputInvalid: {
2107
- borderColor: "#ef4444",
2108
- backgroundColor: "#fef2f2"
2109
- },
2110
- monthGrid: {
2111
- flexDirection: "row",
2112
- flexWrap: "wrap",
2113
- gap: 8
2114
- },
2115
- monthBtn: {
2116
- width: "30%",
2117
- paddingVertical: 10,
2118
- borderRadius: 6,
2119
- backgroundColor: "#f9fafb",
2120
- alignItems: "center"
2121
- },
2122
- monthBtnSelected: {
2123
- backgroundColor: "#fc8917"
2124
- },
2125
- monthBtnCurrent: {
2126
- borderWidth: 1,
2127
- borderColor: "#fc8917"
2128
- },
2129
- monthBtnText: {
2130
- fontSize: 14,
2131
- fontWeight: "500",
2132
- color: "#2c3e50"
2133
- },
2134
- monthBtnTextSelected: {
2135
- color: "#fff",
2136
- fontWeight: "600"
2137
- },
2138
- cellOtherMonth: {
2139
- backgroundColor: "#f9fafb"
2140
- },
2141
- cellOtherMonthText: {
2142
- color: "#9ca3af"
403
+ /**
404
+ * Update events configuration
405
+ */
406
+ updateEvents(events) {
407
+ this.config.events = events;
408
+ this.updateTasks();
409
+ this.notify();
410
+ }
411
+ /**
412
+ * Handle date cell click
413
+ */
414
+ handleDateClick(date, dayIndex) {
415
+ this.selectDate(date, dayIndex);
416
+ }
417
+ /**
418
+ * Check if date has events
419
+ */
420
+ hasEventsForDate(date) {
421
+ return getEventsForDate(this.config.events, date).length > 0;
422
+ }
423
+ /**
424
+ * Get events for a specific date
425
+ */
426
+ getEventsForDate(date) {
427
+ return getEventsForDate(this.config.events, date);
428
+ }
429
+ /**
430
+ * Clear selected date
431
+ */
432
+ clearSelection() {
433
+ this.state.selectedDate = null;
434
+ this.state.selectedDayIndex = null;
435
+ this.state.tasks = [];
436
+ this.notify();
437
+ }
438
+ /**
439
+ * Get category color (with custom colors support)
440
+ */
441
+ getCategoryColor(category) {
442
+ if (!category) return "#fc8917";
443
+ return getCategoryColor(category, this.categoryColors);
444
+ }
445
+ /**
446
+ * Update category colors dynamically
447
+ */
448
+ updateCategoryColors(colors) {
449
+ this.categoryColors = mergeCategoryColors(colors);
450
+ this.notify();
451
+ }
452
+ /**
453
+ * Get all category colors
454
+ */
455
+ getCategoryColors() {
456
+ return { ...this.categoryColors };
457
+ }
458
+ /**
459
+ * Register a new category with color
460
+ */
461
+ registerCategory(name, color) {
462
+ this.categoryColors[name] = color;
463
+ this.notify();
464
+ }
465
+ /**
466
+ * Destroy the engine and cleanup listeners
467
+ */
468
+ destroy() {
469
+ this.listeners.clear();
470
+ }
471
+ };
2143
472
  }
2144
473
  });
2145
474
 
2146
- // src/vanilla/index.ts
2147
- var vanilla_exports = {};
2148
- __export(vanilla_exports, {
2149
- CalendarEngine: () => CalendarEngine,
2150
- DAYS: () => DAYS,
2151
- DEFAULT_CATEGORY_COLORS: () => DEFAULT_CATEGORY_COLORS,
2152
- MONTHS: () => MONTHS,
2153
- MONTHS_FULL: () => MONTHS_FULL,
2154
- VanillaCalendar: () => VanillaCalendar,
2155
- createCalendar: () => createCalendar,
2156
- formatAttendees: () => formatAttendees,
2157
- formatDateForDisplay: () => formatDateForDisplay,
2158
- formatTimeRange: () => formatTimeRange,
2159
- generateCalendarDates: () => generateCalendarDates,
2160
- generateYears: () => generateYears,
2161
- getCategoryColor: () => getCategoryColor,
2162
- getCellClasses: () => getCellClasses,
2163
- getDefaultEventColor: () => getDefaultEventColor,
2164
- getEventsForDate: () => getEventsForDate,
2165
- getMonthYearText: () => getMonthYearText,
2166
- getPopupPositionClass: () => getPopupPositionClass,
2167
- hasEvents: () => hasEvents,
2168
- isSameDay: () => isSameDay,
2169
- isToday: () => isToday,
2170
- isValidHexColor: () => isValidHexColor,
2171
- mergeCategoryColors: () => mergeCategoryColors,
2172
- normalizeDate: () => normalizeDate,
2173
- sortEventsByTime: () => sortEventsByTime
475
+ // src/core/index.ts
476
+ var init_core = __esm({
477
+ "src/core/index.ts"() {
478
+ "use strict";
479
+ init_types();
480
+ init_utils();
481
+ init_calendar_engine();
482
+ init_utils();
483
+ }
2174
484
  });
2175
485
 
2176
- // src/vanilla/Calendar.ts
2177
- var VanillaCalendar = class {
2178
- constructor(props) {
2179
- this.unsubscribe = null;
2180
- this.pickerOpen = false;
2181
- this.yearInput = "";
2182
- this.yearInputValid = true;
2183
- this.clickOutsideHandler = null;
2184
- this.delegatedClickHandler = null;
2185
- this.delegatedInputHandler = null;
2186
- this.delegatedBlurHandler = null;
2187
- this.delegatedKeydownHandler = null;
2188
- this.listenersAttached = false;
2189
- this.props = props;
2190
- if (typeof props.container === "string") {
2191
- const element = document.querySelector(props.container);
2192
- if (!element) {
2193
- throw new Error(`Container element "${props.container}" not found`);
2194
- }
2195
- this.container = element;
2196
- } else {
2197
- this.container = props.container;
2198
- }
2199
- this.engine = new CalendarEngine({
2200
- events: props.events,
2201
- initialDate: props.initialDate,
2202
- minYear: props.minYear,
2203
- maxYear: props.maxYear,
2204
- weekStartsOn: props.weekStartsOn
2205
- });
2206
- this.actions = this.engine.getActions();
2207
- this.applyTheme();
2208
- this.init();
2209
- }
2210
- applyTheme() {
2211
- if (this.props.theme) {
2212
- const root = document.documentElement;
2213
- const theme = this.props.theme;
2214
- if (theme.primary)
2215
- root.style.setProperty("--calendar-primary-color", theme.primary);
2216
- if (theme.secondary)
2217
- root.style.setProperty("--calendar-secondary-color", theme.secondary);
2218
- if (theme.tertiary)
2219
- root.style.setProperty("--calendar-tertiary-color", theme.tertiary);
2220
- if (theme.textColor)
2221
- root.style.setProperty("--calendar-text-color", theme.textColor);
2222
- if (theme.textLight)
2223
- root.style.setProperty("--calendar-text-light", theme.textLight);
2224
- if (theme.background)
2225
- root.style.setProperty("--calendar-background", theme.background);
2226
- if (theme.cellHover)
2227
- root.style.setProperty("--calendar-cell-hover", theme.cellHover);
2228
- if (theme.borderColor)
2229
- root.style.setProperty("--calendar-border-color", theme.borderColor);
2230
- if (theme.todayOutline)
2231
- root.style.setProperty("--calendar-today-outline", theme.todayOutline);
2232
- if (theme.selectedBg)
2233
- root.style.setProperty("--calendar-selected-bg", theme.selectedBg);
2234
- if (theme.eventIndicator)
2235
- root.style.setProperty(
2236
- "--calendar-event-indicator",
2237
- theme.eventIndicator
2238
- );
2239
- }
2240
- }
2241
- init() {
2242
- this.container.classList.add("kalendly-calendar");
2243
- if (this.props.className) {
2244
- this.container.classList.add(this.props.className);
2245
- }
2246
- this.unsubscribe = this.engine.subscribe(() => {
2247
- this.render();
2248
- });
2249
- this.render();
486
+ // src/web-components/CalendarElement.ts
487
+ var CalendarElement_exports = {};
488
+ __export(CalendarElement_exports, {
489
+ CalendarElement: () => CalendarElement,
490
+ defineCalendarElement: () => defineCalendarElement
491
+ });
492
+ function defineCalendarElement(tagName = "kal-calendar") {
493
+ if (typeof customElements !== "undefined" && !customElements.get(tagName)) {
494
+ customElements.define(tagName, CalendarElement);
2250
495
  }
2251
- render() {
2252
- const viewModel = this.engine.getViewModel();
2253
- const defaultRenderEvent = (event) => {
2254
- const timeRange = formatTimeRange(event);
2255
- const attendeesList = formatAttendees(event.attendees);
2256
- let borderColor = event.color || "#3b82f6";
2257
- if (event.category) {
2258
- borderColor = this.engine.getCategoryColor(event.category);
496
+ }
497
+ var isSameDay2, CalendarElement;
498
+ var init_CalendarElement = __esm({
499
+ "src/web-components/CalendarElement.ts"() {
500
+ "use strict";
501
+ init_core();
502
+ isSameDay2 = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
503
+ CalendarElement = class extends HTMLElement {
504
+ constructor() {
505
+ super(...arguments);
506
+ this.engine = null;
507
+ this.unsubscribe = null;
508
+ this.actions = null;
509
+ this.pickerOpen = false;
510
+ this.yearInput = "";
511
+ this.yearInputValid = true;
512
+ this.listenersAttached = false;
513
+ this.clickOutsideHandler = null;
514
+ this.delegatedClickHandler = null;
515
+ this.delegatedInputHandler = null;
516
+ this.delegatedBlurHandler = null;
517
+ this.delegatedKeydownHandler = null;
518
+ // Rich property backing fields
519
+ this._events = [];
520
+ this._theme = null;
521
+ this._categoryColors = null;
522
+ this._renderEvent = null;
523
+ this._renderNoEvents = null;
524
+ // Selection state (availability mode — range)
525
+ this._rangeStart = null;
526
+ this._rangeEnd = null;
527
+ this._timeRangeDate = null;
528
+ this._timeRangeStart = null;
529
+ this._timeRangeEnd = null;
530
+ this._timeRangeComplete = false;
2259
531
  }
2260
- const getCategoryLabel = (category) => {
2261
- const labels = {
2262
- work: "WORK",
2263
- personal: "PERSONAL",
2264
- meeting: "MEETING",
2265
- deadline: "DEADLINE",
2266
- appointment: "APPOINTMENT",
2267
- other: "OTHER"
2268
- };
2269
- return category ? labels[category] || category.toUpperCase() : "";
2270
- };
2271
- const getPriorityLabel = (priority) => {
2272
- const labels = {
2273
- high: "HIGH",
2274
- medium: "MEDIUM",
2275
- low: "LOW"
2276
- };
2277
- return priority ? labels[priority] || priority.toUpperCase() : "";
2278
- };
2279
- const getStatusLabel = (status) => {
2280
- const labels = {
2281
- completed: "COMPLETED",
2282
- cancelled: "CANCELLED",
2283
- tentative: "TENTATIVE",
2284
- scheduled: "SCHEDULED"
2285
- };
2286
- return status ? labels[status] || status.toUpperCase() : "";
2287
- };
2288
- return `
532
+ get events() {
533
+ return this._events;
534
+ }
535
+ set events(val) {
536
+ this._events = val;
537
+ if (this.engine) {
538
+ this.engine.updateEvents(val);
539
+ }
540
+ }
541
+ set theme(val) {
542
+ this._theme = val;
543
+ this.applyTheme();
544
+ }
545
+ set categoryColors(val) {
546
+ this._categoryColors = val;
547
+ if (this.engine) {
548
+ this.engine.updateCategoryColors(val);
549
+ }
550
+ }
551
+ set renderEvent(val) {
552
+ this._renderEvent = val;
553
+ if (this.engine) this.render();
554
+ }
555
+ set renderNoEvents(val) {
556
+ this._renderNoEvents = val;
557
+ if (this.engine) this.render();
558
+ }
559
+ get loading() {
560
+ return this.hasAttribute("loading");
561
+ }
562
+ set loading(val) {
563
+ if (val) this.setAttribute("loading", "");
564
+ else this.removeAttribute("loading");
565
+ }
566
+ connectedCallback() {
567
+ this.classList.add("kalendly-calendar");
568
+ this.initEngine();
569
+ this.render();
570
+ }
571
+ disconnectedCallback() {
572
+ this.cleanup();
573
+ this.classList.remove("kalendly-calendar");
574
+ this.innerHTML = "";
575
+ }
576
+ attributeChangedCallback(name, oldVal, newVal) {
577
+ if (oldVal === newVal) return;
578
+ if (name === "loading") {
579
+ this.render();
580
+ return;
581
+ }
582
+ if (name === "selectable" && newVal === null) {
583
+ this._rangeStart = null;
584
+ this._rangeEnd = null;
585
+ this._timeRangeDate = null;
586
+ this._timeRangeStart = null;
587
+ this._timeRangeEnd = null;
588
+ this._timeRangeComplete = false;
589
+ }
590
+ this.reinit();
591
+ }
592
+ get minYear() {
593
+ const val = this.getAttribute("min-year");
594
+ return val ? parseInt(val, 10) : (/* @__PURE__ */ new Date()).getFullYear() - 30;
595
+ }
596
+ get maxYear() {
597
+ const val = this.getAttribute("max-year");
598
+ return val ? parseInt(val, 10) : (/* @__PURE__ */ new Date()).getFullYear() + 10;
599
+ }
600
+ initEngine() {
601
+ const initialDateAttr = this.getAttribute("initial-date");
602
+ const initialDate = initialDateAttr ? new Date(initialDateAttr) : void 0;
603
+ const weekStartsOnAttr = this.getAttribute("week-starts-on");
604
+ const weekStartsOn = weekStartsOnAttr === "1" ? 1 : 0;
605
+ this.engine = new CalendarEngine({
606
+ events: this._events,
607
+ initialDate,
608
+ minYear: this.minYear,
609
+ maxYear: this.maxYear,
610
+ weekStartsOn,
611
+ categoryColors: this._categoryColors ?? void 0
612
+ });
613
+ this.actions = this.engine.getActions();
614
+ this.applyTheme();
615
+ this.unsubscribe = this.engine.subscribe(() => {
616
+ this.render();
617
+ });
618
+ }
619
+ reinit() {
620
+ if (!this.engine) return;
621
+ this.cleanup();
622
+ this.initEngine();
623
+ this.render();
624
+ }
625
+ cleanup() {
626
+ if (this.unsubscribe) {
627
+ this.unsubscribe();
628
+ this.unsubscribe = null;
629
+ }
630
+ if (this.delegatedClickHandler) {
631
+ this.removeEventListener("click", this.delegatedClickHandler);
632
+ this.delegatedClickHandler = null;
633
+ }
634
+ if (this.delegatedInputHandler) {
635
+ this.removeEventListener("input", this.delegatedInputHandler);
636
+ this.delegatedInputHandler = null;
637
+ }
638
+ if (this.delegatedBlurHandler) {
639
+ this.removeEventListener("blur", this.delegatedBlurHandler, true);
640
+ this.delegatedBlurHandler = null;
641
+ }
642
+ if (this.delegatedKeydownHandler) {
643
+ this.removeEventListener("keydown", this.delegatedKeydownHandler);
644
+ this.delegatedKeydownHandler = null;
645
+ }
646
+ if (this.clickOutsideHandler) {
647
+ document.removeEventListener("click", this.clickOutsideHandler);
648
+ this.clickOutsideHandler = null;
649
+ }
650
+ this.listenersAttached = false;
651
+ if (this.engine) {
652
+ this.engine.destroy();
653
+ this.engine = null;
654
+ }
655
+ this.actions = null;
656
+ }
657
+ applyTheme() {
658
+ if (!this._theme) return;
659
+ const root = document.documentElement;
660
+ const t = this._theme;
661
+ if (t.primary)
662
+ root.style.setProperty("--calendar-primary-color", t.primary);
663
+ if (t.secondary)
664
+ root.style.setProperty("--calendar-secondary-color", t.secondary);
665
+ if (t.tertiary)
666
+ root.style.setProperty("--calendar-tertiary-color", t.tertiary);
667
+ if (t.textColor)
668
+ root.style.setProperty("--calendar-text-color", t.textColor);
669
+ if (t.textLight)
670
+ root.style.setProperty("--calendar-text-light", t.textLight);
671
+ if (t.background)
672
+ root.style.setProperty("--calendar-background", t.background);
673
+ if (t.cellHover)
674
+ root.style.setProperty("--calendar-cell-hover", t.cellHover);
675
+ if (t.borderColor)
676
+ root.style.setProperty("--calendar-border-color", t.borderColor);
677
+ if (t.todayOutline)
678
+ root.style.setProperty("--calendar-today-outline", t.todayOutline);
679
+ if (t.selectedBg)
680
+ root.style.setProperty("--calendar-selected-bg", t.selectedBg);
681
+ if (t.eventIndicator)
682
+ root.style.setProperty("--calendar-event-indicator", t.eventIndicator);
683
+ }
684
+ render() {
685
+ if (!this.engine) return;
686
+ const viewModel = this.engine.getViewModel();
687
+ const useShortMonths = this.hasAttribute("use-short-month-names");
688
+ const title = this.getAttribute("title");
689
+ const minYear = this.minYear;
690
+ const maxYear = this.maxYear;
691
+ const availabilityMode = this.getAttribute("availability-mode");
692
+ const selectable = this.hasAttribute("selectable");
693
+ const isLoading = this.loading;
694
+ const today = /* @__PURE__ */ new Date();
695
+ const todayMonth = today.getMonth();
696
+ const todayYear = today.getFullYear();
697
+ const isCurrentMonth = viewModel.currentYear === todayYear && viewModel.currentMonth === todayMonth;
698
+ const showScrollHint = viewModel.tasks.length > 3;
699
+ const defaultRenderEvent = (event) => {
700
+ const timeRange = formatTimeRange(event);
701
+ const attendeesList = formatAttendees(event.attendees);
702
+ let borderColor = event.color || "#3b82f6";
703
+ if (event.category) {
704
+ borderColor = this.engine.getCategoryColor(event.category);
705
+ }
706
+ const getCategoryLabel = (category) => {
707
+ const labels = {
708
+ work: "WORK",
709
+ personal: "PERSONAL",
710
+ meeting: "MEETING",
711
+ deadline: "DEADLINE",
712
+ appointment: "APPOINTMENT",
713
+ other: "OTHER"
714
+ };
715
+ return category ? labels[category] || category.toUpperCase() : "";
716
+ };
717
+ const getPriorityLabel = (priority) => {
718
+ const labels = {
719
+ high: "HIGH",
720
+ medium: "MEDIUM",
721
+ low: "LOW"
722
+ };
723
+ return priority ? labels[priority] || priority.toUpperCase() : "";
724
+ };
725
+ const getStatusLabel = (status) => {
726
+ const labels = {
727
+ completed: "COMPLETED",
728
+ cancelled: "CANCELLED",
729
+ tentative: "TENTATIVE",
730
+ scheduled: "SCHEDULED"
731
+ };
732
+ return status ? labels[status] || status.toUpperCase() : "";
733
+ };
734
+ return `
2289
735
  <div class="event-card" style="border-left-color: ${borderColor}">
2290
736
  <div class="event-header">
2291
737
  <div class="event-title">${event.name}</div>
@@ -2295,46 +741,46 @@ var VanillaCalendar = class {
2295
741
  ${event.status && event.status !== "scheduled" ? `<span class="badge status-${event.status}">${getStatusLabel(event.status)}</span>` : ""}
2296
742
  </div>
2297
743
  </div>
2298
-
744
+
2299
745
  ${timeRange ? `
2300
746
  <div class="event-time">
2301
747
  <span class="event-time-label">Time:</span>
2302
748
  <span class="event-time-value">${timeRange}</span>
2303
749
  </div>
2304
750
  ` : ""}
2305
-
751
+
2306
752
  ${event.description ? `
2307
753
  <div class="event-description">${event.description}</div>
2308
754
  ` : ""}
2309
-
755
+
2310
756
  ${event.location ? `
2311
757
  <div class="event-time">
2312
758
  <span class="event-time-label">Location:</span>
2313
759
  <span class="event-time-value">${event.location}</span>
2314
760
  </div>
2315
761
  ` : ""}
2316
-
762
+
2317
763
  ${attendeesList ? `
2318
764
  <div class="event-time">
2319
765
  <span class="event-time-label">Attendees:</span>
2320
766
  <span class="event-time-value">${attendeesList}</span>
2321
767
  </div>
2322
768
  ` : ""}
2323
-
769
+
2324
770
  ${event.organizer ? `
2325
771
  <div class="event-time">
2326
772
  <span class="event-time-label">Organizer:</span>
2327
773
  <span class="event-time-value">${event.organizer}</span>
2328
774
  </div>
2329
775
  ` : ""}
2330
-
776
+
2331
777
  ${event.notes ? `
2332
778
  <div class="event-time">
2333
779
  <span class="event-time-label">Notes:</span>
2334
780
  <span class="event-time-value">${event.notes}</span>
2335
781
  </div>
2336
782
  ` : ""}
2337
-
783
+
2338
784
  ${event.url ? `
2339
785
  <div class="event-time">
2340
786
  <a href="${event.url}" target="_blank" rel="noopener noreferrer" class="event-link">
@@ -2342,7 +788,7 @@ var VanillaCalendar = class {
2342
788
  </a>
2343
789
  </div>
2344
790
  ` : ""}
2345
-
791
+
2346
792
  ${event.tags && event.tags.length > 0 ? `
2347
793
  <div class="event-tags">
2348
794
  ${event.tags.map((tag) => `<span class="event-tag">${tag}</span>`).join("")}
@@ -2350,27 +796,50 @@ var VanillaCalendar = class {
2350
796
  ` : ""}
2351
797
  </div>
2352
798
  `;
2353
- };
2354
- const defaultRenderNoEvents = () => '<div class="no-events-message">No events scheduled for this day.</div>';
2355
- const renderEvent = this.props.renderEvent || defaultRenderEvent;
2356
- const renderNoEvents = this.props.renderNoEvents || defaultRenderNoEvents;
2357
- const showScrollHint = viewModel.tasks.length > 3;
2358
- const today = /* @__PURE__ */ new Date();
2359
- const todayMonth = today.getMonth();
2360
- const todayYear = today.getFullYear();
2361
- const minYear = this.props.minYear ?? todayYear - 30;
2362
- const maxYear = this.props.maxYear ?? todayYear + 10;
2363
- const isCurrentMonth = viewModel.currentYear === todayYear && viewModel.currentMonth === todayMonth;
2364
- const html = `
2365
- ${this.props.title ? `
2366
- <div class="page--title">
2367
- <h1>${this.props.title}</h1>
2368
- </div>
799
+ };
800
+ const defaultRenderNoEvents = () => '<div class="no-events-message">No events scheduled for this day.</div>';
801
+ const renderEvent = this._renderEvent || defaultRenderEvent;
802
+ const renderNoEvents = this._renderNoEvents || defaultRenderNoEvents;
803
+ const renderTimeGrid = (events, date) => {
804
+ const isHourBooked = (hour) => events.some((event) => {
805
+ if (!event.startTime || !event.endTime) return true;
806
+ const [startH] = event.startTime.split(":").map(Number);
807
+ const [endH] = event.endTime.split(":").map(Number);
808
+ return hour >= startH && hour < endH;
809
+ });
810
+ const slots = Array.from({ length: 24 }, (_, hour) => {
811
+ const startTime = `${String(hour).padStart(2, "0")}:00`;
812
+ const endTime = `${String(hour + 1).padStart(2, "0")}:00`;
813
+ const booked = isHourBooked(hour);
814
+ const inTimeRange = !booked && selectable && this._timeRangeStart !== null && this._timeRangeEnd !== null && isSameDay2(this._timeRangeDate, date) && startTime >= this._timeRangeStart && endTime <= this._timeRangeEnd;
815
+ const isRangeStart = inTimeRange && startTime === this._timeRangeStart;
816
+ const isRangeEnd = inTimeRange && endTime === this._timeRangeEnd;
817
+ const isInRange = inTimeRange && !isRangeStart && !isRangeEnd;
818
+ const slotClasses = [
819
+ "time-grid__slot",
820
+ booked ? "time-grid__slot--booked" : "time-grid__slot--free",
821
+ isRangeStart ? "time-grid__slot--range-start" : "",
822
+ isRangeEnd ? "time-grid__slot--range-end" : "",
823
+ isInRange ? "time-grid__slot--in-range" : ""
824
+ ].filter(Boolean).join(" ");
825
+ const slotAttrs = !booked && selectable ? `data-action="select-slot" data-start-time="${startTime}" data-end-time="${endTime}" data-date="${date.toISOString()}"` : "";
826
+ return `
827
+ <div class="${slotClasses}" ${slotAttrs}>
828
+ <span class="time-grid__label">${startTime}</span>
829
+ <span class="time-grid__status">${booked ? "Booked" : "Available"}</span>
830
+ </div>`;
831
+ });
832
+ return `<div class="time-grid">${slots.join("")}</div>`;
833
+ };
834
+ const html = `
835
+ ${title ? `
836
+ <div class="page--title">
837
+ <h1>${title}</h1>
838
+ </div>
2369
839
  ` : ""}
2370
840
 
2371
841
  <div class="calendar--content">
2372
842
  <div class="calendar--card">
2373
- <!-- Navigation Header -->
2374
843
  <div class="calendar--nav-header">
2375
844
  <button type="button" class="calendar--nav-arrow" data-action="previous" aria-label="Previous month">
2376
845
  &#8249;
@@ -2384,7 +853,7 @@ var VanillaCalendar = class {
2384
853
  aria-expanded="${this.pickerOpen ? "true" : "false"}"
2385
854
  aria-haspopup="true"
2386
855
  >
2387
- ${this.props.useShortMonthNames ? `${MONTHS[viewModel.currentMonth]} ${viewModel.currentYear}` : viewModel.monthAndYearText}
856
+ ${useShortMonths ? `${MONTHS[viewModel.currentMonth]} ${viewModel.currentYear}` : viewModel.monthAndYearText}
2388
857
  <span class="calendar--picker-chevron">&#9662;</span>
2389
858
  </button>
2390
859
 
@@ -2415,10 +884,10 @@ var VanillaCalendar = class {
2415
884
  </div>
2416
885
 
2417
886
  <div class="calendar--picker-months">
2418
- ${(this.props.useShortMonthNames ? MONTHS : MONTHS_FULL).map((month, index) => {
2419
- const isSelected = index === viewModel.currentMonth;
2420
- const isCurrent = index === todayMonth && viewModel.currentYear === todayYear;
2421
- return `
887
+ ${(useShortMonths ? MONTHS : MONTHS_FULL).map((month, index) => {
888
+ const isSelected = index === viewModel.currentMonth;
889
+ const isCurrent = index === todayMonth && viewModel.currentYear === todayYear;
890
+ return `
2422
891
  <button
2423
892
  type="button"
2424
893
  class="calendar--picker-month${isSelected ? " selected" : ""}${isCurrent ? " current-month" : ""}"
@@ -2426,7 +895,7 @@ var VanillaCalendar = class {
2426
895
  data-month="${index}"
2427
896
  >${month}</button>
2428
897
  `;
2429
- }).join("")}
898
+ }).join("")}
2430
899
  </div>
2431
900
  </div>
2432
901
  ` : ""}
@@ -2451,30 +920,52 @@ var VanillaCalendar = class {
2451
920
  </tr>
2452
921
  </thead>
2453
922
  <tbody data-calendar-body>
2454
- ${viewModel.calendarDates.map(
2455
- (week) => `
2456
- <tr>
2457
- ${week.map((calendarDate, dayIndex) => {
2458
- const classes = getCellClasses(calendarDate);
2459
- const dateString = calendarDate.date.toISOString();
2460
- return `
2461
- <td
2462
- class="${classes.join(" ")}"
2463
- data-date="${dateString}"
2464
- data-day-index="${dayIndex}"
2465
- data-clickable="true"
2466
- >
2467
- ${calendarDate.date.getDate()}
2468
- </td>
2469
- `;
2470
- }).join("")}
2471
- </tr>
2472
- `
2473
- ).join("")}
923
+ ${isLoading ? Array.from(
924
+ { length: 6 },
925
+ () => `<tr>${Array.from(
926
+ { length: 7 },
927
+ () => `<td class="calendar--skeleton" aria-hidden="true"></td>`
928
+ ).join("")}</tr>`
929
+ ).join("") : viewModel.calendarDates.map(
930
+ (week) => `
931
+ <tr>
932
+ ${week.map((calendarDate, dayIndex) => {
933
+ const classes = getCellClasses(calendarDate);
934
+ if (availabilityMode && calendarDate.isCurrentMonth) {
935
+ classes.push(
936
+ calendarDate.hasEvents ? "availability--booked" : "availability--free"
937
+ );
938
+ }
939
+ if (availabilityMode === "day" && selectable && calendarDate.isCurrentMonth) {
940
+ const d = calendarDate.date;
941
+ if (this._rangeStart && isSameDay2(d, this._rangeStart))
942
+ classes.push("availability--range-start");
943
+ if (this._rangeEnd && isSameDay2(d, this._rangeEnd))
944
+ classes.push("availability--range-end");
945
+ if (this._rangeStart && this._rangeEnd) {
946
+ if (d > this._rangeStart && d < this._rangeEnd)
947
+ classes.push("availability--in-range");
948
+ }
949
+ }
950
+ const dateString = calendarDate.date.toISOString();
951
+ return `
952
+ <td
953
+ class="${classes.join(" ")}"
954
+ data-date="${dateString}"
955
+ data-day-index="${dayIndex}"
956
+ data-clickable="true"
957
+ >
958
+ ${calendarDate.date.getDate()}
959
+ </td>
960
+ `;
961
+ }).join("")}
962
+ </tr>
963
+ `
964
+ ).join("")}
2474
965
  </tbody>
2475
966
  </table>
2476
967
 
2477
- ${viewModel.selectedDate ? `
968
+ ${!isLoading && availabilityMode !== "day" && viewModel.selectedDate ? `
2478
969
  <div class="date-popup ${viewModel.popupPositionClass}">
2479
970
  <div class="popup-header">
2480
971
  <h2>${viewModel.scheduleDay}</h2>
@@ -2488,261 +979,360 @@ var VanillaCalendar = class {
2488
979
  ` : ""}
2489
980
 
2490
981
  <div class="events-container">
2491
- ${viewModel.tasks.length > 0 ? `
2492
- ${viewModel.tasks.map((event) => renderEvent(event)).join("")}
2493
- ` : renderNoEvents()}
982
+ ${availabilityMode === "time" ? renderTimeGrid(viewModel.tasks, viewModel.selectedDate) : viewModel.tasks.length > 0 ? viewModel.tasks.map((event) => renderEvent(event)).join("") : renderNoEvents()}
2494
983
  </div>
2495
984
  </div>
2496
985
  ` : ""}
2497
986
  </div>
2498
987
  </div>
2499
988
  `;
2500
- this.container.innerHTML = html;
2501
- this.attachEventListeners();
2502
- }
2503
- attachEventListeners() {
2504
- if (this.listenersAttached) return;
2505
- this.listenersAttached = true;
2506
- const today = /* @__PURE__ */ new Date();
2507
- const todayYear = today.getFullYear();
2508
- const todayMonth = today.getMonth();
2509
- const minYear = this.props.minYear ?? todayYear - 30;
2510
- const maxYear = this.props.maxYear ?? todayYear + 10;
2511
- this.delegatedClickHandler = (e) => {
2512
- const target = e.target;
2513
- const cell = target.closest('td[data-clickable="true"]');
2514
- if (cell && cell.dataset.date) {
2515
- e.stopPropagation();
2516
- const date = new Date(cell.dataset.date);
2517
- const dayIndex = parseInt(cell.dataset.dayIndex || "0");
2518
- this.engine.handleDateClick(date, dayIndex);
2519
- this.container.dispatchEvent(
2520
- new CustomEvent("dateSelect", { detail: { date, dayIndex } })
2521
- );
2522
- return;
989
+ this.innerHTML = html;
990
+ this.attachEventListeners();
2523
991
  }
2524
- const actionEl = target.closest("[data-action]");
2525
- if (!actionEl) return;
2526
- const action = actionEl.dataset.action;
2527
- e.stopPropagation();
2528
- switch (action) {
2529
- case "previous":
2530
- this.actions.previous();
2531
- this.dispatchMonthChange();
2532
- break;
2533
- case "next":
2534
- this.actions.next();
2535
- this.dispatchMonthChange();
2536
- break;
2537
- case "today":
2538
- this.actions.goToToday();
2539
- this.pickerOpen = false;
2540
- this.container.dispatchEvent(
2541
- new CustomEvent("monthChange", {
2542
- detail: { year: todayYear, month: todayMonth }
2543
- })
2544
- );
2545
- break;
2546
- case "toggle-picker":
2547
- this.pickerOpen = !this.pickerOpen;
2548
- if (this.pickerOpen) {
2549
- this.yearInput = String(this.engine.getViewModel().currentYear);
2550
- this.yearInputValid = true;
992
+ attachEventListeners() {
993
+ if (this.listenersAttached) return;
994
+ this.listenersAttached = true;
995
+ const minYear = this.minYear;
996
+ const maxYear = this.maxYear;
997
+ this.delegatedClickHandler = (e) => {
998
+ const target = e.target;
999
+ const cell = target.closest('td[data-clickable="true"]');
1000
+ if (cell && cell.dataset.date) {
1001
+ e.stopPropagation();
1002
+ const date = new Date(cell.dataset.date);
1003
+ const dayIndex = parseInt(cell.dataset.dayIndex || "0");
1004
+ const availMode = this.getAttribute("availability-mode");
1005
+ const isSelectable = this.hasAttribute("selectable");
1006
+ if (availMode === "day" && isSelectable) {
1007
+ const isBooked = cell.classList.contains("availability--booked");
1008
+ if (!isBooked) {
1009
+ let startDate;
1010
+ let endDate;
1011
+ if (this._rangeEnd !== null) {
1012
+ this._rangeStart = date;
1013
+ this._rangeEnd = null;
1014
+ startDate = date;
1015
+ endDate = date;
1016
+ } else if (this._rangeStart === null) {
1017
+ this._rangeStart = date;
1018
+ startDate = date;
1019
+ endDate = date;
1020
+ } else {
1021
+ const [s, e2] = this._rangeStart <= date ? [this._rangeStart, date] : [date, this._rangeStart];
1022
+ const cursor = new Date(s);
1023
+ cursor.setDate(cursor.getDate() + 1);
1024
+ let blocked = false;
1025
+ while (cursor < e2) {
1026
+ if (this.engine.getEventsForDate(cursor).length > 0) {
1027
+ blocked = true;
1028
+ break;
1029
+ }
1030
+ cursor.setDate(cursor.getDate() + 1);
1031
+ }
1032
+ if (blocked) {
1033
+ this._rangeStart = date;
1034
+ this._rangeEnd = null;
1035
+ } else {
1036
+ this._rangeStart = s;
1037
+ this._rangeEnd = e2;
1038
+ }
1039
+ startDate = this._rangeStart;
1040
+ endDate = this._rangeEnd ?? this._rangeStart;
1041
+ }
1042
+ this.dispatchEvent(
1043
+ new CustomEvent("cal-availability-select", {
1044
+ bubbles: true,
1045
+ composed: true,
1046
+ detail: { startDate, endDate }
1047
+ })
1048
+ );
1049
+ }
1050
+ }
1051
+ if (availMode === "time") {
1052
+ this._timeRangeDate = null;
1053
+ this._timeRangeStart = null;
1054
+ this._timeRangeEnd = null;
1055
+ this._timeRangeComplete = false;
1056
+ }
1057
+ this.engine.handleDateClick(date, dayIndex);
1058
+ this.dispatchEvent(
1059
+ new CustomEvent("cal-date-select", {
1060
+ bubbles: true,
1061
+ composed: true,
1062
+ detail: { date, events: this.engine.getEventsForDate(date) }
1063
+ })
1064
+ );
1065
+ return;
2551
1066
  }
2552
- this.render();
2553
- break;
2554
- case "year-prev": {
2555
- const vm = this.engine.getViewModel();
2556
- if (vm.currentYear > minYear) {
2557
- const newYear = vm.currentYear - 1;
2558
- this.yearInput = String(newYear);
2559
- this.updatePickerYear(newYear);
2560
- this.actions.jump(newYear, vm.currentMonth);
2561
- this.dispatchMonthChange();
1067
+ const actionEl = target.closest("[data-action]");
1068
+ if (!actionEl) return;
1069
+ const action = actionEl.dataset.action;
1070
+ e.stopPropagation();
1071
+ switch (action) {
1072
+ case "previous": {
1073
+ const vm = this.engine.getViewModel();
1074
+ const tMonth = vm.currentMonth === 0 ? 11 : vm.currentMonth - 1;
1075
+ const tYear = vm.currentMonth === 0 ? vm.currentYear - 1 : vm.currentYear;
1076
+ this.dispatchMonthChange(tYear, tMonth);
1077
+ this.actions.previous();
1078
+ break;
1079
+ }
1080
+ case "next": {
1081
+ const vm = this.engine.getViewModel();
1082
+ const tMonth = vm.currentMonth === 11 ? 0 : vm.currentMonth + 1;
1083
+ const tYear = vm.currentMonth === 11 ? vm.currentYear + 1 : vm.currentYear;
1084
+ this.dispatchMonthChange(tYear, tMonth);
1085
+ this.actions.next();
1086
+ break;
1087
+ }
1088
+ case "today": {
1089
+ const t = /* @__PURE__ */ new Date();
1090
+ this.dispatchMonthChange(t.getFullYear(), t.getMonth());
1091
+ this.actions.goToToday();
1092
+ this.pickerOpen = false;
1093
+ break;
1094
+ }
1095
+ case "toggle-picker":
1096
+ this.pickerOpen = !this.pickerOpen;
1097
+ if (this.pickerOpen) {
1098
+ this.yearInput = String(this.engine.getViewModel().currentYear);
1099
+ this.yearInputValid = true;
1100
+ }
1101
+ this.render();
1102
+ break;
1103
+ case "year-prev": {
1104
+ const vm = this.engine.getViewModel();
1105
+ if (vm.currentYear > minYear) {
1106
+ const newYear = vm.currentYear - 1;
1107
+ this.yearInput = String(newYear);
1108
+ this.updatePickerYear(newYear);
1109
+ this.dispatchMonthChange(newYear, vm.currentMonth);
1110
+ this.actions.jump(newYear, vm.currentMonth);
1111
+ }
1112
+ break;
1113
+ }
1114
+ case "year-next": {
1115
+ const vm = this.engine.getViewModel();
1116
+ if (vm.currentYear < maxYear) {
1117
+ const newYear = vm.currentYear + 1;
1118
+ this.yearInput = String(newYear);
1119
+ this.updatePickerYear(newYear);
1120
+ this.dispatchMonthChange(newYear, vm.currentMonth);
1121
+ this.actions.jump(newYear, vm.currentMonth);
1122
+ }
1123
+ break;
1124
+ }
1125
+ case "select-month": {
1126
+ const month = parseInt(actionEl.dataset.month || "0", 10);
1127
+ const year = this.engine.getViewModel().currentYear;
1128
+ this.dispatchMonthChange(year, month);
1129
+ this.actions.jump(year, month);
1130
+ this.pickerOpen = false;
1131
+ break;
1132
+ }
1133
+ case "close-popup":
1134
+ this._timeRangeDate = null;
1135
+ this._timeRangeStart = null;
1136
+ this._timeRangeEnd = null;
1137
+ this._timeRangeComplete = false;
1138
+ this.engine.clearSelection();
1139
+ break;
1140
+ case "select-slot": {
1141
+ const slotStart = actionEl.dataset.startTime;
1142
+ const slotEnd = actionEl.dataset.endTime;
1143
+ const slotDate = new Date(actionEl.dataset.date);
1144
+ if (this._timeRangeComplete) {
1145
+ this._timeRangeDate = slotDate;
1146
+ this._timeRangeStart = slotStart;
1147
+ this._timeRangeEnd = slotEnd;
1148
+ this._timeRangeComplete = false;
1149
+ } else if (this._timeRangeStart === null) {
1150
+ this._timeRangeDate = slotDate;
1151
+ this._timeRangeStart = slotStart;
1152
+ this._timeRangeEnd = slotEnd;
1153
+ } else {
1154
+ const newStart = slotStart < this._timeRangeStart ? slotStart : this._timeRangeStart;
1155
+ const newEnd = slotEnd > this._timeRangeEnd ? slotEnd : this._timeRangeEnd;
1156
+ this._timeRangeStart = newStart;
1157
+ this._timeRangeEnd = newEnd;
1158
+ this._timeRangeComplete = true;
1159
+ }
1160
+ this.render();
1161
+ this.dispatchEvent(
1162
+ new CustomEvent("cal-availability-select", {
1163
+ bubbles: true,
1164
+ composed: true,
1165
+ detail: {
1166
+ date: this._timeRangeDate,
1167
+ startTime: this._timeRangeStart,
1168
+ endTime: this._timeRangeEnd
1169
+ }
1170
+ })
1171
+ );
1172
+ break;
1173
+ }
2562
1174
  }
2563
- break;
2564
- }
2565
- case "year-next": {
1175
+ };
1176
+ this.delegatedInputHandler = (e) => {
1177
+ const target = e.target;
1178
+ if (!target.matches("[data-year-input]")) return;
1179
+ const value = target.value;
1180
+ if (value === "" || /^\d+$/.test(value)) {
1181
+ this.yearInput = value;
1182
+ const year = parseInt(value, 10);
1183
+ this.yearInputValid = value === "" || year >= minYear && year <= maxYear;
1184
+ target.classList.toggle("invalid", !this.yearInputValid);
1185
+ }
1186
+ };
1187
+ this.delegatedBlurHandler = (e) => {
1188
+ const target = e.target;
1189
+ if (!target.matches("[data-year-input]")) return;
1190
+ const year = parseInt(this.yearInput, 10);
2566
1191
  const vm = this.engine.getViewModel();
2567
- if (vm.currentYear < maxYear) {
2568
- const newYear = vm.currentYear + 1;
2569
- this.yearInput = String(newYear);
2570
- this.updatePickerYear(newYear);
2571
- this.actions.jump(newYear, vm.currentMonth);
2572
- this.dispatchMonthChange();
1192
+ if (isNaN(year) || year < minYear || year > maxYear) {
1193
+ this.yearInput = String(vm.currentYear);
1194
+ this.yearInputValid = true;
1195
+ target.value = this.yearInput;
1196
+ target.classList.remove("invalid");
1197
+ } else if (year !== vm.currentYear) {
1198
+ this.dispatchMonthChange(year, vm.currentMonth);
1199
+ this.actions.jump(year, vm.currentMonth);
2573
1200
  }
2574
- break;
2575
- }
2576
- case "select-month": {
2577
- const month = parseInt(actionEl.dataset.month || "0", 10);
2578
- this.actions.jump(this.engine.getViewModel().currentYear, month);
2579
- this.pickerOpen = false;
2580
- this.dispatchMonthChange();
2581
- break;
2582
- }
2583
- case "close-popup":
2584
- this.engine.clearSelection();
2585
- break;
1201
+ };
1202
+ this.delegatedKeydownHandler = (e) => {
1203
+ const target = e.target;
1204
+ if (!target.matches("[data-year-input]")) return;
1205
+ if (e.key === "Enter") target.blur();
1206
+ };
1207
+ this.addEventListener("click", this.delegatedClickHandler);
1208
+ this.addEventListener("input", this.delegatedInputHandler);
1209
+ this.addEventListener("blur", this.delegatedBlurHandler, true);
1210
+ this.addEventListener("keydown", this.delegatedKeydownHandler);
1211
+ this.clickOutsideHandler = (e) => {
1212
+ const target = e.target;
1213
+ if (this.contains(target)) return;
1214
+ if (this.pickerOpen) {
1215
+ this.pickerOpen = false;
1216
+ this.render();
1217
+ return;
1218
+ }
1219
+ const popup = this.querySelector(".date-popup");
1220
+ if (popup) {
1221
+ this.engine.clearSelection();
1222
+ }
1223
+ };
1224
+ document.addEventListener("click", this.clickOutsideHandler);
2586
1225
  }
2587
- };
2588
- this.delegatedInputHandler = (e) => {
2589
- const target = e.target;
2590
- if (!target.matches("[data-year-input]")) return;
2591
- const value = target.value;
2592
- if (value === "" || /^\d+$/.test(value)) {
2593
- this.yearInput = value;
2594
- const year = parseInt(value, 10);
2595
- this.yearInputValid = value === "" || year >= minYear && year <= maxYear;
2596
- target.classList.toggle("invalid", !this.yearInputValid);
1226
+ updatePickerYear(year) {
1227
+ const input = this.querySelector("[data-year-input]");
1228
+ if (input) input.value = String(year);
1229
+ const prevBtn = this.querySelector(
1230
+ '[data-action="year-prev"]'
1231
+ );
1232
+ const nextBtn = this.querySelector(
1233
+ '[data-action="year-next"]'
1234
+ );
1235
+ if (prevBtn) prevBtn.disabled = year <= this.minYear;
1236
+ if (nextBtn) nextBtn.disabled = year >= this.maxYear;
2597
1237
  }
2598
- };
2599
- this.delegatedBlurHandler = (e) => {
2600
- const target = e.target;
2601
- if (!target.matches("[data-year-input]")) return;
2602
- const year = parseInt(this.yearInput, 10);
2603
- const vm = this.engine.getViewModel();
2604
- if (isNaN(year) || year < minYear || year > maxYear) {
2605
- this.yearInput = String(vm.currentYear);
2606
- this.yearInputValid = true;
2607
- target.value = this.yearInput;
2608
- target.classList.remove("invalid");
2609
- } else if (year !== vm.currentYear) {
2610
- this.actions.jump(year, vm.currentMonth);
2611
- this.dispatchMonthChange();
1238
+ dispatchMonthChange(year, month) {
1239
+ if (!this.engine) return;
1240
+ const vm = this.engine.getViewModel();
1241
+ this.dispatchEvent(
1242
+ new CustomEvent("cal-month-change", {
1243
+ bubbles: true,
1244
+ composed: true,
1245
+ detail: {
1246
+ year: year ?? vm.currentYear,
1247
+ month: month ?? vm.currentMonth
1248
+ }
1249
+ })
1250
+ );
2612
1251
  }
2613
- };
2614
- this.delegatedKeydownHandler = (e) => {
2615
- const target = e.target;
2616
- if (!target.matches("[data-year-input]")) return;
2617
- if (e.key === "Enter") {
2618
- target.blur();
1252
+ // Public API
1253
+ updateEvents(events) {
1254
+ this.events = events;
2619
1255
  }
2620
- };
2621
- this.container.addEventListener("click", this.delegatedClickHandler);
2622
- this.container.addEventListener("input", this.delegatedInputHandler);
2623
- this.container.addEventListener("blur", this.delegatedBlurHandler, true);
2624
- this.container.addEventListener("keydown", this.delegatedKeydownHandler);
2625
- this.clickOutsideHandler = (e) => {
2626
- const target = e.target;
2627
- if (this.container.contains(target)) return;
2628
- if (this.pickerOpen) {
2629
- this.pickerOpen = false;
2630
- this.render();
2631
- return;
1256
+ updateTheme(theme) {
1257
+ this.theme = theme;
1258
+ }
1259
+ getCurrentDate() {
1260
+ return this.engine?.getViewModel().selectedDate ?? null;
1261
+ }
1262
+ goToDate(date) {
1263
+ const year = date.getFullYear();
1264
+ const month = date.getMonth();
1265
+ this.dispatchMonthChange(year, month);
1266
+ this.actions?.jump(year, month);
2632
1267
  }
2633
- const popup = this.container.querySelector(".date-popup");
2634
- if (popup) {
2635
- this.engine.clearSelection();
1268
+ getEngine() {
1269
+ if (!this.engine)
1270
+ throw new Error("CalendarElement is not connected to the DOM");
1271
+ return this.engine;
2636
1272
  }
2637
1273
  };
2638
- document.addEventListener("click", this.clickOutsideHandler);
2639
- }
2640
- // Targeted update for picker year - avoids full DOM rebuild
2641
- updatePickerYear(year) {
2642
- const input = this.container.querySelector(
2643
- "[data-year-input]"
2644
- );
2645
- if (input) {
2646
- input.value = String(year);
2647
- }
2648
- const today = /* @__PURE__ */ new Date();
2649
- const todayYear = today.getFullYear();
2650
- const minYear = this.props.minYear ?? todayYear - 30;
2651
- const maxYear = this.props.maxYear ?? todayYear + 10;
2652
- const prevBtn = this.container.querySelector(
2653
- '[data-action="year-prev"]'
2654
- );
2655
- const nextBtn = this.container.querySelector(
2656
- '[data-action="year-next"]'
2657
- );
2658
- if (prevBtn) prevBtn.disabled = year <= minYear;
2659
- if (nextBtn) nextBtn.disabled = year >= maxYear;
2660
- }
2661
- dispatchMonthChange() {
2662
- const vm = this.engine.getViewModel();
2663
- this.container.dispatchEvent(
2664
- new CustomEvent("monthChange", {
2665
- detail: { year: vm.currentYear, month: vm.currentMonth }
2666
- })
2667
- );
2668
- }
2669
- updateEvents(events) {
2670
- this.engine.updateEvents(events);
2671
- }
2672
- updateTheme(theme) {
2673
- this.props.theme = theme;
2674
- this.applyTheme();
2675
- }
2676
- getCurrentDate() {
2677
- return this.engine.getViewModel().selectedDate;
1274
+ CalendarElement.observedAttributes = [
1275
+ "initial-date",
1276
+ "min-year",
1277
+ "max-year",
1278
+ "week-starts-on",
1279
+ "title",
1280
+ "use-short-month-names",
1281
+ "availability-mode",
1282
+ "selectable",
1283
+ "loading"
1284
+ ];
2678
1285
  }
2679
- goToDate(date) {
2680
- this.actions.jump(date.getFullYear(), date.getMonth());
2681
- }
2682
- getEngine() {
2683
- return this.engine;
2684
- }
2685
- destroy() {
2686
- if (this.unsubscribe) {
2687
- this.unsubscribe();
2688
- this.unsubscribe = null;
2689
- }
2690
- if (this.delegatedClickHandler) {
2691
- this.container.removeEventListener("click", this.delegatedClickHandler);
2692
- this.delegatedClickHandler = null;
2693
- }
2694
- if (this.delegatedInputHandler) {
2695
- this.container.removeEventListener("input", this.delegatedInputHandler);
2696
- this.delegatedInputHandler = null;
2697
- }
2698
- if (this.delegatedBlurHandler) {
2699
- this.container.removeEventListener(
2700
- "blur",
2701
- this.delegatedBlurHandler,
2702
- true
2703
- );
2704
- this.delegatedBlurHandler = null;
2705
- }
2706
- if (this.delegatedKeydownHandler) {
2707
- this.container.removeEventListener(
2708
- "keydown",
2709
- this.delegatedKeydownHandler
2710
- );
2711
- this.delegatedKeydownHandler = null;
2712
- }
2713
- if (this.clickOutsideHandler) {
2714
- document.removeEventListener("click", this.clickOutsideHandler);
2715
- this.clickOutsideHandler = null;
2716
- }
2717
- this.listenersAttached = false;
2718
- this.engine.destroy();
2719
- this.container.innerHTML = "";
2720
- this.container.classList.remove("kalendly-calendar");
2721
- if (this.props.className) {
2722
- this.container.classList.remove(this.props.className);
1286
+ });
1287
+
1288
+ // src/web-components/index.ts
1289
+ var web_components_exports = {};
1290
+ __export(web_components_exports, {
1291
+ CalendarElement: () => CalendarElement,
1292
+ CalendarEngine: () => CalendarEngine,
1293
+ DAYS: () => DAYS,
1294
+ DEFAULT_CATEGORY_COLORS: () => DEFAULT_CATEGORY_COLORS,
1295
+ MONTHS: () => MONTHS,
1296
+ MONTHS_FULL: () => MONTHS_FULL,
1297
+ defineCalendarElement: () => defineCalendarElement,
1298
+ formatAttendees: () => formatAttendees,
1299
+ formatDateForDisplay: () => formatDateForDisplay,
1300
+ formatTimeRange: () => formatTimeRange,
1301
+ generateCalendarDates: () => generateCalendarDates,
1302
+ generateYears: () => generateYears,
1303
+ getCategoryColor: () => getCategoryColor,
1304
+ getCellClasses: () => getCellClasses,
1305
+ getDefaultEventColor: () => getDefaultEventColor,
1306
+ getEventsForDate: () => getEventsForDate,
1307
+ getMonthYearText: () => getMonthYearText,
1308
+ getPopupPositionClass: () => getPopupPositionClass,
1309
+ hasEvents: () => hasEvents,
1310
+ isSameDay: () => isSameDay,
1311
+ isToday: () => isToday,
1312
+ isValidHexColor: () => isValidHexColor,
1313
+ mergeCategoryColors: () => mergeCategoryColors,
1314
+ normalizeDate: () => normalizeDate,
1315
+ sortEventsByTime: () => sortEventsByTime
1316
+ });
1317
+ module.exports = __toCommonJS(web_components_exports);
1318
+ init_CalendarElement();
1319
+ init_core();
1320
+ if (typeof customElements !== "undefined" && !customElements.get("kal-calendar")) {
1321
+ Promise.resolve().then(() => (init_CalendarElement(), CalendarElement_exports)).then(({ CalendarElement: CalendarElement2 }) => {
1322
+ if (!customElements.get("kal-calendar")) {
1323
+ customElements.define("kal-calendar", CalendarElement2);
2723
1324
  }
2724
- }
2725
- };
2726
- function createCalendar(props) {
2727
- return new VanillaCalendar(props);
1325
+ });
2728
1326
  }
2729
-
2730
- // src/index.ts
2731
- var version = "1.0.0";
2732
- var name = "kalendly";
2733
1327
  // Annotate the CommonJS export names for ESM import in node:
2734
1328
  0 && (module.exports = {
1329
+ CalendarElement,
2735
1330
  CalendarEngine,
2736
1331
  DAYS,
2737
1332
  DEFAULT_CATEGORY_COLORS,
2738
1333
  MONTHS,
2739
1334
  MONTHS_FULL,
2740
- React,
2741
- ReactCalendar,
2742
- ReactNative,
2743
- ReactNativeCalendar,
2744
- Vanilla,
2745
- createVanillaCalendar,
1335
+ defineCalendarElement,
2746
1336
  formatAttendees,
2747
1337
  formatDateForDisplay,
2748
1338
  formatTimeRange,
@@ -2759,9 +1349,7 @@ var name = "kalendly";
2759
1349
  isToday,
2760
1350
  isValidHexColor,
2761
1351
  mergeCategoryColors,
2762
- name,
2763
1352
  normalizeDate,
2764
- sortEventsByTime,
2765
- version
1353
+ sortEventsByTime
2766
1354
  });
2767
1355
  //# sourceMappingURL=index.js.map