79 lines
2.5 KiB
TypeScript
79 lines
2.5 KiB
TypeScript
import dayjs from "dayjs"
|
|
|
|
export const EVENT_REPEAT_INTERVALS = [
|
|
"Keine Wiederholung",
|
|
"Täglich",
|
|
"Wöchentlich",
|
|
"2-wöchentlich",
|
|
"Monatlich",
|
|
"Jährlich"
|
|
] as const
|
|
|
|
const DEFAULT_REPEAT_INTERVAL = "Keine Wiederholung"
|
|
const MAX_OCCURRENCES = 500
|
|
|
|
const addInterval = (date: dayjs.Dayjs, repeatInterval: string) => {
|
|
switch (repeatInterval) {
|
|
case "Täglich":
|
|
return date.add(1, "day")
|
|
case "Wöchentlich":
|
|
return date.add(1, "week")
|
|
case "2-wöchentlich":
|
|
return date.add(2, "week")
|
|
case "Monatlich":
|
|
return date.add(1, "month")
|
|
case "Jährlich":
|
|
return date.add(1, "year")
|
|
default:
|
|
return null
|
|
}
|
|
}
|
|
|
|
export const normalizeRepeatInterval = (value?: string | null) =>
|
|
EVENT_REPEAT_INTERVALS.includes(value as typeof EVENT_REPEAT_INTERVALS[number])
|
|
? value as typeof EVENT_REPEAT_INTERVALS[number]
|
|
: DEFAULT_REPEAT_INTERVAL
|
|
|
|
export const expandRecurringEvent = <T>(
|
|
event: {
|
|
startDate: string | Date
|
|
endDate?: string | Date | null
|
|
repeatInterval?: string | null
|
|
},
|
|
rangeStart: string | Date,
|
|
rangeEnd: string | Date,
|
|
buildOccurrence: (occurrenceStart: dayjs.Dayjs, occurrenceEnd: dayjs.Dayjs | null, occurrenceIndex: number) => T
|
|
) => {
|
|
const repeatInterval = normalizeRepeatInterval(event.repeatInterval)
|
|
const baseStart = dayjs(event.startDate)
|
|
const baseEnd = event.endDate ? dayjs(event.endDate) : null
|
|
const visibleStart = dayjs(rangeStart)
|
|
const visibleEnd = dayjs(rangeEnd)
|
|
const durationMs = baseEnd ? Math.max(baseEnd.diff(baseStart), 0) : 0
|
|
|
|
if (repeatInterval === DEFAULT_REPEAT_INTERVAL) {
|
|
const occurrenceEnd = baseEnd ? baseStart.add(durationMs, "millisecond") : null
|
|
if (baseStart.isAfter(visibleEnd) || (occurrenceEnd && occurrenceEnd.isBefore(visibleStart))) return []
|
|
return [buildOccurrence(baseStart, occurrenceEnd, 0)]
|
|
}
|
|
|
|
const occurrences: T[] = []
|
|
let occurrenceStart = baseStart
|
|
let occurrenceIndex = 0
|
|
|
|
while (occurrenceIndex < MAX_OCCURRENCES && occurrenceStart.isBefore(visibleEnd.add(1, "day"))) {
|
|
const occurrenceEnd = baseEnd ? occurrenceStart.add(durationMs, "millisecond") : null
|
|
|
|
if (!occurrenceStart.isAfter(visibleEnd) && !(occurrenceEnd && occurrenceEnd.isBefore(visibleStart))) {
|
|
occurrences.push(buildOccurrence(occurrenceStart, occurrenceEnd, occurrenceIndex))
|
|
}
|
|
|
|
const nextStart = addInterval(occurrenceStart, repeatInterval)
|
|
if (!nextStart || nextStart.isSame(occurrenceStart)) break
|
|
occurrenceStart = nextStart
|
|
occurrenceIndex += 1
|
|
}
|
|
|
|
return occurrences
|
|
}
|