@primeui/scheduler-core 0.0.1-alpha.1
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/LICENSE +23 -0
- package/README.md +1 -0
- package/dist/calendar/index.d.mts +57 -0
- package/dist/calendar/index.mjs +1 -0
- package/dist/chunk-2B3YLWHA.mjs +196 -0
- package/dist/chunk-2THQAZ26.mjs +1669 -0
- package/dist/chunk-5KORIWDT.mjs +41 -0
- package/dist/chunk-5N4ZOBJV.mjs +866 -0
- package/dist/chunk-6OZAPQZ5.mjs +229 -0
- package/dist/chunk-6PK5WSKT.mjs +369 -0
- package/dist/chunk-6VYWVIGM.mjs +1170 -0
- package/dist/chunk-AAVM7UCG.mjs +100 -0
- package/dist/chunk-C7ADJGNV.mjs +157 -0
- package/dist/chunk-DYW6WUHE.mjs +277 -0
- package/dist/chunk-F5W5HD7S.mjs +285 -0
- package/dist/chunk-FIBAZFC4.mjs +871 -0
- package/dist/chunk-HPC5B3AR.mjs +558 -0
- package/dist/chunk-KQGRXTP5.mjs +650 -0
- package/dist/chunk-NMX4BW42.mjs +672 -0
- package/dist/chunk-NX46LPLF.mjs +440 -0
- package/dist/chunk-NZGJN7HG.mjs +314 -0
- package/dist/chunk-QDMZBJDV.mjs +251 -0
- package/dist/chunk-QR2SVYAD.mjs +1144 -0
- package/dist/chunk-SYJ5O4KH.mjs +136 -0
- package/dist/chunk-TNKJPFGI.mjs +569 -0
- package/dist/chunk-UMAMDBU4.mjs +1 -0
- package/dist/chunk-W2SJW3QQ.mjs +3925 -0
- package/dist/chunk-WFUJWDST.mjs +352 -0
- package/dist/chunk-XUBQ2IQS.mjs +1 -0
- package/dist/chunk-ZUKUKGNK.mjs +613 -0
- package/dist/controllers/index.d.mts +384 -0
- package/dist/controllers/index.mjs +13 -0
- package/dist/date-D_CjQPmM.d.mts +74 -0
- package/dist/display-format-CLVvRt4I.d.mts +57 -0
- package/dist/event/index.d.mts +267 -0
- package/dist/event/index.mjs +8 -0
- package/dist/event-surface-_R_LHD95.d.mts +21 -0
- package/dist/event.positioning-BdzAVPk7.d.mts +51 -0
- package/dist/event.utils-QSNdd-3W.d.mts +35 -0
- package/dist/index.d.mts +1128 -0
- package/dist/index.mjs +1022 -0
- package/dist/interaction/index.d.mts +442 -0
- package/dist/interaction/index.mjs +9 -0
- package/dist/month/index.d.mts +104 -0
- package/dist/month/index.mjs +6 -0
- package/dist/overlay-BYM9B6nC.d.mts +64 -0
- package/dist/resource/index.d.mts +172 -0
- package/dist/resource/index.mjs +1 -0
- package/dist/selection-CO_98HdS.d.mts +56 -0
- package/dist/time-grid/index.d.mts +92 -0
- package/dist/time-grid/index.mjs +13 -0
- package/dist/timeline/index.d.mts +165 -0
- package/dist/timeline/index.mjs +6 -0
- package/dist/touch-BhsMWsjf.d.mts +69 -0
- package/dist/utils/index.d.mts +494 -0
- package/dist/utils/index.mjs +17 -0
- package/dist/views/index.d.mts +51 -0
- package/dist/views/index.mjs +8 -0
- package/dist/views/timeline/index.d.mts +37 -0
- package/dist/views/timeline/index.mjs +4 -0
- package/dist/year/index.d.mts +70 -0
- package/dist/year/index.mjs +6 -0
- package/package.json +58 -0
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
import { isWeekend, isToday, addDays, addMonths, addHours, timeStringToMinutes, startOfDay, addWeeks, startOfWeek, getWeekdayNames, startOfMonth, endOfMonth, formatMonthYearTitle, addYears, getMonthNames } from './chunk-WFUJWDST.mjs';
|
|
2
|
+
import { createSchedulerDateFormatter, formatSlotTime, formatTime } from './chunk-TNKJPFGI.mjs';
|
|
3
|
+
|
|
4
|
+
// src/views/timeline/timeline.utils.ts
|
|
5
|
+
function generateTimelineHeaderCells(startDate, endDate, levels, locale, timeFormat, timeZone, displayConfig) {
|
|
6
|
+
const result = [];
|
|
7
|
+
for (const level of levels) {
|
|
8
|
+
const cells = generateHeaderCellsForLevel(startDate, endDate, level, locale, timeFormat, timeZone, displayConfig);
|
|
9
|
+
result.push(cells);
|
|
10
|
+
}
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
function generateHeaderCellsForLevel(startDate, endDate, level, locale, timeFormat, timeZone, displayConfig) {
|
|
14
|
+
const cells = [];
|
|
15
|
+
let current = new Date(startDate);
|
|
16
|
+
while (current < endDate) {
|
|
17
|
+
const { label, nextDate, colspan } = getHeaderCellData(current, endDate, level, locale, timeFormat, timeZone, displayConfig);
|
|
18
|
+
cells.push({
|
|
19
|
+
date: new Date(current),
|
|
20
|
+
label,
|
|
21
|
+
colspan,
|
|
22
|
+
unit: level.unit,
|
|
23
|
+
isToday: level.unit === "day" && isToday(current),
|
|
24
|
+
isWeekend: level.unit === "day" && isWeekend(current)
|
|
25
|
+
});
|
|
26
|
+
current = nextDate;
|
|
27
|
+
}
|
|
28
|
+
return cells;
|
|
29
|
+
}
|
|
30
|
+
function getHeaderCellData(date, endDate, level, locale, timeFormat, timeZone, displayConfig) {
|
|
31
|
+
const formatter = createSchedulerDateFormatter({ ...displayConfig, locale, timeZone });
|
|
32
|
+
const label = resolveHeaderCellLabel(date, level, formatter, locale, timeFormat, timeZone);
|
|
33
|
+
let nextDate;
|
|
34
|
+
let colspan = 1;
|
|
35
|
+
switch (level.unit) {
|
|
36
|
+
case "hour":
|
|
37
|
+
nextDate = addHours(date, 1);
|
|
38
|
+
break;
|
|
39
|
+
case "day":
|
|
40
|
+
nextDate = addDays(date, 1);
|
|
41
|
+
break;
|
|
42
|
+
case "week":
|
|
43
|
+
nextDate = addDays(date, 7);
|
|
44
|
+
colspan = 7;
|
|
45
|
+
break;
|
|
46
|
+
case "month":
|
|
47
|
+
nextDate = addMonths(date, 1);
|
|
48
|
+
colspan = getDaysInMonthRange(date, endDate);
|
|
49
|
+
break;
|
|
50
|
+
case "quarter":
|
|
51
|
+
nextDate = addMonths(date, 3);
|
|
52
|
+
colspan = 3;
|
|
53
|
+
break;
|
|
54
|
+
case "year":
|
|
55
|
+
nextDate = new Date(date.getFullYear() + 1, 0, 1);
|
|
56
|
+
colspan = 12;
|
|
57
|
+
break;
|
|
58
|
+
default:
|
|
59
|
+
nextDate = addDays(date, 1);
|
|
60
|
+
}
|
|
61
|
+
if (nextDate > endDate) {
|
|
62
|
+
nextDate = endDate;
|
|
63
|
+
}
|
|
64
|
+
return { label, nextDate, colspan };
|
|
65
|
+
}
|
|
66
|
+
function resolveHeaderCellLabel(date, level, formatter, locale, timeFormat, timeZone) {
|
|
67
|
+
if (level.unit === "hour" && !level.format) {
|
|
68
|
+
return formatSlotTime(date, locale, timeFormat, timeZone);
|
|
69
|
+
}
|
|
70
|
+
if (level.unit === "day" && !level.format) {
|
|
71
|
+
return formatter.formatWeekdayDayNumber(date);
|
|
72
|
+
}
|
|
73
|
+
return formatter.format(date, getFormatOptionsForUnit(level.unit, level.format));
|
|
74
|
+
}
|
|
75
|
+
function getFormatOptionsForUnit(unit, customFormat) {
|
|
76
|
+
if (customFormat) {
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
switch (unit) {
|
|
80
|
+
case "hour":
|
|
81
|
+
return { hour: "numeric", minute: "2-digit" };
|
|
82
|
+
case "day":
|
|
83
|
+
return { weekday: "short", day: "numeric" };
|
|
84
|
+
case "week":
|
|
85
|
+
return { month: "short", day: "numeric" };
|
|
86
|
+
case "month":
|
|
87
|
+
return { month: "long" };
|
|
88
|
+
case "quarter":
|
|
89
|
+
return { month: "short" };
|
|
90
|
+
case "year":
|
|
91
|
+
return { year: "numeric" };
|
|
92
|
+
default:
|
|
93
|
+
return {};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function getDaysInMonthRange(monthStart, endDate) {
|
|
97
|
+
const nextMonth = addMonths(monthStart, 1);
|
|
98
|
+
const monthEnd = nextMonth < endDate ? nextMonth : endDate;
|
|
99
|
+
const diffTime = monthEnd.getTime() - monthStart.getTime();
|
|
100
|
+
return Math.ceil(diffTime / (1e3 * 60 * 60 * 24));
|
|
101
|
+
}
|
|
102
|
+
function generateTimelineCells(startDate, endDate, options) {
|
|
103
|
+
const cells = [];
|
|
104
|
+
const { timelineSlotDuration, timelineSlotWidth, timelineMinTime, timelineMaxTime } = options;
|
|
105
|
+
const minMinutes = timeStringToMinutes(timelineMinTime);
|
|
106
|
+
const maxMinutes = timeStringToMinutes(timelineMaxTime);
|
|
107
|
+
const slotsPerDay = Math.ceil((maxMinutes - minMinutes) / timelineSlotDuration);
|
|
108
|
+
let current = startOfDay(startDate);
|
|
109
|
+
let index = 0;
|
|
110
|
+
while (current < endDate) {
|
|
111
|
+
for (let slot = 0; slot < slotsPerDay; slot++) {
|
|
112
|
+
const slotMinutes = minMinutes + slot * timelineSlotDuration;
|
|
113
|
+
const hours = Math.floor(slotMinutes / 60);
|
|
114
|
+
const minutes = slotMinutes % 60;
|
|
115
|
+
const slotStart = new Date(current);
|
|
116
|
+
slotStart.setHours(hours, minutes, 0, 0);
|
|
117
|
+
const slotEnd = new Date(slotStart);
|
|
118
|
+
slotEnd.setMinutes(slotEnd.getMinutes() + timelineSlotDuration);
|
|
119
|
+
cells.push({
|
|
120
|
+
date: new Date(current),
|
|
121
|
+
startTime: slotStart,
|
|
122
|
+
endTime: slotEnd,
|
|
123
|
+
isToday: isToday(current),
|
|
124
|
+
isWeekend: isWeekend(current),
|
|
125
|
+
isBusinessHour: isWithinBusinessHours(slotStart),
|
|
126
|
+
width: timelineSlotWidth,
|
|
127
|
+
index
|
|
128
|
+
});
|
|
129
|
+
index++;
|
|
130
|
+
}
|
|
131
|
+
current = addDays(current, 1);
|
|
132
|
+
}
|
|
133
|
+
return cells;
|
|
134
|
+
}
|
|
135
|
+
function generateDayCells(startDate, endDate, cellWidth) {
|
|
136
|
+
const cells = [];
|
|
137
|
+
let current = startOfDay(startDate);
|
|
138
|
+
let index = 0;
|
|
139
|
+
while (current < endDate) {
|
|
140
|
+
const dayEnd = addDays(current, 1);
|
|
141
|
+
cells.push({
|
|
142
|
+
date: new Date(current),
|
|
143
|
+
startTime: new Date(current),
|
|
144
|
+
endTime: dayEnd,
|
|
145
|
+
isToday: isToday(current),
|
|
146
|
+
isWeekend: isWeekend(current),
|
|
147
|
+
isBusinessHour: !isWeekend(current),
|
|
148
|
+
width: cellWidth,
|
|
149
|
+
index
|
|
150
|
+
});
|
|
151
|
+
current = dayEnd;
|
|
152
|
+
index++;
|
|
153
|
+
}
|
|
154
|
+
return cells;
|
|
155
|
+
}
|
|
156
|
+
function calculateTimelineEventPosition(event, viewStart, viewEnd, totalWidth, totalDuration) {
|
|
157
|
+
const eventStart = typeof event.start === "string" ? new Date(event.start) : event.start;
|
|
158
|
+
const eventEnd = event.end ? typeof event.end === "string" ? new Date(event.end) : event.end : addHours(eventStart, 1);
|
|
159
|
+
if (eventEnd <= viewStart || eventStart >= viewEnd) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
const clampedStart = eventStart < viewStart ? viewStart : eventStart;
|
|
163
|
+
const clampedEnd = eventEnd > viewEnd ? viewEnd : eventEnd;
|
|
164
|
+
const startOffset = clampedStart.getTime() - viewStart.getTime();
|
|
165
|
+
const eventDuration = clampedEnd.getTime() - clampedStart.getTime();
|
|
166
|
+
const left = startOffset / totalDuration * totalWidth;
|
|
167
|
+
const width = eventDuration / totalDuration * totalWidth;
|
|
168
|
+
return {
|
|
169
|
+
left,
|
|
170
|
+
width: Math.max(width, 20),
|
|
171
|
+
row: 0,
|
|
172
|
+
event,
|
|
173
|
+
continuesBefore: eventStart < viewStart,
|
|
174
|
+
continuesAfter: eventEnd > viewEnd
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function positionTimelineEvents(events, viewStart, viewEnd, totalWidth) {
|
|
178
|
+
const totalDuration = viewEnd.getTime() - viewStart.getTime();
|
|
179
|
+
const positions = [];
|
|
180
|
+
for (const event of events) {
|
|
181
|
+
const position = calculateTimelineEventPosition(event, viewStart, viewEnd, totalWidth, totalDuration);
|
|
182
|
+
if (position) {
|
|
183
|
+
positions.push(position);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return assignEventRows(positions);
|
|
187
|
+
}
|
|
188
|
+
function positionTimelineEventsWithBusinessHours(events, viewStart, viewEnd, totalWidth, minTimeMinutes, maxTimeMinutes) {
|
|
189
|
+
const businessMinutesPerDay = maxTimeMinutes - minTimeMinutes;
|
|
190
|
+
const msPerBusinessDay = businessMinutesPerDay * 60 * 1e3;
|
|
191
|
+
const dayCount = Math.ceil((viewEnd.getTime() - viewStart.getTime()) / (24 * 60 * 60 * 1e3));
|
|
192
|
+
const totalBusinessMs = dayCount * msPerBusinessDay;
|
|
193
|
+
const positions = [];
|
|
194
|
+
for (const event of events) {
|
|
195
|
+
const eventStart = typeof event.start === "string" ? new Date(event.start) : event.start;
|
|
196
|
+
const eventEnd = event.end ? typeof event.end === "string" ? new Date(event.end) : event.end : addHours(eventStart, 1);
|
|
197
|
+
if (eventEnd <= viewStart || eventStart >= viewEnd) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const startBusinessOffset = calculateBusinessTimeOffset(eventStart, viewStart, minTimeMinutes, maxTimeMinutes, msPerBusinessDay);
|
|
201
|
+
const endBusinessOffset = calculateBusinessTimeOffset(eventEnd, viewStart, minTimeMinutes, maxTimeMinutes, msPerBusinessDay);
|
|
202
|
+
const clampedStartOffset = Math.max(0, startBusinessOffset);
|
|
203
|
+
const clampedEndOffset = Math.min(totalBusinessMs, endBusinessOffset);
|
|
204
|
+
if (clampedEndOffset <= clampedStartOffset) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const left = clampedStartOffset / totalBusinessMs * totalWidth;
|
|
208
|
+
const width = (clampedEndOffset - clampedStartOffset) / totalBusinessMs * totalWidth;
|
|
209
|
+
positions.push({
|
|
210
|
+
left,
|
|
211
|
+
width: Math.max(width, 20),
|
|
212
|
+
row: 0,
|
|
213
|
+
event,
|
|
214
|
+
continuesBefore: eventStart < viewStart,
|
|
215
|
+
continuesAfter: eventEnd > viewEnd
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
return assignEventRows(positions);
|
|
219
|
+
}
|
|
220
|
+
function calculateBusinessTimeOffset(datetime, viewStart, minTimeMinutes, maxTimeMinutes, msPerBusinessDay) {
|
|
221
|
+
const viewStartDay = startOfDay(viewStart);
|
|
222
|
+
const targetDay = startOfDay(datetime);
|
|
223
|
+
const daysDiff = Math.floor((targetDay.getTime() - viewStartDay.getTime()) / (24 * 60 * 60 * 1e3));
|
|
224
|
+
const fullDaysOffset = daysDiff * msPerBusinessDay;
|
|
225
|
+
const timeMinutes = datetime.getHours() * 60 + datetime.getMinutes();
|
|
226
|
+
const clampedMinutes = Math.max(minTimeMinutes, Math.min(maxTimeMinutes, timeMinutes));
|
|
227
|
+
const minutesIntoBusinessDay = clampedMinutes - minTimeMinutes;
|
|
228
|
+
return fullDaysOffset + minutesIntoBusinessDay * 60 * 1e3;
|
|
229
|
+
}
|
|
230
|
+
function positionTimelineEventsForDayGranularity(events, viewStart, viewEnd, cellWidth) {
|
|
231
|
+
const positions = [];
|
|
232
|
+
const msPerDay = 24 * 60 * 60 * 1e3;
|
|
233
|
+
const viewStartTime = startOfDay(viewStart).getTime();
|
|
234
|
+
for (const event of events) {
|
|
235
|
+
const eventStart = typeof event.start === "string" ? new Date(event.start) : event.start;
|
|
236
|
+
const eventEnd = event.end ? typeof event.end === "string" ? new Date(event.end) : event.end : eventStart;
|
|
237
|
+
if (eventEnd <= viewStart || eventStart >= viewEnd) {
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
const eventStartDay = startOfDay(eventStart);
|
|
241
|
+
const eventEndDay = startOfDay(eventEnd);
|
|
242
|
+
const clampedStartDay = eventStartDay < viewStart ? startOfDay(viewStart) : eventStartDay;
|
|
243
|
+
const clampedEndDay = eventEndDay >= viewEnd ? startOfDay(addDays(viewEnd, -1)) : eventEndDay;
|
|
244
|
+
const startDayIndex = Math.floor((clampedStartDay.getTime() - viewStartTime) / msPerDay);
|
|
245
|
+
const endDayIndex = Math.floor((clampedEndDay.getTime() - viewStartTime) / msPerDay);
|
|
246
|
+
const left = startDayIndex * cellWidth;
|
|
247
|
+
const width = (endDayIndex - startDayIndex + 1) * cellWidth;
|
|
248
|
+
positions.push({
|
|
249
|
+
left,
|
|
250
|
+
width: Math.max(width, cellWidth),
|
|
251
|
+
row: 0,
|
|
252
|
+
event,
|
|
253
|
+
continuesBefore: eventStart < viewStart,
|
|
254
|
+
continuesAfter: eventEnd > viewEnd,
|
|
255
|
+
// Store day indices for overlap detection in day-granularity views
|
|
256
|
+
_startDayIndex: startDayIndex,
|
|
257
|
+
_endDayIndex: endDayIndex
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
return assignEventRowsForDayGranularity(positions);
|
|
261
|
+
}
|
|
262
|
+
function assignEventRowsForDayGranularity(positions) {
|
|
263
|
+
if (positions.length === 0) return positions;
|
|
264
|
+
const sortedPositions = [...positions].sort((a, b) => {
|
|
265
|
+
const aStart = a._startDayIndex;
|
|
266
|
+
const bStart = b._startDayIndex;
|
|
267
|
+
const aEnd = a._endDayIndex;
|
|
268
|
+
const bEnd = b._endDayIndex;
|
|
269
|
+
if (aStart !== bStart) {
|
|
270
|
+
return aStart - bStart;
|
|
271
|
+
}
|
|
272
|
+
const aSpan = aEnd - aStart;
|
|
273
|
+
const bSpan = bEnd - bStart;
|
|
274
|
+
if (aSpan !== bSpan) {
|
|
275
|
+
return bSpan - aSpan;
|
|
276
|
+
}
|
|
277
|
+
return String(a.event.id).localeCompare(String(b.event.id));
|
|
278
|
+
});
|
|
279
|
+
const rows = [];
|
|
280
|
+
for (const pos of sortedPositions) {
|
|
281
|
+
const startDay = pos._startDayIndex;
|
|
282
|
+
const endDay = pos._endDayIndex;
|
|
283
|
+
let assignedRow = -1;
|
|
284
|
+
for (let rowIdx = 0; rowIdx < rows.length; rowIdx++) {
|
|
285
|
+
const rowRanges = rows[rowIdx];
|
|
286
|
+
let hasOverlap = false;
|
|
287
|
+
for (const [existingStart, existingEnd] of rowRanges) {
|
|
288
|
+
if (startDay <= existingEnd && endDay >= existingStart) {
|
|
289
|
+
hasOverlap = true;
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (!hasOverlap) {
|
|
294
|
+
assignedRow = rowIdx;
|
|
295
|
+
rowRanges.push([startDay, endDay]);
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (assignedRow === -1) {
|
|
300
|
+
assignedRow = rows.length;
|
|
301
|
+
rows.push([[startDay, endDay]]);
|
|
302
|
+
}
|
|
303
|
+
pos.row = assignedRow;
|
|
304
|
+
}
|
|
305
|
+
return sortedPositions;
|
|
306
|
+
}
|
|
307
|
+
function getEventTimeRange(event) {
|
|
308
|
+
const eventStart = typeof event.start === "string" ? new Date(event.start) : event.start;
|
|
309
|
+
const eventEnd = event.end ? typeof event.end === "string" ? new Date(event.end) : event.end : new Date(eventStart.getTime() + 60 * 60 * 1e3);
|
|
310
|
+
return {
|
|
311
|
+
start: eventStart.getTime(),
|
|
312
|
+
end: eventEnd.getTime()
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
function timeRangesOverlap(a, b) {
|
|
316
|
+
return a.start < b.end && a.end > b.start;
|
|
317
|
+
}
|
|
318
|
+
function assignEventRows(positions) {
|
|
319
|
+
if (positions.length === 0) return positions;
|
|
320
|
+
const eventTimes = /* @__PURE__ */ new Map();
|
|
321
|
+
for (const pos of positions) {
|
|
322
|
+
eventTimes.set(pos.event.id, getEventTimeRange(pos.event));
|
|
323
|
+
}
|
|
324
|
+
const sortedPositions = [...positions].sort((a, b) => {
|
|
325
|
+
const aTime = eventTimes.get(a.event.id);
|
|
326
|
+
const bTime = eventTimes.get(b.event.id);
|
|
327
|
+
if (aTime.start !== bTime.start) {
|
|
328
|
+
return aTime.start - bTime.start;
|
|
329
|
+
}
|
|
330
|
+
const aDuration = aTime.end - aTime.start;
|
|
331
|
+
const bDuration = bTime.end - bTime.start;
|
|
332
|
+
if (aDuration !== bDuration) {
|
|
333
|
+
return bDuration - aDuration;
|
|
334
|
+
}
|
|
335
|
+
return String(a.event.id).localeCompare(String(b.event.id));
|
|
336
|
+
});
|
|
337
|
+
const rows = [];
|
|
338
|
+
for (const pos of sortedPositions) {
|
|
339
|
+
const eventTime = eventTimes.get(pos.event.id);
|
|
340
|
+
let assignedRow = -1;
|
|
341
|
+
for (let rowIdx = 0; rowIdx < rows.length; rowIdx++) {
|
|
342
|
+
const rowEvents = rows[rowIdx];
|
|
343
|
+
let hasOverlap = false;
|
|
344
|
+
for (const existingEvent of rowEvents) {
|
|
345
|
+
if (timeRangesOverlap(eventTime, existingEvent)) {
|
|
346
|
+
hasOverlap = true;
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (!hasOverlap) {
|
|
351
|
+
assignedRow = rowIdx;
|
|
352
|
+
rowEvents.push(eventTime);
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (assignedRow === -1) {
|
|
357
|
+
assignedRow = rows.length;
|
|
358
|
+
rows.push([eventTime]);
|
|
359
|
+
}
|
|
360
|
+
pos.row = assignedRow;
|
|
361
|
+
}
|
|
362
|
+
return sortedPositions;
|
|
363
|
+
}
|
|
364
|
+
function getEventsInDateRange(events, startDate, endDate) {
|
|
365
|
+
return events.filter((event) => {
|
|
366
|
+
const eventStart = typeof event.start === "string" ? new Date(event.start) : event.start;
|
|
367
|
+
const eventEnd = event.end ? typeof event.end === "string" ? new Date(event.end) : event.end : eventStart;
|
|
368
|
+
return eventStart < endDate && eventEnd > startDate;
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
function getDefaultTimelineHeaderLevels(viewType) {
|
|
372
|
+
switch (viewType) {
|
|
373
|
+
case "timelineDay":
|
|
374
|
+
return [{ unit: "hour", height: 40 }];
|
|
375
|
+
case "timelineWeek":
|
|
376
|
+
return [
|
|
377
|
+
{ unit: "day", height: 30 },
|
|
378
|
+
{ unit: "hour", height: 30 }
|
|
379
|
+
];
|
|
380
|
+
case "timelineMonth":
|
|
381
|
+
return [
|
|
382
|
+
{ unit: "week", height: 30 },
|
|
383
|
+
{ unit: "day", height: 30 }
|
|
384
|
+
];
|
|
385
|
+
case "timelineYear":
|
|
386
|
+
return [{ unit: "month", height: 40 }];
|
|
387
|
+
default:
|
|
388
|
+
return [{ unit: "day", height: 40 }];
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function isWithinBusinessHours(date) {
|
|
392
|
+
const day = date.getDay();
|
|
393
|
+
const isWeekday = day >= 1 && day <= 5;
|
|
394
|
+
const hour = date.getHours();
|
|
395
|
+
const isBusinessHour = hour >= 9 && hour < 17;
|
|
396
|
+
return isWeekday && isBusinessHour;
|
|
397
|
+
}
|
|
398
|
+
function formatTimelineTitle(startDate, endDate, viewType, locale, displayConfig) {
|
|
399
|
+
const formatter = createSchedulerDateFormatter({ ...displayConfig, locale });
|
|
400
|
+
switch (viewType) {
|
|
401
|
+
case "timelineDay":
|
|
402
|
+
return formatter.format(startDate, { weekday: "long", month: "long", day: "numeric", year: "numeric" });
|
|
403
|
+
case "timelineWeek": {
|
|
404
|
+
const weekStart = formatter.format(startDate, { month: "short", day: "numeric" });
|
|
405
|
+
const weekEnd = formatter.format(endDate, { month: "short", day: "numeric", year: "numeric" });
|
|
406
|
+
return `${weekStart} - ${weekEnd}`;
|
|
407
|
+
}
|
|
408
|
+
case "timelineMonth":
|
|
409
|
+
return formatter.formatMonthTitle(startDate);
|
|
410
|
+
case "timelineYear":
|
|
411
|
+
return formatter.formatYear(startDate);
|
|
412
|
+
default:
|
|
413
|
+
return formatter.formatMonthTitle(startDate);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// src/views/timeline/timeline-day.view.ts
|
|
418
|
+
var timelineDayView = {
|
|
419
|
+
name: "timelineDay",
|
|
420
|
+
getTitle(date, locale) {
|
|
421
|
+
return formatTimelineTitle(date, addDays(date, 1), "timelineDay", locale);
|
|
422
|
+
},
|
|
423
|
+
getDateRange(date) {
|
|
424
|
+
const dayStart = startOfDay(date);
|
|
425
|
+
const dayEnd = addDays(dayStart, 1);
|
|
426
|
+
return { start: dayStart, end: dayEnd };
|
|
427
|
+
},
|
|
428
|
+
navigate(date, direction) {
|
|
429
|
+
return addDays(date, direction);
|
|
430
|
+
},
|
|
431
|
+
navigateToDate(target) {
|
|
432
|
+
return startOfDay(target);
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
function getTimelineDayViewData(date, options) {
|
|
436
|
+
const { locale, events, timelineSlotDuration, timelineSlotWidth, timelineMinTime, timelineMaxTime, timelineHeaderLevels, calendar, numberingSystem } = options;
|
|
437
|
+
const displayConfig = { calendar, numberingSystem };
|
|
438
|
+
const dayStart = startOfDay(date);
|
|
439
|
+
const dayEnd = addDays(dayStart, 1);
|
|
440
|
+
const minMinutes = timeStringToMinutes(timelineMinTime);
|
|
441
|
+
const maxMinutes = timeStringToMinutes(timelineMaxTime);
|
|
442
|
+
const totalMinutes = maxMinutes - minMinutes;
|
|
443
|
+
const slotsCount = Math.ceil(totalMinutes / timelineSlotDuration);
|
|
444
|
+
const totalWidth = slotsCount * timelineSlotWidth;
|
|
445
|
+
const visibleStart = new Date(dayStart);
|
|
446
|
+
visibleStart.setHours(Math.floor(minMinutes / 60), minMinutes % 60, 0, 0);
|
|
447
|
+
const visibleEnd = new Date(dayStart);
|
|
448
|
+
visibleEnd.setHours(Math.floor(maxMinutes / 60), maxMinutes % 60, 0, 0);
|
|
449
|
+
const headerLevels = timelineHeaderLevels || getDefaultTimelineHeaderLevels("timelineDay");
|
|
450
|
+
const headerCells = generateTimelineHeaderCells(dayStart, dayEnd, headerLevels, locale, options.timeFormat, options.timezone, displayConfig);
|
|
451
|
+
const cells = generateTimelineCells(dayStart, dayEnd, options);
|
|
452
|
+
const dayEvents = getEventsInDateRange(events, dayStart, dayEnd);
|
|
453
|
+
const eventPositions = positionTimelineEventsWithBusinessHours(dayEvents, dayStart, dayEnd, totalWidth, minMinutes, maxMinutes);
|
|
454
|
+
return {
|
|
455
|
+
date: dayStart,
|
|
456
|
+
title: formatTimelineTitle(dayStart, dayEnd, "timelineDay", locale, displayConfig),
|
|
457
|
+
headerLevels: headerCells,
|
|
458
|
+
cells,
|
|
459
|
+
timeSlots: [],
|
|
460
|
+
events: eventPositions,
|
|
461
|
+
totalWidth,
|
|
462
|
+
dateRange: { start: visibleStart, end: visibleEnd }
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// src/views/timeline/timeline-week.view.ts
|
|
467
|
+
var timelineWeekView = {
|
|
468
|
+
name: "timelineWeek",
|
|
469
|
+
getTitle(date, locale) {
|
|
470
|
+
const weekStart = startOfWeek(date, 0);
|
|
471
|
+
const weekEnd = addDays(weekStart, 6);
|
|
472
|
+
return formatTimelineTitle(weekStart, weekEnd, "timelineWeek", locale);
|
|
473
|
+
},
|
|
474
|
+
getDateRange(date, options) {
|
|
475
|
+
const weekStart = startOfWeek(date, options.firstDayOfWeek);
|
|
476
|
+
const weekEnd = addDays(weekStart, 7);
|
|
477
|
+
return { start: weekStart, end: weekEnd };
|
|
478
|
+
},
|
|
479
|
+
navigate(date, direction) {
|
|
480
|
+
return addWeeks(date, direction);
|
|
481
|
+
},
|
|
482
|
+
navigateToDate(target) {
|
|
483
|
+
return new Date(target);
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
function getTimelineWeekViewData(date, options) {
|
|
487
|
+
const { locale, firstDayOfWeek, events, timelineSlotDuration, timelineSlotWidth, timelineMinTime, timelineMaxTime, timelineHeaderLevels, daysOfWeek, numberOfDays, calendar, numberingSystem } = options;
|
|
488
|
+
const displayConfig = { calendar, numberingSystem };
|
|
489
|
+
const totalDaysToCheck = numberOfDays ?? 7;
|
|
490
|
+
const startDate = numberOfDays ? date : startOfWeek(date, firstDayOfWeek);
|
|
491
|
+
const activeDays = daysOfWeek ?? [0, 1, 2, 3, 4, 5, 6];
|
|
492
|
+
const minMinutes = timeStringToMinutes(timelineMinTime);
|
|
493
|
+
const maxMinutes = timeStringToMinutes(timelineMaxTime);
|
|
494
|
+
const totalMinutes = maxMinutes - minMinutes;
|
|
495
|
+
const slotsPerDay = Math.ceil(totalMinutes / timelineSlotDuration);
|
|
496
|
+
const days = generateDayColumns(startDate, slotsPerDay, timelineSlotWidth, locale, activeDays, totalDaysToCheck, displayConfig);
|
|
497
|
+
const numDays = days.length;
|
|
498
|
+
const totalWidth = slotsPerDay * timelineSlotWidth * numDays;
|
|
499
|
+
const visibleDates = days.map((d) => d.date);
|
|
500
|
+
const actualStartDate = visibleDates[0] || startDate;
|
|
501
|
+
const actualEndDate = visibleDates.length > 0 ? addDays(visibleDates[visibleDates.length - 1], 1) : addDays(startDate, totalDaysToCheck);
|
|
502
|
+
const headerLevels = timelineHeaderLevels || getDefaultTimelineHeaderLevels("timelineWeek");
|
|
503
|
+
const headerCells = generateTimelineHeaderCells(actualStartDate, actualEndDate, headerLevels, locale, options.timeFormat, options.timezone, displayConfig);
|
|
504
|
+
const cells = generateTimelineCells(actualStartDate, actualEndDate, options);
|
|
505
|
+
const weekEvents = getEventsInDateRange(events, actualStartDate, actualEndDate).filter((event) => {
|
|
506
|
+
const eventStart = typeof event.start === "string" ? new Date(event.start) : event.start;
|
|
507
|
+
return activeDays.includes(eventStart.getDay());
|
|
508
|
+
});
|
|
509
|
+
const eventPositions = positionTimelineEventsWithBusinessHours(weekEvents, actualStartDate, actualEndDate, totalWidth, minMinutes, maxMinutes);
|
|
510
|
+
return {
|
|
511
|
+
startDate: actualStartDate,
|
|
512
|
+
endDate: actualEndDate,
|
|
513
|
+
title: formatTimelineTitle(actualStartDate, visibleDates[visibleDates.length - 1] || startDate, "timelineWeek", locale, displayConfig),
|
|
514
|
+
headerLevels: headerCells,
|
|
515
|
+
cells,
|
|
516
|
+
days,
|
|
517
|
+
events: eventPositions,
|
|
518
|
+
totalWidth,
|
|
519
|
+
dateRange: { start: actualStartDate, end: actualEndDate }
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
function generateDayColumns(startDate, slotsPerDay, slotWidth, locale, daysOfWeek, totalDays = 7, displayConfig) {
|
|
523
|
+
const columns = [];
|
|
524
|
+
const dayNames = getWeekdayNames(locale, "short", 0, displayConfig);
|
|
525
|
+
const dayWidth = slotsPerDay * slotWidth;
|
|
526
|
+
const activeDays = daysOfWeek ?? [0, 1, 2, 3, 4, 5, 6];
|
|
527
|
+
let columnIndex = 0;
|
|
528
|
+
for (let i = 0; i < totalDays; i++) {
|
|
529
|
+
const dayDate = addDays(startDate, i);
|
|
530
|
+
const dayOfWeek = dayDate.getDay();
|
|
531
|
+
if (!activeDays.includes(dayOfWeek)) continue;
|
|
532
|
+
columns.push({
|
|
533
|
+
date: dayDate,
|
|
534
|
+
dayName: dayNames[dayOfWeek],
|
|
535
|
+
dayNumber: dayDate.getDate(),
|
|
536
|
+
isToday: isToday(dayDate),
|
|
537
|
+
isWeekend: isWeekend(dayDate),
|
|
538
|
+
startIndex: columnIndex * slotsPerDay,
|
|
539
|
+
endIndex: (columnIndex + 1) * slotsPerDay - 1,
|
|
540
|
+
width: dayWidth
|
|
541
|
+
});
|
|
542
|
+
columnIndex++;
|
|
543
|
+
}
|
|
544
|
+
return columns;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// src/views/timeline/timeline-month.view.ts
|
|
548
|
+
var timelineMonthView = {
|
|
549
|
+
name: "timelineMonth",
|
|
550
|
+
getTitle(date, locale) {
|
|
551
|
+
return formatMonthYearTitle(date, locale);
|
|
552
|
+
},
|
|
553
|
+
getDateRange(date) {
|
|
554
|
+
const monthStart = startOfMonth(date);
|
|
555
|
+
const monthEnd = endOfMonth(date);
|
|
556
|
+
return { start: monthStart, end: addDays(monthEnd, 1) };
|
|
557
|
+
},
|
|
558
|
+
navigate(date, direction) {
|
|
559
|
+
return addMonths(date, direction);
|
|
560
|
+
},
|
|
561
|
+
navigateToDate(target) {
|
|
562
|
+
return startOfMonth(target);
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
function getTimelineMonthViewData(date, options) {
|
|
566
|
+
const { locale, events, timelineHeaderLevels, timelineSlotWidth = 60, calendar, numberingSystem } = options;
|
|
567
|
+
const displayConfig = { calendar, numberingSystem };
|
|
568
|
+
const cellWidth = timelineSlotWidth;
|
|
569
|
+
const monthStart = startOfMonth(date);
|
|
570
|
+
const monthEnd = endOfMonth(date);
|
|
571
|
+
const monthEndPlusOne = addDays(monthEnd, 1);
|
|
572
|
+
const daysInMonth = monthEnd.getDate();
|
|
573
|
+
const totalWidth = daysInMonth * cellWidth;
|
|
574
|
+
const headerLevels = timelineHeaderLevels || getDefaultTimelineHeaderLevels("timelineMonth");
|
|
575
|
+
const headerCells = generateTimelineHeaderCells(monthStart, monthEndPlusOne, headerLevels, locale, options.timeFormat, options.timezone, displayConfig);
|
|
576
|
+
const cells = generateDayCells(monthStart, monthEndPlusOne, cellWidth);
|
|
577
|
+
const days = generateMonthDays(monthStart, daysInMonth, cellWidth, locale, displayConfig);
|
|
578
|
+
const monthEvents = getEventsInDateRange(events, monthStart, monthEndPlusOne);
|
|
579
|
+
const eventPositions = positionTimelineEventsForDayGranularity(monthEvents, monthStart, monthEndPlusOne, cellWidth);
|
|
580
|
+
return {
|
|
581
|
+
year: date.getFullYear(),
|
|
582
|
+
month: date.getMonth(),
|
|
583
|
+
title: formatMonthYearTitle(date, locale, displayConfig),
|
|
584
|
+
headerLevels: headerCells,
|
|
585
|
+
cells,
|
|
586
|
+
days,
|
|
587
|
+
events: eventPositions,
|
|
588
|
+
totalWidth,
|
|
589
|
+
dateRange: { start: monthStart, end: monthEndPlusOne }
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
function generateMonthDays(monthStart, daysInMonth, cellWidth, locale, displayConfig) {
|
|
593
|
+
const days = [];
|
|
594
|
+
const dayNames = getWeekdayNames(locale, "short", 0, displayConfig);
|
|
595
|
+
for (let i = 0; i < daysInMonth; i++) {
|
|
596
|
+
const dayDate = addDays(monthStart, i);
|
|
597
|
+
days.push({
|
|
598
|
+
date: dayDate,
|
|
599
|
+
dayOfMonth: i + 1,
|
|
600
|
+
dayName: dayNames[dayDate.getDay()],
|
|
601
|
+
isToday: isToday(dayDate),
|
|
602
|
+
isWeekend: isWeekend(dayDate),
|
|
603
|
+
width: cellWidth,
|
|
604
|
+
index: i
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
return days;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// src/views/timeline/timeline-year.view.ts
|
|
611
|
+
var timelineYearView = {
|
|
612
|
+
name: "timelineYear",
|
|
613
|
+
getTitle(date, locale) {
|
|
614
|
+
return createSchedulerDateFormatter({ locale }).formatYear(date);
|
|
615
|
+
},
|
|
616
|
+
getDateRange(date) {
|
|
617
|
+
const yearStart = new Date(date.getFullYear(), 0, 1);
|
|
618
|
+
const yearEnd = new Date(date.getFullYear() + 1, 0, 1);
|
|
619
|
+
return { start: yearStart, end: yearEnd };
|
|
620
|
+
},
|
|
621
|
+
navigate(date, direction) {
|
|
622
|
+
return addYears(date, direction);
|
|
623
|
+
},
|
|
624
|
+
navigateToDate(target) {
|
|
625
|
+
return new Date(target.getFullYear(), 0, 1);
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
function getTimelineYearViewData(date, options) {
|
|
629
|
+
const { locale, events, timelineYearEventsPerRow = 2, timelineYearShowTime = true, timeFormat, timezone, calendar, numberingSystem } = options;
|
|
630
|
+
const displayConfig = { calendar, numberingSystem };
|
|
631
|
+
const year = date.getFullYear();
|
|
632
|
+
const yearStart = new Date(year, 0, 1);
|
|
633
|
+
const yearEnd = new Date(year + 1, 0, 1);
|
|
634
|
+
const monthNames = getMonthNames(locale, "long", displayConfig);
|
|
635
|
+
const shortMonthNames = getMonthNames(locale, "short", displayConfig);
|
|
636
|
+
const formatter = createSchedulerDateFormatter({ ...displayConfig, locale });
|
|
637
|
+
const months = [];
|
|
638
|
+
for (let monthIndex = 0; monthIndex < 12; monthIndex++) {
|
|
639
|
+
const monthStart = new Date(year, monthIndex, 1);
|
|
640
|
+
const monthEnd = endOfMonth(monthStart);
|
|
641
|
+
const monthEndPlusOne = addDays(monthEnd, 1);
|
|
642
|
+
const monthEvents = getEventsInDateRange(events, monthStart, monthEndPlusOne);
|
|
643
|
+
const visibleEvents = monthEvents.slice(0, timelineYearEventsPerRow);
|
|
644
|
+
const overflowCount = Math.max(0, monthEvents.length - timelineYearEventsPerRow);
|
|
645
|
+
const yearEvents = visibleEvents.map((event) => ({
|
|
646
|
+
event,
|
|
647
|
+
startTime: timelineYearShowTime ? formatEventTime(event.start, locale, timeFormat, timezone) : "",
|
|
648
|
+
endTime: timelineYearShowTime ? formatEventTime(event.end || event.start, locale, timeFormat, timezone) : ""
|
|
649
|
+
}));
|
|
650
|
+
months.push({
|
|
651
|
+
date: monthStart,
|
|
652
|
+
name: monthNames[monthIndex],
|
|
653
|
+
shortName: shortMonthNames[monthIndex],
|
|
654
|
+
monthIndex,
|
|
655
|
+
events: yearEvents,
|
|
656
|
+
overflowCount
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
return {
|
|
660
|
+
year,
|
|
661
|
+
title: formatter.formatYear(date),
|
|
662
|
+
months,
|
|
663
|
+
dateRange: { start: yearStart, end: yearEnd }
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
function formatEventTime(dateInput, locale, options, timeZone) {
|
|
667
|
+
if (!dateInput) return "";
|
|
668
|
+
const date = typeof dateInput === "string" ? new Date(dateInput) : dateInput;
|
|
669
|
+
return formatTime(date, locale, options, timeZone);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
export { calculateTimelineEventPosition, formatTimelineTitle, generateDayCells, generateTimelineCells, generateTimelineHeaderCells, getDefaultTimelineHeaderLevels, getEventsInDateRange, getTimelineDayViewData, getTimelineMonthViewData, getTimelineWeekViewData, getTimelineYearViewData, positionTimelineEvents, positionTimelineEventsForDayGranularity, positionTimelineEventsWithBusinessHours, timelineDayView, timelineMonthView, timelineWeekView, timelineYearView };
|