@mantine/schedule 9.5.1 → 9.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/components/WeekView/get-week-view-events/find-available-column.cjs +5 -10
- package/cjs/components/WeekView/get-week-view-events/find-available-column.cjs.map +1 -1
- package/cjs/components/WeekView/get-week-view-events/get-week-positioned-events.cjs +5 -3
- package/cjs/components/WeekView/get-week-view-events/get-week-positioned-events.cjs.map +1 -1
- package/cjs/utils/expand-recurring-events/expand-recurring-events.cjs +20 -8
- package/cjs/utils/expand-recurring-events/expand-recurring-events.cjs.map +1 -1
- package/cjs/utils/is-events-overlap/is-events-overlap.cjs +16 -4
- package/cjs/utils/is-events-overlap/is-events-overlap.cjs.map +1 -1
- package/esm/components/WeekView/get-week-view-events/find-available-column.mjs +5 -10
- package/esm/components/WeekView/get-week-view-events/find-available-column.mjs.map +1 -1
- package/esm/components/WeekView/get-week-view-events/get-week-positioned-events.mjs +5 -3
- package/esm/components/WeekView/get-week-view-events/get-week-positioned-events.mjs.map +1 -1
- package/esm/utils/expand-recurring-events/expand-recurring-events.mjs +20 -8
- package/esm/utils/expand-recurring-events/expand-recurring-events.mjs.map +1 -1
- package/esm/utils/is-events-overlap/is-events-overlap.mjs +16 -4
- package/esm/utils/is-events-overlap/is-events-overlap.mjs.map +1 -1
- package/lib/components/WeekView/get-week-view-events/find-available-column.d.ts +7 -3
- package/package.json +4 -4
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
const require_is_all_day_event = require("../../../utils/is-all-day-event/is-all-day-event.cjs");
|
|
3
2
|
const require_is_events_overlap = require("../../../utils/is-events-overlap/is-events-overlap.cjs");
|
|
4
3
|
//#region packages/@mantine/schedule/src/components/WeekView/get-week-view-events/find-available-column.ts
|
|
5
|
-
function columnHasConflict({ columns, columnIndex, event, allDay
|
|
4
|
+
function columnHasConflict({ columns, columnIndex, event, allDay }) {
|
|
6
5
|
const columnKey = `col-${columnIndex}`;
|
|
7
6
|
if (!columns.has(columnKey)) return false;
|
|
8
|
-
return columns.get(columnKey).some((
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}));
|
|
13
|
-
const hasTimeConflict = require_is_events_overlap.isEventsOverlap(e, event);
|
|
14
|
-
if (allDay && eAllDay) return hasTimeConflict;
|
|
15
|
-
if (allDay || eAllDay) return false;
|
|
7
|
+
return columns.get(columnKey).some((columnEvent) => {
|
|
8
|
+
const hasTimeConflict = require_is_events_overlap.isEventsOverlap(columnEvent.event, event);
|
|
9
|
+
if (allDay && columnEvent.allDay) return hasTimeConflict;
|
|
10
|
+
if (allDay || columnEvent.allDay) return false;
|
|
16
11
|
return hasTimeConflict;
|
|
17
12
|
});
|
|
18
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find-available-column.cjs","names":["
|
|
1
|
+
{"version":3,"file":"find-available-column.cjs","names":["isEventsOverlap"],"sources":["../../../../src/components/WeekView/get-week-view-events/find-available-column.ts"],"sourcesContent":["import { ScheduleEventData } from '../../../types';\nimport { isEventsOverlap } from '../../../utils';\n\n/** An event stored in a positioning column together with its precomputed all-day flag. */\nexport interface ColumnEvent {\n event: ScheduleEventData;\n allDay: boolean;\n}\n\ninterface FindAvailableColumnInput {\n columns: Map<string, ColumnEvent[]>;\n event: ScheduleEventData;\n allDay: boolean;\n}\n\nfunction columnHasConflict({\n columns,\n columnIndex,\n event,\n allDay,\n}: FindAvailableColumnInput & { columnIndex: number }): boolean {\n const columnKey = `col-${columnIndex}`;\n if (!columns.has(columnKey)) {\n return false;\n }\n\n const columnEvents = columns.get(columnKey)!;\n\n return columnEvents.some((columnEvent) => {\n const hasTimeConflict = isEventsOverlap(columnEvent.event, event);\n\n if (allDay && columnEvent.allDay) {\n return hasTimeConflict;\n }\n\n if (allDay || columnEvent.allDay) {\n return false;\n }\n\n return hasTimeConflict;\n });\n}\n\nexport function findAvailableColumn(input: FindAvailableColumnInput): number {\n let column = 0;\n\n while (columnHasConflict({ ...input, columnIndex: column })) {\n column++;\n }\n\n return column;\n}\n"],"mappings":";;;AAeA,SAAS,kBAAkB,EACzB,SACA,aACA,OACA,UAC8D;CAC9D,MAAM,YAAY,OAAO;CACzB,IAAI,CAAC,QAAQ,IAAI,SAAS,GACxB,OAAO;CAKT,OAFqB,QAAQ,IAAI,SAEf,CAAC,CAAC,MAAM,gBAAgB;EACxC,MAAM,kBAAkBA,0BAAAA,gBAAgB,YAAY,OAAO,KAAK;EAEhE,IAAI,UAAU,YAAY,QACxB,OAAO;EAGT,IAAI,UAAU,YAAY,QACxB,OAAO;EAGT,OAAO;CACT,CAAC;AACH;AAEA,SAAgB,oBAAoB,OAAyC;CAC3E,IAAI,SAAS;CAEb,OAAO,kBAAkB;EAAE,GAAG;EAAO,aAAa;CAAO,CAAC,GACxD;CAGF,OAAO;AACT"}
|
|
@@ -116,12 +116,14 @@ function getWeekPositionedEvents({ date, events, startTime, endTime, intervalMin
|
|
|
116
116
|
const column = require_find_available_column.findAvailableColumn({
|
|
117
117
|
columns: columnMap,
|
|
118
118
|
event,
|
|
119
|
-
allDay
|
|
120
|
-
allWeekDays: weekDays
|
|
119
|
+
allDay
|
|
121
120
|
});
|
|
122
121
|
const columnKey = `col-${column}`;
|
|
123
122
|
if (!columnMap.has(columnKey)) columnMap.set(columnKey, []);
|
|
124
|
-
columnMap.get(columnKey).push(
|
|
123
|
+
columnMap.get(columnKey).push({
|
|
124
|
+
event,
|
|
125
|
+
allDay: isActuallyAllDay
|
|
126
|
+
});
|
|
125
127
|
const verticalPosition = allDay ? {
|
|
126
128
|
top: 0,
|
|
127
129
|
height: 100
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-week-positioned-events.cjs","names":["getWeekDays","sortEvents","getEventEndDate","calculateEventDays","getHangingStatus","isAllDayEvent","getDayPosition","findAvailableColumn","calculateAllDayEventOffset","assignEventRows","calculateAllDayEventWidth"],"sources":["../../../../src/components/WeekView/get-week-view-events/get-week-positioned-events.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport {\n AnyDateValue,\n DateStringValue,\n DayOfWeek,\n ScheduleEventData,\n WeekPositionedEventData,\n} from '../../../types';\nimport { getDayPosition, getWeekDays, isAllDayEvent, sortEvents } from '../../../utils';\nimport { assignEventRows } from './assign-event-rows';\nimport { calculateAllDayEventOffset } from './calculate-all-day-event-offset';\nimport { calculateAllDayEventWidth } from './calculate-all-day-event-width';\nimport { calculateEventDays } from './calculate-event-days';\nimport { calculateRegularEventOverlaps } from './calculate-regular-event-overlaps';\nimport { findAvailableColumn } from './find-available-column';\nimport { getEventEndDate } from './get-event-end-date';\nimport { getHangingStatus } from './get-hanging-status';\n\nexport interface GetWeekPositionedEventsInput {\n /** Date (week start) at which events are positioned, used to check if events are all-day */\n date: AnyDateValue;\n\n /** List of all events that belong to the given week, extra events must be filtered out before passing to the function */\n events: ScheduleEventData[];\n\n /** Start time of the week view, used to calculate event positions */\n startTime?: string;\n\n /** End time of the week view, used to calculate event positions */\n endTime?: string;\n\n /** Number of minutes per time slot, used to align the canvas to whole slots */\n intervalMinutes?: number;\n\n /** First day of the week, 0 - Sunday, 1 - Monday, etc., used to calculate events positions */\n firstDayOfWeek?: DayOfWeek;\n\n /** Indices of weekend days, 0-6, where 0 is Sunday and 6 is Saturday */\n weekendDays?: DayOfWeek[];\n\n /** If set to false, weekend days are hidden @default true */\n withWeekendDays?: boolean;\n}\n\n/** Events grouped by week day date (YYYY-MM-DD 00:00:00) and by columns */\nexport interface GroupedWeekEvents {\n allDayEvents: WeekPositionedEventData[];\n regularEvents: Record<DateStringValue, WeekPositionedEventData[]>;\n backgroundEvents: Record<DateStringValue, WeekPositionedEventData[]>;\n}\n\nexport function getWeekPositionedEvents({\n date,\n events,\n startTime,\n endTime,\n intervalMinutes,\n firstDayOfWeek = 1,\n weekendDays = [0, 6],\n withWeekendDays = true,\n}: GetWeekPositionedEventsInput): GroupedWeekEvents {\n const weekDays = getWeekDays({ week: date, firstDayOfWeek, withWeekendDays, weekendDays });\n const visibleDaysCount = weekDays.length;\n const weekStartDate = dayjs(weekDays[0]);\n const weekEndDate = dayjs(weekDays[weekDays.length - 1]);\n\n const grouped: GroupedWeekEvents = {\n allDayEvents: [],\n regularEvents: Object.fromEntries(weekDays.map((day) => [day, []])),\n backgroundEvents: Object.fromEntries(weekDays.map((day) => [day, []])),\n };\n\n const columns = new Map<string, ScheduleEventData[]>();\n const allDayColumns = new Map<string, ScheduleEventData[]>();\n const sorted = sortEvents(events);\n\n for (const event of sorted) {\n if (event.display === 'background') {\n const eventStartDate = dayjs(event.start).startOf('day');\n const actualEndDate = getEventEndDate(event);\n const eventWeekDays = calculateEventDays({ event, weekDays, actualEndDate });\n const hanging = getHangingStatus({ eventStartDate, actualEndDate, weekDays });\n\n for (const day of eventWeekDays) {\n const visibleDayIndex = weekDays.indexOf(day);\n if (visibleDayIndex === -1) {\n continue;\n }\n\n const dayStart = dayjs(day);\n const dayEnd = dayStart.endOf('day');\n const eventStart = dayjs(event.start);\n const eventEnd = dayjs(event.end);\n\n const clippedStart = eventStart.isBefore(dayStart) ? dayStart : eventStart;\n const clippedEnd = eventEnd.isAfter(dayEnd) ? dayEnd : eventEnd;\n\n const clippedEvent = {\n ...event,\n start: clippedStart.format('YYYY-MM-DD HH:mm:ss'),\n end: clippedEnd.format('YYYY-MM-DD HH:mm:ss'),\n };\n\n const isAllDay = isAllDayEvent({ event: clippedEvent, date: day });\n const verticalPosition = isAllDay\n ? { top: 0, height: 100 }\n : getDayPosition({ event: clippedEvent, startTime, endTime, intervalMinutes });\n\n if (!isAllDay && verticalPosition.height <= 0) {\n continue;\n }\n\n const dayWeekOffset = (visibleDayIndex / visibleDaysCount) * 100;\n\n grouped.backgroundEvents[day].push({\n ...event,\n position: {\n ...verticalPosition,\n allDay: isAllDay,\n column: 0,\n width: 100,\n offset: 0,\n overlaps: 1,\n weekOffset: dayWeekOffset,\n row: 0,\n hanging,\n },\n });\n }\n\n continue;\n }\n const eventStartDate = dayjs(event.start).startOf('day');\n const actualEndDate = getEventEndDate(event);\n const eventWeekDays = calculateEventDays({ event, weekDays, actualEndDate });\n\n if (eventWeekDays.length === 0) {\n continue;\n }\n\n const isMultiday = actualEndDate.isAfter(eventStartDate);\n const hanging = getHangingStatus({ eventStartDate, actualEndDate, weekDays });\n const isActuallyAllDay = eventWeekDays.some((day) => isAllDayEvent({ event, date: day }));\n const allDay = isMultiday || isActuallyAllDay;\n\n const columnMap = allDay ? allDayColumns : columns;\n const column = findAvailableColumn({\n columns: columnMap,\n event,\n allDay,\n allWeekDays: weekDays,\n });\n\n const columnKey = `col-${column}`;\n if (!columnMap.has(columnKey)) {\n columnMap.set(columnKey, []);\n }\n columnMap.get(columnKey)!.push(event);\n\n const verticalPosition = allDay\n ? { top: 0, height: 100 }\n : getDayPosition({ event, startTime, endTime, intervalMinutes });\n\n if (allDay) {\n const offset = calculateAllDayEventOffset({\n eventStartDate,\n weekStartDate,\n weekDays,\n visibleDaysCount,\n hangingStart: hanging === 'start' || hanging === 'both',\n });\n\n grouped.allDayEvents.push({\n ...event,\n position: {\n ...verticalPosition,\n allDay,\n column,\n width: 0,\n offset,\n overlaps: 0,\n row: 0,\n hanging,\n },\n });\n } else {\n for (const day of eventWeekDays) {\n const visibleDayIndex = weekDays.indexOf(day);\n\n if (visibleDayIndex === -1) {\n continue;\n }\n\n const dayWeekOffset = (visibleDayIndex / visibleDaysCount) * 100;\n\n grouped.regularEvents[day].push({\n ...event,\n position: {\n ...verticalPosition,\n allDay,\n column,\n width: 0,\n offset: 0,\n overlaps: 0,\n weekOffset: dayWeekOffset,\n row: 0,\n hanging,\n },\n });\n }\n }\n }\n\n for (const day of weekDays) {\n calculateRegularEventOverlaps(grouped.regularEvents[day]);\n }\n\n if (grouped.allDayEvents.length > 0) {\n const eventRows = assignEventRows(grouped.allDayEvents);\n const assignedRows = new Set<number>();\n\n for (const event of grouped.allDayEvents) {\n const row = eventRows.get(event)!;\n event.position.row = row;\n assignedRows.add(row);\n }\n\n const rowCount = Math.max(...assignedRows) + 1;\n\n for (const event of grouped.allDayEvents) {\n const eventStartDate = dayjs(event.start).startOf('day');\n const actualEndDate = getEventEndDate(event);\n\n event.position.width = calculateAllDayEventWidth({\n eventStartDate,\n actualEndDate,\n weekStartDate,\n weekEndDate,\n weekDays,\n visibleDaysCount,\n });\n\n event.position.overlaps = rowCount;\n }\n }\n\n return grouped;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"get-week-positioned-events.cjs","names":["getWeekDays","sortEvents","getEventEndDate","calculateEventDays","getHangingStatus","isAllDayEvent","getDayPosition","findAvailableColumn","calculateAllDayEventOffset","assignEventRows","calculateAllDayEventWidth"],"sources":["../../../../src/components/WeekView/get-week-view-events/get-week-positioned-events.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport {\n AnyDateValue,\n DateStringValue,\n DayOfWeek,\n ScheduleEventData,\n WeekPositionedEventData,\n} from '../../../types';\nimport { getDayPosition, getWeekDays, isAllDayEvent, sortEvents } from '../../../utils';\nimport { assignEventRows } from './assign-event-rows';\nimport { calculateAllDayEventOffset } from './calculate-all-day-event-offset';\nimport { calculateAllDayEventWidth } from './calculate-all-day-event-width';\nimport { calculateEventDays } from './calculate-event-days';\nimport { calculateRegularEventOverlaps } from './calculate-regular-event-overlaps';\nimport { ColumnEvent, findAvailableColumn } from './find-available-column';\nimport { getEventEndDate } from './get-event-end-date';\nimport { getHangingStatus } from './get-hanging-status';\n\nexport interface GetWeekPositionedEventsInput {\n /** Date (week start) at which events are positioned, used to check if events are all-day */\n date: AnyDateValue;\n\n /** List of all events that belong to the given week, extra events must be filtered out before passing to the function */\n events: ScheduleEventData[];\n\n /** Start time of the week view, used to calculate event positions */\n startTime?: string;\n\n /** End time of the week view, used to calculate event positions */\n endTime?: string;\n\n /** Number of minutes per time slot, used to align the canvas to whole slots */\n intervalMinutes?: number;\n\n /** First day of the week, 0 - Sunday, 1 - Monday, etc., used to calculate events positions */\n firstDayOfWeek?: DayOfWeek;\n\n /** Indices of weekend days, 0-6, where 0 is Sunday and 6 is Saturday */\n weekendDays?: DayOfWeek[];\n\n /** If set to false, weekend days are hidden @default true */\n withWeekendDays?: boolean;\n}\n\n/** Events grouped by week day date (YYYY-MM-DD 00:00:00) and by columns */\nexport interface GroupedWeekEvents {\n allDayEvents: WeekPositionedEventData[];\n regularEvents: Record<DateStringValue, WeekPositionedEventData[]>;\n backgroundEvents: Record<DateStringValue, WeekPositionedEventData[]>;\n}\n\nexport function getWeekPositionedEvents({\n date,\n events,\n startTime,\n endTime,\n intervalMinutes,\n firstDayOfWeek = 1,\n weekendDays = [0, 6],\n withWeekendDays = true,\n}: GetWeekPositionedEventsInput): GroupedWeekEvents {\n const weekDays = getWeekDays({ week: date, firstDayOfWeek, withWeekendDays, weekendDays });\n const visibleDaysCount = weekDays.length;\n const weekStartDate = dayjs(weekDays[0]);\n const weekEndDate = dayjs(weekDays[weekDays.length - 1]);\n\n const grouped: GroupedWeekEvents = {\n allDayEvents: [],\n regularEvents: Object.fromEntries(weekDays.map((day) => [day, []])),\n backgroundEvents: Object.fromEntries(weekDays.map((day) => [day, []])),\n };\n\n const columns = new Map<string, ColumnEvent[]>();\n const allDayColumns = new Map<string, ColumnEvent[]>();\n const sorted = sortEvents(events);\n\n for (const event of sorted) {\n if (event.display === 'background') {\n const eventStartDate = dayjs(event.start).startOf('day');\n const actualEndDate = getEventEndDate(event);\n const eventWeekDays = calculateEventDays({ event, weekDays, actualEndDate });\n const hanging = getHangingStatus({ eventStartDate, actualEndDate, weekDays });\n\n for (const day of eventWeekDays) {\n const visibleDayIndex = weekDays.indexOf(day);\n if (visibleDayIndex === -1) {\n continue;\n }\n\n const dayStart = dayjs(day);\n const dayEnd = dayStart.endOf('day');\n const eventStart = dayjs(event.start);\n const eventEnd = dayjs(event.end);\n\n const clippedStart = eventStart.isBefore(dayStart) ? dayStart : eventStart;\n const clippedEnd = eventEnd.isAfter(dayEnd) ? dayEnd : eventEnd;\n\n const clippedEvent = {\n ...event,\n start: clippedStart.format('YYYY-MM-DD HH:mm:ss'),\n end: clippedEnd.format('YYYY-MM-DD HH:mm:ss'),\n };\n\n const isAllDay = isAllDayEvent({ event: clippedEvent, date: day });\n const verticalPosition = isAllDay\n ? { top: 0, height: 100 }\n : getDayPosition({ event: clippedEvent, startTime, endTime, intervalMinutes });\n\n if (!isAllDay && verticalPosition.height <= 0) {\n continue;\n }\n\n const dayWeekOffset = (visibleDayIndex / visibleDaysCount) * 100;\n\n grouped.backgroundEvents[day].push({\n ...event,\n position: {\n ...verticalPosition,\n allDay: isAllDay,\n column: 0,\n width: 100,\n offset: 0,\n overlaps: 1,\n weekOffset: dayWeekOffset,\n row: 0,\n hanging,\n },\n });\n }\n\n continue;\n }\n const eventStartDate = dayjs(event.start).startOf('day');\n const actualEndDate = getEventEndDate(event);\n const eventWeekDays = calculateEventDays({ event, weekDays, actualEndDate });\n\n if (eventWeekDays.length === 0) {\n continue;\n }\n\n const isMultiday = actualEndDate.isAfter(eventStartDate);\n const hanging = getHangingStatus({ eventStartDate, actualEndDate, weekDays });\n const isActuallyAllDay = eventWeekDays.some((day) => isAllDayEvent({ event, date: day }));\n const allDay = isMultiday || isActuallyAllDay;\n\n const columnMap = allDay ? allDayColumns : columns;\n const column = findAvailableColumn({\n columns: columnMap,\n event,\n allDay,\n });\n\n const columnKey = `col-${column}`;\n if (!columnMap.has(columnKey)) {\n columnMap.set(columnKey, []);\n }\n columnMap.get(columnKey)!.push({ event, allDay: isActuallyAllDay });\n\n const verticalPosition = allDay\n ? { top: 0, height: 100 }\n : getDayPosition({ event, startTime, endTime, intervalMinutes });\n\n if (allDay) {\n const offset = calculateAllDayEventOffset({\n eventStartDate,\n weekStartDate,\n weekDays,\n visibleDaysCount,\n hangingStart: hanging === 'start' || hanging === 'both',\n });\n\n grouped.allDayEvents.push({\n ...event,\n position: {\n ...verticalPosition,\n allDay,\n column,\n width: 0,\n offset,\n overlaps: 0,\n row: 0,\n hanging,\n },\n });\n } else {\n for (const day of eventWeekDays) {\n const visibleDayIndex = weekDays.indexOf(day);\n\n if (visibleDayIndex === -1) {\n continue;\n }\n\n const dayWeekOffset = (visibleDayIndex / visibleDaysCount) * 100;\n\n grouped.regularEvents[day].push({\n ...event,\n position: {\n ...verticalPosition,\n allDay,\n column,\n width: 0,\n offset: 0,\n overlaps: 0,\n weekOffset: dayWeekOffset,\n row: 0,\n hanging,\n },\n });\n }\n }\n }\n\n for (const day of weekDays) {\n calculateRegularEventOverlaps(grouped.regularEvents[day]);\n }\n\n if (grouped.allDayEvents.length > 0) {\n const eventRows = assignEventRows(grouped.allDayEvents);\n const assignedRows = new Set<number>();\n\n for (const event of grouped.allDayEvents) {\n const row = eventRows.get(event)!;\n event.position.row = row;\n assignedRows.add(row);\n }\n\n const rowCount = Math.max(...assignedRows) + 1;\n\n for (const event of grouped.allDayEvents) {\n const eventStartDate = dayjs(event.start).startOf('day');\n const actualEndDate = getEventEndDate(event);\n\n event.position.width = calculateAllDayEventWidth({\n eventStartDate,\n actualEndDate,\n weekStartDate,\n weekEndDate,\n weekDays,\n visibleDaysCount,\n });\n\n event.position.overlaps = rowCount;\n }\n }\n\n return grouped;\n}\n"],"mappings":"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"}
|
|
@@ -18,6 +18,18 @@ function getExdateSet(recurrence) {
|
|
|
18
18
|
const values = recurrence.exdate.map((value) => (0, dayjs.default)(value)).filter((value) => value.isValid()).map((value) => value.format("YYYY-MM-DD HH:mm:ss"));
|
|
19
19
|
return new Set(values);
|
|
20
20
|
}
|
|
21
|
+
function toWallClockDate(value) {
|
|
22
|
+
return new Date(Date.UTC(value.year(), value.month(), value.date(), value.hour(), value.minute(), value.second(), value.millisecond()));
|
|
23
|
+
}
|
|
24
|
+
function fromWallClockDate(value) {
|
|
25
|
+
const result = new Date(2e3, 0, 1, 12);
|
|
26
|
+
result.setFullYear(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate());
|
|
27
|
+
result.setHours(value.getUTCHours(), value.getUTCMinutes(), value.getUTCSeconds(), value.getUTCMilliseconds());
|
|
28
|
+
return (0, dayjs.default)(result);
|
|
29
|
+
}
|
|
30
|
+
function getWallClockDurationMs(start, end) {
|
|
31
|
+
return toWallClockDate(end).getTime() - toWallClockDate(start).getTime();
|
|
32
|
+
}
|
|
21
33
|
function getRRuleString(rule) {
|
|
22
34
|
const normalized = rule.trim();
|
|
23
35
|
if (!normalized.includes("\n")) return normalized.startsWith("RRULE:") ? normalized.replace(/^RRULE:/, "") : normalized;
|
|
@@ -29,10 +41,10 @@ function getOccurrenceStartsInRange(event, rangeStart, rangeEnd, expansionLimit,
|
|
|
29
41
|
if (!dtstart.isValid() || !event.recurrence) return [];
|
|
30
42
|
try {
|
|
31
43
|
const options = RRule.parseString(getRRuleString(event.recurrence.rrule));
|
|
32
|
-
options.dtstart = dtstart
|
|
44
|
+
options.dtstart = toWallClockDate(dtstart);
|
|
33
45
|
const rule = new RRule(options);
|
|
34
|
-
const searchStart = rangeStart.
|
|
35
|
-
return rule.between(searchStart, rangeEnd
|
|
46
|
+
const searchStart = new Date(toWallClockDate(rangeStart).getTime() - Math.max(0, durationMs));
|
|
47
|
+
return rule.between(searchStart, toWallClockDate(rangeEnd), true).slice(0, expansionLimit);
|
|
36
48
|
} catch {
|
|
37
49
|
return [];
|
|
38
50
|
}
|
|
@@ -43,8 +55,7 @@ function isEventInRange(event, rangeStart, rangeEnd) {
|
|
|
43
55
|
if (!start.isValid() || !end.isValid()) return false;
|
|
44
56
|
return overlapsRange(start, end, rangeStart, rangeEnd);
|
|
45
57
|
}
|
|
46
|
-
function createGeneratedInstance(event, occurrenceStart,
|
|
47
|
-
const occurrenceEnd = occurrenceStart.add(durationMs, "millisecond");
|
|
58
|
+
function createGeneratedInstance(event, occurrenceStart, occurrenceEnd) {
|
|
48
59
|
const recurrenceId = occurrenceStart.format("YYYY-MM-DD HH:mm:ss");
|
|
49
60
|
const { recurrence, ...eventWithoutRecurrence } = event;
|
|
50
61
|
return {
|
|
@@ -90,11 +101,12 @@ function expandRecurringEvents({ events, rangeStart, rangeEnd, expansionLimit =
|
|
|
90
101
|
const eventStart = (0, dayjs.default)(event.recurrence?.dtstart || event.start);
|
|
91
102
|
const eventEnd = (0, dayjs.default)(event.end);
|
|
92
103
|
if (!eventStart.isValid() || !eventEnd.isValid()) continue;
|
|
93
|
-
const durationMs =
|
|
104
|
+
const durationMs = getWallClockDurationMs((0, dayjs.default)(event.start), eventEnd);
|
|
94
105
|
if (durationMs < 0) continue;
|
|
95
106
|
const exdate = getExdateSet(event.recurrence);
|
|
96
107
|
const starts = getOccurrenceStartsInRange(event, start, end, expansionLimit, durationMs);
|
|
97
|
-
for (const
|
|
108
|
+
for (const wallClockStart of starts) {
|
|
109
|
+
const occurrenceStart = fromWallClockDate(wallClockStart);
|
|
98
110
|
const recurrenceId = occurrenceStart.format("YYYY-MM-DD HH:mm:ss");
|
|
99
111
|
if (exdate.has(recurrenceId)) continue;
|
|
100
112
|
const overrideKey = getRecurrenceKey(event.id, recurrenceId);
|
|
@@ -106,7 +118,7 @@ function expandRecurringEvents({ events, rangeStart, rangeEnd, expansionLimit =
|
|
|
106
118
|
}
|
|
107
119
|
continue;
|
|
108
120
|
}
|
|
109
|
-
const generated = createGeneratedInstance(event, occurrenceStart, durationMs);
|
|
121
|
+
const generated = createGeneratedInstance(event, occurrenceStart, fromWallClockDate(new Date(wallClockStart.getTime() + durationMs)));
|
|
110
122
|
if (overlapsRange((0, dayjs.default)(generated.start), (0, dayjs.default)(generated.end), start, end)) output.push(require_validate_event.validateEvent(generated));
|
|
111
123
|
}
|
|
112
124
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expand-recurring-events.cjs","names":["rruleAll","validateEvent"],"sources":["../../../src/utils/expand-recurring-events/expand-recurring-events.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport * as rruleAll from 'rrule';\n\nconst RRule = (\n 'default' in rruleAll ? (rruleAll as any).default.RRule : rruleAll.RRule\n) as typeof rruleAll.RRule;\nimport { DateTimeStringValue, ScheduleEventData, ScheduleRecurrenceData } from '../../types';\nimport { validateEvent } from '../validate-event/validate-event';\n\ninterface ExpandRecurringEventsInput {\n events: ScheduleEventData[] | undefined;\n rangeStart: Date | string;\n rangeEnd: Date | string;\n expansionLimit?: number;\n}\n\nfunction getRecurrenceKey(recurringEventId: string | number, recurrenceId: DateTimeStringValue) {\n return `${recurringEventId}::${recurrenceId}`;\n}\n\nfunction overlapsRange(\n eventStart: dayjs.Dayjs,\n eventEnd: dayjs.Dayjs,\n rangeStart: dayjs.Dayjs,\n rangeEnd: dayjs.Dayjs\n) {\n return (\n (eventEnd.isAfter(rangeStart) || eventEnd.isSame(rangeStart)) &&\n (eventStart.isBefore(rangeEnd) || eventStart.isSame(rangeEnd))\n );\n}\n\nfunction getExdateSet(recurrence: ScheduleRecurrenceData | undefined) {\n if (!recurrence?.exdate) {\n return new Set<string>();\n }\n\n const values = recurrence.exdate\n .map((value) => dayjs(value))\n .filter((value) => value.isValid())\n .map((value) => value.format('YYYY-MM-DD HH:mm:ss'));\n\n return new Set(values);\n}\n\nfunction getRRuleString(rule: string) {\n const normalized = rule.trim();\n if (!normalized.includes('\\n')) {\n return normalized.startsWith('RRULE:') ? normalized.replace(/^RRULE:/, '') : normalized;\n }\n\n const rruleLine = normalized\n .split('\\n')\n .map((line) => line.trim())\n .find((line) => line.startsWith('RRULE:'));\n\n return rruleLine ? rruleLine.replace(/^RRULE:/, '') : normalized;\n}\n\nfunction getOccurrenceStartsInRange(\n event: ScheduleEventData,\n rangeStart: dayjs.Dayjs,\n rangeEnd: dayjs.Dayjs,\n expansionLimit: number,\n durationMs: number\n) {\n const dtstart = dayjs(event.recurrence?.dtstart || event.start);\n if (!dtstart.isValid() || !event.recurrence) {\n return [];\n }\n\n try {\n const options = RRule.parseString(getRRuleString(event.recurrence.rrule));\n options.dtstart = dtstart.toDate();\n const rule = new RRule(options);\n\n const searchStart = rangeStart.subtract(Math.max(0, durationMs), 'millisecond').toDate();\n const results = rule.between(searchStart, rangeEnd.toDate(), true);\n return results.slice(0, expansionLimit).map((value) => dayjs(value));\n } catch {\n return [];\n }\n}\n\nfunction isEventInRange(event: ScheduleEventData, rangeStart: dayjs.Dayjs, rangeEnd: dayjs.Dayjs) {\n const start = dayjs(event.start);\n const end = dayjs(event.end);\n\n if (!start.isValid() || !end.isValid()) {\n return false;\n }\n\n return overlapsRange(start, end, rangeStart, rangeEnd);\n}\n\nfunction createGeneratedInstance(\n event: ScheduleEventData,\n occurrenceStart: dayjs.Dayjs,\n durationMs: number\n): ScheduleEventData {\n const occurrenceEnd = occurrenceStart.add(durationMs, 'millisecond');\n const recurrenceId = occurrenceStart.format('YYYY-MM-DD HH:mm:ss');\n const { recurrence, ...eventWithoutRecurrence } = event;\n\n return {\n ...eventWithoutRecurrence,\n id: `${event.id}::${recurrenceId}`,\n start: occurrenceStart.format('YYYY-MM-DD HH:mm:ss'),\n end: occurrenceEnd.format('YYYY-MM-DD HH:mm:ss'),\n recurringEventId: event.id,\n recurrenceId,\n recurringInstance: {\n isRecurringInstance: true,\n recurringEventId: event.id,\n recurrenceId,\n originalStart: occurrenceStart.format('YYYY-MM-DD HH:mm:ss'),\n originalEnd: occurrenceEnd.format('YYYY-MM-DD HH:mm:ss'),\n },\n };\n}\n\nconst DEFAULT_EXPANSION_LIMIT = 2000;\n\nexport function expandRecurringEvents({\n events,\n rangeStart,\n rangeEnd,\n expansionLimit = DEFAULT_EXPANSION_LIMIT,\n}: ExpandRecurringEventsInput): ScheduleEventData[] {\n if (!events || events.length === 0) {\n return [];\n }\n\n const start = dayjs(rangeStart);\n const end = dayjs(rangeEnd);\n\n if (!start.isValid() || !end.isValid()) {\n return [];\n }\n\n const regularEvents: ScheduleEventData[] = [];\n const recurringEvents: ScheduleEventData[] = [];\n const overrides = new Map<string, ScheduleEventData>();\n\n for (const event of events) {\n if (event.recurringEventId && event.recurrenceId) {\n overrides.set(getRecurrenceKey(event.recurringEventId, event.recurrenceId), event);\n continue;\n }\n\n if (event.recurrence?.rrule) {\n recurringEvents.push(event);\n continue;\n }\n\n regularEvents.push(event);\n }\n\n const output: ScheduleEventData[] = [];\n const consumedOverrideIds = new Set<string | number>();\n\n for (const event of regularEvents) {\n if (isEventInRange(event, start, end)) {\n output.push(validateEvent(event));\n }\n }\n\n for (const event of recurringEvents) {\n const eventStart = dayjs(event.recurrence?.dtstart || event.start);\n const eventEnd = dayjs(event.end);\n\n if (!eventStart.isValid() || !eventEnd.isValid()) {\n continue;\n }\n\n const durationMs = eventEnd.diff(dayjs(event.start), 'millisecond');\n if (durationMs < 0) {\n continue;\n }\n\n const exdate = getExdateSet(event.recurrence);\n const starts = getOccurrenceStartsInRange(event, start, end, expansionLimit, durationMs);\n\n for (const occurrenceStart of starts) {\n const recurrenceId = occurrenceStart.format('YYYY-MM-DD HH:mm:ss');\n if (exdate.has(recurrenceId)) {\n continue;\n }\n\n const overrideKey = getRecurrenceKey(event.id, recurrenceId);\n const override = overrides.get(overrideKey);\n if (override) {\n if (isEventInRange(override, start, end)) {\n output.push(validateEvent(override));\n consumedOverrideIds.add(override.id);\n }\n continue;\n }\n\n const generated = createGeneratedInstance(event, occurrenceStart, durationMs);\n if (overlapsRange(dayjs(generated.start), dayjs(generated.end), start, end)) {\n output.push(validateEvent(generated));\n }\n }\n }\n\n for (const override of overrides.values()) {\n if (consumedOverrideIds.has(override.id)) {\n continue;\n }\n if (isEventInRange(override, start, end)) {\n output.push(validateEvent(override));\n }\n }\n\n return output;\n}\n\nexport { DEFAULT_EXPANSION_LIMIT };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"expand-recurring-events.cjs","names":["rruleAll","validateEvent"],"sources":["../../../src/utils/expand-recurring-events/expand-recurring-events.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport * as rruleAll from 'rrule';\n\nconst RRule = (\n 'default' in rruleAll ? (rruleAll as any).default.RRule : rruleAll.RRule\n) as typeof rruleAll.RRule;\nimport { DateTimeStringValue, ScheduleEventData, ScheduleRecurrenceData } from '../../types';\nimport { validateEvent } from '../validate-event/validate-event';\n\ninterface ExpandRecurringEventsInput {\n events: ScheduleEventData[] | undefined;\n rangeStart: Date | string;\n rangeEnd: Date | string;\n expansionLimit?: number;\n}\n\nfunction getRecurrenceKey(recurringEventId: string | number, recurrenceId: DateTimeStringValue) {\n return `${recurringEventId}::${recurrenceId}`;\n}\n\nfunction overlapsRange(\n eventStart: dayjs.Dayjs,\n eventEnd: dayjs.Dayjs,\n rangeStart: dayjs.Dayjs,\n rangeEnd: dayjs.Dayjs\n) {\n return (\n (eventEnd.isAfter(rangeStart) || eventEnd.isSame(rangeStart)) &&\n (eventStart.isBefore(rangeEnd) || eventStart.isSame(rangeEnd))\n );\n}\n\nfunction getExdateSet(recurrence: ScheduleRecurrenceData | undefined) {\n if (!recurrence?.exdate) {\n return new Set<string>();\n }\n\n const values = recurrence.exdate\n .map((value) => dayjs(value))\n .filter((value) => value.isValid())\n .map((value) => value.format('YYYY-MM-DD HH:mm:ss'));\n\n return new Set(values);\n}\n\nfunction toWallClockDate(value: dayjs.Dayjs) {\n return new Date(\n Date.UTC(\n value.year(),\n value.month(),\n value.date(),\n value.hour(),\n value.minute(),\n value.second(),\n value.millisecond()\n )\n );\n}\n\nfunction fromWallClockDate(value: Date) {\n const result = new Date(2000, 0, 1, 12);\n result.setFullYear(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate());\n result.setHours(\n value.getUTCHours(),\n value.getUTCMinutes(),\n value.getUTCSeconds(),\n value.getUTCMilliseconds()\n );\n\n return dayjs(result);\n}\n\nfunction getWallClockDurationMs(start: dayjs.Dayjs, end: dayjs.Dayjs) {\n return toWallClockDate(end).getTime() - toWallClockDate(start).getTime();\n}\n\nfunction getRRuleString(rule: string) {\n const normalized = rule.trim();\n if (!normalized.includes('\\n')) {\n return normalized.startsWith('RRULE:') ? normalized.replace(/^RRULE:/, '') : normalized;\n }\n\n const rruleLine = normalized\n .split('\\n')\n .map((line) => line.trim())\n .find((line) => line.startsWith('RRULE:'));\n\n return rruleLine ? rruleLine.replace(/^RRULE:/, '') : normalized;\n}\n\nfunction getOccurrenceStartsInRange(\n event: ScheduleEventData,\n rangeStart: dayjs.Dayjs,\n rangeEnd: dayjs.Dayjs,\n expansionLimit: number,\n durationMs: number\n) {\n const dtstart = dayjs(event.recurrence?.dtstart || event.start);\n if (!dtstart.isValid() || !event.recurrence) {\n return [];\n }\n\n try {\n const options = RRule.parseString(getRRuleString(event.recurrence.rrule));\n options.dtstart = toWallClockDate(dtstart);\n const rule = new RRule(options);\n\n const searchStart = new Date(toWallClockDate(rangeStart).getTime() - Math.max(0, durationMs));\n const results = rule.between(searchStart, toWallClockDate(rangeEnd), true);\n return results.slice(0, expansionLimit);\n } catch {\n return [];\n }\n}\n\nfunction isEventInRange(event: ScheduleEventData, rangeStart: dayjs.Dayjs, rangeEnd: dayjs.Dayjs) {\n const start = dayjs(event.start);\n const end = dayjs(event.end);\n\n if (!start.isValid() || !end.isValid()) {\n return false;\n }\n\n return overlapsRange(start, end, rangeStart, rangeEnd);\n}\n\nfunction createGeneratedInstance(\n event: ScheduleEventData,\n occurrenceStart: dayjs.Dayjs,\n occurrenceEnd: dayjs.Dayjs\n): ScheduleEventData {\n const recurrenceId = occurrenceStart.format('YYYY-MM-DD HH:mm:ss');\n const { recurrence, ...eventWithoutRecurrence } = event;\n\n return {\n ...eventWithoutRecurrence,\n id: `${event.id}::${recurrenceId}`,\n start: occurrenceStart.format('YYYY-MM-DD HH:mm:ss'),\n end: occurrenceEnd.format('YYYY-MM-DD HH:mm:ss'),\n recurringEventId: event.id,\n recurrenceId,\n recurringInstance: {\n isRecurringInstance: true,\n recurringEventId: event.id,\n recurrenceId,\n originalStart: occurrenceStart.format('YYYY-MM-DD HH:mm:ss'),\n originalEnd: occurrenceEnd.format('YYYY-MM-DD HH:mm:ss'),\n },\n };\n}\n\nconst DEFAULT_EXPANSION_LIMIT = 2000;\n\nexport function expandRecurringEvents({\n events,\n rangeStart,\n rangeEnd,\n expansionLimit = DEFAULT_EXPANSION_LIMIT,\n}: ExpandRecurringEventsInput): ScheduleEventData[] {\n if (!events || events.length === 0) {\n return [];\n }\n\n const start = dayjs(rangeStart);\n const end = dayjs(rangeEnd);\n\n if (!start.isValid() || !end.isValid()) {\n return [];\n }\n\n const regularEvents: ScheduleEventData[] = [];\n const recurringEvents: ScheduleEventData[] = [];\n const overrides = new Map<string, ScheduleEventData>();\n\n for (const event of events) {\n if (event.recurringEventId && event.recurrenceId) {\n overrides.set(getRecurrenceKey(event.recurringEventId, event.recurrenceId), event);\n continue;\n }\n\n if (event.recurrence?.rrule) {\n recurringEvents.push(event);\n continue;\n }\n\n regularEvents.push(event);\n }\n\n const output: ScheduleEventData[] = [];\n const consumedOverrideIds = new Set<string | number>();\n\n for (const event of regularEvents) {\n if (isEventInRange(event, start, end)) {\n output.push(validateEvent(event));\n }\n }\n\n for (const event of recurringEvents) {\n const eventStart = dayjs(event.recurrence?.dtstart || event.start);\n const eventEnd = dayjs(event.end);\n\n if (!eventStart.isValid() || !eventEnd.isValid()) {\n continue;\n }\n\n const durationMs = getWallClockDurationMs(dayjs(event.start), eventEnd);\n if (durationMs < 0) {\n continue;\n }\n\n const exdate = getExdateSet(event.recurrence);\n const starts = getOccurrenceStartsInRange(event, start, end, expansionLimit, durationMs);\n\n for (const wallClockStart of starts) {\n const occurrenceStart = fromWallClockDate(wallClockStart);\n const recurrenceId = occurrenceStart.format('YYYY-MM-DD HH:mm:ss');\n if (exdate.has(recurrenceId)) {\n continue;\n }\n\n const overrideKey = getRecurrenceKey(event.id, recurrenceId);\n const override = overrides.get(overrideKey);\n if (override) {\n if (isEventInRange(override, start, end)) {\n output.push(validateEvent(override));\n consumedOverrideIds.add(override.id);\n }\n continue;\n }\n\n const occurrenceEnd = fromWallClockDate(new Date(wallClockStart.getTime() + durationMs));\n const generated = createGeneratedInstance(event, occurrenceStart, occurrenceEnd);\n if (overlapsRange(dayjs(generated.start), dayjs(generated.end), start, end)) {\n output.push(validateEvent(generated));\n }\n }\n }\n\n for (const override of overrides.values()) {\n if (consumedOverrideIds.has(override.id)) {\n continue;\n }\n if (isEventInRange(override, start, end)) {\n output.push(validateEvent(override));\n }\n }\n\n return output;\n}\n\nexport { DEFAULT_EXPANSION_LIMIT };\n"],"mappings":"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"}
|
|
@@ -3,12 +3,24 @@ const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
|
3
3
|
let dayjs = require("dayjs");
|
|
4
4
|
dayjs = require_runtime.__toESM(dayjs);
|
|
5
5
|
//#region packages/@mantine/schedule/src/utils/is-events-overlap/is-events-overlap.ts
|
|
6
|
+
const timestampsCache = /* @__PURE__ */ new WeakMap();
|
|
7
|
+
function getTimestamps(event) {
|
|
8
|
+
const cached = timestampsCache.get(event);
|
|
9
|
+
if (cached && cached.start === event.start && cached.end === event.end) return cached;
|
|
10
|
+
const timestamps = {
|
|
11
|
+
start: event.start,
|
|
12
|
+
end: event.end,
|
|
13
|
+
startTimestamp: (0, dayjs.default)(event.start).valueOf(),
|
|
14
|
+
endTimestamp: (0, dayjs.default)(event.end).valueOf()
|
|
15
|
+
};
|
|
16
|
+
timestampsCache.set(event, timestamps);
|
|
17
|
+
return timestamps;
|
|
18
|
+
}
|
|
6
19
|
/** Returns true if two events overlap in time */
|
|
7
20
|
function isEventsOverlap(event1, event2) {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
return start1 < (0, dayjs.default)(event2.end).valueOf() && start2 < end1;
|
|
21
|
+
const first = getTimestamps(event1);
|
|
22
|
+
const second = getTimestamps(event2);
|
|
23
|
+
return first.startTimestamp < second.endTimestamp && second.startTimestamp < first.endTimestamp;
|
|
12
24
|
}
|
|
13
25
|
//#endregion
|
|
14
26
|
exports.isEventsOverlap = isEventsOverlap;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"is-events-overlap.cjs","names":[],"sources":["../../../src/utils/is-events-overlap/is-events-overlap.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { ScheduleEventData } from '../../types';\n\
|
|
1
|
+
{"version":3,"file":"is-events-overlap.cjs","names":[],"sources":["../../../src/utils/is-events-overlap/is-events-overlap.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { ScheduleEventData } from '../../types';\n\ninterface CachedTimestamps {\n start: ScheduleEventData['start'];\n end: ScheduleEventData['end'];\n startTimestamp: number;\n endTimestamp: number;\n}\n\nconst timestampsCache = new WeakMap<ScheduleEventData, CachedTimestamps>();\n\nfunction getTimestamps(event: ScheduleEventData): CachedTimestamps {\n const cached = timestampsCache.get(event);\n\n if (cached && cached.start === event.start && cached.end === event.end) {\n return cached;\n }\n\n const timestamps: CachedTimestamps = {\n start: event.start,\n end: event.end,\n startTimestamp: dayjs(event.start).valueOf(),\n endTimestamp: dayjs(event.end).valueOf(),\n };\n\n timestampsCache.set(event, timestamps);\n\n return timestamps;\n}\n\n/** Returns true if two events overlap in time */\nexport function isEventsOverlap(event1: ScheduleEventData, event2: ScheduleEventData) {\n const first = getTimestamps(event1);\n const second = getTimestamps(event2);\n return first.startTimestamp < second.endTimestamp && second.startTimestamp < first.endTimestamp;\n}\n"],"mappings":";;;;;AAUA,MAAM,kCAAkB,IAAI,QAA6C;AAEzE,SAAS,cAAc,OAA4C;CACjE,MAAM,SAAS,gBAAgB,IAAI,KAAK;CAExC,IAAI,UAAU,OAAO,UAAU,MAAM,SAAS,OAAO,QAAQ,MAAM,KACjE,OAAO;CAGT,MAAM,aAA+B;EACnC,OAAO,MAAM;EACb,KAAK,MAAM;EACX,iBAAA,GAAA,MAAA,QAAA,CAAsB,MAAM,KAAK,CAAC,CAAC,QAAQ;EAC3C,eAAA,GAAA,MAAA,QAAA,CAAoB,MAAM,GAAG,CAAC,CAAC,QAAQ;CACzC;CAEA,gBAAgB,IAAI,OAAO,UAAU;CAErC,OAAO;AACT;;AAGA,SAAgB,gBAAgB,QAA2B,QAA2B;CACpF,MAAM,QAAQ,cAAc,MAAM;CAClC,MAAM,SAAS,cAAc,MAAM;CACnC,OAAO,MAAM,iBAAiB,OAAO,gBAAgB,OAAO,iBAAiB,MAAM;AACrF"}
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { isAllDayEvent } from "../../../utils/is-all-day-event/is-all-day-event.mjs";
|
|
3
2
|
import { isEventsOverlap } from "../../../utils/is-events-overlap/is-events-overlap.mjs";
|
|
4
3
|
//#region packages/@mantine/schedule/src/components/WeekView/get-week-view-events/find-available-column.ts
|
|
5
|
-
function columnHasConflict({ columns, columnIndex, event, allDay
|
|
4
|
+
function columnHasConflict({ columns, columnIndex, event, allDay }) {
|
|
6
5
|
const columnKey = `col-${columnIndex}`;
|
|
7
6
|
if (!columns.has(columnKey)) return false;
|
|
8
|
-
return columns.get(columnKey).some((
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}));
|
|
13
|
-
const hasTimeConflict = isEventsOverlap(e, event);
|
|
14
|
-
if (allDay && eAllDay) return hasTimeConflict;
|
|
15
|
-
if (allDay || eAllDay) return false;
|
|
7
|
+
return columns.get(columnKey).some((columnEvent) => {
|
|
8
|
+
const hasTimeConflict = isEventsOverlap(columnEvent.event, event);
|
|
9
|
+
if (allDay && columnEvent.allDay) return hasTimeConflict;
|
|
10
|
+
if (allDay || columnEvent.allDay) return false;
|
|
16
11
|
return hasTimeConflict;
|
|
17
12
|
});
|
|
18
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find-available-column.mjs","names":[],"sources":["../../../../src/components/WeekView/get-week-view-events/find-available-column.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"find-available-column.mjs","names":[],"sources":["../../../../src/components/WeekView/get-week-view-events/find-available-column.ts"],"sourcesContent":["import { ScheduleEventData } from '../../../types';\nimport { isEventsOverlap } from '../../../utils';\n\n/** An event stored in a positioning column together with its precomputed all-day flag. */\nexport interface ColumnEvent {\n event: ScheduleEventData;\n allDay: boolean;\n}\n\ninterface FindAvailableColumnInput {\n columns: Map<string, ColumnEvent[]>;\n event: ScheduleEventData;\n allDay: boolean;\n}\n\nfunction columnHasConflict({\n columns,\n columnIndex,\n event,\n allDay,\n}: FindAvailableColumnInput & { columnIndex: number }): boolean {\n const columnKey = `col-${columnIndex}`;\n if (!columns.has(columnKey)) {\n return false;\n }\n\n const columnEvents = columns.get(columnKey)!;\n\n return columnEvents.some((columnEvent) => {\n const hasTimeConflict = isEventsOverlap(columnEvent.event, event);\n\n if (allDay && columnEvent.allDay) {\n return hasTimeConflict;\n }\n\n if (allDay || columnEvent.allDay) {\n return false;\n }\n\n return hasTimeConflict;\n });\n}\n\nexport function findAvailableColumn(input: FindAvailableColumnInput): number {\n let column = 0;\n\n while (columnHasConflict({ ...input, columnIndex: column })) {\n column++;\n }\n\n return column;\n}\n"],"mappings":";;;AAeA,SAAS,kBAAkB,EACzB,SACA,aACA,OACA,UAC8D;CAC9D,MAAM,YAAY,OAAO;CACzB,IAAI,CAAC,QAAQ,IAAI,SAAS,GACxB,OAAO;CAKT,OAFqB,QAAQ,IAAI,SAEf,CAAC,CAAC,MAAM,gBAAgB;EACxC,MAAM,kBAAkB,gBAAgB,YAAY,OAAO,KAAK;EAEhE,IAAI,UAAU,YAAY,QACxB,OAAO;EAGT,IAAI,UAAU,YAAY,QACxB,OAAO;EAGT,OAAO;CACT,CAAC;AACH;AAEA,SAAgB,oBAAoB,OAAyC;CAC3E,IAAI,SAAS;CAEb,OAAO,kBAAkB;EAAE,GAAG;EAAO,aAAa;CAAO,CAAC,GACxD;CAGF,OAAO;AACT"}
|
|
@@ -114,12 +114,14 @@ function getWeekPositionedEvents({ date, events, startTime, endTime, intervalMin
|
|
|
114
114
|
const column = findAvailableColumn({
|
|
115
115
|
columns: columnMap,
|
|
116
116
|
event,
|
|
117
|
-
allDay
|
|
118
|
-
allWeekDays: weekDays
|
|
117
|
+
allDay
|
|
119
118
|
});
|
|
120
119
|
const columnKey = `col-${column}`;
|
|
121
120
|
if (!columnMap.has(columnKey)) columnMap.set(columnKey, []);
|
|
122
|
-
columnMap.get(columnKey).push(
|
|
121
|
+
columnMap.get(columnKey).push({
|
|
122
|
+
event,
|
|
123
|
+
allDay: isActuallyAllDay
|
|
124
|
+
});
|
|
123
125
|
const verticalPosition = allDay ? {
|
|
124
126
|
top: 0,
|
|
125
127
|
height: 100
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-week-positioned-events.mjs","names":[],"sources":["../../../../src/components/WeekView/get-week-view-events/get-week-positioned-events.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport {\n AnyDateValue,\n DateStringValue,\n DayOfWeek,\n ScheduleEventData,\n WeekPositionedEventData,\n} from '../../../types';\nimport { getDayPosition, getWeekDays, isAllDayEvent, sortEvents } from '../../../utils';\nimport { assignEventRows } from './assign-event-rows';\nimport { calculateAllDayEventOffset } from './calculate-all-day-event-offset';\nimport { calculateAllDayEventWidth } from './calculate-all-day-event-width';\nimport { calculateEventDays } from './calculate-event-days';\nimport { calculateRegularEventOverlaps } from './calculate-regular-event-overlaps';\nimport { findAvailableColumn } from './find-available-column';\nimport { getEventEndDate } from './get-event-end-date';\nimport { getHangingStatus } from './get-hanging-status';\n\nexport interface GetWeekPositionedEventsInput {\n /** Date (week start) at which events are positioned, used to check if events are all-day */\n date: AnyDateValue;\n\n /** List of all events that belong to the given week, extra events must be filtered out before passing to the function */\n events: ScheduleEventData[];\n\n /** Start time of the week view, used to calculate event positions */\n startTime?: string;\n\n /** End time of the week view, used to calculate event positions */\n endTime?: string;\n\n /** Number of minutes per time slot, used to align the canvas to whole slots */\n intervalMinutes?: number;\n\n /** First day of the week, 0 - Sunday, 1 - Monday, etc., used to calculate events positions */\n firstDayOfWeek?: DayOfWeek;\n\n /** Indices of weekend days, 0-6, where 0 is Sunday and 6 is Saturday */\n weekendDays?: DayOfWeek[];\n\n /** If set to false, weekend days are hidden @default true */\n withWeekendDays?: boolean;\n}\n\n/** Events grouped by week day date (YYYY-MM-DD 00:00:00) and by columns */\nexport interface GroupedWeekEvents {\n allDayEvents: WeekPositionedEventData[];\n regularEvents: Record<DateStringValue, WeekPositionedEventData[]>;\n backgroundEvents: Record<DateStringValue, WeekPositionedEventData[]>;\n}\n\nexport function getWeekPositionedEvents({\n date,\n events,\n startTime,\n endTime,\n intervalMinutes,\n firstDayOfWeek = 1,\n weekendDays = [0, 6],\n withWeekendDays = true,\n}: GetWeekPositionedEventsInput): GroupedWeekEvents {\n const weekDays = getWeekDays({ week: date, firstDayOfWeek, withWeekendDays, weekendDays });\n const visibleDaysCount = weekDays.length;\n const weekStartDate = dayjs(weekDays[0]);\n const weekEndDate = dayjs(weekDays[weekDays.length - 1]);\n\n const grouped: GroupedWeekEvents = {\n allDayEvents: [],\n regularEvents: Object.fromEntries(weekDays.map((day) => [day, []])),\n backgroundEvents: Object.fromEntries(weekDays.map((day) => [day, []])),\n };\n\n const columns = new Map<string, ScheduleEventData[]>();\n const allDayColumns = new Map<string, ScheduleEventData[]>();\n const sorted = sortEvents(events);\n\n for (const event of sorted) {\n if (event.display === 'background') {\n const eventStartDate = dayjs(event.start).startOf('day');\n const actualEndDate = getEventEndDate(event);\n const eventWeekDays = calculateEventDays({ event, weekDays, actualEndDate });\n const hanging = getHangingStatus({ eventStartDate, actualEndDate, weekDays });\n\n for (const day of eventWeekDays) {\n const visibleDayIndex = weekDays.indexOf(day);\n if (visibleDayIndex === -1) {\n continue;\n }\n\n const dayStart = dayjs(day);\n const dayEnd = dayStart.endOf('day');\n const eventStart = dayjs(event.start);\n const eventEnd = dayjs(event.end);\n\n const clippedStart = eventStart.isBefore(dayStart) ? dayStart : eventStart;\n const clippedEnd = eventEnd.isAfter(dayEnd) ? dayEnd : eventEnd;\n\n const clippedEvent = {\n ...event,\n start: clippedStart.format('YYYY-MM-DD HH:mm:ss'),\n end: clippedEnd.format('YYYY-MM-DD HH:mm:ss'),\n };\n\n const isAllDay = isAllDayEvent({ event: clippedEvent, date: day });\n const verticalPosition = isAllDay\n ? { top: 0, height: 100 }\n : getDayPosition({ event: clippedEvent, startTime, endTime, intervalMinutes });\n\n if (!isAllDay && verticalPosition.height <= 0) {\n continue;\n }\n\n const dayWeekOffset = (visibleDayIndex / visibleDaysCount) * 100;\n\n grouped.backgroundEvents[day].push({\n ...event,\n position: {\n ...verticalPosition,\n allDay: isAllDay,\n column: 0,\n width: 100,\n offset: 0,\n overlaps: 1,\n weekOffset: dayWeekOffset,\n row: 0,\n hanging,\n },\n });\n }\n\n continue;\n }\n const eventStartDate = dayjs(event.start).startOf('day');\n const actualEndDate = getEventEndDate(event);\n const eventWeekDays = calculateEventDays({ event, weekDays, actualEndDate });\n\n if (eventWeekDays.length === 0) {\n continue;\n }\n\n const isMultiday = actualEndDate.isAfter(eventStartDate);\n const hanging = getHangingStatus({ eventStartDate, actualEndDate, weekDays });\n const isActuallyAllDay = eventWeekDays.some((day) => isAllDayEvent({ event, date: day }));\n const allDay = isMultiday || isActuallyAllDay;\n\n const columnMap = allDay ? allDayColumns : columns;\n const column = findAvailableColumn({\n columns: columnMap,\n event,\n allDay,\n allWeekDays: weekDays,\n });\n\n const columnKey = `col-${column}`;\n if (!columnMap.has(columnKey)) {\n columnMap.set(columnKey, []);\n }\n columnMap.get(columnKey)!.push(event);\n\n const verticalPosition = allDay\n ? { top: 0, height: 100 }\n : getDayPosition({ event, startTime, endTime, intervalMinutes });\n\n if (allDay) {\n const offset = calculateAllDayEventOffset({\n eventStartDate,\n weekStartDate,\n weekDays,\n visibleDaysCount,\n hangingStart: hanging === 'start' || hanging === 'both',\n });\n\n grouped.allDayEvents.push({\n ...event,\n position: {\n ...verticalPosition,\n allDay,\n column,\n width: 0,\n offset,\n overlaps: 0,\n row: 0,\n hanging,\n },\n });\n } else {\n for (const day of eventWeekDays) {\n const visibleDayIndex = weekDays.indexOf(day);\n\n if (visibleDayIndex === -1) {\n continue;\n }\n\n const dayWeekOffset = (visibleDayIndex / visibleDaysCount) * 100;\n\n grouped.regularEvents[day].push({\n ...event,\n position: {\n ...verticalPosition,\n allDay,\n column,\n width: 0,\n offset: 0,\n overlaps: 0,\n weekOffset: dayWeekOffset,\n row: 0,\n hanging,\n },\n });\n }\n }\n }\n\n for (const day of weekDays) {\n calculateRegularEventOverlaps(grouped.regularEvents[day]);\n }\n\n if (grouped.allDayEvents.length > 0) {\n const eventRows = assignEventRows(grouped.allDayEvents);\n const assignedRows = new Set<number>();\n\n for (const event of grouped.allDayEvents) {\n const row = eventRows.get(event)!;\n event.position.row = row;\n assignedRows.add(row);\n }\n\n const rowCount = Math.max(...assignedRows) + 1;\n\n for (const event of grouped.allDayEvents) {\n const eventStartDate = dayjs(event.start).startOf('day');\n const actualEndDate = getEventEndDate(event);\n\n event.position.width = calculateAllDayEventWidth({\n eventStartDate,\n actualEndDate,\n weekStartDate,\n weekEndDate,\n weekDays,\n visibleDaysCount,\n });\n\n event.position.overlaps = rowCount;\n }\n }\n\n return grouped;\n}\n"],"mappings":";;;;;;;;;;;;;;;AAmDA,SAAgB,wBAAwB,EACtC,MACA,QACA,WACA,SACA,iBACA,iBAAiB,GACjB,cAAc,CAAC,GAAG,CAAC,GACnB,kBAAkB,QACgC;CAClD,MAAM,WAAW,YAAY;EAAE,MAAM;EAAM;EAAgB;EAAiB;CAAY,CAAC;CACzF,MAAM,mBAAmB,SAAS;CAClC,MAAM,gBAAgB,MAAM,SAAS,EAAE;CACvC,MAAM,cAAc,MAAM,SAAS,SAAS,SAAS,EAAE;CAEvD,MAAM,UAA6B;EACjC,cAAc,CAAC;EACf,eAAe,OAAO,YAAY,SAAS,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;EAClE,kBAAkB,OAAO,YAAY,SAAS,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;CACvE;CAEA,MAAM,0BAAU,IAAI,IAAiC;CACrD,MAAM,gCAAgB,IAAI,IAAiC;CAC3D,MAAM,SAAS,WAAW,MAAM;CAEhC,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,MAAM,YAAY,cAAc;GAClC,MAAM,iBAAiB,MAAM,MAAM,KAAK,CAAC,CAAC,QAAQ,KAAK;GACvD,MAAM,gBAAgB,gBAAgB,KAAK;GAC3C,MAAM,gBAAgB,mBAAmB;IAAE;IAAO;IAAU;GAAc,CAAC;GAC3E,MAAM,UAAU,iBAAiB;IAAE;IAAgB;IAAe;GAAS,CAAC;GAE5E,KAAK,MAAM,OAAO,eAAe;IAC/B,MAAM,kBAAkB,SAAS,QAAQ,GAAG;IAC5C,IAAI,oBAAoB,IACtB;IAGF,MAAM,WAAW,MAAM,GAAG;IAC1B,MAAM,SAAS,SAAS,MAAM,KAAK;IACnC,MAAM,aAAa,MAAM,MAAM,KAAK;IACpC,MAAM,WAAW,MAAM,MAAM,GAAG;IAEhC,MAAM,eAAe,WAAW,SAAS,QAAQ,IAAI,WAAW;IAChE,MAAM,aAAa,SAAS,QAAQ,MAAM,IAAI,SAAS;IAEvD,MAAM,eAAe;KACnB,GAAG;KACH,OAAO,aAAa,OAAO,qBAAqB;KAChD,KAAK,WAAW,OAAO,qBAAqB;IAC9C;IAEA,MAAM,WAAW,cAAc;KAAE,OAAO;KAAc,MAAM;IAAI,CAAC;IACjE,MAAM,mBAAmB,WACrB;KAAE,KAAK;KAAG,QAAQ;IAAI,IACtB,eAAe;KAAE,OAAO;KAAc;KAAW;KAAS;IAAgB,CAAC;IAE/E,IAAI,CAAC,YAAY,iBAAiB,UAAU,GAC1C;IAGF,MAAM,gBAAiB,kBAAkB,mBAAoB;IAE7D,QAAQ,iBAAiB,IAAI,CAAC,KAAK;KACjC,GAAG;KACH,UAAU;MACR,GAAG;MACH,QAAQ;MACR,QAAQ;MACR,OAAO;MACP,QAAQ;MACR,UAAU;MACV,YAAY;MACZ,KAAK;MACL;KACF;IACF,CAAC;GACH;GAEA;EACF;EACA,MAAM,iBAAiB,MAAM,MAAM,KAAK,CAAC,CAAC,QAAQ,KAAK;EACvD,MAAM,gBAAgB,gBAAgB,KAAK;EAC3C,MAAM,gBAAgB,mBAAmB;GAAE;GAAO;GAAU;EAAc,CAAC;EAE3E,IAAI,cAAc,WAAW,GAC3B;EAGF,MAAM,aAAa,cAAc,QAAQ,cAAc;EACvD,MAAM,UAAU,iBAAiB;GAAE;GAAgB;GAAe;EAAS,CAAC;EAC5E,MAAM,mBAAmB,cAAc,MAAM,QAAQ,cAAc;GAAE;GAAO,MAAM;EAAI,CAAC,CAAC;EACxF,MAAM,SAAS,cAAc;EAE7B,MAAM,YAAY,SAAS,gBAAgB;EAC3C,MAAM,SAAS,oBAAoB;GACjC,SAAS;GACT;GACA;GACA,aAAa;EACf,CAAC;EAED,MAAM,YAAY,OAAO;EACzB,IAAI,CAAC,UAAU,IAAI,SAAS,GAC1B,UAAU,IAAI,WAAW,CAAC,CAAC;EAE7B,UAAU,IAAI,SAAS,CAAC,CAAE,KAAK,KAAK;EAEpC,MAAM,mBAAmB,SACrB;GAAE,KAAK;GAAG,QAAQ;EAAI,IACtB,eAAe;GAAE;GAAO;GAAW;GAAS;EAAgB,CAAC;EAEjE,IAAI,QAAQ;GACV,MAAM,SAAS,2BAA2B;IACxC;IACA;IACA;IACA;IACA,cAAc,YAAY,WAAW,YAAY;GACnD,CAAC;GAED,QAAQ,aAAa,KAAK;IACxB,GAAG;IACH,UAAU;KACR,GAAG;KACH;KACA;KACA,OAAO;KACP;KACA,UAAU;KACV,KAAK;KACL;IACF;GACF,CAAC;EACH,OACE,KAAK,MAAM,OAAO,eAAe;GAC/B,MAAM,kBAAkB,SAAS,QAAQ,GAAG;GAE5C,IAAI,oBAAoB,IACtB;GAGF,MAAM,gBAAiB,kBAAkB,mBAAoB;GAE7D,QAAQ,cAAc,IAAI,CAAC,KAAK;IAC9B,GAAG;IACH,UAAU;KACR,GAAG;KACH;KACA;KACA,OAAO;KACP,QAAQ;KACR,UAAU;KACV,YAAY;KACZ,KAAK;KACL;IACF;GACF,CAAC;EACH;CAEJ;CAEA,KAAK,MAAM,OAAO,UAChB,8BAA8B,QAAQ,cAAc,IAAI;CAG1D,IAAI,QAAQ,aAAa,SAAS,GAAG;EACnC,MAAM,YAAY,gBAAgB,QAAQ,YAAY;EACtD,MAAM,+BAAe,IAAI,IAAY;EAErC,KAAK,MAAM,SAAS,QAAQ,cAAc;GACxC,MAAM,MAAM,UAAU,IAAI,KAAK;GAC/B,MAAM,SAAS,MAAM;GACrB,aAAa,IAAI,GAAG;EACtB;EAEA,MAAM,WAAW,KAAK,IAAI,GAAG,YAAY,IAAI;EAE7C,KAAK,MAAM,SAAS,QAAQ,cAAc;GACxC,MAAM,iBAAiB,MAAM,MAAM,KAAK,CAAC,CAAC,QAAQ,KAAK;GACvD,MAAM,gBAAgB,gBAAgB,KAAK;GAE3C,MAAM,SAAS,QAAQ,0BAA0B;IAC/C;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GAED,MAAM,SAAS,WAAW;EAC5B;CACF;CAEA,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"get-week-positioned-events.mjs","names":[],"sources":["../../../../src/components/WeekView/get-week-view-events/get-week-positioned-events.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport {\n AnyDateValue,\n DateStringValue,\n DayOfWeek,\n ScheduleEventData,\n WeekPositionedEventData,\n} from '../../../types';\nimport { getDayPosition, getWeekDays, isAllDayEvent, sortEvents } from '../../../utils';\nimport { assignEventRows } from './assign-event-rows';\nimport { calculateAllDayEventOffset } from './calculate-all-day-event-offset';\nimport { calculateAllDayEventWidth } from './calculate-all-day-event-width';\nimport { calculateEventDays } from './calculate-event-days';\nimport { calculateRegularEventOverlaps } from './calculate-regular-event-overlaps';\nimport { ColumnEvent, findAvailableColumn } from './find-available-column';\nimport { getEventEndDate } from './get-event-end-date';\nimport { getHangingStatus } from './get-hanging-status';\n\nexport interface GetWeekPositionedEventsInput {\n /** Date (week start) at which events are positioned, used to check if events are all-day */\n date: AnyDateValue;\n\n /** List of all events that belong to the given week, extra events must be filtered out before passing to the function */\n events: ScheduleEventData[];\n\n /** Start time of the week view, used to calculate event positions */\n startTime?: string;\n\n /** End time of the week view, used to calculate event positions */\n endTime?: string;\n\n /** Number of minutes per time slot, used to align the canvas to whole slots */\n intervalMinutes?: number;\n\n /** First day of the week, 0 - Sunday, 1 - Monday, etc., used to calculate events positions */\n firstDayOfWeek?: DayOfWeek;\n\n /** Indices of weekend days, 0-6, where 0 is Sunday and 6 is Saturday */\n weekendDays?: DayOfWeek[];\n\n /** If set to false, weekend days are hidden @default true */\n withWeekendDays?: boolean;\n}\n\n/** Events grouped by week day date (YYYY-MM-DD 00:00:00) and by columns */\nexport interface GroupedWeekEvents {\n allDayEvents: WeekPositionedEventData[];\n regularEvents: Record<DateStringValue, WeekPositionedEventData[]>;\n backgroundEvents: Record<DateStringValue, WeekPositionedEventData[]>;\n}\n\nexport function getWeekPositionedEvents({\n date,\n events,\n startTime,\n endTime,\n intervalMinutes,\n firstDayOfWeek = 1,\n weekendDays = [0, 6],\n withWeekendDays = true,\n}: GetWeekPositionedEventsInput): GroupedWeekEvents {\n const weekDays = getWeekDays({ week: date, firstDayOfWeek, withWeekendDays, weekendDays });\n const visibleDaysCount = weekDays.length;\n const weekStartDate = dayjs(weekDays[0]);\n const weekEndDate = dayjs(weekDays[weekDays.length - 1]);\n\n const grouped: GroupedWeekEvents = {\n allDayEvents: [],\n regularEvents: Object.fromEntries(weekDays.map((day) => [day, []])),\n backgroundEvents: Object.fromEntries(weekDays.map((day) => [day, []])),\n };\n\n const columns = new Map<string, ColumnEvent[]>();\n const allDayColumns = new Map<string, ColumnEvent[]>();\n const sorted = sortEvents(events);\n\n for (const event of sorted) {\n if (event.display === 'background') {\n const eventStartDate = dayjs(event.start).startOf('day');\n const actualEndDate = getEventEndDate(event);\n const eventWeekDays = calculateEventDays({ event, weekDays, actualEndDate });\n const hanging = getHangingStatus({ eventStartDate, actualEndDate, weekDays });\n\n for (const day of eventWeekDays) {\n const visibleDayIndex = weekDays.indexOf(day);\n if (visibleDayIndex === -1) {\n continue;\n }\n\n const dayStart = dayjs(day);\n const dayEnd = dayStart.endOf('day');\n const eventStart = dayjs(event.start);\n const eventEnd = dayjs(event.end);\n\n const clippedStart = eventStart.isBefore(dayStart) ? dayStart : eventStart;\n const clippedEnd = eventEnd.isAfter(dayEnd) ? dayEnd : eventEnd;\n\n const clippedEvent = {\n ...event,\n start: clippedStart.format('YYYY-MM-DD HH:mm:ss'),\n end: clippedEnd.format('YYYY-MM-DD HH:mm:ss'),\n };\n\n const isAllDay = isAllDayEvent({ event: clippedEvent, date: day });\n const verticalPosition = isAllDay\n ? { top: 0, height: 100 }\n : getDayPosition({ event: clippedEvent, startTime, endTime, intervalMinutes });\n\n if (!isAllDay && verticalPosition.height <= 0) {\n continue;\n }\n\n const dayWeekOffset = (visibleDayIndex / visibleDaysCount) * 100;\n\n grouped.backgroundEvents[day].push({\n ...event,\n position: {\n ...verticalPosition,\n allDay: isAllDay,\n column: 0,\n width: 100,\n offset: 0,\n overlaps: 1,\n weekOffset: dayWeekOffset,\n row: 0,\n hanging,\n },\n });\n }\n\n continue;\n }\n const eventStartDate = dayjs(event.start).startOf('day');\n const actualEndDate = getEventEndDate(event);\n const eventWeekDays = calculateEventDays({ event, weekDays, actualEndDate });\n\n if (eventWeekDays.length === 0) {\n continue;\n }\n\n const isMultiday = actualEndDate.isAfter(eventStartDate);\n const hanging = getHangingStatus({ eventStartDate, actualEndDate, weekDays });\n const isActuallyAllDay = eventWeekDays.some((day) => isAllDayEvent({ event, date: day }));\n const allDay = isMultiday || isActuallyAllDay;\n\n const columnMap = allDay ? allDayColumns : columns;\n const column = findAvailableColumn({\n columns: columnMap,\n event,\n allDay,\n });\n\n const columnKey = `col-${column}`;\n if (!columnMap.has(columnKey)) {\n columnMap.set(columnKey, []);\n }\n columnMap.get(columnKey)!.push({ event, allDay: isActuallyAllDay });\n\n const verticalPosition = allDay\n ? { top: 0, height: 100 }\n : getDayPosition({ event, startTime, endTime, intervalMinutes });\n\n if (allDay) {\n const offset = calculateAllDayEventOffset({\n eventStartDate,\n weekStartDate,\n weekDays,\n visibleDaysCount,\n hangingStart: hanging === 'start' || hanging === 'both',\n });\n\n grouped.allDayEvents.push({\n ...event,\n position: {\n ...verticalPosition,\n allDay,\n column,\n width: 0,\n offset,\n overlaps: 0,\n row: 0,\n hanging,\n },\n });\n } else {\n for (const day of eventWeekDays) {\n const visibleDayIndex = weekDays.indexOf(day);\n\n if (visibleDayIndex === -1) {\n continue;\n }\n\n const dayWeekOffset = (visibleDayIndex / visibleDaysCount) * 100;\n\n grouped.regularEvents[day].push({\n ...event,\n position: {\n ...verticalPosition,\n allDay,\n column,\n width: 0,\n offset: 0,\n overlaps: 0,\n weekOffset: dayWeekOffset,\n row: 0,\n hanging,\n },\n });\n }\n }\n }\n\n for (const day of weekDays) {\n calculateRegularEventOverlaps(grouped.regularEvents[day]);\n }\n\n if (grouped.allDayEvents.length > 0) {\n const eventRows = assignEventRows(grouped.allDayEvents);\n const assignedRows = new Set<number>();\n\n for (const event of grouped.allDayEvents) {\n const row = eventRows.get(event)!;\n event.position.row = row;\n assignedRows.add(row);\n }\n\n const rowCount = Math.max(...assignedRows) + 1;\n\n for (const event of grouped.allDayEvents) {\n const eventStartDate = dayjs(event.start).startOf('day');\n const actualEndDate = getEventEndDate(event);\n\n event.position.width = calculateAllDayEventWidth({\n eventStartDate,\n actualEndDate,\n weekStartDate,\n weekEndDate,\n weekDays,\n visibleDaysCount,\n });\n\n event.position.overlaps = rowCount;\n }\n }\n\n return grouped;\n}\n"],"mappings":"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"}
|
|
@@ -15,6 +15,18 @@ function getExdateSet(recurrence) {
|
|
|
15
15
|
const values = recurrence.exdate.map((value) => dayjs(value)).filter((value) => value.isValid()).map((value) => value.format("YYYY-MM-DD HH:mm:ss"));
|
|
16
16
|
return new Set(values);
|
|
17
17
|
}
|
|
18
|
+
function toWallClockDate(value) {
|
|
19
|
+
return new Date(Date.UTC(value.year(), value.month(), value.date(), value.hour(), value.minute(), value.second(), value.millisecond()));
|
|
20
|
+
}
|
|
21
|
+
function fromWallClockDate(value) {
|
|
22
|
+
const result = new Date(2e3, 0, 1, 12);
|
|
23
|
+
result.setFullYear(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate());
|
|
24
|
+
result.setHours(value.getUTCHours(), value.getUTCMinutes(), value.getUTCSeconds(), value.getUTCMilliseconds());
|
|
25
|
+
return dayjs(result);
|
|
26
|
+
}
|
|
27
|
+
function getWallClockDurationMs(start, end) {
|
|
28
|
+
return toWallClockDate(end).getTime() - toWallClockDate(start).getTime();
|
|
29
|
+
}
|
|
18
30
|
function getRRuleString(rule) {
|
|
19
31
|
const normalized = rule.trim();
|
|
20
32
|
if (!normalized.includes("\n")) return normalized.startsWith("RRULE:") ? normalized.replace(/^RRULE:/, "") : normalized;
|
|
@@ -26,10 +38,10 @@ function getOccurrenceStartsInRange(event, rangeStart, rangeEnd, expansionLimit,
|
|
|
26
38
|
if (!dtstart.isValid() || !event.recurrence) return [];
|
|
27
39
|
try {
|
|
28
40
|
const options = RRule.parseString(getRRuleString(event.recurrence.rrule));
|
|
29
|
-
options.dtstart = dtstart
|
|
41
|
+
options.dtstart = toWallClockDate(dtstart);
|
|
30
42
|
const rule = new RRule(options);
|
|
31
|
-
const searchStart = rangeStart.
|
|
32
|
-
return rule.between(searchStart, rangeEnd
|
|
43
|
+
const searchStart = new Date(toWallClockDate(rangeStart).getTime() - Math.max(0, durationMs));
|
|
44
|
+
return rule.between(searchStart, toWallClockDate(rangeEnd), true).slice(0, expansionLimit);
|
|
33
45
|
} catch {
|
|
34
46
|
return [];
|
|
35
47
|
}
|
|
@@ -40,8 +52,7 @@ function isEventInRange(event, rangeStart, rangeEnd) {
|
|
|
40
52
|
if (!start.isValid() || !end.isValid()) return false;
|
|
41
53
|
return overlapsRange(start, end, rangeStart, rangeEnd);
|
|
42
54
|
}
|
|
43
|
-
function createGeneratedInstance(event, occurrenceStart,
|
|
44
|
-
const occurrenceEnd = occurrenceStart.add(durationMs, "millisecond");
|
|
55
|
+
function createGeneratedInstance(event, occurrenceStart, occurrenceEnd) {
|
|
45
56
|
const recurrenceId = occurrenceStart.format("YYYY-MM-DD HH:mm:ss");
|
|
46
57
|
const { recurrence, ...eventWithoutRecurrence } = event;
|
|
47
58
|
return {
|
|
@@ -87,11 +98,12 @@ function expandRecurringEvents({ events, rangeStart, rangeEnd, expansionLimit =
|
|
|
87
98
|
const eventStart = dayjs(event.recurrence?.dtstart || event.start);
|
|
88
99
|
const eventEnd = dayjs(event.end);
|
|
89
100
|
if (!eventStart.isValid() || !eventEnd.isValid()) continue;
|
|
90
|
-
const durationMs =
|
|
101
|
+
const durationMs = getWallClockDurationMs(dayjs(event.start), eventEnd);
|
|
91
102
|
if (durationMs < 0) continue;
|
|
92
103
|
const exdate = getExdateSet(event.recurrence);
|
|
93
104
|
const starts = getOccurrenceStartsInRange(event, start, end, expansionLimit, durationMs);
|
|
94
|
-
for (const
|
|
105
|
+
for (const wallClockStart of starts) {
|
|
106
|
+
const occurrenceStart = fromWallClockDate(wallClockStart);
|
|
95
107
|
const recurrenceId = occurrenceStart.format("YYYY-MM-DD HH:mm:ss");
|
|
96
108
|
if (exdate.has(recurrenceId)) continue;
|
|
97
109
|
const overrideKey = getRecurrenceKey(event.id, recurrenceId);
|
|
@@ -103,7 +115,7 @@ function expandRecurringEvents({ events, rangeStart, rangeEnd, expansionLimit =
|
|
|
103
115
|
}
|
|
104
116
|
continue;
|
|
105
117
|
}
|
|
106
|
-
const generated = createGeneratedInstance(event, occurrenceStart, durationMs);
|
|
118
|
+
const generated = createGeneratedInstance(event, occurrenceStart, fromWallClockDate(new Date(wallClockStart.getTime() + durationMs)));
|
|
107
119
|
if (overlapsRange(dayjs(generated.start), dayjs(generated.end), start, end)) output.push(validateEvent(generated));
|
|
108
120
|
}
|
|
109
121
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expand-recurring-events.mjs","names":[],"sources":["../../../src/utils/expand-recurring-events/expand-recurring-events.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport * as rruleAll from 'rrule';\n\nconst RRule = (\n 'default' in rruleAll ? (rruleAll as any).default.RRule : rruleAll.RRule\n) as typeof rruleAll.RRule;\nimport { DateTimeStringValue, ScheduleEventData, ScheduleRecurrenceData } from '../../types';\nimport { validateEvent } from '../validate-event/validate-event';\n\ninterface ExpandRecurringEventsInput {\n events: ScheduleEventData[] | undefined;\n rangeStart: Date | string;\n rangeEnd: Date | string;\n expansionLimit?: number;\n}\n\nfunction getRecurrenceKey(recurringEventId: string | number, recurrenceId: DateTimeStringValue) {\n return `${recurringEventId}::${recurrenceId}`;\n}\n\nfunction overlapsRange(\n eventStart: dayjs.Dayjs,\n eventEnd: dayjs.Dayjs,\n rangeStart: dayjs.Dayjs,\n rangeEnd: dayjs.Dayjs\n) {\n return (\n (eventEnd.isAfter(rangeStart) || eventEnd.isSame(rangeStart)) &&\n (eventStart.isBefore(rangeEnd) || eventStart.isSame(rangeEnd))\n );\n}\n\nfunction getExdateSet(recurrence: ScheduleRecurrenceData | undefined) {\n if (!recurrence?.exdate) {\n return new Set<string>();\n }\n\n const values = recurrence.exdate\n .map((value) => dayjs(value))\n .filter((value) => value.isValid())\n .map((value) => value.format('YYYY-MM-DD HH:mm:ss'));\n\n return new Set(values);\n}\n\nfunction getRRuleString(rule: string) {\n const normalized = rule.trim();\n if (!normalized.includes('\\n')) {\n return normalized.startsWith('RRULE:') ? normalized.replace(/^RRULE:/, '') : normalized;\n }\n\n const rruleLine = normalized\n .split('\\n')\n .map((line) => line.trim())\n .find((line) => line.startsWith('RRULE:'));\n\n return rruleLine ? rruleLine.replace(/^RRULE:/, '') : normalized;\n}\n\nfunction getOccurrenceStartsInRange(\n event: ScheduleEventData,\n rangeStart: dayjs.Dayjs,\n rangeEnd: dayjs.Dayjs,\n expansionLimit: number,\n durationMs: number\n) {\n const dtstart = dayjs(event.recurrence?.dtstart || event.start);\n if (!dtstart.isValid() || !event.recurrence) {\n return [];\n }\n\n try {\n const options = RRule.parseString(getRRuleString(event.recurrence.rrule));\n options.dtstart = dtstart.toDate();\n const rule = new RRule(options);\n\n const searchStart = rangeStart.subtract(Math.max(0, durationMs), 'millisecond').toDate();\n const results = rule.between(searchStart, rangeEnd.toDate(), true);\n return results.slice(0, expansionLimit).map((value) => dayjs(value));\n } catch {\n return [];\n }\n}\n\nfunction isEventInRange(event: ScheduleEventData, rangeStart: dayjs.Dayjs, rangeEnd: dayjs.Dayjs) {\n const start = dayjs(event.start);\n const end = dayjs(event.end);\n\n if (!start.isValid() || !end.isValid()) {\n return false;\n }\n\n return overlapsRange(start, end, rangeStart, rangeEnd);\n}\n\nfunction createGeneratedInstance(\n event: ScheduleEventData,\n occurrenceStart: dayjs.Dayjs,\n durationMs: number\n): ScheduleEventData {\n const occurrenceEnd = occurrenceStart.add(durationMs, 'millisecond');\n const recurrenceId = occurrenceStart.format('YYYY-MM-DD HH:mm:ss');\n const { recurrence, ...eventWithoutRecurrence } = event;\n\n return {\n ...eventWithoutRecurrence,\n id: `${event.id}::${recurrenceId}`,\n start: occurrenceStart.format('YYYY-MM-DD HH:mm:ss'),\n end: occurrenceEnd.format('YYYY-MM-DD HH:mm:ss'),\n recurringEventId: event.id,\n recurrenceId,\n recurringInstance: {\n isRecurringInstance: true,\n recurringEventId: event.id,\n recurrenceId,\n originalStart: occurrenceStart.format('YYYY-MM-DD HH:mm:ss'),\n originalEnd: occurrenceEnd.format('YYYY-MM-DD HH:mm:ss'),\n },\n };\n}\n\nconst DEFAULT_EXPANSION_LIMIT = 2000;\n\nexport function expandRecurringEvents({\n events,\n rangeStart,\n rangeEnd,\n expansionLimit = DEFAULT_EXPANSION_LIMIT,\n}: ExpandRecurringEventsInput): ScheduleEventData[] {\n if (!events || events.length === 0) {\n return [];\n }\n\n const start = dayjs(rangeStart);\n const end = dayjs(rangeEnd);\n\n if (!start.isValid() || !end.isValid()) {\n return [];\n }\n\n const regularEvents: ScheduleEventData[] = [];\n const recurringEvents: ScheduleEventData[] = [];\n const overrides = new Map<string, ScheduleEventData>();\n\n for (const event of events) {\n if (event.recurringEventId && event.recurrenceId) {\n overrides.set(getRecurrenceKey(event.recurringEventId, event.recurrenceId), event);\n continue;\n }\n\n if (event.recurrence?.rrule) {\n recurringEvents.push(event);\n continue;\n }\n\n regularEvents.push(event);\n }\n\n const output: ScheduleEventData[] = [];\n const consumedOverrideIds = new Set<string | number>();\n\n for (const event of regularEvents) {\n if (isEventInRange(event, start, end)) {\n output.push(validateEvent(event));\n }\n }\n\n for (const event of recurringEvents) {\n const eventStart = dayjs(event.recurrence?.dtstart || event.start);\n const eventEnd = dayjs(event.end);\n\n if (!eventStart.isValid() || !eventEnd.isValid()) {\n continue;\n }\n\n const durationMs = eventEnd.diff(dayjs(event.start), 'millisecond');\n if (durationMs < 0) {\n continue;\n }\n\n const exdate = getExdateSet(event.recurrence);\n const starts = getOccurrenceStartsInRange(event, start, end, expansionLimit, durationMs);\n\n for (const occurrenceStart of starts) {\n const recurrenceId = occurrenceStart.format('YYYY-MM-DD HH:mm:ss');\n if (exdate.has(recurrenceId)) {\n continue;\n }\n\n const overrideKey = getRecurrenceKey(event.id, recurrenceId);\n const override = overrides.get(overrideKey);\n if (override) {\n if (isEventInRange(override, start, end)) {\n output.push(validateEvent(override));\n consumedOverrideIds.add(override.id);\n }\n continue;\n }\n\n const generated = createGeneratedInstance(event, occurrenceStart, durationMs);\n if (overlapsRange(dayjs(generated.start), dayjs(generated.end), start, end)) {\n output.push(validateEvent(generated));\n }\n }\n }\n\n for (const override of overrides.values()) {\n if (consumedOverrideIds.has(override.id)) {\n continue;\n }\n if (isEventInRange(override, start, end)) {\n output.push(validateEvent(override));\n }\n }\n\n return output;\n}\n\nexport { DEFAULT_EXPANSION_LIMIT };\n"],"mappings":";;;;;AAGA,MAAM,QACJ,aAAa,WAAY,SAAiB,QAAQ,QAAQ,SAAS;AAYrE,SAAS,iBAAiB,kBAAmC,cAAmC;CAC9F,OAAO,GAAG,iBAAiB,IAAI;AACjC;AAEA,SAAS,cACP,YACA,UACA,YACA,UACA;CACA,QACG,SAAS,QAAQ,UAAU,KAAK,SAAS,OAAO,UAAU,OAC1D,WAAW,SAAS,QAAQ,KAAK,WAAW,OAAO,QAAQ;AAEhE;AAEA,SAAS,aAAa,YAAgD;CACpE,IAAI,CAAC,YAAY,QACf,uBAAO,IAAI,IAAY;CAGzB,MAAM,SAAS,WAAW,OACvB,KAAK,UAAU,MAAM,KAAK,CAAC,CAAC,CAC5B,QAAQ,UAAU,MAAM,QAAQ,CAAC,CAAC,CAClC,KAAK,UAAU,MAAM,OAAO,qBAAqB,CAAC;CAErD,OAAO,IAAI,IAAI,MAAM;AACvB;AAEA,SAAS,eAAe,MAAc;CACpC,MAAM,aAAa,KAAK,KAAK;CAC7B,IAAI,CAAC,WAAW,SAAS,IAAI,GAC3B,OAAO,WAAW,WAAW,QAAQ,IAAI,WAAW,QAAQ,WAAW,EAAE,IAAI;CAG/E,MAAM,YAAY,WACf,MAAM,IAAI,CAAC,CACX,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAC1B,MAAM,SAAS,KAAK,WAAW,QAAQ,CAAC;CAE3C,OAAO,YAAY,UAAU,QAAQ,WAAW,EAAE,IAAI;AACxD;AAEA,SAAS,2BACP,OACA,YACA,UACA,gBACA,YACA;CACA,MAAM,UAAU,MAAM,MAAM,YAAY,WAAW,MAAM,KAAK;CAC9D,IAAI,CAAC,QAAQ,QAAQ,KAAK,CAAC,MAAM,YAC/B,OAAO,CAAC;CAGV,IAAI;EACF,MAAM,UAAU,MAAM,YAAY,eAAe,MAAM,WAAW,KAAK,CAAC;EACxE,QAAQ,UAAU,QAAQ,OAAO;EACjC,MAAM,OAAO,IAAI,MAAM,OAAO;EAE9B,MAAM,cAAc,WAAW,SAAS,KAAK,IAAI,GAAG,UAAU,GAAG,aAAa,CAAC,CAAC,OAAO;EAEvF,OADgB,KAAK,QAAQ,aAAa,SAAS,OAAO,GAAG,IAChD,CAAC,CAAC,MAAM,GAAG,cAAc,CAAC,CAAC,KAAK,UAAU,MAAM,KAAK,CAAC;CACrE,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,SAAS,eAAe,OAA0B,YAAyB,UAAuB;CAChG,MAAM,QAAQ,MAAM,MAAM,KAAK;CAC/B,MAAM,MAAM,MAAM,MAAM,GAAG;CAE3B,IAAI,CAAC,MAAM,QAAQ,KAAK,CAAC,IAAI,QAAQ,GACnC,OAAO;CAGT,OAAO,cAAc,OAAO,KAAK,YAAY,QAAQ;AACvD;AAEA,SAAS,wBACP,OACA,iBACA,YACmB;CACnB,MAAM,gBAAgB,gBAAgB,IAAI,YAAY,aAAa;CACnE,MAAM,eAAe,gBAAgB,OAAO,qBAAqB;CACjE,MAAM,EAAE,YAAY,GAAG,2BAA2B;CAElD,OAAO;EACL,GAAG;EACH,IAAI,GAAG,MAAM,GAAG,IAAI;EACpB,OAAO,gBAAgB,OAAO,qBAAqB;EACnD,KAAK,cAAc,OAAO,qBAAqB;EAC/C,kBAAkB,MAAM;EACxB;EACA,mBAAmB;GACjB,qBAAqB;GACrB,kBAAkB,MAAM;GACxB;GACA,eAAe,gBAAgB,OAAO,qBAAqB;GAC3D,aAAa,cAAc,OAAO,qBAAqB;EACzD;CACF;AACF;AAEA,MAAM,0BAA0B;AAEhC,SAAgB,sBAAsB,EACpC,QACA,YACA,UACA,iBAAiB,2BACiC;CAClD,IAAI,CAAC,UAAU,OAAO,WAAW,GAC/B,OAAO,CAAC;CAGV,MAAM,QAAQ,MAAM,UAAU;CAC9B,MAAM,MAAM,MAAM,QAAQ;CAE1B,IAAI,CAAC,MAAM,QAAQ,KAAK,CAAC,IAAI,QAAQ,GACnC,OAAO,CAAC;CAGV,MAAM,gBAAqC,CAAC;CAC5C,MAAM,kBAAuC,CAAC;CAC9C,MAAM,4BAAY,IAAI,IAA+B;CAErD,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,MAAM,oBAAoB,MAAM,cAAc;GAChD,UAAU,IAAI,iBAAiB,MAAM,kBAAkB,MAAM,YAAY,GAAG,KAAK;GACjF;EACF;EAEA,IAAI,MAAM,YAAY,OAAO;GAC3B,gBAAgB,KAAK,KAAK;GAC1B;EACF;EAEA,cAAc,KAAK,KAAK;CAC1B;CAEA,MAAM,SAA8B,CAAC;CACrC,MAAM,sCAAsB,IAAI,IAAqB;CAErD,KAAK,MAAM,SAAS,eAClB,IAAI,eAAe,OAAO,OAAO,GAAG,GAClC,OAAO,KAAK,cAAc,KAAK,CAAC;CAIpC,KAAK,MAAM,SAAS,iBAAiB;EACnC,MAAM,aAAa,MAAM,MAAM,YAAY,WAAW,MAAM,KAAK;EACjE,MAAM,WAAW,MAAM,MAAM,GAAG;EAEhC,IAAI,CAAC,WAAW,QAAQ,KAAK,CAAC,SAAS,QAAQ,GAC7C;EAGF,MAAM,aAAa,SAAS,KAAK,MAAM,MAAM,KAAK,GAAG,aAAa;EAClE,IAAI,aAAa,GACf;EAGF,MAAM,SAAS,aAAa,MAAM,UAAU;EAC5C,MAAM,SAAS,2BAA2B,OAAO,OAAO,KAAK,gBAAgB,UAAU;EAEvF,KAAK,MAAM,mBAAmB,QAAQ;GACpC,MAAM,eAAe,gBAAgB,OAAO,qBAAqB;GACjE,IAAI,OAAO,IAAI,YAAY,GACzB;GAGF,MAAM,cAAc,iBAAiB,MAAM,IAAI,YAAY;GAC3D,MAAM,WAAW,UAAU,IAAI,WAAW;GAC1C,IAAI,UAAU;IACZ,IAAI,eAAe,UAAU,OAAO,GAAG,GAAG;KACxC,OAAO,KAAK,cAAc,QAAQ,CAAC;KACnC,oBAAoB,IAAI,SAAS,EAAE;IACrC;IACA;GACF;GAEA,MAAM,YAAY,wBAAwB,OAAO,iBAAiB,UAAU;GAC5E,IAAI,cAAc,MAAM,UAAU,KAAK,GAAG,MAAM,UAAU,GAAG,GAAG,OAAO,GAAG,GACxE,OAAO,KAAK,cAAc,SAAS,CAAC;EAExC;CACF;CAEA,KAAK,MAAM,YAAY,UAAU,OAAO,GAAG;EACzC,IAAI,oBAAoB,IAAI,SAAS,EAAE,GACrC;EAEF,IAAI,eAAe,UAAU,OAAO,GAAG,GACrC,OAAO,KAAK,cAAc,QAAQ,CAAC;CAEvC;CAEA,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"expand-recurring-events.mjs","names":[],"sources":["../../../src/utils/expand-recurring-events/expand-recurring-events.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport * as rruleAll from 'rrule';\n\nconst RRule = (\n 'default' in rruleAll ? (rruleAll as any).default.RRule : rruleAll.RRule\n) as typeof rruleAll.RRule;\nimport { DateTimeStringValue, ScheduleEventData, ScheduleRecurrenceData } from '../../types';\nimport { validateEvent } from '../validate-event/validate-event';\n\ninterface ExpandRecurringEventsInput {\n events: ScheduleEventData[] | undefined;\n rangeStart: Date | string;\n rangeEnd: Date | string;\n expansionLimit?: number;\n}\n\nfunction getRecurrenceKey(recurringEventId: string | number, recurrenceId: DateTimeStringValue) {\n return `${recurringEventId}::${recurrenceId}`;\n}\n\nfunction overlapsRange(\n eventStart: dayjs.Dayjs,\n eventEnd: dayjs.Dayjs,\n rangeStart: dayjs.Dayjs,\n rangeEnd: dayjs.Dayjs\n) {\n return (\n (eventEnd.isAfter(rangeStart) || eventEnd.isSame(rangeStart)) &&\n (eventStart.isBefore(rangeEnd) || eventStart.isSame(rangeEnd))\n );\n}\n\nfunction getExdateSet(recurrence: ScheduleRecurrenceData | undefined) {\n if (!recurrence?.exdate) {\n return new Set<string>();\n }\n\n const values = recurrence.exdate\n .map((value) => dayjs(value))\n .filter((value) => value.isValid())\n .map((value) => value.format('YYYY-MM-DD HH:mm:ss'));\n\n return new Set(values);\n}\n\nfunction toWallClockDate(value: dayjs.Dayjs) {\n return new Date(\n Date.UTC(\n value.year(),\n value.month(),\n value.date(),\n value.hour(),\n value.minute(),\n value.second(),\n value.millisecond()\n )\n );\n}\n\nfunction fromWallClockDate(value: Date) {\n const result = new Date(2000, 0, 1, 12);\n result.setFullYear(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate());\n result.setHours(\n value.getUTCHours(),\n value.getUTCMinutes(),\n value.getUTCSeconds(),\n value.getUTCMilliseconds()\n );\n\n return dayjs(result);\n}\n\nfunction getWallClockDurationMs(start: dayjs.Dayjs, end: dayjs.Dayjs) {\n return toWallClockDate(end).getTime() - toWallClockDate(start).getTime();\n}\n\nfunction getRRuleString(rule: string) {\n const normalized = rule.trim();\n if (!normalized.includes('\\n')) {\n return normalized.startsWith('RRULE:') ? normalized.replace(/^RRULE:/, '') : normalized;\n }\n\n const rruleLine = normalized\n .split('\\n')\n .map((line) => line.trim())\n .find((line) => line.startsWith('RRULE:'));\n\n return rruleLine ? rruleLine.replace(/^RRULE:/, '') : normalized;\n}\n\nfunction getOccurrenceStartsInRange(\n event: ScheduleEventData,\n rangeStart: dayjs.Dayjs,\n rangeEnd: dayjs.Dayjs,\n expansionLimit: number,\n durationMs: number\n) {\n const dtstart = dayjs(event.recurrence?.dtstart || event.start);\n if (!dtstart.isValid() || !event.recurrence) {\n return [];\n }\n\n try {\n const options = RRule.parseString(getRRuleString(event.recurrence.rrule));\n options.dtstart = toWallClockDate(dtstart);\n const rule = new RRule(options);\n\n const searchStart = new Date(toWallClockDate(rangeStart).getTime() - Math.max(0, durationMs));\n const results = rule.between(searchStart, toWallClockDate(rangeEnd), true);\n return results.slice(0, expansionLimit);\n } catch {\n return [];\n }\n}\n\nfunction isEventInRange(event: ScheduleEventData, rangeStart: dayjs.Dayjs, rangeEnd: dayjs.Dayjs) {\n const start = dayjs(event.start);\n const end = dayjs(event.end);\n\n if (!start.isValid() || !end.isValid()) {\n return false;\n }\n\n return overlapsRange(start, end, rangeStart, rangeEnd);\n}\n\nfunction createGeneratedInstance(\n event: ScheduleEventData,\n occurrenceStart: dayjs.Dayjs,\n occurrenceEnd: dayjs.Dayjs\n): ScheduleEventData {\n const recurrenceId = occurrenceStart.format('YYYY-MM-DD HH:mm:ss');\n const { recurrence, ...eventWithoutRecurrence } = event;\n\n return {\n ...eventWithoutRecurrence,\n id: `${event.id}::${recurrenceId}`,\n start: occurrenceStart.format('YYYY-MM-DD HH:mm:ss'),\n end: occurrenceEnd.format('YYYY-MM-DD HH:mm:ss'),\n recurringEventId: event.id,\n recurrenceId,\n recurringInstance: {\n isRecurringInstance: true,\n recurringEventId: event.id,\n recurrenceId,\n originalStart: occurrenceStart.format('YYYY-MM-DD HH:mm:ss'),\n originalEnd: occurrenceEnd.format('YYYY-MM-DD HH:mm:ss'),\n },\n };\n}\n\nconst DEFAULT_EXPANSION_LIMIT = 2000;\n\nexport function expandRecurringEvents({\n events,\n rangeStart,\n rangeEnd,\n expansionLimit = DEFAULT_EXPANSION_LIMIT,\n}: ExpandRecurringEventsInput): ScheduleEventData[] {\n if (!events || events.length === 0) {\n return [];\n }\n\n const start = dayjs(rangeStart);\n const end = dayjs(rangeEnd);\n\n if (!start.isValid() || !end.isValid()) {\n return [];\n }\n\n const regularEvents: ScheduleEventData[] = [];\n const recurringEvents: ScheduleEventData[] = [];\n const overrides = new Map<string, ScheduleEventData>();\n\n for (const event of events) {\n if (event.recurringEventId && event.recurrenceId) {\n overrides.set(getRecurrenceKey(event.recurringEventId, event.recurrenceId), event);\n continue;\n }\n\n if (event.recurrence?.rrule) {\n recurringEvents.push(event);\n continue;\n }\n\n regularEvents.push(event);\n }\n\n const output: ScheduleEventData[] = [];\n const consumedOverrideIds = new Set<string | number>();\n\n for (const event of regularEvents) {\n if (isEventInRange(event, start, end)) {\n output.push(validateEvent(event));\n }\n }\n\n for (const event of recurringEvents) {\n const eventStart = dayjs(event.recurrence?.dtstart || event.start);\n const eventEnd = dayjs(event.end);\n\n if (!eventStart.isValid() || !eventEnd.isValid()) {\n continue;\n }\n\n const durationMs = getWallClockDurationMs(dayjs(event.start), eventEnd);\n if (durationMs < 0) {\n continue;\n }\n\n const exdate = getExdateSet(event.recurrence);\n const starts = getOccurrenceStartsInRange(event, start, end, expansionLimit, durationMs);\n\n for (const wallClockStart of starts) {\n const occurrenceStart = fromWallClockDate(wallClockStart);\n const recurrenceId = occurrenceStart.format('YYYY-MM-DD HH:mm:ss');\n if (exdate.has(recurrenceId)) {\n continue;\n }\n\n const overrideKey = getRecurrenceKey(event.id, recurrenceId);\n const override = overrides.get(overrideKey);\n if (override) {\n if (isEventInRange(override, start, end)) {\n output.push(validateEvent(override));\n consumedOverrideIds.add(override.id);\n }\n continue;\n }\n\n const occurrenceEnd = fromWallClockDate(new Date(wallClockStart.getTime() + durationMs));\n const generated = createGeneratedInstance(event, occurrenceStart, occurrenceEnd);\n if (overlapsRange(dayjs(generated.start), dayjs(generated.end), start, end)) {\n output.push(validateEvent(generated));\n }\n }\n }\n\n for (const override of overrides.values()) {\n if (consumedOverrideIds.has(override.id)) {\n continue;\n }\n if (isEventInRange(override, start, end)) {\n output.push(validateEvent(override));\n }\n }\n\n return output;\n}\n\nexport { DEFAULT_EXPANSION_LIMIT };\n"],"mappings":";;;;;AAGA,MAAM,QACJ,aAAa,WAAY,SAAiB,QAAQ,QAAQ,SAAS;AAYrE,SAAS,iBAAiB,kBAAmC,cAAmC;CAC9F,OAAO,GAAG,iBAAiB,IAAI;AACjC;AAEA,SAAS,cACP,YACA,UACA,YACA,UACA;CACA,QACG,SAAS,QAAQ,UAAU,KAAK,SAAS,OAAO,UAAU,OAC1D,WAAW,SAAS,QAAQ,KAAK,WAAW,OAAO,QAAQ;AAEhE;AAEA,SAAS,aAAa,YAAgD;CACpE,IAAI,CAAC,YAAY,QACf,uBAAO,IAAI,IAAY;CAGzB,MAAM,SAAS,WAAW,OACvB,KAAK,UAAU,MAAM,KAAK,CAAC,CAAC,CAC5B,QAAQ,UAAU,MAAM,QAAQ,CAAC,CAAC,CAClC,KAAK,UAAU,MAAM,OAAO,qBAAqB,CAAC;CAErD,OAAO,IAAI,IAAI,MAAM;AACvB;AAEA,SAAS,gBAAgB,OAAoB;CAC3C,OAAO,IAAI,KACT,KAAK,IACH,MAAM,KAAK,GACX,MAAM,MAAM,GACZ,MAAM,KAAK,GACX,MAAM,KAAK,GACX,MAAM,OAAO,GACb,MAAM,OAAO,GACb,MAAM,YAAY,CACpB,CACF;AACF;AAEA,SAAS,kBAAkB,OAAa;CACtC,MAAM,SAAS,IAAI,KAAK,KAAM,GAAG,GAAG,EAAE;CACtC,OAAO,YAAY,MAAM,eAAe,GAAG,MAAM,YAAY,GAAG,MAAM,WAAW,CAAC;CAClF,OAAO,SACL,MAAM,YAAY,GAClB,MAAM,cAAc,GACpB,MAAM,cAAc,GACpB,MAAM,mBAAmB,CAC3B;CAEA,OAAO,MAAM,MAAM;AACrB;AAEA,SAAS,uBAAuB,OAAoB,KAAkB;CACpE,OAAO,gBAAgB,GAAG,CAAC,CAAC,QAAQ,IAAI,gBAAgB,KAAK,CAAC,CAAC,QAAQ;AACzE;AAEA,SAAS,eAAe,MAAc;CACpC,MAAM,aAAa,KAAK,KAAK;CAC7B,IAAI,CAAC,WAAW,SAAS,IAAI,GAC3B,OAAO,WAAW,WAAW,QAAQ,IAAI,WAAW,QAAQ,WAAW,EAAE,IAAI;CAG/E,MAAM,YAAY,WACf,MAAM,IAAI,CAAC,CACX,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAC1B,MAAM,SAAS,KAAK,WAAW,QAAQ,CAAC;CAE3C,OAAO,YAAY,UAAU,QAAQ,WAAW,EAAE,IAAI;AACxD;AAEA,SAAS,2BACP,OACA,YACA,UACA,gBACA,YACA;CACA,MAAM,UAAU,MAAM,MAAM,YAAY,WAAW,MAAM,KAAK;CAC9D,IAAI,CAAC,QAAQ,QAAQ,KAAK,CAAC,MAAM,YAC/B,OAAO,CAAC;CAGV,IAAI;EACF,MAAM,UAAU,MAAM,YAAY,eAAe,MAAM,WAAW,KAAK,CAAC;EACxE,QAAQ,UAAU,gBAAgB,OAAO;EACzC,MAAM,OAAO,IAAI,MAAM,OAAO;EAE9B,MAAM,cAAc,IAAI,KAAK,gBAAgB,UAAU,CAAC,CAAC,QAAQ,IAAI,KAAK,IAAI,GAAG,UAAU,CAAC;EAE5F,OADgB,KAAK,QAAQ,aAAa,gBAAgB,QAAQ,GAAG,IACxD,CAAC,CAAC,MAAM,GAAG,cAAc;CACxC,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,SAAS,eAAe,OAA0B,YAAyB,UAAuB;CAChG,MAAM,QAAQ,MAAM,MAAM,KAAK;CAC/B,MAAM,MAAM,MAAM,MAAM,GAAG;CAE3B,IAAI,CAAC,MAAM,QAAQ,KAAK,CAAC,IAAI,QAAQ,GACnC,OAAO;CAGT,OAAO,cAAc,OAAO,KAAK,YAAY,QAAQ;AACvD;AAEA,SAAS,wBACP,OACA,iBACA,eACmB;CACnB,MAAM,eAAe,gBAAgB,OAAO,qBAAqB;CACjE,MAAM,EAAE,YAAY,GAAG,2BAA2B;CAElD,OAAO;EACL,GAAG;EACH,IAAI,GAAG,MAAM,GAAG,IAAI;EACpB,OAAO,gBAAgB,OAAO,qBAAqB;EACnD,KAAK,cAAc,OAAO,qBAAqB;EAC/C,kBAAkB,MAAM;EACxB;EACA,mBAAmB;GACjB,qBAAqB;GACrB,kBAAkB,MAAM;GACxB;GACA,eAAe,gBAAgB,OAAO,qBAAqB;GAC3D,aAAa,cAAc,OAAO,qBAAqB;EACzD;CACF;AACF;AAEA,MAAM,0BAA0B;AAEhC,SAAgB,sBAAsB,EACpC,QACA,YACA,UACA,iBAAiB,2BACiC;CAClD,IAAI,CAAC,UAAU,OAAO,WAAW,GAC/B,OAAO,CAAC;CAGV,MAAM,QAAQ,MAAM,UAAU;CAC9B,MAAM,MAAM,MAAM,QAAQ;CAE1B,IAAI,CAAC,MAAM,QAAQ,KAAK,CAAC,IAAI,QAAQ,GACnC,OAAO,CAAC;CAGV,MAAM,gBAAqC,CAAC;CAC5C,MAAM,kBAAuC,CAAC;CAC9C,MAAM,4BAAY,IAAI,IAA+B;CAErD,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,MAAM,oBAAoB,MAAM,cAAc;GAChD,UAAU,IAAI,iBAAiB,MAAM,kBAAkB,MAAM,YAAY,GAAG,KAAK;GACjF;EACF;EAEA,IAAI,MAAM,YAAY,OAAO;GAC3B,gBAAgB,KAAK,KAAK;GAC1B;EACF;EAEA,cAAc,KAAK,KAAK;CAC1B;CAEA,MAAM,SAA8B,CAAC;CACrC,MAAM,sCAAsB,IAAI,IAAqB;CAErD,KAAK,MAAM,SAAS,eAClB,IAAI,eAAe,OAAO,OAAO,GAAG,GAClC,OAAO,KAAK,cAAc,KAAK,CAAC;CAIpC,KAAK,MAAM,SAAS,iBAAiB;EACnC,MAAM,aAAa,MAAM,MAAM,YAAY,WAAW,MAAM,KAAK;EACjE,MAAM,WAAW,MAAM,MAAM,GAAG;EAEhC,IAAI,CAAC,WAAW,QAAQ,KAAK,CAAC,SAAS,QAAQ,GAC7C;EAGF,MAAM,aAAa,uBAAuB,MAAM,MAAM,KAAK,GAAG,QAAQ;EACtE,IAAI,aAAa,GACf;EAGF,MAAM,SAAS,aAAa,MAAM,UAAU;EAC5C,MAAM,SAAS,2BAA2B,OAAO,OAAO,KAAK,gBAAgB,UAAU;EAEvF,KAAK,MAAM,kBAAkB,QAAQ;GACnC,MAAM,kBAAkB,kBAAkB,cAAc;GACxD,MAAM,eAAe,gBAAgB,OAAO,qBAAqB;GACjE,IAAI,OAAO,IAAI,YAAY,GACzB;GAGF,MAAM,cAAc,iBAAiB,MAAM,IAAI,YAAY;GAC3D,MAAM,WAAW,UAAU,IAAI,WAAW;GAC1C,IAAI,UAAU;IACZ,IAAI,eAAe,UAAU,OAAO,GAAG,GAAG;KACxC,OAAO,KAAK,cAAc,QAAQ,CAAC;KACnC,oBAAoB,IAAI,SAAS,EAAE;IACrC;IACA;GACF;GAGA,MAAM,YAAY,wBAAwB,OAAO,iBAD3B,kBAAkB,IAAI,KAAK,eAAe,QAAQ,IAAI,UAAU,CACR,CAAC;GAC/E,IAAI,cAAc,MAAM,UAAU,KAAK,GAAG,MAAM,UAAU,GAAG,GAAG,OAAO,GAAG,GACxE,OAAO,KAAK,cAAc,SAAS,CAAC;EAExC;CACF;CAEA,KAAK,MAAM,YAAY,UAAU,OAAO,GAAG;EACzC,IAAI,oBAAoB,IAAI,SAAS,EAAE,GACrC;EAEF,IAAI,eAAe,UAAU,OAAO,GAAG,GACrC,OAAO,KAAK,cAAc,QAAQ,CAAC;CAEvC;CAEA,OAAO;AACT"}
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import dayjs from "dayjs";
|
|
3
3
|
//#region packages/@mantine/schedule/src/utils/is-events-overlap/is-events-overlap.ts
|
|
4
|
+
const timestampsCache = /* @__PURE__ */ new WeakMap();
|
|
5
|
+
function getTimestamps(event) {
|
|
6
|
+
const cached = timestampsCache.get(event);
|
|
7
|
+
if (cached && cached.start === event.start && cached.end === event.end) return cached;
|
|
8
|
+
const timestamps = {
|
|
9
|
+
start: event.start,
|
|
10
|
+
end: event.end,
|
|
11
|
+
startTimestamp: dayjs(event.start).valueOf(),
|
|
12
|
+
endTimestamp: dayjs(event.end).valueOf()
|
|
13
|
+
};
|
|
14
|
+
timestampsCache.set(event, timestamps);
|
|
15
|
+
return timestamps;
|
|
16
|
+
}
|
|
4
17
|
/** Returns true if two events overlap in time */
|
|
5
18
|
function isEventsOverlap(event1, event2) {
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
return start1 < dayjs(event2.end).valueOf() && start2 < end1;
|
|
19
|
+
const first = getTimestamps(event1);
|
|
20
|
+
const second = getTimestamps(event2);
|
|
21
|
+
return first.startTimestamp < second.endTimestamp && second.startTimestamp < first.endTimestamp;
|
|
10
22
|
}
|
|
11
23
|
//#endregion
|
|
12
24
|
export { isEventsOverlap };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"is-events-overlap.mjs","names":[],"sources":["../../../src/utils/is-events-overlap/is-events-overlap.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { ScheduleEventData } from '../../types';\n\
|
|
1
|
+
{"version":3,"file":"is-events-overlap.mjs","names":[],"sources":["../../../src/utils/is-events-overlap/is-events-overlap.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { ScheduleEventData } from '../../types';\n\ninterface CachedTimestamps {\n start: ScheduleEventData['start'];\n end: ScheduleEventData['end'];\n startTimestamp: number;\n endTimestamp: number;\n}\n\nconst timestampsCache = new WeakMap<ScheduleEventData, CachedTimestamps>();\n\nfunction getTimestamps(event: ScheduleEventData): CachedTimestamps {\n const cached = timestampsCache.get(event);\n\n if (cached && cached.start === event.start && cached.end === event.end) {\n return cached;\n }\n\n const timestamps: CachedTimestamps = {\n start: event.start,\n end: event.end,\n startTimestamp: dayjs(event.start).valueOf(),\n endTimestamp: dayjs(event.end).valueOf(),\n };\n\n timestampsCache.set(event, timestamps);\n\n return timestamps;\n}\n\n/** Returns true if two events overlap in time */\nexport function isEventsOverlap(event1: ScheduleEventData, event2: ScheduleEventData) {\n const first = getTimestamps(event1);\n const second = getTimestamps(event2);\n return first.startTimestamp < second.endTimestamp && second.startTimestamp < first.endTimestamp;\n}\n"],"mappings":";;;AAUA,MAAM,kCAAkB,IAAI,QAA6C;AAEzE,SAAS,cAAc,OAA4C;CACjE,MAAM,SAAS,gBAAgB,IAAI,KAAK;CAExC,IAAI,UAAU,OAAO,UAAU,MAAM,SAAS,OAAO,QAAQ,MAAM,KACjE,OAAO;CAGT,MAAM,aAA+B;EACnC,OAAO,MAAM;EACb,KAAK,MAAM;EACX,gBAAgB,MAAM,MAAM,KAAK,CAAC,CAAC,QAAQ;EAC3C,cAAc,MAAM,MAAM,GAAG,CAAC,CAAC,QAAQ;CACzC;CAEA,gBAAgB,IAAI,OAAO,UAAU;CAErC,OAAO;AACT;;AAGA,SAAgB,gBAAgB,QAA2B,QAA2B;CACpF,MAAM,QAAQ,cAAc,MAAM;CAClC,MAAM,SAAS,cAAc,MAAM;CACnC,OAAO,MAAM,iBAAiB,OAAO,gBAAgB,OAAO,iBAAiB,MAAM;AACrF"}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ScheduleEventData } from '../../../types';
|
|
2
|
+
/** An event stored in a positioning column together with its precomputed all-day flag. */
|
|
3
|
+
export interface ColumnEvent {
|
|
4
|
+
event: ScheduleEventData;
|
|
5
|
+
allDay: boolean;
|
|
6
|
+
}
|
|
2
7
|
interface FindAvailableColumnInput {
|
|
3
|
-
columns: Map<string,
|
|
8
|
+
columns: Map<string, ColumnEvent[]>;
|
|
4
9
|
event: ScheduleEventData;
|
|
5
10
|
allDay: boolean;
|
|
6
|
-
allWeekDays: DateStringValue[];
|
|
7
11
|
}
|
|
8
12
|
export declare function findAvailableColumn(input: FindAvailableColumnInput): number;
|
|
9
13
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mantine/schedule",
|
|
3
|
-
"version": "9.5.
|
|
3
|
+
"version": "9.5.2",
|
|
4
4
|
"description": "Schedule Mantine component",
|
|
5
5
|
"homepage": "https://mantine.dev/",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"directory": "packages/@mantine/schedule"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@mantine/core": "9.5.
|
|
39
|
-
"@mantine/dates": "9.5.
|
|
40
|
-
"@mantine/hooks": "9.5.
|
|
38
|
+
"@mantine/core": "9.5.2",
|
|
39
|
+
"@mantine/dates": "9.5.2",
|
|
40
|
+
"@mantine/hooks": "9.5.2",
|
|
41
41
|
"react": "^19.2.0",
|
|
42
42
|
"react-dom": "^19.2.0"
|
|
43
43
|
},
|