kalendly 0.1.6 → 0.2.0

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 (38) hide show
  1. package/README.md +318 -680
  2. package/dist/core/index.js +3 -3
  3. package/dist/index.d.mts +53 -601
  4. package/dist/index.d.ts +53 -601
  5. package/dist/index.js +760 -2350
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +734 -2339
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/{vanilla/index.umd.js → index.umd.js} +323 -307
  10. package/dist/index.umd.js.map +1 -0
  11. package/package.json +41 -91
  12. package/dist/react/index.d.mts +0 -251
  13. package/dist/react/index.d.ts +0 -251
  14. package/dist/react/index.js +0 -969
  15. package/dist/react/index.js.map +0 -1
  16. package/dist/react/index.mjs +0 -924
  17. package/dist/react/index.mjs.map +0 -1
  18. package/dist/react-native/index.d.mts +0 -642
  19. package/dist/react-native/index.d.ts +0 -642
  20. package/dist/react-native/index.js +0 -1649
  21. package/dist/react-native/index.js.map +0 -1
  22. package/dist/react-native/index.mjs +0 -1615
  23. package/dist/react-native/index.mjs.map +0 -1
  24. package/dist/vanilla/index.d.mts +0 -265
  25. package/dist/vanilla/index.d.ts +0 -265
  26. package/dist/vanilla/index.js +0 -1048
  27. package/dist/vanilla/index.js.map +0 -1
  28. package/dist/vanilla/index.mjs +0 -997
  29. package/dist/vanilla/index.mjs.map +0 -1
  30. package/dist/vanilla/index.umd.js.map +0 -1
  31. package/dist/vue/components/Calendar.vue.d.ts +0 -43
  32. package/dist/vue/components/Calendar.vue.d.ts.map +0 -1
  33. package/dist/vue/index.d.ts +0 -134
  34. package/dist/vue/index.d.ts.map +0 -1
  35. package/dist/vue/index.js +0 -1
  36. package/dist/vue/index.mjs +0 -717
  37. package/dist/vue/types.d.ts +0 -21
  38. 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,2030 +185,523 @@ 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: []
275
- };
276
- }
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)
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"
318
236
  };
319
237
  }
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.props = props;
2185
- if (typeof props.container === "string") {
2186
- const element = document.querySelector(props.container);
2187
- if (!element) {
2188
- throw new Error(`Container element "${props.container}" not found`);
2189
- }
2190
- this.container = element;
2191
- } else {
2192
- this.container = props.container;
2193
- }
2194
- this.engine = new CalendarEngine({
2195
- events: props.events,
2196
- initialDate: props.initialDate,
2197
- minYear: props.minYear,
2198
- maxYear: props.maxYear,
2199
- weekStartsOn: props.weekStartsOn
2200
- });
2201
- this.actions = this.engine.getActions();
2202
- this.applyTheme();
2203
- this.init();
2204
- }
2205
- applyTheme() {
2206
- if (this.props.theme) {
2207
- const root = document.documentElement;
2208
- const theme = this.props.theme;
2209
- if (theme.primary)
2210
- root.style.setProperty("--calendar-primary-color", theme.primary);
2211
- if (theme.secondary)
2212
- root.style.setProperty("--calendar-secondary-color", theme.secondary);
2213
- if (theme.tertiary)
2214
- root.style.setProperty("--calendar-tertiary-color", theme.tertiary);
2215
- if (theme.textColor)
2216
- root.style.setProperty("--calendar-text-color", theme.textColor);
2217
- if (theme.textLight)
2218
- root.style.setProperty("--calendar-text-light", theme.textLight);
2219
- if (theme.background)
2220
- root.style.setProperty("--calendar-background", theme.background);
2221
- if (theme.cellHover)
2222
- root.style.setProperty("--calendar-cell-hover", theme.cellHover);
2223
- if (theme.borderColor)
2224
- root.style.setProperty("--calendar-border-color", theme.borderColor);
2225
- if (theme.todayOutline)
2226
- root.style.setProperty("--calendar-today-outline", theme.todayOutline);
2227
- if (theme.selectedBg)
2228
- root.style.setProperty("--calendar-selected-bg", theme.selectedBg);
2229
- if (theme.eventIndicator)
2230
- root.style.setProperty(
2231
- "--calendar-event-indicator",
2232
- theme.eventIndicator
2233
- );
2234
- }
2235
- }
2236
- init() {
2237
- this.container.classList.add("kalendly-calendar");
2238
- if (this.props.className) {
2239
- this.container.classList.add(this.props.className);
2240
- }
2241
- this.unsubscribe = this.engine.subscribe(() => {
2242
- this.render();
2243
- });
2244
- 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);
2245
495
  }
2246
- render() {
2247
- const viewModel = this.engine.getViewModel();
2248
- const defaultRenderEvent = (event) => {
2249
- const timeRange = formatTimeRange(event);
2250
- const attendeesList = formatAttendees(event.attendees);
2251
- let borderColor = event.color || "#3b82f6";
2252
- if (event.category) {
2253
- borderColor = this.engine.getCategoryColor(event.category);
496
+ }
497
+ var CalendarElement;
498
+ var init_CalendarElement = __esm({
499
+ "src/web-components/CalendarElement.ts"() {
500
+ "use strict";
501
+ init_core();
502
+ CalendarElement = class extends HTMLElement {
503
+ constructor() {
504
+ super(...arguments);
505
+ this.engine = null;
506
+ this.unsubscribe = null;
507
+ this.actions = null;
508
+ this.pickerOpen = false;
509
+ this.yearInput = "";
510
+ this.yearInputValid = true;
511
+ this.listenersAttached = false;
512
+ this.clickOutsideHandler = null;
513
+ this.delegatedClickHandler = null;
514
+ this.delegatedInputHandler = null;
515
+ this.delegatedBlurHandler = null;
516
+ this.delegatedKeydownHandler = null;
517
+ // Rich property backing fields
518
+ this._events = [];
519
+ this._theme = null;
520
+ this._categoryColors = null;
521
+ this._renderEvent = null;
522
+ this._renderNoEvents = null;
2254
523
  }
2255
- const getCategoryLabel = (category) => {
2256
- const labels = {
2257
- work: "WORK",
2258
- personal: "PERSONAL",
2259
- meeting: "MEETING",
2260
- deadline: "DEADLINE",
2261
- appointment: "APPOINTMENT",
2262
- other: "OTHER"
2263
- };
2264
- return category ? labels[category] || category.toUpperCase() : "";
2265
- };
2266
- const getPriorityLabel = (priority) => {
2267
- const labels = {
2268
- high: "HIGH",
2269
- medium: "MEDIUM",
2270
- low: "LOW"
2271
- };
2272
- return priority ? labels[priority] || priority.toUpperCase() : "";
2273
- };
2274
- const getStatusLabel = (status) => {
2275
- const labels = {
2276
- completed: "COMPLETED",
2277
- cancelled: "CANCELLED",
2278
- tentative: "TENTATIVE",
2279
- scheduled: "SCHEDULED"
2280
- };
2281
- return status ? labels[status] || status.toUpperCase() : "";
2282
- };
2283
- return `
524
+ get events() {
525
+ return this._events;
526
+ }
527
+ set events(val) {
528
+ this._events = val;
529
+ if (this.engine) {
530
+ this.engine.updateEvents(val);
531
+ }
532
+ }
533
+ set theme(val) {
534
+ this._theme = val;
535
+ this.applyTheme();
536
+ }
537
+ set categoryColors(val) {
538
+ this._categoryColors = val;
539
+ if (this.engine) {
540
+ this.engine.updateCategoryColors(val);
541
+ }
542
+ }
543
+ set renderEvent(val) {
544
+ this._renderEvent = val;
545
+ if (this.engine) this.render();
546
+ }
547
+ set renderNoEvents(val) {
548
+ this._renderNoEvents = val;
549
+ if (this.engine) this.render();
550
+ }
551
+ connectedCallback() {
552
+ this.classList.add("kalendly-calendar");
553
+ this.initEngine();
554
+ this.render();
555
+ }
556
+ disconnectedCallback() {
557
+ this.cleanup();
558
+ this.classList.remove("kalendly-calendar");
559
+ this.innerHTML = "";
560
+ }
561
+ attributeChangedCallback(_name, oldVal, newVal) {
562
+ if (oldVal === newVal) return;
563
+ this.reinit();
564
+ }
565
+ get minYear() {
566
+ const val = this.getAttribute("min-year");
567
+ return val ? parseInt(val, 10) : (/* @__PURE__ */ new Date()).getFullYear() - 30;
568
+ }
569
+ get maxYear() {
570
+ const val = this.getAttribute("max-year");
571
+ return val ? parseInt(val, 10) : (/* @__PURE__ */ new Date()).getFullYear() + 10;
572
+ }
573
+ initEngine() {
574
+ const initialDateAttr = this.getAttribute("initial-date");
575
+ const initialDate = initialDateAttr ? new Date(initialDateAttr) : void 0;
576
+ const weekStartsOnAttr = this.getAttribute("week-starts-on");
577
+ const weekStartsOn = weekStartsOnAttr === "1" ? 1 : 0;
578
+ this.engine = new CalendarEngine({
579
+ events: this._events,
580
+ initialDate,
581
+ minYear: this.minYear,
582
+ maxYear: this.maxYear,
583
+ weekStartsOn,
584
+ categoryColors: this._categoryColors ?? void 0
585
+ });
586
+ this.actions = this.engine.getActions();
587
+ this.applyTheme();
588
+ this.unsubscribe = this.engine.subscribe(() => {
589
+ this.render();
590
+ });
591
+ }
592
+ reinit() {
593
+ if (!this.engine) return;
594
+ this.cleanup();
595
+ this.initEngine();
596
+ this.render();
597
+ }
598
+ cleanup() {
599
+ if (this.unsubscribe) {
600
+ this.unsubscribe();
601
+ this.unsubscribe = null;
602
+ }
603
+ if (this.delegatedClickHandler) {
604
+ this.removeEventListener("click", this.delegatedClickHandler);
605
+ this.delegatedClickHandler = null;
606
+ }
607
+ if (this.delegatedInputHandler) {
608
+ this.removeEventListener("input", this.delegatedInputHandler);
609
+ this.delegatedInputHandler = null;
610
+ }
611
+ if (this.delegatedBlurHandler) {
612
+ this.removeEventListener("blur", this.delegatedBlurHandler, true);
613
+ this.delegatedBlurHandler = null;
614
+ }
615
+ if (this.delegatedKeydownHandler) {
616
+ this.removeEventListener("keydown", this.delegatedKeydownHandler);
617
+ this.delegatedKeydownHandler = null;
618
+ }
619
+ if (this.clickOutsideHandler) {
620
+ document.removeEventListener("click", this.clickOutsideHandler);
621
+ this.clickOutsideHandler = null;
622
+ }
623
+ this.listenersAttached = false;
624
+ if (this.engine) {
625
+ this.engine.destroy();
626
+ this.engine = null;
627
+ }
628
+ this.actions = null;
629
+ }
630
+ applyTheme() {
631
+ if (!this._theme) return;
632
+ const root = document.documentElement;
633
+ const t = this._theme;
634
+ if (t.primary)
635
+ root.style.setProperty("--calendar-primary-color", t.primary);
636
+ if (t.secondary)
637
+ root.style.setProperty("--calendar-secondary-color", t.secondary);
638
+ if (t.tertiary)
639
+ root.style.setProperty("--calendar-tertiary-color", t.tertiary);
640
+ if (t.textColor)
641
+ root.style.setProperty("--calendar-text-color", t.textColor);
642
+ if (t.textLight)
643
+ root.style.setProperty("--calendar-text-light", t.textLight);
644
+ if (t.background)
645
+ root.style.setProperty("--calendar-background", t.background);
646
+ if (t.cellHover)
647
+ root.style.setProperty("--calendar-cell-hover", t.cellHover);
648
+ if (t.borderColor)
649
+ root.style.setProperty("--calendar-border-color", t.borderColor);
650
+ if (t.todayOutline)
651
+ root.style.setProperty("--calendar-today-outline", t.todayOutline);
652
+ if (t.selectedBg)
653
+ root.style.setProperty("--calendar-selected-bg", t.selectedBg);
654
+ if (t.eventIndicator)
655
+ root.style.setProperty("--calendar-event-indicator", t.eventIndicator);
656
+ }
657
+ render() {
658
+ if (!this.engine) return;
659
+ const viewModel = this.engine.getViewModel();
660
+ const useShortMonths = this.hasAttribute("use-short-month-names");
661
+ const title = this.getAttribute("title");
662
+ const minYear = this.minYear;
663
+ const maxYear = this.maxYear;
664
+ const today = /* @__PURE__ */ new Date();
665
+ const todayMonth = today.getMonth();
666
+ const todayYear = today.getFullYear();
667
+ const isCurrentMonth = viewModel.currentYear === todayYear && viewModel.currentMonth === todayMonth;
668
+ const showScrollHint = viewModel.tasks.length > 3;
669
+ const defaultRenderEvent = (event) => {
670
+ const timeRange = formatTimeRange(event);
671
+ const attendeesList = formatAttendees(event.attendees);
672
+ let borderColor = event.color || "#3b82f6";
673
+ if (event.category) {
674
+ borderColor = this.engine.getCategoryColor(event.category);
675
+ }
676
+ const getCategoryLabel = (category) => {
677
+ const labels = {
678
+ work: "WORK",
679
+ personal: "PERSONAL",
680
+ meeting: "MEETING",
681
+ deadline: "DEADLINE",
682
+ appointment: "APPOINTMENT",
683
+ other: "OTHER"
684
+ };
685
+ return category ? labels[category] || category.toUpperCase() : "";
686
+ };
687
+ const getPriorityLabel = (priority) => {
688
+ const labels = {
689
+ high: "HIGH",
690
+ medium: "MEDIUM",
691
+ low: "LOW"
692
+ };
693
+ return priority ? labels[priority] || priority.toUpperCase() : "";
694
+ };
695
+ const getStatusLabel = (status) => {
696
+ const labels = {
697
+ completed: "COMPLETED",
698
+ cancelled: "CANCELLED",
699
+ tentative: "TENTATIVE",
700
+ scheduled: "SCHEDULED"
701
+ };
702
+ return status ? labels[status] || status.toUpperCase() : "";
703
+ };
704
+ return `
2284
705
  <div class="event-card" style="border-left-color: ${borderColor}">
2285
706
  <div class="event-header">
2286
707
  <div class="event-title">${event.name}</div>
@@ -2290,46 +711,46 @@ var VanillaCalendar = class {
2290
711
  ${event.status && event.status !== "scheduled" ? `<span class="badge status-${event.status}">${getStatusLabel(event.status)}</span>` : ""}
2291
712
  </div>
2292
713
  </div>
2293
-
714
+
2294
715
  ${timeRange ? `
2295
716
  <div class="event-time">
2296
717
  <span class="event-time-label">Time:</span>
2297
718
  <span class="event-time-value">${timeRange}</span>
2298
719
  </div>
2299
720
  ` : ""}
2300
-
721
+
2301
722
  ${event.description ? `
2302
723
  <div class="event-description">${event.description}</div>
2303
724
  ` : ""}
2304
-
725
+
2305
726
  ${event.location ? `
2306
727
  <div class="event-time">
2307
728
  <span class="event-time-label">Location:</span>
2308
729
  <span class="event-time-value">${event.location}</span>
2309
730
  </div>
2310
731
  ` : ""}
2311
-
732
+
2312
733
  ${attendeesList ? `
2313
734
  <div class="event-time">
2314
735
  <span class="event-time-label">Attendees:</span>
2315
736
  <span class="event-time-value">${attendeesList}</span>
2316
737
  </div>
2317
738
  ` : ""}
2318
-
739
+
2319
740
  ${event.organizer ? `
2320
741
  <div class="event-time">
2321
742
  <span class="event-time-label">Organizer:</span>
2322
743
  <span class="event-time-value">${event.organizer}</span>
2323
744
  </div>
2324
745
  ` : ""}
2325
-
746
+
2326
747
  ${event.notes ? `
2327
748
  <div class="event-time">
2328
749
  <span class="event-time-label">Notes:</span>
2329
750
  <span class="event-time-value">${event.notes}</span>
2330
751
  </div>
2331
752
  ` : ""}
2332
-
753
+
2333
754
  ${event.url ? `
2334
755
  <div class="event-time">
2335
756
  <a href="${event.url}" target="_blank" rel="noopener noreferrer" class="event-link">
@@ -2337,7 +758,7 @@ var VanillaCalendar = class {
2337
758
  </a>
2338
759
  </div>
2339
760
  ` : ""}
2340
-
761
+
2341
762
  ${event.tags && event.tags.length > 0 ? `
2342
763
  <div class="event-tags">
2343
764
  ${event.tags.map((tag) => `<span class="event-tag">${tag}</span>`).join("")}
@@ -2345,27 +766,19 @@ var VanillaCalendar = class {
2345
766
  ` : ""}
2346
767
  </div>
2347
768
  `;
2348
- };
2349
- const defaultRenderNoEvents = () => '<div class="no-events-message">No events scheduled for this day.</div>';
2350
- const renderEvent = this.props.renderEvent || defaultRenderEvent;
2351
- const renderNoEvents = this.props.renderNoEvents || defaultRenderNoEvents;
2352
- const showScrollHint = viewModel.tasks.length > 3;
2353
- const today = /* @__PURE__ */ new Date();
2354
- const todayMonth = today.getMonth();
2355
- const todayYear = today.getFullYear();
2356
- const minYear = this.props.minYear ?? todayYear - 30;
2357
- const maxYear = this.props.maxYear ?? todayYear + 10;
2358
- const isCurrentMonth = viewModel.currentYear === todayYear && viewModel.currentMonth === todayMonth;
2359
- const html = `
2360
- ${this.props.title ? `
2361
- <div class="page--title">
2362
- <h1>${this.props.title}</h1>
2363
- </div>
769
+ };
770
+ const defaultRenderNoEvents = () => '<div class="no-events-message">No events scheduled for this day.</div>';
771
+ const renderEvent = this._renderEvent || defaultRenderEvent;
772
+ const renderNoEvents = this._renderNoEvents || defaultRenderNoEvents;
773
+ const html = `
774
+ ${title ? `
775
+ <div class="page--title">
776
+ <h1>${title}</h1>
777
+ </div>
2364
778
  ` : ""}
2365
779
 
2366
780
  <div class="calendar--content">
2367
781
  <div class="calendar--card">
2368
- <!-- Navigation Header -->
2369
782
  <div class="calendar--nav-header">
2370
783
  <button type="button" class="calendar--nav-arrow" data-action="previous" aria-label="Previous month">
2371
784
  &#8249;
@@ -2379,7 +792,7 @@ var VanillaCalendar = class {
2379
792
  aria-expanded="${this.pickerOpen ? "true" : "false"}"
2380
793
  aria-haspopup="true"
2381
794
  >
2382
- ${this.props.useShortMonthNames ? `${MONTHS[viewModel.currentMonth]} ${viewModel.currentYear}` : viewModel.monthAndYearText}
795
+ ${useShortMonths ? `${MONTHS[viewModel.currentMonth]} ${viewModel.currentYear}` : viewModel.monthAndYearText}
2383
796
  <span class="calendar--picker-chevron">&#9662;</span>
2384
797
  </button>
2385
798
 
@@ -2410,10 +823,10 @@ var VanillaCalendar = class {
2410
823
  </div>
2411
824
 
2412
825
  <div class="calendar--picker-months">
2413
- ${(this.props.useShortMonthNames ? MONTHS : MONTHS_FULL).map((month, index) => {
2414
- const isSelected = index === viewModel.currentMonth;
2415
- const isCurrent = index === todayMonth && viewModel.currentYear === todayYear;
2416
- return `
826
+ ${(useShortMonths ? MONTHS : MONTHS_FULL).map((month, index) => {
827
+ const isSelected = index === viewModel.currentMonth;
828
+ const isCurrent = index === todayMonth && viewModel.currentYear === todayYear;
829
+ return `
2417
830
  <button
2418
831
  type="button"
2419
832
  class="calendar--picker-month${isSelected ? " selected" : ""}${isCurrent ? " current-month" : ""}"
@@ -2421,7 +834,7 @@ var VanillaCalendar = class {
2421
834
  data-month="${index}"
2422
835
  >${month}</button>
2423
836
  `;
2424
- }).join("")}
837
+ }).join("")}
2425
838
  </div>
2426
839
  </div>
2427
840
  ` : ""}
@@ -2447,12 +860,12 @@ var VanillaCalendar = class {
2447
860
  </thead>
2448
861
  <tbody data-calendar-body>
2449
862
  ${viewModel.calendarDates.map(
2450
- (week) => `
863
+ (week) => `
2451
864
  <tr>
2452
865
  ${week.map((calendarDate, dayIndex) => {
2453
- const classes = getCellClasses(calendarDate);
2454
- const dateString = calendarDate.date.toISOString();
2455
- return `
866
+ const classes = getCellClasses(calendarDate);
867
+ const dateString = calendarDate.date.toISOString();
868
+ return `
2456
869
  <td
2457
870
  class="${classes.join(" ")}"
2458
871
  data-date="${dateString}"
@@ -2462,10 +875,10 @@ var VanillaCalendar = class {
2462
875
  ${calendarDate.date.getDate()}
2463
876
  </td>
2464
877
  `;
2465
- }).join("")}
878
+ }).join("")}
2466
879
  </tr>
2467
880
  `
2468
- ).join("")}
881
+ ).join("")}
2469
882
  </tbody>
2470
883
  </table>
2471
884
 
@@ -2483,250 +896,249 @@ var VanillaCalendar = class {
2483
896
  ` : ""}
2484
897
 
2485
898
  <div class="events-container">
2486
- ${viewModel.tasks.length > 0 ? `
2487
- ${viewModel.tasks.map((event) => renderEvent(event)).join("")}
2488
- ` : renderNoEvents()}
899
+ ${viewModel.tasks.length > 0 ? viewModel.tasks.map((event) => renderEvent(event)).join("") : renderNoEvents()}
2489
900
  </div>
2490
901
  </div>
2491
902
  ` : ""}
2492
903
  </div>
2493
904
  </div>
2494
905
  `;
2495
- this.container.innerHTML = html;
2496
- this.attachEventListeners();
2497
- }
2498
- attachEventListeners() {
2499
- const today = /* @__PURE__ */ new Date();
2500
- const todayYear = today.getFullYear();
2501
- const todayMonth = today.getMonth();
2502
- const minYear = this.props.minYear ?? todayYear - 30;
2503
- const maxYear = this.props.maxYear ?? todayYear + 10;
2504
- const tableBody = this.container.querySelector("[data-calendar-body]");
2505
- if (tableBody) {
2506
- tableBody.addEventListener("click", (e) => {
2507
- const target = e.target;
2508
- const cell = target.closest('td[data-clickable="true"]');
2509
- if (cell && cell.dataset.date) {
2510
- const date = new Date(cell.dataset.date);
2511
- const dayIndex = parseInt(cell.dataset.dayIndex || "0");
2512
- this.engine.handleDateClick(date, dayIndex);
2513
- this.container.dispatchEvent(
2514
- new CustomEvent("dateSelect", {
2515
- detail: { date, dayIndex }
2516
- })
2517
- );
2518
- }
2519
- });
2520
- }
2521
- const prevBtn = this.container.querySelector('[data-action="previous"]');
2522
- const nextBtn = this.container.querySelector('[data-action="next"]');
2523
- if (prevBtn) {
2524
- prevBtn.addEventListener("click", () => {
2525
- this.actions.previous();
2526
- this.dispatchMonthChange();
2527
- });
2528
- }
2529
- if (nextBtn) {
2530
- nextBtn.addEventListener("click", () => {
2531
- this.actions.next();
2532
- this.dispatchMonthChange();
2533
- });
2534
- }
2535
- const todayBtn = this.container.querySelector('[data-action="today"]');
2536
- if (todayBtn) {
2537
- todayBtn.addEventListener("click", () => {
2538
- this.actions.goToToday();
2539
- this.pickerOpen = false;
2540
- this.container.dispatchEvent(
2541
- new CustomEvent("monthChange", {
2542
- detail: { year: todayYear, month: todayMonth }
2543
- })
906
+ this.innerHTML = html;
907
+ this.attachEventListeners();
908
+ }
909
+ attachEventListeners() {
910
+ if (this.listenersAttached) return;
911
+ this.listenersAttached = true;
912
+ const minYear = this.minYear;
913
+ const maxYear = this.maxYear;
914
+ this.delegatedClickHandler = (e) => {
915
+ const target = e.target;
916
+ const cell = target.closest('td[data-clickable="true"]');
917
+ if (cell && cell.dataset.date) {
918
+ e.stopPropagation();
919
+ const date = new Date(cell.dataset.date);
920
+ const dayIndex = parseInt(cell.dataset.dayIndex || "0");
921
+ this.engine.handleDateClick(date, dayIndex);
922
+ this.dispatchEvent(
923
+ new CustomEvent("cal-date-select", {
924
+ bubbles: true,
925
+ composed: true,
926
+ detail: { date, events: this.engine.getEventsForDate(date) }
927
+ })
928
+ );
929
+ return;
930
+ }
931
+ const actionEl = target.closest("[data-action]");
932
+ if (!actionEl) return;
933
+ const action = actionEl.dataset.action;
934
+ e.stopPropagation();
935
+ switch (action) {
936
+ case "previous":
937
+ this.actions.previous();
938
+ this.dispatchMonthChange();
939
+ break;
940
+ case "next":
941
+ this.actions.next();
942
+ this.dispatchMonthChange();
943
+ break;
944
+ case "today":
945
+ this.actions.goToToday();
946
+ this.pickerOpen = false;
947
+ this.dispatchMonthChange();
948
+ break;
949
+ case "toggle-picker":
950
+ this.pickerOpen = !this.pickerOpen;
951
+ if (this.pickerOpen) {
952
+ this.yearInput = String(this.engine.getViewModel().currentYear);
953
+ this.yearInputValid = true;
954
+ }
955
+ this.render();
956
+ break;
957
+ case "year-prev": {
958
+ const vm = this.engine.getViewModel();
959
+ if (vm.currentYear > minYear) {
960
+ const newYear = vm.currentYear - 1;
961
+ this.yearInput = String(newYear);
962
+ this.updatePickerYear(newYear);
963
+ this.actions.jump(newYear, vm.currentMonth);
964
+ this.dispatchMonthChange();
965
+ }
966
+ break;
967
+ }
968
+ case "year-next": {
969
+ const vm = this.engine.getViewModel();
970
+ if (vm.currentYear < maxYear) {
971
+ const newYear = vm.currentYear + 1;
972
+ this.yearInput = String(newYear);
973
+ this.updatePickerYear(newYear);
974
+ this.actions.jump(newYear, vm.currentMonth);
975
+ this.dispatchMonthChange();
976
+ }
977
+ break;
978
+ }
979
+ case "select-month": {
980
+ const month = parseInt(actionEl.dataset.month || "0", 10);
981
+ this.actions.jump(this.engine.getViewModel().currentYear, month);
982
+ this.pickerOpen = false;
983
+ this.dispatchMonthChange();
984
+ break;
985
+ }
986
+ case "close-popup":
987
+ this.engine.clearSelection();
988
+ break;
989
+ }
990
+ };
991
+ this.delegatedInputHandler = (e) => {
992
+ const target = e.target;
993
+ if (!target.matches("[data-year-input]")) return;
994
+ const value = target.value;
995
+ if (value === "" || /^\d+$/.test(value)) {
996
+ this.yearInput = value;
997
+ const year = parseInt(value, 10);
998
+ this.yearInputValid = value === "" || year >= minYear && year <= maxYear;
999
+ target.classList.toggle("invalid", !this.yearInputValid);
1000
+ }
1001
+ };
1002
+ this.delegatedBlurHandler = (e) => {
1003
+ const target = e.target;
1004
+ if (!target.matches("[data-year-input]")) return;
1005
+ const year = parseInt(this.yearInput, 10);
1006
+ const vm = this.engine.getViewModel();
1007
+ if (isNaN(year) || year < minYear || year > maxYear) {
1008
+ this.yearInput = String(vm.currentYear);
1009
+ this.yearInputValid = true;
1010
+ target.value = this.yearInput;
1011
+ target.classList.remove("invalid");
1012
+ } else if (year !== vm.currentYear) {
1013
+ this.actions.jump(year, vm.currentMonth);
1014
+ this.dispatchMonthChange();
1015
+ }
1016
+ };
1017
+ this.delegatedKeydownHandler = (e) => {
1018
+ const target = e.target;
1019
+ if (!target.matches("[data-year-input]")) return;
1020
+ if (e.key === "Enter") target.blur();
1021
+ };
1022
+ this.addEventListener("click", this.delegatedClickHandler);
1023
+ this.addEventListener("input", this.delegatedInputHandler);
1024
+ this.addEventListener("blur", this.delegatedBlurHandler, true);
1025
+ this.addEventListener("keydown", this.delegatedKeydownHandler);
1026
+ this.clickOutsideHandler = (e) => {
1027
+ const target = e.target;
1028
+ if (this.contains(target)) return;
1029
+ if (this.pickerOpen) {
1030
+ this.pickerOpen = false;
1031
+ this.render();
1032
+ return;
1033
+ }
1034
+ const popup = this.querySelector(".date-popup");
1035
+ if (popup) {
1036
+ this.engine.clearSelection();
1037
+ }
1038
+ };
1039
+ document.addEventListener("click", this.clickOutsideHandler);
1040
+ }
1041
+ updatePickerYear(year) {
1042
+ const input = this.querySelector("[data-year-input]");
1043
+ if (input) input.value = String(year);
1044
+ const prevBtn = this.querySelector(
1045
+ '[data-action="year-prev"]'
2544
1046
  );
2545
- });
2546
- }
2547
- const pickerBtn = this.container.querySelector(
2548
- '[data-action="toggle-picker"]'
2549
- );
2550
- if (pickerBtn) {
2551
- pickerBtn.addEventListener("click", (e) => {
2552
- e.stopPropagation();
2553
- this.pickerOpen = !this.pickerOpen;
2554
- if (this.pickerOpen) {
2555
- this.yearInput = String(this.engine.getViewModel().currentYear);
2556
- this.yearInputValid = true;
2557
- }
2558
- this.render();
2559
- });
2560
- }
2561
- const yearPrevBtn = this.container.querySelector(
2562
- '[data-action="year-prev"]'
2563
- );
2564
- const yearNextBtn = this.container.querySelector(
2565
- '[data-action="year-next"]'
2566
- );
2567
- if (yearPrevBtn) {
2568
- yearPrevBtn.addEventListener("click", (e) => {
2569
- e.stopPropagation();
2570
- const vm = this.engine.getViewModel();
2571
- if (vm.currentYear > minYear) {
2572
- this.actions.jump(vm.currentYear - 1, vm.currentMonth);
2573
- this.yearInput = String(vm.currentYear - 1);
2574
- this.dispatchMonthChange();
2575
- }
2576
- });
2577
- }
2578
- if (yearNextBtn) {
2579
- yearNextBtn.addEventListener("click", (e) => {
2580
- e.stopPropagation();
2581
- const vm = this.engine.getViewModel();
2582
- if (vm.currentYear < maxYear) {
2583
- this.actions.jump(vm.currentYear + 1, vm.currentMonth);
2584
- this.yearInput = String(vm.currentYear + 1);
2585
- this.dispatchMonthChange();
2586
- }
2587
- });
2588
- }
2589
- const yearInputEl = this.container.querySelector(
2590
- "[data-year-input]"
2591
- );
2592
- if (yearInputEl) {
2593
- yearInputEl.addEventListener("input", (e) => {
2594
- e.stopPropagation();
2595
- const value = e.target.value;
2596
- if (value === "" || /^\d+$/.test(value)) {
2597
- this.yearInput = value;
2598
- const year = parseInt(value, 10);
2599
- this.yearInputValid = value === "" || year >= minYear && year <= maxYear;
2600
- this.render();
2601
- }
2602
- });
2603
- yearInputEl.addEventListener("blur", (e) => {
2604
- e.stopPropagation();
2605
- const year = parseInt(this.yearInput, 10);
1047
+ const nextBtn = this.querySelector(
1048
+ '[data-action="year-next"]'
1049
+ );
1050
+ if (prevBtn) prevBtn.disabled = year <= this.minYear;
1051
+ if (nextBtn) nextBtn.disabled = year >= this.maxYear;
1052
+ }
1053
+ dispatchMonthChange() {
1054
+ if (!this.engine) return;
2606
1055
  const vm = this.engine.getViewModel();
2607
- if (isNaN(year) || year < minYear || year > maxYear) {
2608
- this.yearInput = String(vm.currentYear);
2609
- this.yearInputValid = true;
2610
- } else {
2611
- this.actions.jump(year, vm.currentMonth);
2612
- this.dispatchMonthChange();
2613
- }
2614
- this.render();
2615
- });
2616
- yearInputEl.addEventListener("keydown", (e) => {
2617
- if (e.key === "Enter") {
2618
- yearInputEl.blur();
2619
- }
2620
- });
2621
- }
2622
- const monthBtns = this.container.querySelectorAll(
2623
- '[data-action="select-month"]'
2624
- );
2625
- monthBtns.forEach((btn) => {
2626
- btn.addEventListener("click", (e) => {
2627
- e.stopPropagation();
2628
- const month = parseInt(btn.dataset.month || "0", 10);
2629
- this.actions.jump(this.engine.getViewModel().currentYear, month);
2630
- this.pickerOpen = false;
2631
- this.dispatchMonthChange();
2632
- });
2633
- });
2634
- const closeBtn = this.container.querySelector(
2635
- '[data-action="close-popup"]'
2636
- );
2637
- if (closeBtn) {
2638
- closeBtn.addEventListener("click", (e) => {
2639
- e.stopPropagation();
2640
- this.engine.clearSelection();
2641
- });
2642
- }
2643
- if (this.clickOutsideHandler) {
2644
- document.removeEventListener("click", this.clickOutsideHandler);
2645
- }
2646
- this.clickOutsideHandler = (e) => {
2647
- const target = e.target;
2648
- const pickerBtn2 = this.container.querySelector(
2649
- '[data-action="toggle-picker"]'
2650
- );
2651
- if (pickerBtn2 && (target === pickerBtn2 || pickerBtn2.contains(target))) {
2652
- return;
1056
+ this.dispatchEvent(
1057
+ new CustomEvent("cal-month-change", {
1058
+ bubbles: true,
1059
+ composed: true,
1060
+ detail: { year: vm.currentYear, month: vm.currentMonth }
1061
+ })
1062
+ );
2653
1063
  }
2654
- const pickerContainer = this.container.querySelector(
2655
- "[data-picker-container]"
2656
- );
2657
- if (this.pickerOpen && pickerContainer && !pickerContainer.contains(target)) {
2658
- this.pickerOpen = false;
2659
- this.render();
2660
- return;
1064
+ // Public API
1065
+ updateEvents(events) {
1066
+ this.events = events;
1067
+ }
1068
+ updateTheme(theme) {
1069
+ this.theme = theme;
1070
+ }
1071
+ getCurrentDate() {
1072
+ return this.engine?.getViewModel().selectedDate ?? null;
2661
1073
  }
2662
- const popup = this.container.querySelector(".date-popup");
2663
- if (popup && !popup.contains(target) && !target.closest("[data-calendar-body]")) {
2664
- this.engine.clearSelection();
1074
+ goToDate(date) {
1075
+ this.actions?.jump(date.getFullYear(), date.getMonth());
1076
+ }
1077
+ getEngine() {
1078
+ if (!this.engine)
1079
+ throw new Error("CalendarElement is not connected to the DOM");
1080
+ return this.engine;
2665
1081
  }
2666
1082
  };
2667
- document.addEventListener("click", this.clickOutsideHandler);
2668
- }
2669
- dispatchMonthChange() {
2670
- const vm = this.engine.getViewModel();
2671
- this.container.dispatchEvent(
2672
- new CustomEvent("monthChange", {
2673
- detail: { year: vm.currentYear, month: vm.currentMonth }
2674
- })
2675
- );
2676
- }
2677
- updateEvents(events) {
2678
- this.engine.updateEvents(events);
1083
+ CalendarElement.observedAttributes = [
1084
+ "initial-date",
1085
+ "min-year",
1086
+ "max-year",
1087
+ "week-starts-on",
1088
+ "title",
1089
+ "use-short-month-names"
1090
+ ];
2679
1091
  }
2680
- updateTheme(theme) {
2681
- this.props.theme = theme;
2682
- this.applyTheme();
2683
- }
2684
- getCurrentDate() {
2685
- return this.engine.getViewModel().selectedDate;
2686
- }
2687
- goToDate(date) {
2688
- this.actions.jump(date.getFullYear(), date.getMonth());
2689
- }
2690
- getEngine() {
2691
- return this.engine;
2692
- }
2693
- destroy() {
2694
- if (this.unsubscribe) {
2695
- this.unsubscribe();
2696
- this.unsubscribe = null;
2697
- }
2698
- if (this.clickOutsideHandler) {
2699
- document.removeEventListener("click", this.clickOutsideHandler);
2700
- this.clickOutsideHandler = null;
2701
- }
2702
- this.engine.destroy();
2703
- this.container.innerHTML = "";
2704
- this.container.classList.remove("kalendly-calendar");
2705
- if (this.props.className) {
2706
- this.container.classList.remove(this.props.className);
1092
+ });
1093
+
1094
+ // src/web-components/index.ts
1095
+ var web_components_exports = {};
1096
+ __export(web_components_exports, {
1097
+ CalendarElement: () => CalendarElement,
1098
+ CalendarEngine: () => CalendarEngine,
1099
+ DAYS: () => DAYS,
1100
+ DEFAULT_CATEGORY_COLORS: () => DEFAULT_CATEGORY_COLORS,
1101
+ MONTHS: () => MONTHS,
1102
+ MONTHS_FULL: () => MONTHS_FULL,
1103
+ defineCalendarElement: () => defineCalendarElement,
1104
+ formatAttendees: () => formatAttendees,
1105
+ formatDateForDisplay: () => formatDateForDisplay,
1106
+ formatTimeRange: () => formatTimeRange,
1107
+ generateCalendarDates: () => generateCalendarDates,
1108
+ generateYears: () => generateYears,
1109
+ getCategoryColor: () => getCategoryColor,
1110
+ getCellClasses: () => getCellClasses,
1111
+ getDefaultEventColor: () => getDefaultEventColor,
1112
+ getEventsForDate: () => getEventsForDate,
1113
+ getMonthYearText: () => getMonthYearText,
1114
+ getPopupPositionClass: () => getPopupPositionClass,
1115
+ hasEvents: () => hasEvents,
1116
+ isSameDay: () => isSameDay,
1117
+ isToday: () => isToday,
1118
+ isValidHexColor: () => isValidHexColor,
1119
+ mergeCategoryColors: () => mergeCategoryColors,
1120
+ normalizeDate: () => normalizeDate,
1121
+ sortEventsByTime: () => sortEventsByTime
1122
+ });
1123
+ module.exports = __toCommonJS(web_components_exports);
1124
+ init_CalendarElement();
1125
+ init_core();
1126
+ if (typeof customElements !== "undefined" && !customElements.get("kal-calendar")) {
1127
+ Promise.resolve().then(() => (init_CalendarElement(), CalendarElement_exports)).then(({ CalendarElement: CalendarElement2 }) => {
1128
+ if (!customElements.get("kal-calendar")) {
1129
+ customElements.define("kal-calendar", CalendarElement2);
2707
1130
  }
2708
- }
2709
- };
2710
- function createCalendar(props) {
2711
- return new VanillaCalendar(props);
1131
+ });
2712
1132
  }
2713
-
2714
- // src/index.ts
2715
- var version = "1.0.0";
2716
- var name = "kalendly";
2717
1133
  // Annotate the CommonJS export names for ESM import in node:
2718
1134
  0 && (module.exports = {
1135
+ CalendarElement,
2719
1136
  CalendarEngine,
2720
1137
  DAYS,
2721
1138
  DEFAULT_CATEGORY_COLORS,
2722
1139
  MONTHS,
2723
1140
  MONTHS_FULL,
2724
- React,
2725
- ReactCalendar,
2726
- ReactNative,
2727
- ReactNativeCalendar,
2728
- Vanilla,
2729
- createVanillaCalendar,
1141
+ defineCalendarElement,
2730
1142
  formatAttendees,
2731
1143
  formatDateForDisplay,
2732
1144
  formatTimeRange,
@@ -2743,9 +1155,7 @@ var name = "kalendly";
2743
1155
  isToday,
2744
1156
  isValidHexColor,
2745
1157
  mergeCategoryColors,
2746
- name,
2747
1158
  normalizeDate,
2748
- sortEventsByTime,
2749
- version
1159
+ sortEventsByTime
2750
1160
  });
2751
1161
  //# sourceMappingURL=index.js.map