@forcecalendar/interface 1.0.9 → 1.0.11
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
|
@@ -272,10 +272,15 @@ export class ForceCalendar extends BaseComponent {
|
|
|
272
272
|
flex-direction: column;
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
-
/* Ensure view
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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%;
|
|
@@ -344,11 +349,147 @@ export class ForceCalendar extends BaseComponent {
|
|
|
344
349
|
justify-content: space-between;
|
|
345
350
|
width: 100%;
|
|
346
351
|
}
|
|
347
|
-
|
|
352
|
+
|
|
348
353
|
#create-event-btn {
|
|
349
354
|
flex: 1;
|
|
350
355
|
}
|
|
351
356
|
}
|
|
357
|
+
|
|
358
|
+
/* Month View Styles (inline rendering for Locker Service compatibility) */
|
|
359
|
+
.fc-month-view {
|
|
360
|
+
display: flex;
|
|
361
|
+
flex-direction: column;
|
|
362
|
+
height: 100%;
|
|
363
|
+
background: var(--fc-background);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.fc-month-header {
|
|
367
|
+
display: grid;
|
|
368
|
+
grid-template-columns: repeat(7, 1fr);
|
|
369
|
+
border-bottom: 1px solid var(--fc-border-color);
|
|
370
|
+
background: var(--fc-background-alt);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.fc-month-header-cell {
|
|
374
|
+
padding: 12px 8px;
|
|
375
|
+
text-align: center;
|
|
376
|
+
font-size: 11px;
|
|
377
|
+
font-weight: 600;
|
|
378
|
+
color: var(--fc-text-light);
|
|
379
|
+
text-transform: uppercase;
|
|
380
|
+
letter-spacing: 0.05em;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.fc-month-body {
|
|
384
|
+
display: flex;
|
|
385
|
+
flex-direction: column;
|
|
386
|
+
flex: 1;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.fc-month-week {
|
|
390
|
+
display: grid;
|
|
391
|
+
grid-template-columns: repeat(7, 1fr);
|
|
392
|
+
flex: 1;
|
|
393
|
+
min-height: 100px;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.fc-month-day {
|
|
397
|
+
background: var(--fc-background);
|
|
398
|
+
border-right: 1px solid var(--fc-border-color);
|
|
399
|
+
border-bottom: 1px solid var(--fc-border-color);
|
|
400
|
+
padding: 4px;
|
|
401
|
+
min-height: 80px;
|
|
402
|
+
cursor: pointer;
|
|
403
|
+
transition: background-color 0.15s ease;
|
|
404
|
+
display: flex;
|
|
405
|
+
flex-direction: column;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.fc-month-day:hover {
|
|
409
|
+
background: var(--fc-background-hover);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.fc-month-day:last-child {
|
|
413
|
+
border-right: none;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.fc-month-day.other-month {
|
|
417
|
+
background: var(--fc-background-alt);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.fc-month-day.other-month .fc-day-number {
|
|
421
|
+
color: var(--fc-text-light);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.fc-month-day.today {
|
|
425
|
+
background: rgba(37, 99, 235, 0.05);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.fc-month-day.today .fc-day-number {
|
|
429
|
+
background: var(--fc-primary-color);
|
|
430
|
+
color: white;
|
|
431
|
+
border-radius: 50%;
|
|
432
|
+
width: 24px;
|
|
433
|
+
height: 24px;
|
|
434
|
+
display: flex;
|
|
435
|
+
align-items: center;
|
|
436
|
+
justify-content: center;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.fc-day-number {
|
|
440
|
+
font-size: 13px;
|
|
441
|
+
font-weight: 500;
|
|
442
|
+
color: var(--fc-text-color);
|
|
443
|
+
padding: 2px 4px;
|
|
444
|
+
margin-bottom: 4px;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.fc-day-events {
|
|
448
|
+
display: flex;
|
|
449
|
+
flex-direction: column;
|
|
450
|
+
gap: 2px;
|
|
451
|
+
flex: 1;
|
|
452
|
+
overflow: hidden;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.fc-event {
|
|
456
|
+
font-size: 11px;
|
|
457
|
+
padding: 2px 6px;
|
|
458
|
+
border-radius: 3px;
|
|
459
|
+
color: white;
|
|
460
|
+
white-space: nowrap;
|
|
461
|
+
overflow: hidden;
|
|
462
|
+
text-overflow: ellipsis;
|
|
463
|
+
cursor: pointer;
|
|
464
|
+
transition: transform 0.1s ease;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.fc-event:hover {
|
|
468
|
+
transform: scale(1.02);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.fc-more-events {
|
|
472
|
+
font-size: 10px;
|
|
473
|
+
color: var(--fc-text-light);
|
|
474
|
+
padding: 2px 4px;
|
|
475
|
+
font-weight: 500;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/* Week View Styles (inline rendering for Locker Service compatibility) */
|
|
479
|
+
.fc-week-view {
|
|
480
|
+
display: flex;
|
|
481
|
+
flex-direction: column;
|
|
482
|
+
height: 100%;
|
|
483
|
+
background: var(--fc-background);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/* Day View Styles (inline rendering for Locker Service compatibility) */
|
|
487
|
+
.fc-day-view {
|
|
488
|
+
display: flex;
|
|
489
|
+
flex-direction: column;
|
|
490
|
+
height: 100%;
|
|
491
|
+
background: var(--fc-background);
|
|
492
|
+
}
|
|
352
493
|
`;
|
|
353
494
|
}
|
|
354
495
|
|
|
@@ -421,50 +562,64 @@ export class ForceCalendar extends BaseComponent {
|
|
|
421
562
|
}
|
|
422
563
|
|
|
423
564
|
renderView() {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const tagName = `forcecal-${this.currentView}`;
|
|
429
|
-
return `<${tagName} id="calendar-view"></${tagName}>`;
|
|
565
|
+
// Use a plain div container - we'll manually instantiate view classes
|
|
566
|
+
// This bypasses Locker Service's custom element restrictions
|
|
567
|
+
return '<div id="calendar-view-container"></div>';
|
|
430
568
|
}
|
|
431
569
|
|
|
432
570
|
afterRender() {
|
|
433
|
-
//
|
|
434
|
-
const
|
|
435
|
-
console.log('[ForceCalendar] afterRender -
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
if (
|
|
439
|
-
//
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
console.log('[ForceCalendar]
|
|
457
|
-
|
|
458
|
-
//
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
571
|
+
// Manually instantiate and mount view component (bypasses Locker Service)
|
|
572
|
+
const container = this.$('#calendar-view-container');
|
|
573
|
+
console.log('[ForceCalendar] afterRender - container:', !!container, 'stateManager:', !!this.stateManager, 'currentView:', this.currentView);
|
|
574
|
+
|
|
575
|
+
// Only create view once per view type change
|
|
576
|
+
if (container && this.stateManager && this.currentView) {
|
|
577
|
+
// Check if container actually has content (render() clears shadow DOM)
|
|
578
|
+
if (this._currentViewInstance && this._currentViewInstance._viewType === this.currentView && container.children.length > 0) {
|
|
579
|
+
console.log('[ForceCalendar] View already exists with content, skipping creation');
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Clean up previous view if exists
|
|
584
|
+
if (this._currentViewInstance) {
|
|
585
|
+
if (this._currentViewInstance.cleanup) {
|
|
586
|
+
this._currentViewInstance.cleanup();
|
|
587
|
+
}
|
|
588
|
+
if (this._viewUnsubscribe) {
|
|
589
|
+
this._viewUnsubscribe();
|
|
590
|
+
this._viewUnsubscribe = null;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
console.log('[ForceCalendar] Creating view for:', this.currentView);
|
|
595
|
+
|
|
596
|
+
// Create a simple view renderer that doesn't use custom elements
|
|
597
|
+
try {
|
|
598
|
+
const viewRenderer = this._createViewRenderer(this.currentView);
|
|
599
|
+
if (viewRenderer) {
|
|
600
|
+
viewRenderer._viewType = this.currentView;
|
|
601
|
+
this._currentViewInstance = viewRenderer;
|
|
602
|
+
viewRenderer.stateManager = this.stateManager;
|
|
603
|
+
viewRenderer.container = container;
|
|
604
|
+
|
|
605
|
+
console.log('[ForceCalendar] Calling viewRenderer.render()');
|
|
606
|
+
viewRenderer.render();
|
|
607
|
+
console.log('[ForceCalendar] viewRenderer.render() completed');
|
|
608
|
+
|
|
609
|
+
// Subscribe to state changes (store unsubscribe function)
|
|
610
|
+
this._viewUnsubscribe = this.stateManager.subscribe((newState, oldState) => {
|
|
611
|
+
// Only re-render on data changes, not view changes
|
|
612
|
+
if (newState.events !== oldState?.events ||
|
|
613
|
+
newState.currentDate !== oldState?.currentDate) {
|
|
614
|
+
if (viewRenderer && viewRenderer.render) {
|
|
615
|
+
viewRenderer.render();
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
} catch (err) {
|
|
621
|
+
console.error('[ForceCalendar] Error creating/rendering view:', err);
|
|
622
|
+
}
|
|
468
623
|
}
|
|
469
624
|
|
|
470
625
|
// Add event listeners for buttons using tracked addListener
|
|
@@ -510,6 +665,129 @@ export class ForceCalendar extends BaseComponent {
|
|
|
510
665
|
}
|
|
511
666
|
}
|
|
512
667
|
|
|
668
|
+
_createViewRenderer(viewName) {
|
|
669
|
+
// Create a simple view renderer that bypasses custom elements
|
|
670
|
+
// This is necessary for Salesforce Locker Service compatibility
|
|
671
|
+
const self = this;
|
|
672
|
+
|
|
673
|
+
return {
|
|
674
|
+
stateManager: null,
|
|
675
|
+
container: null,
|
|
676
|
+
_listeners: [],
|
|
677
|
+
|
|
678
|
+
cleanup() {
|
|
679
|
+
this._listeners.forEach(({ element, event, handler }) => {
|
|
680
|
+
element.removeEventListener(event, handler);
|
|
681
|
+
});
|
|
682
|
+
this._listeners = [];
|
|
683
|
+
},
|
|
684
|
+
|
|
685
|
+
addListener(element, event, handler) {
|
|
686
|
+
element.addEventListener(event, handler);
|
|
687
|
+
this._listeners.push({ element, event, handler });
|
|
688
|
+
},
|
|
689
|
+
|
|
690
|
+
render() {
|
|
691
|
+
console.log('[ViewRenderer] render called, container:', !!this.container, 'stateManager:', !!this.stateManager);
|
|
692
|
+
if (!this.container || !this.stateManager) return;
|
|
693
|
+
|
|
694
|
+
const viewData = this.stateManager.getViewData();
|
|
695
|
+
console.log('[ViewRenderer] viewData:', viewData);
|
|
696
|
+
console.log('[ViewRenderer] viewData.weeks:', viewData?.weeks);
|
|
697
|
+
|
|
698
|
+
if (!viewData || !viewData.weeks) {
|
|
699
|
+
this.container.innerHTML = '<div style="padding: 20px; text-align: center; background: #fee; color: #c00;">No viewData.weeks available. viewData keys: ' + (viewData ? Object.keys(viewData).join(', ') : 'null') + '</div>';
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
this.cleanup();
|
|
704
|
+
const config = this.stateManager.getState().config;
|
|
705
|
+
console.log('[ViewRenderer] Rendering month view with', viewData.weeks.length, 'weeks');
|
|
706
|
+
const html = this._renderMonthView(viewData, config);
|
|
707
|
+
console.log('[ViewRenderer] HTML length:', html.length);
|
|
708
|
+
this.container.innerHTML = html;
|
|
709
|
+
console.log('[ViewRenderer] innerHTML set, container children:', this.container.children.length);
|
|
710
|
+
this._attachEventHandlers();
|
|
711
|
+
},
|
|
712
|
+
|
|
713
|
+
_renderMonthView(viewData, config) {
|
|
714
|
+
const weekStartsOn = config.weekStartsOn || 0;
|
|
715
|
+
const dayNames = [];
|
|
716
|
+
for (let i = 0; i < 7; i++) {
|
|
717
|
+
const dayIndex = (weekStartsOn + i) % 7;
|
|
718
|
+
dayNames.push(['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][dayIndex]);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// Using inline styles for Locker Service compatibility
|
|
722
|
+
let html = `
|
|
723
|
+
<div class="fc-month-view" style="display: flex; flex-direction: column; height: 100%; min-height: 400px; background: #fff; border: 1px solid #e5e7eb;">
|
|
724
|
+
<div class="fc-month-header" style="display: grid; grid-template-columns: repeat(7, 1fr); border-bottom: 1px solid #e5e7eb; background: #f9fafb;">
|
|
725
|
+
${dayNames.map(d => `<div class="fc-month-header-cell" style="padding: 12px 8px; text-align: center; font-size: 11px; font-weight: 600; color: #6b7280; text-transform: uppercase;">${d}</div>`).join('')}
|
|
726
|
+
</div>
|
|
727
|
+
<div class="fc-month-body" style="display: flex; flex-direction: column; flex: 1;">
|
|
728
|
+
`;
|
|
729
|
+
|
|
730
|
+
viewData.weeks.forEach(week => {
|
|
731
|
+
html += '<div class="fc-month-week" style="display: grid; grid-template-columns: repeat(7, 1fr); flex: 1; min-height: 80px;">';
|
|
732
|
+
week.days.forEach(day => {
|
|
733
|
+
const isOtherMonth = !day.isCurrentMonth;
|
|
734
|
+
const isToday = day.isToday;
|
|
735
|
+
|
|
736
|
+
const dayBg = isOtherMonth ? '#f3f4f6' : '#fff';
|
|
737
|
+
const dayNumColor = isOtherMonth ? '#9ca3af' : '#111827';
|
|
738
|
+
const todayStyle = isToday ? 'background: #2563eb; color: white; border-radius: 50%; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center;' : '';
|
|
739
|
+
|
|
740
|
+
const events = day.events || [];
|
|
741
|
+
const visibleEvents = events.slice(0, 3);
|
|
742
|
+
const moreCount = events.length - 3;
|
|
743
|
+
|
|
744
|
+
html += `
|
|
745
|
+
<div class="fc-month-day" data-date="${day.date}" style="background: ${dayBg}; border-right: 1px solid #e5e7eb; border-bottom: 1px solid #e5e7eb; padding: 4px; min-height: 80px; cursor: pointer;">
|
|
746
|
+
<div class="fc-day-number" style="font-size: 13px; font-weight: 500; color: ${dayNumColor}; padding: 2px 4px; margin-bottom: 4px; ${todayStyle}">${day.dayOfMonth}</div>
|
|
747
|
+
<div class="fc-day-events" style="display: flex; flex-direction: column; gap: 2px;">
|
|
748
|
+
${visibleEvents.map(evt => `
|
|
749
|
+
<div class="fc-event" data-event-id="${evt.id}" style="background-color: ${evt.backgroundColor || '#2563eb'}; font-size: 11px; padding: 2px 6px; border-radius: 3px; color: white; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer;">
|
|
750
|
+
${evt.title}
|
|
751
|
+
</div>
|
|
752
|
+
`).join('')}
|
|
753
|
+
${moreCount > 0 ? `<div class="fc-more-events" style="font-size: 10px; color: #6b7280; padding: 2px 4px; font-weight: 500;">+${moreCount} more</div>` : ''}
|
|
754
|
+
</div>
|
|
755
|
+
</div>
|
|
756
|
+
`;
|
|
757
|
+
});
|
|
758
|
+
html += '</div>';
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
html += '</div></div>';
|
|
762
|
+
return html;
|
|
763
|
+
},
|
|
764
|
+
|
|
765
|
+
_attachEventHandlers() {
|
|
766
|
+
const stateManager = this.stateManager;
|
|
767
|
+
|
|
768
|
+
// Day click handlers
|
|
769
|
+
this.container.querySelectorAll('.fc-month-day').forEach(dayEl => {
|
|
770
|
+
this.addListener(dayEl, 'click', (e) => {
|
|
771
|
+
const date = new Date(dayEl.dataset.date);
|
|
772
|
+
stateManager.selectDate(date);
|
|
773
|
+
});
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
// Event click handlers
|
|
777
|
+
this.container.querySelectorAll('.fc-event').forEach(eventEl => {
|
|
778
|
+
this.addListener(eventEl, 'click', (e) => {
|
|
779
|
+
e.stopPropagation();
|
|
780
|
+
const eventId = eventEl.dataset.eventId;
|
|
781
|
+
const event = stateManager.getEvents().find(ev => ev.id === eventId);
|
|
782
|
+
if (event) {
|
|
783
|
+
stateManager.selectEvent(event);
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
|
|
513
791
|
handleNavigation(event) {
|
|
514
792
|
const action = event.currentTarget.dataset.action;
|
|
515
793
|
switch (action) {
|