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