Calendar/wwwroot/js/utils/AllDayLayoutEngine.js
2026-02-03 00:02:25 +01:00

108 lines
No EOL
4.3 KiB
JavaScript

export class AllDayLayoutEngine {
constructor(weekDates) {
this.weekDates = weekDates;
this.tracks = [];
}
/**
* Calculate layout for all events using clean day-based logic
*/
calculateLayout(events) {
let layouts = [];
// Reset tracks for new calculation
this.tracks = [new Array(this.weekDates.length).fill(false)];
// Filter to only visible events
const visibleEvents = events.filter(event => this.isEventVisible(event));
// Process events in input order (no sorting)
for (const event of visibleEvents) {
const startDay = this.getEventStartDay(event);
const endDay = this.getEventEndDay(event);
if (startDay > 0 && endDay > 0) {
const track = this.findAvailableTrack(startDay - 1, endDay - 1); // Convert to 0-based for tracks
// Mark days as occupied
for (let day = startDay - 1; day <= endDay - 1; day++) {
this.tracks[track][day] = true;
}
const layout = {
calenderEvent: event,
gridArea: `${track + 1} / ${startDay} / ${track + 2} / ${endDay + 1}`,
startColumn: startDay,
endColumn: endDay,
row: track + 1,
columnSpan: endDay - startDay + 1
};
layouts.push(layout);
}
}
return layouts;
}
/**
* Find available track for event spanning from startDay to endDay (0-based indices)
*/
findAvailableTrack(startDay, endDay) {
for (let trackIndex = 0; trackIndex < this.tracks.length; trackIndex++) {
if (this.isTrackAvailable(trackIndex, startDay, endDay)) {
return trackIndex;
}
}
// Create new track if none available
this.tracks.push(new Array(this.weekDates.length).fill(false));
return this.tracks.length - 1;
}
/**
* Check if track is available for the given day range (0-based indices)
*/
isTrackAvailable(trackIndex, startDay, endDay) {
for (let day = startDay; day <= endDay; day++) {
if (this.tracks[trackIndex][day]) {
return false;
}
}
return true;
}
/**
* Get start day index for event (1-based, 0 if not visible)
*/
getEventStartDay(event) {
const eventStartDate = this.formatDate(event.start);
const firstVisibleDate = this.weekDates[0];
// If event starts before visible range, clip to first visible day
const clippedStartDate = eventStartDate < firstVisibleDate ? firstVisibleDate : eventStartDate;
const dayIndex = this.weekDates.indexOf(clippedStartDate);
return dayIndex >= 0 ? dayIndex + 1 : 0;
}
/**
* Get end day index for event (1-based, 0 if not visible)
*/
getEventEndDay(event) {
const eventEndDate = this.formatDate(event.end);
const lastVisibleDate = this.weekDates[this.weekDates.length - 1];
// If event ends after visible range, clip to last visible day
const clippedEndDate = eventEndDate > lastVisibleDate ? lastVisibleDate : eventEndDate;
const dayIndex = this.weekDates.indexOf(clippedEndDate);
return dayIndex >= 0 ? dayIndex + 1 : 0;
}
/**
* Check if event is visible in the current date range
*/
isEventVisible(event) {
if (this.weekDates.length === 0)
return false;
const eventStartDate = this.formatDate(event.start);
const eventEndDate = this.formatDate(event.end);
const firstVisibleDate = this.weekDates[0];
const lastVisibleDate = this.weekDates[this.weekDates.length - 1];
// Event overlaps if it doesn't end before visible range starts
// AND doesn't start after visible range ends
return !(eventEndDate < firstVisibleDate || eventStartDate > lastVisibleDate);
}
/**
* Format date to YYYY-MM-DD string using local date
*/
formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
}
//# sourceMappingURL=AllDayLayoutEngine.js.map