@forcecalendar/interface 1.0.27 → 1.0.29
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/README.md +9 -0
- package/dist/force-calendar-interface.esm.js +102 -55
- package/dist/force-calendar-interface.esm.js.map +1 -1
- package/dist/force-calendar-interface.umd.js.map +1 -1
- package/package.json +3 -1
- package/src/components/EventForm.js +180 -176
- package/src/components/ForceCalendar.js +416 -394
- package/src/core/BaseComponent.js +146 -144
- package/src/core/EventBus.js +197 -197
- package/src/core/StateManager.js +405 -399
- package/src/index.js +3 -3
- package/src/renderers/BaseViewRenderer.js +195 -193
- package/src/renderers/DayViewRenderer.js +133 -118
- package/src/renderers/MonthViewRenderer.js +74 -72
- package/src/renderers/WeekViewRenderer.js +118 -96
- package/src/utils/DOMUtils.js +277 -277
- package/src/utils/DateUtils.js +164 -164
- package/src/utils/StyleUtils.js +286 -249
|
@@ -7,92 +7,94 @@
|
|
|
7
7
|
import { BaseViewRenderer } from './BaseViewRenderer.js';
|
|
8
8
|
|
|
9
9
|
export class DayViewRenderer extends BaseViewRenderer {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
constructor(container, stateManager) {
|
|
11
|
+
super(container, stateManager);
|
|
12
|
+
this.hourHeight = 60; // pixels per hour
|
|
13
|
+
this.totalHeight = 24 * this.hourHeight; // 1440px for 24 hours
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
render() {
|
|
17
|
+
if (!this.container || !this.stateManager) return;
|
|
18
|
+
|
|
19
|
+
const viewData = this.stateManager.getViewData();
|
|
20
|
+
if (!viewData) {
|
|
21
|
+
this.container.innerHTML =
|
|
22
|
+
'<div style="padding: 20px; text-align: center; color: #666;">No data available for day view.</div>';
|
|
23
|
+
return;
|
|
14
24
|
}
|
|
15
25
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
this.cleanup();
|
|
26
|
-
const config = this.stateManager.getState().config;
|
|
27
|
-
const html = this._renderDayView(viewData, config);
|
|
28
|
-
this.container.innerHTML = html;
|
|
29
|
-
this._attachEventHandlers();
|
|
30
|
-
this._scrollToCurrentTime();
|
|
31
|
-
}
|
|
26
|
+
this.cleanup();
|
|
27
|
+
const config = this.stateManager.getState().config;
|
|
28
|
+
const html = this._renderDayView(viewData, config);
|
|
29
|
+
this.container.innerHTML = html;
|
|
30
|
+
this._attachEventHandlers();
|
|
31
|
+
this._scrollToCurrentTime();
|
|
32
|
+
}
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
_renderDayView(viewData, _config) {
|
|
35
|
+
const currentDate = this.stateManager?.getState()?.currentDate || new Date();
|
|
36
|
+
const dayData = this._extractDayData(viewData, currentDate);
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
if (!dayData) {
|
|
39
|
+
return '<div style="padding: 20px; text-align: center; color: #666;">No data available for day view.</div>';
|
|
40
|
+
}
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
const { dayDate, dayName, isToday, allDayEvents, timedEvents } = dayData;
|
|
43
|
+
const hours = Array.from({ length: 24 }, (_, i) => i);
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
return `
|
|
45
46
|
<div class="fc-day-view" style="display: flex; flex-direction: column; height: 100%; background: #fff; overflow: hidden;">
|
|
46
47
|
${this._renderHeader(dayDate, dayName, isToday)}
|
|
47
48
|
${this._renderAllDayRow(allDayEvents, dayDate)}
|
|
48
49
|
${this._renderTimeGrid(timedEvents, isToday, dayDate, hours)}
|
|
49
50
|
</div>
|
|
50
51
|
`;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
timedEvents = Array.from(eventMap.values());
|
|
75
|
-
} else {
|
|
76
|
-
timedEvents = [];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
_extractDayData(viewData, currentDate) {
|
|
55
|
+
let dayDate, dayName, isToday, allDayEvents, timedEvents;
|
|
56
|
+
const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
|
57
|
+
|
|
58
|
+
if (viewData.type === 'day' && viewData.date) {
|
|
59
|
+
// Core day view structure
|
|
60
|
+
dayDate = new Date(viewData.date);
|
|
61
|
+
dayName = viewData.dayName || dayNames[dayDate.getDay()];
|
|
62
|
+
isToday = viewData.isToday !== undefined ? viewData.isToday : this.isToday(dayDate);
|
|
63
|
+
allDayEvents = viewData.allDayEvents || [];
|
|
64
|
+
|
|
65
|
+
// Extract timed events from hours array
|
|
66
|
+
if (viewData.hours && Array.isArray(viewData.hours)) {
|
|
67
|
+
const eventMap = new Map();
|
|
68
|
+
viewData.hours.forEach(hour => {
|
|
69
|
+
(hour.events || []).forEach(evt => {
|
|
70
|
+
if (!eventMap.has(evt.id)) {
|
|
71
|
+
eventMap.set(evt.id, evt);
|
|
77
72
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
timedEvents = Array.from(eventMap.values());
|
|
76
|
+
} else {
|
|
77
|
+
timedEvents = [];
|
|
78
|
+
}
|
|
79
|
+
} else if (viewData.days && viewData.days.length > 0) {
|
|
80
|
+
// Enriched structure with days array
|
|
81
|
+
const dayDataItem =
|
|
82
|
+
viewData.days.find(d => this.isSameDay(new Date(d.date), currentDate)) || viewData.days[0];
|
|
83
|
+
dayDate = new Date(dayDataItem.date);
|
|
84
|
+
dayName = dayNames[dayDate.getDay()];
|
|
85
|
+
isToday = this.isToday(dayDate);
|
|
86
|
+
const events = dayDataItem.events || [];
|
|
87
|
+
allDayEvents = events.filter(e => e.allDay);
|
|
88
|
+
timedEvents = events.filter(e => !e.allDay);
|
|
89
|
+
} else {
|
|
90
|
+
return null;
|
|
92
91
|
}
|
|
93
92
|
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
return { dayDate, dayName, isToday, allDayEvents, timedEvents };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
_renderHeader(dayDate, dayName, isToday) {
|
|
97
|
+
return `
|
|
96
98
|
<div class="fc-day-header" style="display: grid; grid-template-columns: 60px 1fr; border-bottom: 1px solid #e5e7eb; background: #f9fafb; flex-shrink: 0;">
|
|
97
99
|
<div style="border-right: 1px solid #e5e7eb;"></div>
|
|
98
100
|
<div style="padding: 16px 24px;">
|
|
@@ -105,28 +107,32 @@ export class DayViewRenderer extends BaseViewRenderer {
|
|
|
105
107
|
</div>
|
|
106
108
|
</div>
|
|
107
109
|
`;
|
|
108
|
-
|
|
110
|
+
}
|
|
109
111
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
+
_renderAllDayRow(allDayEvents, dayDate) {
|
|
113
|
+
return `
|
|
112
114
|
<div class="fc-all-day-row" style="display: grid; grid-template-columns: 60px 1fr; border-bottom: 1px solid #e5e7eb; background: #fafafa; min-height: 36px; flex-shrink: 0;">
|
|
113
115
|
<div style="font-size: 9px; color: #6b7280; display: flex; align-items: center; justify-content: center; border-right: 1px solid #e5e7eb; text-transform: uppercase; font-weight: 700;">
|
|
114
116
|
All day
|
|
115
117
|
</div>
|
|
116
118
|
<div class="fc-all-day-cell" data-date="${dayDate.toISOString()}" style="padding: 6px 12px; display: flex; flex-wrap: wrap; gap: 4px;">
|
|
117
|
-
${allDayEvents
|
|
119
|
+
${allDayEvents
|
|
120
|
+
.map(
|
|
121
|
+
evt => `
|
|
118
122
|
<div class="fc-event fc-all-day-event" data-event-id="${this.escapeHTML(evt.id)}"
|
|
119
123
|
style="background-color: ${this.getEventColor(evt)}; font-size: 12px; padding: 4px 8px; border-radius: 4px; color: white; cursor: pointer; font-weight: 500;">
|
|
120
124
|
${this.escapeHTML(evt.title)}
|
|
121
125
|
</div>
|
|
122
|
-
`
|
|
126
|
+
`
|
|
127
|
+
)
|
|
128
|
+
.join('')}
|
|
123
129
|
</div>
|
|
124
130
|
</div>
|
|
125
131
|
`;
|
|
126
|
-
|
|
132
|
+
}
|
|
127
133
|
|
|
128
|
-
|
|
129
|
-
|
|
134
|
+
_renderTimeGrid(timedEvents, isToday, dayDate, hours) {
|
|
135
|
+
return `
|
|
130
136
|
<div id="day-scroll-container" class="fc-time-grid-container" style="flex: 1; overflow-y: auto; overflow-x: hidden; position: relative;">
|
|
131
137
|
<div class="fc-time-grid" style="display: grid; grid-template-columns: 60px 1fr; position: relative; height: ${this.totalHeight}px;">
|
|
132
138
|
${this._renderTimeGutter(hours)}
|
|
@@ -134,22 +140,26 @@ export class DayViewRenderer extends BaseViewRenderer {
|
|
|
134
140
|
</div>
|
|
135
141
|
</div>
|
|
136
142
|
`;
|
|
137
|
-
|
|
143
|
+
}
|
|
138
144
|
|
|
139
|
-
|
|
140
|
-
|
|
145
|
+
_renderTimeGutter(hours) {
|
|
146
|
+
return `
|
|
141
147
|
<div class="fc-time-gutter" style="border-right: 1px solid #e5e7eb; background: #fafafa;">
|
|
142
|
-
${hours
|
|
148
|
+
${hours
|
|
149
|
+
.map(
|
|
150
|
+
h => `
|
|
143
151
|
<div style="height: ${this.hourHeight}px; font-size: 11px; color: #6b7280; text-align: right; padding-right: 12px; font-weight: 500;">
|
|
144
152
|
${h === 0 ? '' : this.formatHour(h)}
|
|
145
153
|
</div>
|
|
146
|
-
`
|
|
154
|
+
`
|
|
155
|
+
)
|
|
156
|
+
.join('')}
|
|
147
157
|
</div>
|
|
148
158
|
`;
|
|
149
|
-
|
|
159
|
+
}
|
|
150
160
|
|
|
151
|
-
|
|
152
|
-
|
|
161
|
+
_renderDayColumn(timedEvents, isToday, dayDate, hours) {
|
|
162
|
+
return `
|
|
153
163
|
<div class="fc-day-column" data-date="${dayDate.toISOString()}" style="position: relative; cursor: pointer;">
|
|
154
164
|
<!-- Hour grid lines -->
|
|
155
165
|
${hours.map(() => `<div style="height: ${this.hourHeight}px; border-bottom: 1px solid #f3f4f6;"></div>`).join('')}
|
|
@@ -161,38 +171,43 @@ export class DayViewRenderer extends BaseViewRenderer {
|
|
|
161
171
|
${timedEvents.map(evt => this.renderTimedEvent(evt, { compact: false })).join('')}
|
|
162
172
|
</div>
|
|
163
173
|
`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
_attachEventHandlers() {
|
|
177
|
+
this.addListener(this.container, 'click', e => {
|
|
178
|
+
const dayEl = e.target.closest('.fc-day-column');
|
|
179
|
+
if (!dayEl || !this.container.contains(dayEl)) return;
|
|
180
|
+
if (e.target.closest('.fc-event')) return;
|
|
181
|
+
|
|
182
|
+
const date = new Date(dayEl.dataset.date);
|
|
183
|
+
const rect = dayEl.getBoundingClientRect();
|
|
184
|
+
const scrollContainer = this.container.querySelector('#day-scroll-container');
|
|
185
|
+
const y = e.clientY - rect.top + (scrollContainer ? scrollContainer.scrollTop : 0);
|
|
186
|
+
|
|
187
|
+
// Calculate time from click position
|
|
188
|
+
date.setHours(
|
|
189
|
+
Math.floor(y / this.hourHeight),
|
|
190
|
+
Math.floor((y % this.hourHeight) / (this.hourHeight / 60)),
|
|
191
|
+
0,
|
|
192
|
+
0
|
|
193
|
+
);
|
|
194
|
+
this.stateManager.selectDate(date);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Common event handlers (event clicks)
|
|
198
|
+
this.attachCommonEventHandlers();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
_scrollToCurrentTime() {
|
|
202
|
+
if (this._scrolled) return;
|
|
203
|
+
|
|
204
|
+
const scrollContainer = this.container.querySelector('#day-scroll-container');
|
|
205
|
+
if (scrollContainer) {
|
|
206
|
+
// Scroll to 8 AM, minus some offset for visibility
|
|
207
|
+
scrollContainer.scrollTop = 8 * this.hourHeight - 50;
|
|
208
|
+
this._scrolled = true;
|
|
164
209
|
}
|
|
165
|
-
|
|
166
|
-
_attachEventHandlers() {
|
|
167
|
-
this.addListener(this.container, 'click', (e) => {
|
|
168
|
-
const dayEl = e.target.closest('.fc-day-column');
|
|
169
|
-
if (!dayEl || !this.container.contains(dayEl)) return;
|
|
170
|
-
if (e.target.closest('.fc-event')) return;
|
|
171
|
-
|
|
172
|
-
const date = new Date(dayEl.dataset.date);
|
|
173
|
-
const rect = dayEl.getBoundingClientRect();
|
|
174
|
-
const scrollContainer = this.container.querySelector('#day-scroll-container');
|
|
175
|
-
const y = e.clientY - rect.top + (scrollContainer ? scrollContainer.scrollTop : 0);
|
|
176
|
-
|
|
177
|
-
// Calculate time from click position
|
|
178
|
-
date.setHours(Math.floor(y / this.hourHeight), Math.floor((y % this.hourHeight) / (this.hourHeight / 60)), 0, 0);
|
|
179
|
-
this.stateManager.selectDate(date);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
// Common event handlers (event clicks)
|
|
183
|
-
this.attachCommonEventHandlers();
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
_scrollToCurrentTime() {
|
|
187
|
-
if (this._scrolled) return;
|
|
188
|
-
|
|
189
|
-
const scrollContainer = this.container.querySelector('#day-scroll-container');
|
|
190
|
-
if (scrollContainer) {
|
|
191
|
-
// Scroll to 8 AM, minus some offset for visibility
|
|
192
|
-
scrollContainer.scrollTop = 8 * this.hourHeight - 50;
|
|
193
|
-
this._scrolled = true;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
210
|
+
}
|
|
196
211
|
}
|
|
197
212
|
|
|
198
213
|
export default DayViewRenderer;
|
|
@@ -7,32 +7,33 @@
|
|
|
7
7
|
import { BaseViewRenderer } from './BaseViewRenderer.js';
|
|
8
8
|
|
|
9
9
|
export class MonthViewRenderer extends BaseViewRenderer {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
constructor(container, stateManager) {
|
|
11
|
+
super(container, stateManager);
|
|
12
|
+
this.maxEventsToShow = 3;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
render() {
|
|
16
|
+
if (!this.container || !this.stateManager) return;
|
|
17
|
+
|
|
18
|
+
const viewData = this.stateManager.getViewData();
|
|
19
|
+
if (!viewData || !viewData.weeks) {
|
|
20
|
+
this.container.innerHTML =
|
|
21
|
+
'<div style="padding: 20px; text-align: center; color: #666;">No data available for month view.</div>';
|
|
22
|
+
return;
|
|
13
23
|
}
|
|
14
24
|
|
|
15
|
-
|
|
16
|
-
|
|
25
|
+
this.cleanup();
|
|
26
|
+
const config = this.stateManager.getState().config;
|
|
27
|
+
const html = this._renderMonthView(viewData, config);
|
|
28
|
+
this.container.innerHTML = html;
|
|
29
|
+
this._attachEventHandlers();
|
|
30
|
+
}
|
|
17
31
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
32
|
+
_renderMonthView(viewData, config) {
|
|
33
|
+
const weekStartsOn = config.weekStartsOn || 0;
|
|
34
|
+
const dayNames = this._getDayNames(weekStartsOn);
|
|
23
35
|
|
|
24
|
-
|
|
25
|
-
const config = this.stateManager.getState().config;
|
|
26
|
-
const html = this._renderMonthView(viewData, config);
|
|
27
|
-
this.container.innerHTML = html;
|
|
28
|
-
this._attachEventHandlers();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
_renderMonthView(viewData, config) {
|
|
32
|
-
const weekStartsOn = config.weekStartsOn || 0;
|
|
33
|
-
const dayNames = this._getDayNames(weekStartsOn);
|
|
34
|
-
|
|
35
|
-
let html = `
|
|
36
|
+
let html = `
|
|
36
37
|
<div class="fc-month-view" style="display: flex; flex-direction: column; height: 100%; min-height: 400px; background: #fff; border: 1px solid #e5e7eb;">
|
|
37
38
|
<div class="fc-month-header" style="display: grid; grid-template-columns: repeat(7, 1fr); border-bottom: 1px solid #e5e7eb; background: #f9fafb;">
|
|
38
39
|
${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('')}
|
|
@@ -40,50 +41,51 @@ export class MonthViewRenderer extends BaseViewRenderer {
|
|
|
40
41
|
<div class="fc-month-body" style="display: flex; flex-direction: column; flex: 1;">
|
|
41
42
|
`;
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
viewData.weeks.forEach(week => {
|
|
45
|
+
html += this._renderWeek(week);
|
|
46
|
+
});
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
html += '</div></div>';
|
|
49
|
+
return html;
|
|
50
|
+
}
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
return dayNames;
|
|
52
|
+
_getDayNames(weekStartsOn) {
|
|
53
|
+
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
54
|
+
const dayNames = [];
|
|
55
|
+
for (let i = 0; i < 7; i++) {
|
|
56
|
+
const dayIndex = (weekStartsOn + i) % 7;
|
|
57
|
+
dayNames.push(days[dayIndex]);
|
|
59
58
|
}
|
|
59
|
+
return dayNames;
|
|
60
|
+
}
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
_renderWeek(week) {
|
|
63
|
+
let html =
|
|
64
|
+
'<div class="fc-month-week" style="display: grid; grid-template-columns: repeat(7, 1fr); flex: 1; min-height: 80px;">';
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
week.days.forEach(day => {
|
|
67
|
+
html += this._renderDay(day);
|
|
68
|
+
});
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
html += '</div>';
|
|
71
|
+
return html;
|
|
72
|
+
}
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
_renderDay(day) {
|
|
75
|
+
const isOtherMonth = !day.isCurrentMonth;
|
|
76
|
+
const isToday = day.isToday;
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
const dayBg = isOtherMonth ? '#f3f4f6' : '#fff';
|
|
79
|
+
const dayNumColor = isOtherMonth ? '#9ca3af' : '#111827';
|
|
80
|
+
const todayStyle = isToday
|
|
81
|
+
? 'background: #2563eb; color: white; border-radius: 50%; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center;'
|
|
82
|
+
: '';
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
const events = day.events || [];
|
|
85
|
+
const visibleEvents = events.slice(0, this.maxEventsToShow);
|
|
86
|
+
const moreCount = events.length - this.maxEventsToShow;
|
|
85
87
|
|
|
86
|
-
|
|
88
|
+
return `
|
|
87
89
|
<div class="fc-month-day" data-date="${day.date}"
|
|
88
90
|
style="background: ${dayBg}; border-right: 1px solid #e5e7eb; border-bottom: 1px solid #e5e7eb; padding: 4px; min-height: 80px; cursor: pointer; display: flex; flex-direction: column;">
|
|
89
91
|
<div class="fc-day-number" style="font-size: 13px; font-weight: 500; color: ${dayNumColor}; padding: 2px 4px; margin-bottom: 4px; ${todayStyle}">
|
|
@@ -95,31 +97,31 @@ export class MonthViewRenderer extends BaseViewRenderer {
|
|
|
95
97
|
</div>
|
|
96
98
|
</div>
|
|
97
99
|
`;
|
|
98
|
-
|
|
100
|
+
}
|
|
99
101
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
_renderEvent(event) {
|
|
103
|
+
const color = this.getEventColor(event);
|
|
104
|
+
return `
|
|
103
105
|
<div class="fc-event" data-event-id="${this.escapeHTML(event.id)}"
|
|
104
106
|
style="background-color: ${color}; font-size: 11px; padding: 2px 6px; border-radius: 3px; color: white; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer;">
|
|
105
107
|
${this.escapeHTML(event.title)}
|
|
106
108
|
</div>
|
|
107
109
|
`;
|
|
108
|
-
|
|
110
|
+
}
|
|
109
111
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
_attachEventHandlers() {
|
|
113
|
+
this.addListener(this.container, 'click', e => {
|
|
114
|
+
const dayEl = e.target.closest('.fc-month-day');
|
|
115
|
+
if (!dayEl || !this.container.contains(dayEl)) return;
|
|
116
|
+
if (e.target.closest('.fc-event')) return;
|
|
115
117
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
const date = new Date(dayEl.dataset.date);
|
|
119
|
+
this.stateManager.selectDate(date);
|
|
120
|
+
});
|
|
119
121
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
// Common event handlers (event clicks)
|
|
123
|
+
this.attachCommonEventHandlers();
|
|
124
|
+
}
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
export default MonthViewRenderer;
|