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