@forcecalendar/interface 1.0.8 → 1.0.10

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forcecalendar/interface",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "type": "module",
5
5
  "description": "Official interface layer for forceCalendar Core - Enterprise calendar components",
6
6
  "main": "dist/force-calendar-interface.umd.js",
@@ -272,10 +272,15 @@ export class ForceCalendar extends BaseComponent {
272
272
  flex-direction: column;
273
273
  }
274
274
 
275
- /* Ensure view components have proper dimensions */
276
- forcecal-month,
277
- forcecal-week,
278
- forcecal-day {
275
+ /* Ensure view container has proper dimensions */
276
+ #calendar-view-container {
277
+ display: block;
278
+ width: 100%;
279
+ height: 100%;
280
+ flex: 1;
281
+ }
282
+
283
+ #calendar-view-container > * {
279
284
  display: block;
280
285
  width: 100%;
281
286
  height: 100%;
@@ -421,31 +426,41 @@ export class ForceCalendar extends BaseComponent {
421
426
  }
422
427
 
423
428
  renderView() {
424
- if (!this.currentView) {
425
- return '<div>Loading view...</div>';
426
- }
427
-
428
- const tagName = `forcecal-${this.currentView}`;
429
- return `<${tagName} id="calendar-view"></${tagName}>`;
429
+ // Use a plain div container - we'll manually instantiate view classes
430
+ // This bypasses Locker Service's custom element restrictions
431
+ return '<div id="calendar-view-container"></div>';
430
432
  }
431
433
 
432
434
  afterRender() {
433
- // Set up view component using global registry for Locker Service compatibility
434
- const viewElement = this.$('#calendar-view');
435
- console.log('[ForceCalendar] afterRender - viewElement:', viewElement);
436
- console.log('[ForceCalendar] afterRender - stateManager:', !!this.stateManager);
437
-
438
- if (viewElement && this.stateManager) {
439
- // Store stateManager in global registry (bypasses Locker Service proxy issues)
440
- const registryId = this._registryId || (this._registryId = 'fc-' + Math.random().toString(36).substr(2, 9));
441
- window.__forceCalendarRegistry = window.__forceCalendarRegistry || {};
442
- window.__forceCalendarRegistry[registryId] = this.stateManager;
443
-
444
- // Pass registry ID via attribute (attributes work through Locker Service)
445
- viewElement.setAttribute('data-state-registry', registryId);
446
- console.log('[ForceCalendar] Set registry ID:', registryId);
447
- } else {
448
- console.log('[ForceCalendar] Could not set stateManager - viewElement:', !!viewElement, 'stateManager:', !!this.stateManager);
435
+ // Manually instantiate and mount view component (bypasses Locker Service)
436
+ const container = this.$('#calendar-view-container');
437
+ console.log('[ForceCalendar] afterRender - container:', !!container, 'stateManager:', !!this.stateManager, 'currentView:', this.currentView);
438
+
439
+ if (container && this.stateManager && this.currentView) {
440
+ // Clean up previous view if exists
441
+ if (this._currentViewInstance) {
442
+ if (this._currentViewInstance.cleanup) {
443
+ this._currentViewInstance.cleanup();
444
+ }
445
+ }
446
+
447
+ console.log('[ForceCalendar] Creating view for:', this.currentView);
448
+
449
+ // Create a simple view renderer that doesn't use custom elements
450
+ const viewRenderer = this._createViewRenderer(this.currentView);
451
+ if (viewRenderer) {
452
+ this._currentViewInstance = viewRenderer;
453
+ viewRenderer.stateManager = this.stateManager;
454
+ viewRenderer.container = container;
455
+ viewRenderer.render();
456
+
457
+ // Subscribe to state changes
458
+ this.stateManager.subscribe((newState, oldState) => {
459
+ if (viewRenderer && viewRenderer.render) {
460
+ viewRenderer.render();
461
+ }
462
+ });
463
+ }
449
464
  }
450
465
 
451
466
  // Add event listeners for buttons using tracked addListener
@@ -491,6 +506,138 @@ export class ForceCalendar extends BaseComponent {
491
506
  }
492
507
  }
493
508
 
509
+ _createViewRenderer(viewName) {
510
+ // Create a simple view renderer that bypasses custom elements
511
+ // This is necessary for Salesforce Locker Service compatibility
512
+ const self = this;
513
+
514
+ return {
515
+ stateManager: null,
516
+ container: null,
517
+ _listeners: [],
518
+
519
+ cleanup() {
520
+ this._listeners.forEach(({ element, event, handler }) => {
521
+ element.removeEventListener(event, handler);
522
+ });
523
+ this._listeners = [];
524
+ },
525
+
526
+ addListener(element, event, handler) {
527
+ element.addEventListener(event, handler);
528
+ this._listeners.push({ element, event, handler });
529
+ },
530
+
531
+ render() {
532
+ if (!this.container || !this.stateManager) return;
533
+
534
+ const viewData = this.stateManager.getViewData();
535
+ if (!viewData || !viewData.weeks) {
536
+ this.container.innerHTML = '<div style="padding: 20px; text-align: center;">Loading calendar...</div>';
537
+ return;
538
+ }
539
+
540
+ this.cleanup();
541
+ const config = this.stateManager.getState().config;
542
+ const html = this._renderMonthView(viewData, config);
543
+ this.container.innerHTML = html;
544
+ this._attachEventHandlers();
545
+ },
546
+
547
+ _renderMonthView(viewData, config) {
548
+ const weekStartsOn = config.weekStartsOn || 0;
549
+ const dayNames = [];
550
+ for (let i = 0; i < 7; i++) {
551
+ const dayIndex = (weekStartsOn + i) % 7;
552
+ dayNames.push(['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][dayIndex]);
553
+ }
554
+
555
+ let html = `
556
+ <style>
557
+ .fc-month-view { display: flex; flex-direction: column; height: 100%; background: #fff; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 13px; }
558
+ .fc-month-header { display: grid; grid-template-columns: repeat(7, 1fr); background: #fafafa; border-bottom: 1px solid #e5e7eb; }
559
+ .fc-month-header-cell { padding: 8px; text-align: left; font-weight: 600; font-size: 10px; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.5px; }
560
+ .fc-month-body { flex: 1; display: flex; flex-direction: column; }
561
+ .fc-month-week { flex: 1; display: grid; grid-template-columns: repeat(7, 1fr); border-bottom: 1px solid #e5e7eb; }
562
+ .fc-month-week:last-child { border-bottom: none; }
563
+ .fc-month-day { background: #fff; padding: 4px; position: relative; border-right: 1px solid #e5e7eb; min-height: 80px; cursor: pointer; }
564
+ .fc-month-day:last-child { border-right: none; }
565
+ .fc-month-day:hover { background: #f9fafb; }
566
+ .fc-month-day.other-month { background: #f9fafb; }
567
+ .fc-month-day.other-month .fc-day-number { color: #d1d5db; }
568
+ .fc-month-day.today .fc-day-number { background: #ef4444; color: white; border-radius: 50%; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; }
569
+ .fc-day-number { font-size: 12px; font-weight: 500; color: #111827; padding: 4px; }
570
+ .fc-day-events { display: flex; flex-direction: column; gap: 2px; margin-top: 2px; }
571
+ .fc-event { font-size: 11px; padding: 2px 6px; border-radius: 2px; background: #2563eb; color: white; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; }
572
+ .fc-event:hover { opacity: 0.9; }
573
+ .fc-more-events { font-size: 10px; color: #6b7280; padding: 2px 4px; cursor: pointer; }
574
+ .fc-more-events:hover { color: #111827; text-decoration: underline; }
575
+ </style>
576
+ <div class="fc-month-view">
577
+ <div class="fc-month-header">
578
+ ${dayNames.map(d => `<div class="fc-month-header-cell">${d}</div>`).join('')}
579
+ </div>
580
+ <div class="fc-month-body">
581
+ `;
582
+
583
+ viewData.weeks.forEach(week => {
584
+ html += '<div class="fc-month-week">';
585
+ week.days.forEach(day => {
586
+ const classes = ['fc-month-day'];
587
+ if (!day.isCurrentMonth) classes.push('other-month');
588
+ if (day.isToday) classes.push('today');
589
+
590
+ const events = day.events || [];
591
+ const visibleEvents = events.slice(0, 3);
592
+ const moreCount = events.length - 3;
593
+
594
+ html += `
595
+ <div class="${classes.join(' ')}" data-date="${day.date}">
596
+ <div class="fc-day-number">${day.dayOfMonth}</div>
597
+ <div class="fc-day-events">
598
+ ${visibleEvents.map(evt => `
599
+ <div class="fc-event" data-event-id="${evt.id}" style="background-color: ${evt.backgroundColor || '#2563eb'}">
600
+ ${evt.title}
601
+ </div>
602
+ `).join('')}
603
+ ${moreCount > 0 ? `<div class="fc-more-events">+${moreCount} more</div>` : ''}
604
+ </div>
605
+ </div>
606
+ `;
607
+ });
608
+ html += '</div>';
609
+ });
610
+
611
+ html += '</div></div>';
612
+ return html;
613
+ },
614
+
615
+ _attachEventHandlers() {
616
+ const stateManager = this.stateManager;
617
+
618
+ // Day click handlers
619
+ this.container.querySelectorAll('.fc-month-day').forEach(dayEl => {
620
+ this.addListener(dayEl, 'click', (e) => {
621
+ const date = new Date(dayEl.dataset.date);
622
+ stateManager.selectDate(date);
623
+ });
624
+ });
625
+
626
+ // Event click handlers
627
+ this.container.querySelectorAll('.fc-event').forEach(eventEl => {
628
+ this.addListener(eventEl, 'click', (e) => {
629
+ e.stopPropagation();
630
+ const eventId = eventEl.dataset.eventId;
631
+ const event = stateManager.getEvents().find(ev => ev.id === eventId);
632
+ if (event) {
633
+ stateManager.selectEvent(event);
634
+ }
635
+ });
636
+ });
637
+ }
638
+ };
639
+ }
640
+
494
641
  handleNavigation(event) {
495
642
  const action = event.currentTarget.dataset.action;
496
643
  switch (action) {