Faster event updates (avoid full re-render)

This commit is contained in:
Leo Vasanko 2025-08-25 19:05:14 -06:00
parent 895bc96899
commit e50f90f277
2 changed files with 88 additions and 7 deletions

View File

@ -87,7 +87,7 @@ const vwm = createVirtualWeekManager({
contentHeight, contentHeight,
}) })
const visibleWeeks = vwm.visibleWeeks const visibleWeeks = vwm.visibleWeeks
const { scheduleWindowUpdate, resetWeeks } = vwm const { scheduleWindowUpdate, resetWeeks, refreshEvents } = vwm
// Scroll managers (after scheduleWindowUpdate available) // Scroll managers (after scheduleWindowUpdate available)
const scrollManager = createScrollManager({ viewport, scheduleRebuild: scheduleWindowUpdate }) const scrollManager = createScrollManager({ viewport, scheduleRebuild: scheduleWindowUpdate })
@ -395,11 +395,17 @@ watch(
watch( watch(
() => calendarStore.events, () => calendarStore.events,
() => { () => {
resetWeeks('events') refreshEvents('events')
}, },
{ deep: true }, { deep: true },
) )
// Reflect selection & events by rebuilding day objects in-place
watch(
() => [selection.value.startDate, selection.value.dayCount],
() => refreshEvents('selection'),
)
// Rebuild if viewport height changes (e.g., resize) // Rebuild if viewport height changes (e.g., resize)
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
if (viewport.value) viewportHeight.value = viewport.value.clientHeight if (viewport.value) viewportHeight.value = viewport.value.clientHeight

View File

@ -69,17 +69,14 @@ export function createVirtualWeekManager({
} }
} }
for (let i = 0; i < 7; i++) { const collectEventsForDate = (dateStr, curDateObj) => {
const dateStr = toLocalString(cur, DEFAULT_TZ)
const storedEvents = [] const storedEvents = []
for (const ev of calendarStore.events.values()) { for (const ev of calendarStore.events.values()) {
if (!ev.isRepeating && dateStr >= ev.startDate && dateStr <= ev.endDate) { if (!ev.isRepeating && dateStr >= ev.startDate && dateStr <= ev.endDate) {
storedEvents.push(ev) storedEvents.push(ev)
} }
} }
const dayEvents = [...storedEvents] const dayEvents = [...storedEvents]
// Expand repeating events
for (const base of repeatingBases) { for (const base of repeatingBases) {
if (dateStr >= base.startDate && dateStr <= base.endDate) { if (dateStr >= base.startDate && dateStr <= base.endDate) {
dayEvents.push({ ...base, _recurrenceIndex: 0, _baseId: base.id }) dayEvents.push({ ...base, _recurrenceIndex: 0, _baseId: base.id })
@ -88,7 +85,7 @@ export function createVirtualWeekManager({
const baseStart = fromLocalString(base.startDate, DEFAULT_TZ) const baseStart = fromLocalString(base.startDate, DEFAULT_TZ)
const baseEnd = fromLocalString(base.endDate, DEFAULT_TZ) const baseEnd = fromLocalString(base.endDate, DEFAULT_TZ)
const spanDays = Math.max(0, differenceInCalendarDays(baseEnd, baseStart)) const spanDays = Math.max(0, differenceInCalendarDays(baseEnd, baseStart))
const currentDate = fromLocalString(dateStr, DEFAULT_TZ) const currentDate = curDateObj
let occurrenceFound = false let occurrenceFound = false
for (let offset = 0; offset <= spanDays && !occurrenceFound; offset++) { for (let offset = 0; offset <= spanDays && !occurrenceFound; offset++) {
const candidateStart = addDays(currentDate, -offset) const candidateStart = addDays(currentDate, -offset)
@ -114,6 +111,12 @@ export function createVirtualWeekManager({
} }
} }
} }
return dayEvents
}
for (let i = 0; i < 7; i++) {
const dateStr = toLocalString(cur, DEFAULT_TZ)
const dayEvents = collectEventsForDate(dateStr, fromLocalString(dateStr, DEFAULT_TZ))
const dow = cur.getDay() const dow = cur.getDay()
const isFirst = cur.getDate() === 1 const isFirst = cur.getDate() === 1
if (isFirst) { if (isFirst) {
@ -281,6 +284,77 @@ export function createVirtualWeekManager({
scheduleWindowUpdate(reason) scheduleWindowUpdate(reason)
} }
// Reflective update of only events inside currently visible weeks (keeps week objects stable)
function refreshEvents(reason = 'events-refresh') {
if (!visibleWeeks.value.length) return
const repeatingBases = []
if (calendarStore.events) {
for (const ev of calendarStore.events.values()) if (ev.isRepeating) repeatingBases.push(ev)
}
const selStart = selection.value.startDate
const selCount = selection.value.dayCount
const selEnd = selStart && selCount > 0 ? addDaysStr(selStart, selCount - 1) : null
for (const week of visibleWeeks.value) {
for (const day of week.days) {
const dateStr = day.date
// Update selection flag
if (selStart && selEnd) day.isSelected = dateStr >= selStart && dateStr <= selEnd
else day.isSelected = false
// Rebuild events list for this day
const storedEvents = []
for (const ev of calendarStore.events.values()) {
if (!ev.isRepeating && dateStr >= ev.startDate && dateStr <= ev.endDate) {
storedEvents.push(ev)
}
}
const dayEvents = [...storedEvents]
for (const base of repeatingBases) {
if (dateStr >= base.startDate && dateStr <= base.endDate) {
dayEvents.push({ ...base, _recurrenceIndex: 0, _baseId: base.id })
continue
}
const baseStart = fromLocalString(base.startDate, DEFAULT_TZ)
const baseEnd = fromLocalString(base.endDate, DEFAULT_TZ)
const spanDays = Math.max(0, differenceInCalendarDays(baseEnd, baseStart))
const currentDate = fromLocalString(dateStr, DEFAULT_TZ)
let occurrenceFound = false
for (let offset = 0; offset <= spanDays && !occurrenceFound; offset++) {
const candidateStart = addDays(currentDate, -offset)
const candidateStartStr = toLocalString(candidateStart, DEFAULT_TZ)
const occurrenceIndex = getOccurrenceIndex(base, candidateStartStr, DEFAULT_TZ)
if (occurrenceIndex !== null) {
const virtualEndDate = getVirtualOccurrenceEndDate(
base,
candidateStartStr,
DEFAULT_TZ,
)
if (dateStr >= candidateStartStr && dateStr <= virtualEndDate) {
const virtualId = base.id + '_v_' + candidateStartStr
const alreadyExists = dayEvents.some((ev) => ev.id === virtualId)
if (!alreadyExists) {
dayEvents.push({
...base,
id: virtualId,
startDate: candidateStartStr,
endDate: virtualEndDate,
_recurrenceIndex: occurrenceIndex,
_baseId: base.id,
})
}
occurrenceFound = true
}
}
}
}
day.events = dayEvents
}
}
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line no-console
console.debug('[VirtualWeeks] refreshEvents', reason, { weeks: visibleWeeks.value.length })
}
}
function goToToday() { function goToToday() {
const top = addDays(new Date(calendarStore.now), -21) const top = addDays(new Date(calendarStore.now), -21)
const targetWeekIndex = getWeekIndex(top) const targetWeekIndex = getWeekIndex(top)
@ -300,6 +374,7 @@ export function createVirtualWeekManager({
scheduleWindowUpdate, scheduleWindowUpdate,
resetWeeks, resetWeeks,
updateVisibleWeeks, updateVisibleWeeks,
refreshEvents,
getWeekIndex, getWeekIndex,
getFirstDayForVirtualWeek, getFirstDayForVirtualWeek,
goToToday, goToToday,