This commit is contained in:
metacryst
2026-04-28 20:05:00 -05:00
commit 0d6c7683ff
123 changed files with 20922 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
import calendarUtil from "../calendarUtil.js"
import "./DesktopMonthGrid.js"
class DesktopMonthView extends Shadow {
constructor(calendars, events, currentDate, weekStartsOn = 0, onEventClick = null, onDayDoubleClick = null) {
super()
this.calendars = calendars;
this.events = events;
this.currentDate = currentDate;
this.weekStartsOn = weekStartsOn;
this.onEventClick = onEventClick;
this.onDayDoubleClick = onDayDoubleClick;
}
render() {
const weeks = this.buildMonthWeeks();
DesktopMonthGrid(weeks, this.calendars, this.weekStartsOn, this.onEventClick, this.onDayDoubleClick)
.width(100, pct)
.height(100, pct)
}
buildMonthWeeks() {
const month = this.currentDate.getMonth();
const allDays = calendarUtil.buildMonthGrid(this.currentDate, this.weekStartsOn);
const gridStart = allDays[0];
const weeks = [];
for (let w = 0; w < 6; w++) {
weeks.push(allDays.slice(w * 7, w * 7 + 7));
}
const gridEnd = calendarUtil.endOfDay(weeks[weeks.length - 1][6]);
const monthStart = calendarUtil.startOfDay(new Date(this.currentDate.getFullYear(), this.currentDate.getMonth(), 1));
const monthEnd = calendarUtil.endOfDay(new Date(this.currentDate.getFullYear(), this.currentDate.getMonth() + 1, 0));
const expanded = calendarUtil.expandRecurringEvents(this.events, gridStart, gridEnd);
const relevantEvents = expanded.filter(event =>
calendarUtil.rangesOverlap(event.time_start, event.time_end, gridStart, gridEnd) &&
calendarUtil.rangesOverlap(event.time_start, event.time_end, monthStart, monthEnd) &&
this.calendars.some(c => event.calendars?.some(id => id === c.id))
);
return weeks.map(week => this.buildWeekData(week, month, relevantEvents));
}
buildWeekData(week, currentMonth, events) {
const weekStart = calendarUtil.startOfDay(week[0]);
const weekEnd = calendarUtil.endOfDay(week[6]);
const weekEvents = events.filter(event =>
calendarUtil.rangesOverlap(event.time_start, event.time_end, weekStart, weekEnd)
);
weekEvents.sort((a, b) => {
const aSpan = a.all_day || this.isMultiDay(a);
const bSpan = b.all_day || this.isMultiDay(b);
if (aSpan !== bSpan) return aSpan ? -1 : 1;
return a.time_start - b.time_start;
});
const slotRows = [];
weekEvents.forEach(event => {
const startCol = Math.max(0, this.dayIndex(event.time_start, week));
const endCol = Math.min(6, this.dayIndex(event.time_end, week));
let row = 0;
while (true) {
if (!slotRows[row]) slotRows[row] = new Array(7).fill(null);
if (slotRows[row].slice(startCol, endCol + 1).every(v => v === null)) break;
row++;
}
for (let c = startCol; c <= endCol; c++) {
slotRows[row][c] = {
event,
isStart: c === startCol,
isEnd: c === endCol,
isSingleDay: startCol === endCol
};
}
});
const slotMap = Array.from({ length: 7 }, (_, col) =>
slotRows.map(row => row[col] ?? null)
);
return {
days: week.map(day => ({
day,
isCurrentMonth: day.getMonth() === currentMonth,
events: weekEvents.filter(event => {
const effectiveEnd = event.all_day ? calendarUtil.endOfDay(event.time_end) : event.time_end;
return calendarUtil.rangesOverlap(
event.time_start, effectiveEnd,
calendarUtil.startOfDay(day), calendarUtil.endOfDay(day)
);
})
})),
slotMap
};
}
isMultiDay(event) {
return calendarUtil.startOfDay(event.time_start).getTime() !==
calendarUtil.startOfDay(event.time_end).getTime();
}
dayIndex(date, week) {
const dayStart = calendarUtil.startOfDay(date);
for (let i = 0; i < 7; i++) {
if (calendarUtil.startOfDay(week[i]).getTime() === dayStart.getTime()) return i;
}
return date.getTime() < week[0].getTime() ? 0 : 6;
}
}
register(DesktopMonthView)