@forcecalendar/interface 1.0.27 → 1.0.28
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 +414 -392
- 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 -192
- 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,63 +7,66 @@
|
|
|
7
7
|
import { BaseViewRenderer } from './BaseViewRenderer.js';
|
|
8
8
|
|
|
9
9
|
export class WeekViewRenderer 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 || !viewData.days || viewData.days.length === 0) {
|
|
21
|
+
this.container.innerHTML =
|
|
22
|
+
'<div style="padding: 20px; text-align: center; color: #666;">No data available for week view.</div>';
|
|
23
|
+
return;
|
|
14
24
|
}
|
|
15
25
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
dayName: dayNames[dayDate.getDay()],
|
|
46
|
-
dayOfMonth: dayDate.getDate(),
|
|
47
|
-
isToday: this.isToday(dayDate),
|
|
48
|
-
timedEvents: events.filter(e => !e.allDay),
|
|
49
|
-
allDayEvents: events.filter(e => e.allDay)
|
|
50
|
-
};
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
return `
|
|
26
|
+
this.cleanup();
|
|
27
|
+
const config = this.stateManager.getState().config;
|
|
28
|
+
const html = this._renderWeekView(viewData, config);
|
|
29
|
+
this.container.innerHTML = html;
|
|
30
|
+
this._attachEventHandlers();
|
|
31
|
+
this._scrollToCurrentTime();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_renderWeekView(viewData, config) {
|
|
35
|
+
const days = viewData.days;
|
|
36
|
+
const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
37
|
+
const hours = Array.from({ length: 24 }, (_, i) => i);
|
|
38
|
+
|
|
39
|
+
// Process days to categorize events
|
|
40
|
+
const processedDays = days.map(day => {
|
|
41
|
+
const dayDate = new Date(day.date);
|
|
42
|
+
const events = day.events || [];
|
|
43
|
+
return {
|
|
44
|
+
...day,
|
|
45
|
+
date: dayDate,
|
|
46
|
+
dayName: dayNames[dayDate.getDay()],
|
|
47
|
+
dayOfMonth: dayDate.getDate(),
|
|
48
|
+
isToday: this.isToday(dayDate),
|
|
49
|
+
timedEvents: events.filter(e => !e.allDay),
|
|
50
|
+
allDayEvents: events.filter(e => e.allDay)
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return `
|
|
54
55
|
<div class="fc-week-view" style="display: flex; flex-direction: column; height: 100%; background: #fff; overflow: hidden;">
|
|
55
56
|
${this._renderHeader(processedDays)}
|
|
56
57
|
${this._renderAllDayRow(processedDays)}
|
|
57
58
|
${this._renderTimeGrid(processedDays, hours)}
|
|
58
59
|
</div>
|
|
59
60
|
`;
|
|
60
|
-
|
|
61
|
+
}
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
_renderHeader(days) {
|
|
64
|
+
return `
|
|
64
65
|
<div class="fc-week-header" style="display: grid; grid-template-columns: 60px repeat(7, 1fr); border-bottom: 1px solid #e5e7eb; background: #f9fafb; flex-shrink: 0;">
|
|
65
66
|
<div style="border-right: 1px solid #e5e7eb;"></div>
|
|
66
|
-
${days
|
|
67
|
+
${days
|
|
68
|
+
.map(
|
|
69
|
+
day => `
|
|
67
70
|
<div style="padding: 12px 8px; text-align: center; border-right: 1px solid #e5e7eb;">
|
|
68
71
|
<div style="font-size: 10px; font-weight: 700; color: #6b7280; text-transform: uppercase; letter-spacing: 0.1em;">
|
|
69
72
|
${day.dayName}
|
|
@@ -72,33 +75,43 @@ export class WeekViewRenderer extends BaseViewRenderer {
|
|
|
72
75
|
${day.dayOfMonth}
|
|
73
76
|
</div>
|
|
74
77
|
</div>
|
|
75
|
-
`
|
|
78
|
+
`
|
|
79
|
+
)
|
|
80
|
+
.join('')}
|
|
76
81
|
</div>
|
|
77
82
|
`;
|
|
78
|
-
|
|
83
|
+
}
|
|
79
84
|
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
_renderAllDayRow(days) {
|
|
86
|
+
return `
|
|
82
87
|
<div class="fc-all-day-row" style="display: grid; grid-template-columns: 60px repeat(7, 1fr); border-bottom: 1px solid #e5e7eb; background: #fafafa; min-height: 32px; flex-shrink: 0;">
|
|
83
88
|
<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;">
|
|
84
89
|
All day
|
|
85
90
|
</div>
|
|
86
|
-
${days
|
|
91
|
+
${days
|
|
92
|
+
.map(
|
|
93
|
+
day => `
|
|
87
94
|
<div class="fc-all-day-cell" data-date="${day.date.toISOString()}" style="border-right: 1px solid #e5e7eb; padding: 4px; display: flex; flex-direction: column; gap: 2px;">
|
|
88
|
-
${day.allDayEvents
|
|
95
|
+
${day.allDayEvents
|
|
96
|
+
.map(
|
|
97
|
+
evt => `
|
|
89
98
|
<div class="fc-event fc-all-day-event" data-event-id="${this.escapeHTML(evt.id)}"
|
|
90
99
|
style="background-color: ${this.getEventColor(evt)}; font-size: 10px; padding: 2px 4px; border-radius: 2px; color: white; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
|
91
100
|
${this.escapeHTML(evt.title)}
|
|
92
101
|
</div>
|
|
93
|
-
`
|
|
102
|
+
`
|
|
103
|
+
)
|
|
104
|
+
.join('')}
|
|
94
105
|
</div>
|
|
95
|
-
`
|
|
106
|
+
`
|
|
107
|
+
)
|
|
108
|
+
.join('')}
|
|
96
109
|
</div>
|
|
97
110
|
`;
|
|
98
|
-
|
|
111
|
+
}
|
|
99
112
|
|
|
100
|
-
|
|
101
|
-
|
|
113
|
+
_renderTimeGrid(days, hours) {
|
|
114
|
+
return `
|
|
102
115
|
<div id="week-scroll-container" class="fc-time-grid-container" style="flex: 1; overflow-y: auto; overflow-x: hidden; position: relative;">
|
|
103
116
|
<div class="fc-time-grid" style="display: grid; grid-template-columns: 60px repeat(7, 1fr); position: relative; height: ${this.totalHeight}px;">
|
|
104
117
|
${this._renderTimeGutter(hours)}
|
|
@@ -106,22 +119,26 @@ export class WeekViewRenderer extends BaseViewRenderer {
|
|
|
106
119
|
</div>
|
|
107
120
|
</div>
|
|
108
121
|
`;
|
|
109
|
-
|
|
122
|
+
}
|
|
110
123
|
|
|
111
|
-
|
|
112
|
-
|
|
124
|
+
_renderTimeGutter(hours) {
|
|
125
|
+
return `
|
|
113
126
|
<div class="fc-time-gutter" style="border-right: 1px solid #e5e7eb; background: #fafafa;">
|
|
114
|
-
${hours
|
|
127
|
+
${hours
|
|
128
|
+
.map(
|
|
129
|
+
h => `
|
|
115
130
|
<div style="height: ${this.hourHeight}px; font-size: 10px; color: #6b7280; text-align: right; padding-right: 8px; font-weight: 500;">
|
|
116
131
|
${h === 0 ? '' : this.formatHour(h)}
|
|
117
132
|
</div>
|
|
118
|
-
`
|
|
133
|
+
`
|
|
134
|
+
)
|
|
135
|
+
.join('')}
|
|
119
136
|
</div>
|
|
120
137
|
`;
|
|
121
|
-
|
|
138
|
+
}
|
|
122
139
|
|
|
123
|
-
|
|
124
|
-
|
|
140
|
+
_renderDayColumn(day, hours) {
|
|
141
|
+
return `
|
|
125
142
|
<div class="fc-week-day-column" data-date="${day.date.toISOString()}" style="border-right: 1px solid #e5e7eb; position: relative; cursor: pointer;">
|
|
126
143
|
<!-- Hour grid lines -->
|
|
127
144
|
${hours.map(() => `<div style="height: ${this.hourHeight}px; border-bottom: 1px solid #f3f4f6;"></div>`).join('')}
|
|
@@ -133,38 +150,43 @@ export class WeekViewRenderer extends BaseViewRenderer {
|
|
|
133
150
|
${day.timedEvents.map(evt => this.renderTimedEvent(evt, { compact: true })).join('')}
|
|
134
151
|
</div>
|
|
135
152
|
`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
_attachEventHandlers() {
|
|
156
|
+
this.addListener(this.container, 'click', e => {
|
|
157
|
+
const dayEl = e.target.closest('.fc-week-day-column');
|
|
158
|
+
if (!dayEl || !this.container.contains(dayEl)) return;
|
|
159
|
+
if (e.target.closest('.fc-event')) return;
|
|
160
|
+
|
|
161
|
+
const date = new Date(dayEl.dataset.date);
|
|
162
|
+
const rect = dayEl.getBoundingClientRect();
|
|
163
|
+
const scrollContainer = this.container.querySelector('#week-scroll-container');
|
|
164
|
+
const y = e.clientY - rect.top + (scrollContainer ? scrollContainer.scrollTop : 0);
|
|
165
|
+
|
|
166
|
+
// Calculate time from click position
|
|
167
|
+
date.setHours(
|
|
168
|
+
Math.floor(y / this.hourHeight),
|
|
169
|
+
Math.floor((y % this.hourHeight) / (this.hourHeight / 60)),
|
|
170
|
+
0,
|
|
171
|
+
0
|
|
172
|
+
);
|
|
173
|
+
this.stateManager.selectDate(date);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Common event handlers (event clicks)
|
|
177
|
+
this.attachCommonEventHandlers();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
_scrollToCurrentTime() {
|
|
181
|
+
if (this._scrolled) return;
|
|
182
|
+
|
|
183
|
+
const scrollContainer = this.container.querySelector('#week-scroll-container');
|
|
184
|
+
if (scrollContainer) {
|
|
185
|
+
// Scroll to 8 AM, minus some offset for visibility
|
|
186
|
+
scrollContainer.scrollTop = 8 * this.hourHeight - 50;
|
|
187
|
+
this._scrolled = true;
|
|
136
188
|
}
|
|
137
|
-
|
|
138
|
-
_attachEventHandlers() {
|
|
139
|
-
this.addListener(this.container, 'click', (e) => {
|
|
140
|
-
const dayEl = e.target.closest('.fc-week-day-column');
|
|
141
|
-
if (!dayEl || !this.container.contains(dayEl)) return;
|
|
142
|
-
if (e.target.closest('.fc-event')) return;
|
|
143
|
-
|
|
144
|
-
const date = new Date(dayEl.dataset.date);
|
|
145
|
-
const rect = dayEl.getBoundingClientRect();
|
|
146
|
-
const scrollContainer = this.container.querySelector('#week-scroll-container');
|
|
147
|
-
const y = e.clientY - rect.top + (scrollContainer ? scrollContainer.scrollTop : 0);
|
|
148
|
-
|
|
149
|
-
// Calculate time from click position
|
|
150
|
-
date.setHours(Math.floor(y / this.hourHeight), Math.floor((y % this.hourHeight) / (this.hourHeight / 60)), 0, 0);
|
|
151
|
-
this.stateManager.selectDate(date);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// Common event handlers (event clicks)
|
|
155
|
-
this.attachCommonEventHandlers();
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
_scrollToCurrentTime() {
|
|
159
|
-
if (this._scrolled) return;
|
|
160
|
-
|
|
161
|
-
const scrollContainer = this.container.querySelector('#week-scroll-container');
|
|
162
|
-
if (scrollContainer) {
|
|
163
|
-
// Scroll to 8 AM, minus some offset for visibility
|
|
164
|
-
scrollContainer.scrollTop = 8 * this.hourHeight - 50;
|
|
165
|
-
this._scrolled = true;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
189
|
+
}
|
|
168
190
|
}
|
|
169
191
|
|
|
170
192
|
export default WeekViewRenderer;
|