@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,650 @@
|
|
|
1
|
+
import { getDatePartsInTimezone, isSameDayInTimezone } from './chunk-QDMZBJDV.mjs';
|
|
2
|
+
import { startOfDay, addDays, isSameDay } from './chunk-WFUJWDST.mjs';
|
|
3
|
+
import { resolveSchedulerCalendarModeForView, getSchedulerCalendarAdapter } from './chunk-DYW6WUHE.mjs';
|
|
4
|
+
import { calculateResourceRowLayouts, getTotalResourceHeight } from './chunk-QR2SVYAD.mjs';
|
|
5
|
+
|
|
6
|
+
// src/timeline/layout.ts
|
|
7
|
+
var DEFAULT_TIMELINE_EVENT_MIN_HEIGHT = 36;
|
|
8
|
+
var TIMELINE_LANE_GAP = 2;
|
|
9
|
+
var TIMELINE_LANE_BASE_PADDING = 4;
|
|
10
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
11
|
+
function resolveTimelineEventMinHeight(value) {
|
|
12
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : DEFAULT_TIMELINE_EVENT_MIN_HEIGHT;
|
|
13
|
+
}
|
|
14
|
+
function getTimelineLaneStackHeight(laneCount, eventMinHeight, eventGap = TIMELINE_LANE_GAP) {
|
|
15
|
+
const gap = Number.isFinite(eventGap) && eventGap >= 0 ? eventGap : TIMELINE_LANE_GAP;
|
|
16
|
+
return TIMELINE_LANE_BASE_PADDING * 2 + Math.max(1, laneCount) * (eventMinHeight + gap);
|
|
17
|
+
}
|
|
18
|
+
function resolveTimelineResourceRowHeight(input) {
|
|
19
|
+
const eventHeight = resolveTimelineEventMinHeight(input.eventHeight);
|
|
20
|
+
const eventGap = typeof input.eventGap === "number" && Number.isFinite(input.eventGap) && input.eventGap >= 0 ? input.eventGap : TIMELINE_LANE_GAP;
|
|
21
|
+
const compactRowHeight = getTimelineLaneStackHeight(1, eventHeight, eventGap);
|
|
22
|
+
const minRowHeight = typeof input.minRowHeight === "number" && Number.isFinite(input.minRowHeight) && input.minRowHeight > 0 ? input.minRowHeight : compactRowHeight;
|
|
23
|
+
if (input.mode === "fixed") {
|
|
24
|
+
return minRowHeight;
|
|
25
|
+
}
|
|
26
|
+
return Math.max(minRowHeight, getTimelineLaneStackHeight(input.laneCount, eventHeight, eventGap));
|
|
27
|
+
}
|
|
28
|
+
function isTimelineMonthGranularityView(view) {
|
|
29
|
+
return view === "timelineMonth" || view === "resourceTimelineMonth";
|
|
30
|
+
}
|
|
31
|
+
function getTimelineVisibleDayStarts(view, start, end) {
|
|
32
|
+
const dates = [];
|
|
33
|
+
if (view === "timelineYear") return dates;
|
|
34
|
+
if (view === "timelineDay" || view === "resourceTimelineDay") {
|
|
35
|
+
dates.push(startOfDay(start));
|
|
36
|
+
return dates;
|
|
37
|
+
}
|
|
38
|
+
let current = startOfDay(start);
|
|
39
|
+
const stop = startOfDay(end);
|
|
40
|
+
const includeStopDay = end.getTime() > stop.getTime();
|
|
41
|
+
while (current < stop || includeStopDay && current.getTime() === stop.getTime()) {
|
|
42
|
+
dates.push(new Date(current));
|
|
43
|
+
current = addDays(current, 1);
|
|
44
|
+
}
|
|
45
|
+
return dates;
|
|
46
|
+
}
|
|
47
|
+
function getTimelineCalendarAdapter(view, calendar, locale) {
|
|
48
|
+
const mode = resolveSchedulerCalendarModeForView(calendar ?? "gregory", view);
|
|
49
|
+
return getSchedulerCalendarAdapter(mode, { locale });
|
|
50
|
+
}
|
|
51
|
+
function getCalendarMonthKey(date) {
|
|
52
|
+
return `${date.era ?? ""}:${date.year}:${date.month}`;
|
|
53
|
+
}
|
|
54
|
+
function buildTimelineCells(input) {
|
|
55
|
+
const result = [];
|
|
56
|
+
const { view, start, end, now, slotsPerDay, slotDurationMin, slotWidth, minTimeMin, dayWidth, timezone, labels } = input;
|
|
57
|
+
const calendarAdapter = getTimelineCalendarAdapter(view, input.calendar, input.locale);
|
|
58
|
+
const getDayState = (day) => {
|
|
59
|
+
if (timezone) {
|
|
60
|
+
const parts = getDatePartsInTimezone(day, timezone);
|
|
61
|
+
return {
|
|
62
|
+
isWeekend: parts.dayOfWeek === 0 || parts.dayOfWeek === 6,
|
|
63
|
+
isToday: isSameDayInTimezone(day, now, timezone)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const dow = day.getDay();
|
|
67
|
+
return {
|
|
68
|
+
isWeekend: dow === 0 || dow === 6,
|
|
69
|
+
isToday: isSameDay(day, now)
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
if (view === "timelineYear") {
|
|
73
|
+
const startCalendarDate = calendarAdapter.fromDate(start);
|
|
74
|
+
const yearStart = calendarAdapter.startOfMonth({ ...startCalendarDate, month: 1, day: 1 });
|
|
75
|
+
const monthsInYear = calendarAdapter.getMonthsInYear(startCalendarDate);
|
|
76
|
+
const nowCalendarDate = calendarAdapter.fromDate(now);
|
|
77
|
+
const monthCellWidth = Math.max(120, slotWidth);
|
|
78
|
+
for (let month = 0; month < monthsInYear; month++) {
|
|
79
|
+
const cellStartCalendarDate = calendarAdapter.add(yearStart, { months: month });
|
|
80
|
+
const cellEndCalendarDate = calendarAdapter.add(cellStartCalendarDate, { months: 1 });
|
|
81
|
+
const cellStart = calendarAdapter.toDate(cellStartCalendarDate);
|
|
82
|
+
const cellEnd = calendarAdapter.toDate(cellEndCalendarDate);
|
|
83
|
+
result.push({
|
|
84
|
+
startDate: cellStart,
|
|
85
|
+
endDate: cellEnd,
|
|
86
|
+
width: monthCellWidth,
|
|
87
|
+
label: labels.month(cellStart),
|
|
88
|
+
isToday: nowCalendarDate.year === cellStartCalendarDate.year && nowCalendarDate.month === cellStartCalendarDate.month,
|
|
89
|
+
isWeekend: false
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
if (isTimelineMonthGranularityView(view)) {
|
|
95
|
+
for (const day of getTimelineVisibleDayStarts(view, start, end)) {
|
|
96
|
+
const { isWeekend, isToday } = getDayState(day);
|
|
97
|
+
result.push({
|
|
98
|
+
startDate: startOfDay(day),
|
|
99
|
+
endDate: addDays(startOfDay(day), 1),
|
|
100
|
+
width: slotWidth,
|
|
101
|
+
label: labels.day(day),
|
|
102
|
+
isToday,
|
|
103
|
+
isWeekend
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
const pushSlotsForDay = (day) => {
|
|
109
|
+
const { isWeekend, isToday } = getDayState(day);
|
|
110
|
+
for (let slot = 0; slot < slotsPerDay; slot++) {
|
|
111
|
+
const slotStartMin = minTimeMin + slot * slotDurationMin;
|
|
112
|
+
const cellStart = new Date(day);
|
|
113
|
+
cellStart.setHours(0, 0, 0, 0);
|
|
114
|
+
cellStart.setMinutes(slotStartMin);
|
|
115
|
+
const cellEnd = new Date(cellStart);
|
|
116
|
+
cellEnd.setMinutes(cellEnd.getMinutes() + slotDurationMin);
|
|
117
|
+
result.push({
|
|
118
|
+
startDate: cellStart,
|
|
119
|
+
endDate: cellEnd,
|
|
120
|
+
width: slotWidth,
|
|
121
|
+
label: labels.hour(cellStart),
|
|
122
|
+
isToday,
|
|
123
|
+
isWeekend
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
const days = getTimelineVisibleDayStarts(view, start, end);
|
|
128
|
+
for (const day of days) {
|
|
129
|
+
pushSlotsForDay(day);
|
|
130
|
+
}
|
|
131
|
+
if (result.length === 0) {
|
|
132
|
+
const current = startOfDay(start);
|
|
133
|
+
const { isWeekend, isToday } = getDayState(current);
|
|
134
|
+
result.push({
|
|
135
|
+
startDate: new Date(current),
|
|
136
|
+
endDate: addDays(current, 1),
|
|
137
|
+
width: dayWidth,
|
|
138
|
+
label: labels.day(current),
|
|
139
|
+
isToday,
|
|
140
|
+
isWeekend
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
function buildTimelineHeaderTiers(input) {
|
|
146
|
+
const { view, cells, visibleDayStarts, dayWidth, labels } = input;
|
|
147
|
+
const calendarAdapter = getTimelineCalendarAdapter(view, input.calendar, input.locale);
|
|
148
|
+
if (cells.length === 0) return [];
|
|
149
|
+
const groupByDay = () => ({
|
|
150
|
+
cells: visibleDayStarts.map((day, index) => ({
|
|
151
|
+
label: labels.day(day),
|
|
152
|
+
width: dayWidth,
|
|
153
|
+
left: index * dayWidth,
|
|
154
|
+
startDate: new Date(day),
|
|
155
|
+
endDate: addDays(day, 1)
|
|
156
|
+
}))
|
|
157
|
+
});
|
|
158
|
+
const slotTier = () => {
|
|
159
|
+
let left = 0;
|
|
160
|
+
return {
|
|
161
|
+
cells: cells.map((cell) => {
|
|
162
|
+
const result = {
|
|
163
|
+
label: cell.label,
|
|
164
|
+
width: cell.width,
|
|
165
|
+
left,
|
|
166
|
+
startDate: cell.startDate,
|
|
167
|
+
endDate: cell.endDate
|
|
168
|
+
};
|
|
169
|
+
left += cell.width;
|
|
170
|
+
return result;
|
|
171
|
+
})
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
if (view === "timelineDay" || view === "resourceTimelineDay") {
|
|
175
|
+
return [slotTier()];
|
|
176
|
+
}
|
|
177
|
+
if (view === "timelineWeek" || view === "resourceTimelineWeek" || view === "timelineMonth" || view === "resourceTimelineMonth") {
|
|
178
|
+
const monthTier = { cells: [] };
|
|
179
|
+
const dayTier = groupByDay();
|
|
180
|
+
const hourTier = slotTier();
|
|
181
|
+
let currentMonthKey = "";
|
|
182
|
+
let monthWidth = 0;
|
|
183
|
+
let monthLeft = 0;
|
|
184
|
+
let timelineLeft = 0;
|
|
185
|
+
let monthAnchor = null;
|
|
186
|
+
for (const day of visibleDayStarts) {
|
|
187
|
+
const monthKey = getCalendarMonthKey(calendarAdapter.fromDate(day));
|
|
188
|
+
if (monthKey !== currentMonthKey) {
|
|
189
|
+
if (monthAnchor !== null) {
|
|
190
|
+
monthTier.cells.push({
|
|
191
|
+
label: labels.monthYear(monthAnchor),
|
|
192
|
+
width: monthWidth,
|
|
193
|
+
left: monthLeft,
|
|
194
|
+
startDate: monthAnchor,
|
|
195
|
+
endDate: day
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
currentMonthKey = monthKey;
|
|
199
|
+
monthWidth = 0;
|
|
200
|
+
monthLeft = timelineLeft;
|
|
201
|
+
monthAnchor = day;
|
|
202
|
+
}
|
|
203
|
+
monthWidth += dayWidth;
|
|
204
|
+
timelineLeft += dayWidth;
|
|
205
|
+
}
|
|
206
|
+
if (monthAnchor !== null) {
|
|
207
|
+
const lastDay = visibleDayStarts[visibleDayStarts.length - 1];
|
|
208
|
+
monthTier.cells.push({
|
|
209
|
+
label: labels.monthYear(monthAnchor),
|
|
210
|
+
width: monthWidth,
|
|
211
|
+
left: monthLeft,
|
|
212
|
+
startDate: monthAnchor,
|
|
213
|
+
endDate: addDays(lastDay, 1)
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return isTimelineMonthGranularityView(view) ? [monthTier, dayTier] : [monthTier, dayTier, hourTier];
|
|
217
|
+
}
|
|
218
|
+
if (view === "timelineYear") {
|
|
219
|
+
const quarterTier = { cells: [] };
|
|
220
|
+
const monthTier = { cells: [] };
|
|
221
|
+
let quarterLeft = 0;
|
|
222
|
+
for (let quarter = 0; quarter < Math.ceil(cells.length / 3); quarter++) {
|
|
223
|
+
const quarterCells = cells.slice(quarter * 3, quarter * 3 + 3);
|
|
224
|
+
const quarterWidth = quarterCells.reduce((sum, cell) => sum + cell.width, 0);
|
|
225
|
+
if (quarterCells.length > 0) {
|
|
226
|
+
quarterTier.cells.push({
|
|
227
|
+
label: labels.quarter(quarter),
|
|
228
|
+
width: quarterWidth,
|
|
229
|
+
left: quarterLeft,
|
|
230
|
+
startDate: quarterCells[0].startDate,
|
|
231
|
+
endDate: quarterCells[quarterCells.length - 1].endDate
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
quarterLeft += quarterWidth;
|
|
235
|
+
}
|
|
236
|
+
let monthLeft = 0;
|
|
237
|
+
for (const cell of cells) {
|
|
238
|
+
monthTier.cells.push({
|
|
239
|
+
label: cell.label,
|
|
240
|
+
width: cell.width,
|
|
241
|
+
left: monthLeft,
|
|
242
|
+
startDate: cell.startDate,
|
|
243
|
+
endDate: cell.endDate
|
|
244
|
+
});
|
|
245
|
+
monthLeft += cell.width;
|
|
246
|
+
}
|
|
247
|
+
return [quarterTier, monthTier];
|
|
248
|
+
}
|
|
249
|
+
return [slotTier()];
|
|
250
|
+
}
|
|
251
|
+
function filterTimelineLaneEvents(events, input) {
|
|
252
|
+
const { resourceId, matchIds, viewStart, viewEnd } = input;
|
|
253
|
+
return events.filter((event) => {
|
|
254
|
+
if (resourceId !== void 0) {
|
|
255
|
+
if (matchIds) {
|
|
256
|
+
const primary = event.resourceId;
|
|
257
|
+
const secondary = event.resourceIds;
|
|
258
|
+
const primaryMatch = primary != null && matchIds.has(primary);
|
|
259
|
+
const secondaryMatch = secondary ? secondary.some((rid) => matchIds.has(rid)) : false;
|
|
260
|
+
if (!primaryMatch && !secondaryMatch) return false;
|
|
261
|
+
} else if (event.resourceId !== resourceId && !(event.resourceIds && event.resourceIds.includes(resourceId))) {
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const eventStart = event.start instanceof Date ? event.start : new Date(event.start);
|
|
266
|
+
const eventEnd = event.end ? event.end instanceof Date ? event.end : new Date(event.end) : eventStart;
|
|
267
|
+
return eventStart < viewEnd && eventEnd > viewStart;
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
function timeToTimelinePixels(input) {
|
|
271
|
+
const { date, view, cells, visibleDayStarts, dayWidth, minTimeMin, visibleMinutesPerDay, totalWidth } = input;
|
|
272
|
+
if (view === "timelineYear") {
|
|
273
|
+
const first = cells[0]?.startDate;
|
|
274
|
+
const last = cells[cells.length - 1]?.endDate;
|
|
275
|
+
if (!first || !last) return 0;
|
|
276
|
+
const total = last.getTime() - first.getTime();
|
|
277
|
+
const offset = date.getTime() - first.getTime();
|
|
278
|
+
return offset / total * totalWidth;
|
|
279
|
+
}
|
|
280
|
+
if (visibleDayStarts.length === 0) return 0;
|
|
281
|
+
if (isTimelineMonthGranularityView(view)) {
|
|
282
|
+
const firstDay2 = visibleDayStarts[0].getTime();
|
|
283
|
+
const targetDay = startOfDay(date).getTime();
|
|
284
|
+
const dayIndex = Math.floor((targetDay - firstDay2) / DAY_MS);
|
|
285
|
+
return Math.min(Math.max(dayIndex * dayWidth, 0), totalWidth);
|
|
286
|
+
}
|
|
287
|
+
const target = date.getTime();
|
|
288
|
+
const firstDay = visibleDayStarts[0].getTime();
|
|
289
|
+
const lastDay = visibleDayStarts[visibleDayStarts.length - 1].getTime();
|
|
290
|
+
if (target <= firstDay) return 0;
|
|
291
|
+
if (target >= lastDay + 24 * 36e5) return totalWidth;
|
|
292
|
+
for (let index = 0; index < visibleDayStarts.length; index++) {
|
|
293
|
+
const dayStart = visibleDayStarts[index].getTime();
|
|
294
|
+
const nextDayStart = dayStart + 24 * 36e5;
|
|
295
|
+
if (target < nextDayStart) {
|
|
296
|
+
const minutesIntoDay = (target - dayStart) / 6e4;
|
|
297
|
+
const clamped = Math.min(Math.max(minutesIntoDay, minTimeMin), minTimeMin + visibleMinutesPerDay);
|
|
298
|
+
const dayProgress = (clamped - minTimeMin) / visibleMinutesPerDay;
|
|
299
|
+
return index * dayWidth + dayProgress * dayWidth;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return totalWidth;
|
|
303
|
+
}
|
|
304
|
+
function getTimelineDayGranularityEventRect(event, input) {
|
|
305
|
+
const firstDay = input.visibleDayStarts[0];
|
|
306
|
+
if (!firstDay) {
|
|
307
|
+
return { left: 0, width: input.dayWidth };
|
|
308
|
+
}
|
|
309
|
+
const lastIndex = Math.max(0, input.visibleDayStarts.length - 1);
|
|
310
|
+
const eventStart = event.start instanceof Date ? event.start : new Date(event.start);
|
|
311
|
+
const eventEnd = event.end ? event.end instanceof Date ? event.end : new Date(event.end) : eventStart;
|
|
312
|
+
const startDay = startOfDay(eventStart);
|
|
313
|
+
let endDay = startOfDay(eventEnd);
|
|
314
|
+
if (eventEnd.getTime() > eventStart.getTime() && eventEnd.getTime() === endDay.getTime()) {
|
|
315
|
+
endDay = addDays(endDay, -1);
|
|
316
|
+
}
|
|
317
|
+
const firstDayTime = firstDay.getTime();
|
|
318
|
+
const startIndex = Math.min(Math.max(Math.floor((startDay.getTime() - firstDayTime) / DAY_MS), 0), lastIndex);
|
|
319
|
+
const endIndex = Math.min(Math.max(Math.floor((endDay.getTime() - firstDayTime) / DAY_MS), startIndex), lastIndex);
|
|
320
|
+
return {
|
|
321
|
+
left: startIndex * input.dayWidth,
|
|
322
|
+
width: Math.max((endIndex - startIndex + 1) * input.dayWidth, input.dayWidth)
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
function getTimelineEventRect(event, input) {
|
|
326
|
+
if (isTimelineMonthGranularityView(input.view)) {
|
|
327
|
+
return getTimelineDayGranularityEventRect(event, input);
|
|
328
|
+
}
|
|
329
|
+
const eventStart = event.start instanceof Date ? event.start : new Date(event.start);
|
|
330
|
+
const eventEnd = event.end ? event.end instanceof Date ? event.end : new Date(event.end) : new Date(eventStart.getTime() + input.fallbackDurationMinutes * 6e4);
|
|
331
|
+
const left = timeToTimelinePixels({ ...input, date: eventStart });
|
|
332
|
+
const right = timeToTimelinePixels({ ...input, date: eventEnd });
|
|
333
|
+
return {
|
|
334
|
+
left,
|
|
335
|
+
width: Math.max(right - left, 20)
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
function computeTimelineLaneAssignment(events, stackingMode) {
|
|
339
|
+
const sorted = [...events].sort((a, b) => {
|
|
340
|
+
const as = (a.start instanceof Date ? a.start : new Date(a.start)).getTime();
|
|
341
|
+
const bs = (b.start instanceof Date ? b.start : new Date(b.start)).getTime();
|
|
342
|
+
if (as !== bs) return as - bs;
|
|
343
|
+
const ae = a.end ? (a.end instanceof Date ? a.end : new Date(a.end)).getTime() : as;
|
|
344
|
+
const be = b.end ? (b.end instanceof Date ? b.end : new Date(b.end)).getTime() : bs;
|
|
345
|
+
return be - ae;
|
|
346
|
+
});
|
|
347
|
+
const assignment = /* @__PURE__ */ new Map();
|
|
348
|
+
if (stackingMode === "collapse") {
|
|
349
|
+
for (const event of sorted) {
|
|
350
|
+
if (event.id != null) assignment.set(event.id, 0);
|
|
351
|
+
}
|
|
352
|
+
return { assignment, laneCount: 1 };
|
|
353
|
+
}
|
|
354
|
+
if (stackingMode === "expand") {
|
|
355
|
+
let index = 0;
|
|
356
|
+
for (const event of sorted) {
|
|
357
|
+
if (event.id != null) assignment.set(event.id, index);
|
|
358
|
+
index++;
|
|
359
|
+
}
|
|
360
|
+
return { assignment, laneCount: Math.max(1, sorted.length) };
|
|
361
|
+
}
|
|
362
|
+
const lanes = [];
|
|
363
|
+
for (const event of sorted) {
|
|
364
|
+
const start = (event.start instanceof Date ? event.start : new Date(event.start)).getTime();
|
|
365
|
+
const end = event.end ? (event.end instanceof Date ? event.end : new Date(event.end)).getTime() : start;
|
|
366
|
+
let laneIndex = -1;
|
|
367
|
+
for (let index = 0; index < lanes.length; index++) {
|
|
368
|
+
const occupants = lanes[index];
|
|
369
|
+
const overlaps = occupants.some((occupant) => !(occupant.end <= start || occupant.start >= end));
|
|
370
|
+
if (!overlaps) {
|
|
371
|
+
laneIndex = index;
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (laneIndex < 0) {
|
|
376
|
+
laneIndex = lanes.length;
|
|
377
|
+
lanes.push([]);
|
|
378
|
+
}
|
|
379
|
+
lanes[laneIndex].push({ start, end });
|
|
380
|
+
if (event.id != null) assignment.set(event.id, laneIndex);
|
|
381
|
+
}
|
|
382
|
+
return { assignment, laneCount: Math.max(1, lanes.length) };
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// src/timeline/view-model.ts
|
|
386
|
+
var TIMELINE_VIEWS_WITH_RESOURCES = /* @__PURE__ */ new Set(["timelineDay", "timelineWeek", "timelineMonth", "resourceTimelineDay", "resourceTimelineWeek", "resourceTimelineMonth"]);
|
|
387
|
+
function getEffectiveViewStart(cells, fallback) {
|
|
388
|
+
return cells[0]?.startDate ?? fallback;
|
|
389
|
+
}
|
|
390
|
+
function getEffectiveViewEnd(cells, fallback) {
|
|
391
|
+
return cells[cells.length - 1]?.endDate ?? fallback;
|
|
392
|
+
}
|
|
393
|
+
function buildDescendantIdMap(resources) {
|
|
394
|
+
const map = /* @__PURE__ */ new Map();
|
|
395
|
+
const collect = (resource) => {
|
|
396
|
+
const ids = /* @__PURE__ */ new Set([resource.id]);
|
|
397
|
+
for (const child of resource.children ?? []) {
|
|
398
|
+
const childIds = collect(child);
|
|
399
|
+
childIds.forEach((id) => ids.add(id));
|
|
400
|
+
}
|
|
401
|
+
map.set(resource.id, ids);
|
|
402
|
+
return ids;
|
|
403
|
+
};
|
|
404
|
+
for (const resource of resources) {
|
|
405
|
+
collect(resource);
|
|
406
|
+
}
|
|
407
|
+
return map;
|
|
408
|
+
}
|
|
409
|
+
function buildVisibleHeaderTiers(input) {
|
|
410
|
+
const { headerTiers, visibleCells, virtualRange } = input;
|
|
411
|
+
if (!virtualRange) return headerTiers;
|
|
412
|
+
const visibleWidth = visibleCells.reduce((sum, cell) => sum + cell.width, 0);
|
|
413
|
+
const windowStart = virtualRange.startOffset;
|
|
414
|
+
const windowEnd = windowStart + visibleWidth;
|
|
415
|
+
return headerTiers.map((tier) => {
|
|
416
|
+
let offset = 0;
|
|
417
|
+
return {
|
|
418
|
+
cells: tier.cells.flatMap((cell) => {
|
|
419
|
+
const cellStart = Number.isFinite(cell.left) ? cell.left : offset;
|
|
420
|
+
const cellEnd = cellStart + cell.width;
|
|
421
|
+
offset = cellEnd;
|
|
422
|
+
if (cellEnd <= windowStart || cellStart >= windowEnd) {
|
|
423
|
+
return [];
|
|
424
|
+
}
|
|
425
|
+
return [
|
|
426
|
+
{
|
|
427
|
+
...cell,
|
|
428
|
+
left: Math.max(cellStart, windowStart),
|
|
429
|
+
width: Math.max(0, Math.min(cellEnd, windowEnd) - Math.max(cellStart, windowStart))
|
|
430
|
+
}
|
|
431
|
+
];
|
|
432
|
+
})
|
|
433
|
+
};
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
function eventIntersectsRange(event, viewStart, viewEnd) {
|
|
437
|
+
const eventStart = event.start instanceof Date ? event.start : new Date(event.start);
|
|
438
|
+
const eventEnd = event.end ? event.end instanceof Date ? event.end : new Date(event.end) : eventStart;
|
|
439
|
+
return eventStart < viewEnd && eventEnd > viewStart;
|
|
440
|
+
}
|
|
441
|
+
function computeVisualLaneAssignment(events, getRect, stackingMode) {
|
|
442
|
+
if (stackingMode === "collapse" || stackingMode === "expand") {
|
|
443
|
+
return computeTimelineLaneAssignment(events, stackingMode);
|
|
444
|
+
}
|
|
445
|
+
const sorted = [...events].map((event) => ({ event, rect: getRect(event) })).sort((a, b) => {
|
|
446
|
+
if (a.rect.left !== b.rect.left) return a.rect.left - b.rect.left;
|
|
447
|
+
return b.rect.width - a.rect.width;
|
|
448
|
+
});
|
|
449
|
+
const assignment = /* @__PURE__ */ new Map();
|
|
450
|
+
const lanes = [];
|
|
451
|
+
for (const item of sorted) {
|
|
452
|
+
const start = item.rect.left;
|
|
453
|
+
const end = item.rect.left + item.rect.width;
|
|
454
|
+
let laneIndex = -1;
|
|
455
|
+
for (let index = 0; index < lanes.length; index++) {
|
|
456
|
+
const overlaps = lanes[index].some((occupant) => !(occupant.end <= start || occupant.start >= end));
|
|
457
|
+
if (!overlaps) {
|
|
458
|
+
laneIndex = index;
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if (laneIndex < 0) {
|
|
463
|
+
laneIndex = lanes.length;
|
|
464
|
+
lanes.push([]);
|
|
465
|
+
}
|
|
466
|
+
lanes[laneIndex].push({ start, end });
|
|
467
|
+
if (item.event.id != null) {
|
|
468
|
+
assignment.set(item.event.id, laneIndex);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return { assignment, laneCount: Math.max(1, lanes.length) };
|
|
472
|
+
}
|
|
473
|
+
function buildTimelineViewModel(input) {
|
|
474
|
+
const monthGranularity = isTimelineMonthGranularityView(input.view);
|
|
475
|
+
const slotsPerDay = monthGranularity ? 1 : Math.max(1, Math.ceil((input.maxTimeMin - input.minTimeMin) / input.slotDurationMin));
|
|
476
|
+
const slotWidth = Math.max(20, input.slotWidth);
|
|
477
|
+
const dayWidth = monthGranularity ? slotWidth : slotsPerDay * slotWidth;
|
|
478
|
+
const cells = buildTimelineCells({
|
|
479
|
+
view: input.view,
|
|
480
|
+
start: input.viewStart,
|
|
481
|
+
end: input.viewEnd,
|
|
482
|
+
now: input.now,
|
|
483
|
+
slotsPerDay,
|
|
484
|
+
slotDurationMin: input.slotDurationMin,
|
|
485
|
+
slotWidth,
|
|
486
|
+
minTimeMin: input.minTimeMin,
|
|
487
|
+
dayWidth,
|
|
488
|
+
timezone: input.timezone,
|
|
489
|
+
locale: input.locale,
|
|
490
|
+
calendar: input.calendar,
|
|
491
|
+
labels: input.labels
|
|
492
|
+
});
|
|
493
|
+
const visibleDayStarts = getTimelineVisibleDayStarts(input.view, input.viewStart, input.viewEnd);
|
|
494
|
+
const effectiveViewStart = getEffectiveViewStart(cells, input.viewStart);
|
|
495
|
+
const effectiveViewEnd = getEffectiveViewEnd(cells, input.viewEnd);
|
|
496
|
+
const totalWidth = cells.reduce((sum, cell) => sum + cell.width, 0);
|
|
497
|
+
const visibleMinutesPerDay = Math.max(1, input.maxTimeMin - input.minTimeMin);
|
|
498
|
+
const resources = TIMELINE_VIEWS_WITH_RESOURCES.has(input.view) ? input.resources ?? [] : [];
|
|
499
|
+
const visibleResources = resources.length > 0 ? input.visibleResources ?? [] : [];
|
|
500
|
+
const descendantIdMap = buildDescendantIdMap(resources);
|
|
501
|
+
const timelineEventMinHeight = resolveTimelineEventMinHeight(input.timelineEventMinHeight);
|
|
502
|
+
const rowAutoHeight = input.rowAutoHeight ?? input.view.startsWith("resourceTimeline");
|
|
503
|
+
const rowHeightMode = rowAutoHeight ? "auto" : "fixed";
|
|
504
|
+
const headerTiers = buildTimelineHeaderTiers({
|
|
505
|
+
view: input.view,
|
|
506
|
+
cells,
|
|
507
|
+
visibleDayStarts,
|
|
508
|
+
dayWidth,
|
|
509
|
+
labels: {
|
|
510
|
+
day: input.labels.day,
|
|
511
|
+
monthYear: input.labels.monthYear,
|
|
512
|
+
quarter: input.labels.quarter
|
|
513
|
+
},
|
|
514
|
+
locale: input.locale,
|
|
515
|
+
calendar: input.calendar
|
|
516
|
+
});
|
|
517
|
+
const positionInput = {
|
|
518
|
+
view: input.view,
|
|
519
|
+
cells,
|
|
520
|
+
visibleDayStarts,
|
|
521
|
+
dayWidth,
|
|
522
|
+
minTimeMin: input.minTimeMin,
|
|
523
|
+
visibleMinutesPerDay,
|
|
524
|
+
totalWidth
|
|
525
|
+
};
|
|
526
|
+
const getEventsForLane = (resourceId) => filterTimelineLaneEvents(input.events, {
|
|
527
|
+
resourceId,
|
|
528
|
+
matchIds: null,
|
|
529
|
+
viewStart: effectiveViewStart,
|
|
530
|
+
viewEnd: effectiveViewEnd
|
|
531
|
+
});
|
|
532
|
+
const isAggregatedRow = (resourceId) => {
|
|
533
|
+
if (resourceId === void 0 || !input.showAggregatedEvents) return false;
|
|
534
|
+
const descendants = descendantIdMap.get(resourceId);
|
|
535
|
+
if (!descendants || descendants.size <= 1) return false;
|
|
536
|
+
return !(input.isResourceExpanded?.(resourceId) ?? true);
|
|
537
|
+
};
|
|
538
|
+
const getAggregateEvents = (resourceId) => {
|
|
539
|
+
if (resourceId === void 0 || !isAggregatedRow(resourceId)) return [];
|
|
540
|
+
return filterTimelineLaneEvents(input.events, {
|
|
541
|
+
resourceId,
|
|
542
|
+
matchIds: descendantIdMap.get(resourceId) ?? /* @__PURE__ */ new Set([resourceId]),
|
|
543
|
+
viewStart: effectiveViewStart,
|
|
544
|
+
viewEnd: effectiveViewEnd
|
|
545
|
+
});
|
|
546
|
+
};
|
|
547
|
+
const getTimelineEventPosition = (event) => getTimelineEventRect(event, {
|
|
548
|
+
...positionInput,
|
|
549
|
+
fallbackDurationMinutes: input.slotDurationMin
|
|
550
|
+
});
|
|
551
|
+
const laneModels = /* @__PURE__ */ new Map();
|
|
552
|
+
const laneResourceIds = visibleResources.length > 0 ? visibleResources.map((resource) => resource.id) : [void 0];
|
|
553
|
+
for (const resourceId of laneResourceIds) {
|
|
554
|
+
const events = getEventsForLane(resourceId);
|
|
555
|
+
const { assignment, laneCount } = monthGranularity ? computeVisualLaneAssignment(events, getTimelineEventPosition, input.stackingMode) : computeTimelineLaneAssignment(events, input.stackingMode);
|
|
556
|
+
const aggregateEvents = getAggregateEvents(resourceId);
|
|
557
|
+
laneModels.set(resourceId, {
|
|
558
|
+
resourceId,
|
|
559
|
+
events,
|
|
560
|
+
assignment,
|
|
561
|
+
laneCount,
|
|
562
|
+
aggregateEvents,
|
|
563
|
+
aggregateCount: aggregateEvents.length,
|
|
564
|
+
isAggregated: isAggregatedRow(resourceId)
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
const laneCounts = /* @__PURE__ */ new Map();
|
|
568
|
+
const expandedRowHeights = /* @__PURE__ */ new Map();
|
|
569
|
+
const baseRowHeight = resolveTimelineResourceRowHeight({
|
|
570
|
+
laneCount: 1,
|
|
571
|
+
eventHeight: timelineEventMinHeight,
|
|
572
|
+
minRowHeight: input.resourceRowHeight,
|
|
573
|
+
mode: rowHeightMode
|
|
574
|
+
});
|
|
575
|
+
for (const resource of visibleResources) {
|
|
576
|
+
const laneCount = laneModels.get(resource.id)?.laneCount ?? 1;
|
|
577
|
+
laneCounts.set(resource.id, laneCount);
|
|
578
|
+
expandedRowHeights.set(
|
|
579
|
+
resource.id,
|
|
580
|
+
resolveTimelineResourceRowHeight({
|
|
581
|
+
laneCount,
|
|
582
|
+
eventHeight: timelineEventMinHeight,
|
|
583
|
+
minRowHeight: input.resourceRowHeight,
|
|
584
|
+
mode: rowHeightMode
|
|
585
|
+
})
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
const resourceLayouts = visibleResources.length > 0 ? calculateResourceRowLayouts(visibleResources, baseRowHeight, expandedRowHeights, laneCounts) : [];
|
|
589
|
+
const totalResourceHeight = getTotalResourceHeight(resourceLayouts);
|
|
590
|
+
const virtualScrollCells = cells.map((cell, index) => ({
|
|
591
|
+
date: cell.startDate,
|
|
592
|
+
startTime: cell.startDate,
|
|
593
|
+
endTime: cell.endDate,
|
|
594
|
+
isToday: cell.isToday,
|
|
595
|
+
isWeekend: cell.isWeekend,
|
|
596
|
+
isBusinessHour: false,
|
|
597
|
+
width: cell.width,
|
|
598
|
+
index
|
|
599
|
+
}));
|
|
600
|
+
const virtualScrollEvents = input.events.filter((event) => eventIntersectsRange(event, input.viewStart, input.viewEnd)).map((event) => {
|
|
601
|
+
const rect = getTimelineEventPosition(event);
|
|
602
|
+
return {
|
|
603
|
+
left: rect.left,
|
|
604
|
+
width: rect.width,
|
|
605
|
+
row: 0,
|
|
606
|
+
event,
|
|
607
|
+
continuesBefore: false,
|
|
608
|
+
continuesAfter: false
|
|
609
|
+
};
|
|
610
|
+
});
|
|
611
|
+
const visibleCells = input.virtualRange ? cells.slice(input.virtualRange.startIndex, input.virtualRange.endIndex + 1) : cells;
|
|
612
|
+
const visibleHeaderTiers = buildVisibleHeaderTiers({
|
|
613
|
+
headerTiers,
|
|
614
|
+
visibleCells,
|
|
615
|
+
virtualRange: input.virtualRange
|
|
616
|
+
});
|
|
617
|
+
return {
|
|
618
|
+
cells,
|
|
619
|
+
visibleDayStarts,
|
|
620
|
+
effectiveViewStart,
|
|
621
|
+
effectiveViewEnd,
|
|
622
|
+
totalWidth,
|
|
623
|
+
visibleMinutesPerDay,
|
|
624
|
+
resources,
|
|
625
|
+
visibleResources,
|
|
626
|
+
headerTiers,
|
|
627
|
+
visibleCells,
|
|
628
|
+
visibleHeaderTiers,
|
|
629
|
+
laneModels,
|
|
630
|
+
resourceLayouts,
|
|
631
|
+
totalResourceHeight,
|
|
632
|
+
virtualScrollCells,
|
|
633
|
+
virtualScrollEvents,
|
|
634
|
+
getEventsForLane,
|
|
635
|
+
getAggregateEvents,
|
|
636
|
+
getAggregateCount: (resourceId) => getAggregateEvents(resourceId).length,
|
|
637
|
+
getEventLeft: (event) => getTimelineEventPosition(event).left,
|
|
638
|
+
getEventWidth: (event) => getTimelineEventPosition(event).width,
|
|
639
|
+
getEventLane: (event, resourceId) => {
|
|
640
|
+
const entry = laneModels.get(resourceId);
|
|
641
|
+
if (!entry || event.id == null) return 0;
|
|
642
|
+
return entry.assignment.get(event.id) ?? 0;
|
|
643
|
+
},
|
|
644
|
+
getLaneCount: (resourceId) => laneModels.get(resourceId)?.laneCount ?? 1,
|
|
645
|
+
isAggregatedRow,
|
|
646
|
+
timeToPixels: (date) => timeToTimelinePixels({ ...positionInput, date })
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
export { DEFAULT_TIMELINE_EVENT_MIN_HEIGHT, TIMELINE_LANE_BASE_PADDING, TIMELINE_LANE_GAP, buildTimelineCells, buildTimelineHeaderTiers, buildTimelineViewModel, computeTimelineLaneAssignment, filterTimelineLaneEvents, getTimelineEventRect, getTimelineLaneStackHeight, getTimelineVisibleDayStarts, isTimelineMonthGranularityView, resolveTimelineEventMinHeight, resolveTimelineResourceRowHeight, timeToTimelinePixels };
|