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,101 @@
import calendarUtil from "../calendarUtil.js";
class DayHeaderRow extends Shadow {
constructor(groupedDay, calendars) {
super()
this.groupedDay = groupedDay;
this.calendars = calendars;
}
render() {
const day = this.groupedDay.day;
const today = calendarUtil.isToday(day);
const allDayEvents = this.groupedDay.allDay;
const maxEvents = allDayEvents.length;
VStack(() => {
HStack(() => {
VStack(() => {
p(day.toLocaleDateString("en-US", { weekday: "long" }).toUpperCase())
.margin(0)
.fontSize(0.72, em)
.fontWeight("600")
.letterSpacing(0.04, em)
.opacity(today ? 1 : 0.5)
.textAlign("left")
h3(day.toLocaleDateString("en-US", { month: "long", day: "numeric" }))
.margin(0)
.fontSize(1.35, em)
.fontWeight("700")
.lineHeight("1")
})
.color(today ? "var(--quillred)" : "var(--headertext)")
.flex(1)
.alignItems("flex-start")
.justifyContent("center")
.gap(0.3, em)
.paddingTop(0.85, em)
.paddingBottom(maxEvents > 0 ? (maxEvents * 2.0) + 0.7 : 0.35, em)
.paddingHorizontal(0.75, em)
.boxSizing("border-box")
})
.width(100, pct)
.alignItems("stretch")
if (allDayEvents.length > 0) {
this.allDaySection(allDayEvents);
}
})
.width(100, pct)
.position("relative")
.background("var(--sidebottombars)")
.borderBottom("1px solid var(--divider)")
}
allDaySection(events) {
const rowHeight = 1.75;
const gap = 0.25;
const totalHeight = events.length * rowHeight + (events.length - 1) * gap;
ZStack(() => {
events.forEach((event, slot) => {
const color = calendarUtil.getCalendarColor(this.calendars, event.calendars.find(id => this.calendars.some(c => c.id === id)) ?? event.calendars[0]);
const topEm = slot * (rowHeight + gap);
HStack(() => {
p(event.title)
.margin(0)
.fontSize(0.72, em)
.fontWeight("600")
.color("white")
.whiteSpace("nowrap")
.overflow("hidden")
})
.position("absolute")
.top(topEm, em)
.left(0.25, em)
.right(0.25, em)
.height(rowHeight, em)
.padding(0.35, em)
.background(color)
.borderRadius(0.25, em)
.alignItems("center")
.boxSizing("border-box")
.overflow("hidden")
.pointerEvents("auto")
.cursor("pointer")
.onTap(() => $("bottomsheet-").showEvent(event))
})
})
.position("absolute")
.bottom(0.25, em)
.left(0)
.right(0)
.height(totalHeight, em)
.boxSizing("border-box")
.pointerEvents("none")
}
}
register(DayHeaderRow)

124
calendar/Day/DayView.js Normal file
View File

@@ -0,0 +1,124 @@
import calendarUtil from "../calendarUtil.js"
import "../Week/SpacerCell.js"
import "../Week/TimedLabelsColumn.js"
import "../Week/TimedWeekGrid.js"
import "./DayHeaderRow.js"
css(`
dayview- {
scrollbar-width: none;
-ms-overflow-style: none;
}
dayview- .VStack::-webkit-scrollbar {
display: none;
width: 0px;
height: 0px;
}
dayview- .VStack::-webkit-scrollbar-thumb {
background: transparent;
}
dayview- .VStack::-webkit-scrollbar-track {
background: transparent;
}
`)
let _saved = null;
class DayView extends Shadow {
constructor(calendars, events, currentDate, onSlotTap = null, isCenter = false) {
super()
this.calendars = calendars;
this.events = events;
this.currentDate = currentDate;
this.onSlotTap = onSlotTap;
this.isCenter = isCenter;
this.slots = calendarUtil.generateTimeSlots({ stepMinutes: 30 });
this.slotHeight = 2;
this.sidebarWidth = 3;
}
render() {
ZStack(() => {
const filteredEvents = this.filterEventsForDay(this.events);
const groupedDay = this.groupEventsForDay(filteredEvents);
const weekNumber = calendarUtil.getWeekNumber(this.currentDate);
VStack(() => {
HStack(() => {
SpacerCell(weekNumber, this.sidebarWidth)
DayHeaderRow(groupedDay, this.calendars)
})
.boxSizing("border-box")
.position("sticky")
.top(0)
.width(100, pct)
.zIndex(2)
HStack(() => {
TimedLabelsColumn(this.slots, this.slotHeight, this.sidebarWidth)
TimedWeekGrid([groupedDay], this.slots, [groupedDay], this.calendars, this.slotHeight, "day", this.onSlotTap)
})
.boxSizing("border-box")
.onAppear(() => {
this.scrollToEight();
})
})
})
.position("relative")
.gap(1, em)
.boxSizing("border-box")
.width(100, pct)
.height(100, pct)
.fontSize(0.9, em)
.overscrollBehavior("none")
.overflowY("scroll")
.display("block")
}
filterEventsForDay(events) {
const dayStart = calendarUtil.startOfDay(this.currentDate);
const dayEnd = calendarUtil.endOfDay(this.currentDate);
const expanded = calendarUtil.expandRecurringEvents(events, dayStart, dayEnd);
return expanded.filter(event => {
const end = event.all_day ? calendarUtil.endOfDay(event.time_end) : calendarUtil.timedEnd(event);
return calendarUtil.rangesOverlap(event.time_start, end, dayStart, dayEnd)
&& this.calendars.some(c => event.calendars.some(c2 => c2 === c.id));
});
}
groupEventsForDay(events) {
const dayStart = calendarUtil.startOfDay(this.currentDate);
const dayEnd = calendarUtil.endOfDay(this.currentDate);
return {
day: this.currentDate,
allDay: events.filter(event => {
if (!event.all_day) return false;
const end = calendarUtil.endOfDay(event.time_end);
return calendarUtil.rangesOverlap(event.time_start, end, dayStart, dayEnd);
}),
timed: events.filter(event => {
return !event.all_day && calendarUtil.rangesOverlap(event.time_start, calendarUtil.timedEnd(event), dayStart, dayEnd);
})
};
}
scrollToEight() {
requestAnimationFrame(() => {
const fs = parseFloat(getComputedStyle(this).fontSize);
const slotsBeforeEight = this.slots.findIndex(s => s.hour24 === 8 && s.minute === 0);
const defaultTarget = slotsBeforeEight * this.slotHeight * fs;
if (this.isCenter) {
const dateKey = calendarUtil.toDateInput(this.currentDate);
this.scrollTop = (_saved?.dateKey === dateKey) ? _saved.scrollTop : defaultTarget;
this.addEventListener('scroll', () => { _saved = { dateKey, scrollTop: this.scrollTop }; }, { passive: true });
} else {
this.scrollTop = defaultTarget;
}
})
}
}
register(DayView)