@mantine/schedule 9.0.1 → 9.1.0
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/DayView/DayView.cjs +3 -1
- package/cjs/components/DayView/DayView.cjs.map +1 -1
- package/cjs/components/MonthView/get-month-view-events/get-month-positioned-events.cjs +2 -1
- package/cjs/components/MonthView/get-month-view-events/get-month-positioned-events.cjs.map +1 -1
- package/cjs/components/ScheduleEvent/ScheduleEvent.cjs +3 -1
- package/cjs/components/ScheduleEvent/ScheduleEvent.cjs.map +1 -1
- package/cjs/components/WeekView/WeekView.cjs +5 -3
- package/cjs/components/WeekView/WeekView.cjs.map +1 -1
- package/cjs/hooks/use-event-resize.cjs +8 -1
- package/cjs/hooks/use-event-resize.cjs.map +1 -1
- package/cjs/utils/expand-recurring-events/expand-recurring-events.cjs +4 -2
- package/cjs/utils/expand-recurring-events/expand-recurring-events.cjs.map +1 -1
- package/cjs/utils/is-all-day-event/is-all-day-event.cjs +3 -2
- package/cjs/utils/is-all-day-event/is-all-day-event.cjs.map +1 -1
- package/cjs/utils/is-event-in-time-range/is-event-in-time-range.cjs +8 -1
- package/cjs/utils/is-event-in-time-range/is-event-in-time-range.cjs.map +1 -1
- package/cjs/utils/is-multiday-event/is-multiday-event.cjs +2 -1
- package/cjs/utils/is-multiday-event/is-multiday-event.cjs.map +1 -1
- package/esm/components/DayView/DayView.mjs +3 -1
- package/esm/components/DayView/DayView.mjs.map +1 -1
- package/esm/components/MonthView/get-month-view-events/get-month-positioned-events.mjs +2 -1
- package/esm/components/MonthView/get-month-view-events/get-month-positioned-events.mjs.map +1 -1
- package/esm/components/ScheduleEvent/ScheduleEvent.mjs +3 -1
- package/esm/components/ScheduleEvent/ScheduleEvent.mjs.map +1 -1
- package/esm/components/WeekView/WeekView.mjs +5 -3
- package/esm/components/WeekView/WeekView.mjs.map +1 -1
- package/esm/hooks/use-event-resize.mjs +8 -1
- package/esm/hooks/use-event-resize.mjs.map +1 -1
- package/esm/utils/expand-recurring-events/expand-recurring-events.mjs +2 -1
- package/esm/utils/expand-recurring-events/expand-recurring-events.mjs.map +1 -1
- package/esm/utils/is-all-day-event/is-all-day-event.mjs +3 -2
- package/esm/utils/is-all-day-event/is-all-day-event.mjs.map +1 -1
- package/esm/utils/is-event-in-time-range/is-event-in-time-range.mjs +8 -1
- package/esm/utils/is-event-in-time-range/is-event-in-time-range.mjs.map +1 -1
- package/esm/utils/is-multiday-event/is-multiday-event.mjs +2 -1
- package/esm/utils/is-multiday-event/is-multiday-event.mjs.map +1 -1
- package/lib/components/ScheduleEvent/ScheduleEvent.d.ts +1 -1
- package/lib/components/WeekView/WeekView.d.ts +2 -0
- package/lib/hooks/use-event-resize.d.ts +1 -0
- package/package.json +4 -4
|
@@ -6,6 +6,7 @@ import { useCallback, useEffect, useEffectEvent, useRef, useState } from "react"
|
|
|
6
6
|
function useEventResize({ enabled = false, mode = "default", startTime, endTime, intervalMinutes, onEventResize, canResizeEvent }) {
|
|
7
7
|
const [resizeState, setResizeState] = useState(null);
|
|
8
8
|
const resizeRef = useRef(null);
|
|
9
|
+
const justResizedRef = useRef(false);
|
|
9
10
|
const stableOnEventResize = useEffectEvent(onEventResize || (() => {}));
|
|
10
11
|
const parsedStartTime = parseTimeString(startTime);
|
|
11
12
|
const parsedEndTime = parseTimeString(endTime);
|
|
@@ -98,6 +99,10 @@ function useEventResize({ enabled = false, mode = "default", startTime, endTime,
|
|
|
98
99
|
}
|
|
99
100
|
resizeRef.current = null;
|
|
100
101
|
setResizeState(null);
|
|
102
|
+
justResizedRef.current = true;
|
|
103
|
+
requestAnimationFrame(() => {
|
|
104
|
+
justResizedRef.current = false;
|
|
105
|
+
});
|
|
101
106
|
};
|
|
102
107
|
document.addEventListener("pointermove", handlePointerMove);
|
|
103
108
|
document.addEventListener("pointerup", handlePointerUp);
|
|
@@ -122,12 +127,14 @@ function useEventResize({ enabled = false, mode = "default", startTime, endTime,
|
|
|
122
127
|
mode,
|
|
123
128
|
canResizeEvent
|
|
124
129
|
]);
|
|
130
|
+
const wasResizing = useCallback(() => justResizedRef.current, []);
|
|
125
131
|
return {
|
|
126
132
|
handleResizeStart,
|
|
127
133
|
isResizing,
|
|
128
134
|
resizingEventId: resizeState?.eventId ?? null,
|
|
129
135
|
getResizePosition,
|
|
130
|
-
isResizableEvent
|
|
136
|
+
isResizableEvent,
|
|
137
|
+
wasResizing
|
|
131
138
|
};
|
|
132
139
|
}
|
|
133
140
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-event-resize.mjs","names":[],"sources":["../../src/hooks/use-event-resize.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { useCallback, useEffect, useEffectEvent, useRef, useState } from 'react';\nimport { DateTimeStringValue, ScheduleEventData, ScheduleMode } from '../types';\nimport { parseTimeString } from '../utils/parse-time-string/parse-time-string';\n\ntype ResizeEdge = 'top' | 'bottom';\n\ninterface ResizeState {\n eventId: string | number;\n event: ScheduleEventData;\n edge: ResizeEdge;\n container: HTMLElement;\n originalTop: number;\n originalHeight: number;\n currentTop: number;\n currentHeight: number;\n eventDate: string;\n originalStart: DateTimeStringValue;\n originalEnd: DateTimeStringValue;\n}\n\nexport interface UseEventResizeInput {\n enabled?: boolean;\n mode?: ScheduleMode;\n startTime: string;\n endTime: string;\n intervalMinutes: number;\n onEventResize?: (data: {\n eventId: string | number;\n newStart: DateTimeStringValue;\n newEnd: DateTimeStringValue;\n event: ScheduleEventData;\n }) => void;\n canResizeEvent?: (event: ScheduleEventData) => boolean;\n}\n\nexport function useEventResize({\n enabled = false,\n mode = 'default',\n startTime,\n endTime,\n intervalMinutes,\n onEventResize,\n canResizeEvent,\n}: UseEventResizeInput) {\n const [resizeState, setResizeState] = useState<ResizeState | null>(null);\n const resizeRef = useRef<ResizeState | null>(null);\n const stableOnEventResize = useEffectEvent(onEventResize || (() => {}));\n\n const parsedStartTime = parseTimeString(startTime);\n const parsedEndTime = parseTimeString(endTime);\n const startMinutes = parsedStartTime.hours * 60 + parsedStartTime.minutes;\n const endMinutes = parsedEndTime.hours * 60 + parsedEndTime.minutes;\n const totalMinutes = endMinutes - startMinutes;\n const minHeightPercent = (intervalMinutes / totalMinutes) * 100;\n\n const clampAndSnap = useCallback(\n (minutes: number): number => {\n const snapped = Math.round(minutes / intervalMinutes) * intervalMinutes;\n return Math.max(0, Math.min(totalMinutes, snapped));\n },\n [totalMinutes, intervalMinutes]\n );\n\n const percentToDateTime = useCallback(\n (percent: number, eventDate: string): DateTimeStringValue => {\n const minutes = (percent / 100) * totalMinutes;\n const snappedMinutes = clampAndSnap(minutes);\n const totalMins = startMinutes + snappedMinutes;\n const hours = Math.floor(totalMins / 60);\n const mins = totalMins % 60;\n return `${eventDate} ${String(hours).padStart(2, '0')}:${String(mins).padStart(2, '0')}:00`;\n },\n [totalMinutes, startMinutes, clampAndSnap]\n );\n\n const snapPercent = useCallback(\n (percent: number): number => {\n const minutes = (percent / 100) * totalMinutes;\n const snappedMinutes = clampAndSnap(minutes);\n return (snappedMinutes / totalMinutes) * 100;\n },\n [totalMinutes, clampAndSnap]\n );\n\n const handleResizeStart = useCallback(\n ({\n event,\n edge,\n container,\n originalTop,\n originalHeight,\n eventDate,\n pointerEvent,\n }: {\n event: ScheduleEventData;\n edge: ResizeEdge;\n container: HTMLElement;\n originalTop: number;\n originalHeight: number;\n eventDate: string;\n pointerEvent: React.PointerEvent;\n }) => {\n if (!enabled || mode === 'static') {\n return;\n }\n\n pointerEvent.preventDefault();\n pointerEvent.stopPropagation();\n\n const state: ResizeState = {\n eventId: event.id,\n event,\n edge,\n container,\n originalTop,\n originalHeight,\n currentTop: originalTop,\n currentHeight: originalHeight,\n eventDate,\n originalStart: dayjs(event.start).format('YYYY-MM-DD HH:mm:ss'),\n originalEnd: dayjs(event.end).format('YYYY-MM-DD HH:mm:ss'),\n };\n\n resizeRef.current = state;\n setResizeState(state);\n },\n [enabled, mode]\n );\n\n const isResizing = resizeState !== null;\n\n useEffect(() => {\n if (!isResizing) {\n return undefined;\n }\n\n const savedUserSelect = document.body.style.userSelect;\n document.body.style.userSelect = 'none';\n\n const handlePointerMove = (e: PointerEvent) => {\n const state = resizeRef.current;\n if (!state) {\n return;\n }\n\n const containerRect = state.container.getBoundingClientRect();\n const relativeY = e.clientY - containerRect.top;\n const rawPercent = Math.max(0, Math.min(100, (relativeY / containerRect.height) * 100));\n const snappedPercent = snapPercent(rawPercent);\n\n let newTop = state.originalTop;\n let newHeight = state.originalHeight;\n\n if (state.edge === 'bottom') {\n newHeight = Math.max(minHeightPercent, snappedPercent - state.originalTop);\n } else {\n const originalBottom = state.originalTop + state.originalHeight;\n newTop = Math.min(snappedPercent, originalBottom - minHeightPercent);\n newHeight = originalBottom - newTop;\n }\n\n resizeRef.current = { ...state, currentTop: newTop, currentHeight: newHeight };\n setResizeState(resizeRef.current);\n };\n\n const handlePointerUp = () => {\n const state = resizeRef.current;\n if (state) {\n if (\n state.currentTop !== state.originalTop ||\n state.currentHeight !== state.originalHeight\n ) {\n let newStart: DateTimeStringValue;\n let newEnd: DateTimeStringValue;\n\n if (state.edge === 'top') {\n newStart = percentToDateTime(state.currentTop, state.eventDate);\n newEnd = state.originalEnd;\n } else {\n newStart = state.originalStart;\n newEnd = percentToDateTime(state.currentTop + state.currentHeight, state.eventDate);\n }\n\n stableOnEventResize({\n eventId: state.eventId,\n newStart,\n newEnd,\n event: state.event,\n });\n }\n }\n resizeRef.current = null;\n setResizeState(null);\n };\n\n document.addEventListener('pointermove', handlePointerMove);\n document.addEventListener('pointerup', handlePointerUp);\n\n return () => {\n document.body.style.userSelect = savedUserSelect;\n document.removeEventListener('pointermove', handlePointerMove);\n document.removeEventListener('pointerup', handlePointerUp);\n };\n }, [isResizing]);\n\n const getResizePosition = useCallback(\n (eventId: string | number) => {\n if (!resizeState || resizeState.eventId !== eventId) {\n return null;\n }\n return { top: resizeState.currentTop, height: resizeState.currentHeight };\n },\n [resizeState]\n );\n\n const isResizableEvent = useCallback(\n (event: ScheduleEventData) => {\n if (!enabled || mode === 'static' || event.display === 'background') {\n return false;\n }\n return canResizeEvent ? canResizeEvent(event) : true;\n },\n [enabled, mode, canResizeEvent]\n );\n\n return {\n handleResizeStart,\n isResizing,\n resizingEventId: resizeState?.eventId ?? null,\n getResizePosition,\n isResizableEvent,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-event-resize.mjs","names":[],"sources":["../../src/hooks/use-event-resize.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { useCallback, useEffect, useEffectEvent, useRef, useState } from 'react';\nimport { DateTimeStringValue, ScheduleEventData, ScheduleMode } from '../types';\nimport { parseTimeString } from '../utils/parse-time-string/parse-time-string';\n\ntype ResizeEdge = 'top' | 'bottom';\n\ninterface ResizeState {\n eventId: string | number;\n event: ScheduleEventData;\n edge: ResizeEdge;\n container: HTMLElement;\n originalTop: number;\n originalHeight: number;\n currentTop: number;\n currentHeight: number;\n eventDate: string;\n originalStart: DateTimeStringValue;\n originalEnd: DateTimeStringValue;\n}\n\nexport interface UseEventResizeInput {\n enabled?: boolean;\n mode?: ScheduleMode;\n startTime: string;\n endTime: string;\n intervalMinutes: number;\n onEventResize?: (data: {\n eventId: string | number;\n newStart: DateTimeStringValue;\n newEnd: DateTimeStringValue;\n event: ScheduleEventData;\n }) => void;\n canResizeEvent?: (event: ScheduleEventData) => boolean;\n}\n\nexport function useEventResize({\n enabled = false,\n mode = 'default',\n startTime,\n endTime,\n intervalMinutes,\n onEventResize,\n canResizeEvent,\n}: UseEventResizeInput) {\n const [resizeState, setResizeState] = useState<ResizeState | null>(null);\n const resizeRef = useRef<ResizeState | null>(null);\n const justResizedRef = useRef(false);\n const stableOnEventResize = useEffectEvent(onEventResize || (() => {}));\n\n const parsedStartTime = parseTimeString(startTime);\n const parsedEndTime = parseTimeString(endTime);\n const startMinutes = parsedStartTime.hours * 60 + parsedStartTime.minutes;\n const endMinutes = parsedEndTime.hours * 60 + parsedEndTime.minutes;\n const totalMinutes = endMinutes - startMinutes;\n const minHeightPercent = (intervalMinutes / totalMinutes) * 100;\n\n const clampAndSnap = useCallback(\n (minutes: number): number => {\n const snapped = Math.round(minutes / intervalMinutes) * intervalMinutes;\n return Math.max(0, Math.min(totalMinutes, snapped));\n },\n [totalMinutes, intervalMinutes]\n );\n\n const percentToDateTime = useCallback(\n (percent: number, eventDate: string): DateTimeStringValue => {\n const minutes = (percent / 100) * totalMinutes;\n const snappedMinutes = clampAndSnap(minutes);\n const totalMins = startMinutes + snappedMinutes;\n const hours = Math.floor(totalMins / 60);\n const mins = totalMins % 60;\n return `${eventDate} ${String(hours).padStart(2, '0')}:${String(mins).padStart(2, '0')}:00`;\n },\n [totalMinutes, startMinutes, clampAndSnap]\n );\n\n const snapPercent = useCallback(\n (percent: number): number => {\n const minutes = (percent / 100) * totalMinutes;\n const snappedMinutes = clampAndSnap(minutes);\n return (snappedMinutes / totalMinutes) * 100;\n },\n [totalMinutes, clampAndSnap]\n );\n\n const handleResizeStart = useCallback(\n ({\n event,\n edge,\n container,\n originalTop,\n originalHeight,\n eventDate,\n pointerEvent,\n }: {\n event: ScheduleEventData;\n edge: ResizeEdge;\n container: HTMLElement;\n originalTop: number;\n originalHeight: number;\n eventDate: string;\n pointerEvent: React.PointerEvent;\n }) => {\n if (!enabled || mode === 'static') {\n return;\n }\n\n pointerEvent.preventDefault();\n pointerEvent.stopPropagation();\n\n const state: ResizeState = {\n eventId: event.id,\n event,\n edge,\n container,\n originalTop,\n originalHeight,\n currentTop: originalTop,\n currentHeight: originalHeight,\n eventDate,\n originalStart: dayjs(event.start).format('YYYY-MM-DD HH:mm:ss'),\n originalEnd: dayjs(event.end).format('YYYY-MM-DD HH:mm:ss'),\n };\n\n resizeRef.current = state;\n setResizeState(state);\n },\n [enabled, mode]\n );\n\n const isResizing = resizeState !== null;\n\n useEffect(() => {\n if (!isResizing) {\n return undefined;\n }\n\n const savedUserSelect = document.body.style.userSelect;\n document.body.style.userSelect = 'none';\n\n const handlePointerMove = (e: PointerEvent) => {\n const state = resizeRef.current;\n if (!state) {\n return;\n }\n\n const containerRect = state.container.getBoundingClientRect();\n const relativeY = e.clientY - containerRect.top;\n const rawPercent = Math.max(0, Math.min(100, (relativeY / containerRect.height) * 100));\n const snappedPercent = snapPercent(rawPercent);\n\n let newTop = state.originalTop;\n let newHeight = state.originalHeight;\n\n if (state.edge === 'bottom') {\n newHeight = Math.max(minHeightPercent, snappedPercent - state.originalTop);\n } else {\n const originalBottom = state.originalTop + state.originalHeight;\n newTop = Math.min(snappedPercent, originalBottom - minHeightPercent);\n newHeight = originalBottom - newTop;\n }\n\n resizeRef.current = { ...state, currentTop: newTop, currentHeight: newHeight };\n setResizeState(resizeRef.current);\n };\n\n const handlePointerUp = () => {\n const state = resizeRef.current;\n if (state) {\n if (\n state.currentTop !== state.originalTop ||\n state.currentHeight !== state.originalHeight\n ) {\n let newStart: DateTimeStringValue;\n let newEnd: DateTimeStringValue;\n\n if (state.edge === 'top') {\n newStart = percentToDateTime(state.currentTop, state.eventDate);\n newEnd = state.originalEnd;\n } else {\n newStart = state.originalStart;\n newEnd = percentToDateTime(state.currentTop + state.currentHeight, state.eventDate);\n }\n\n stableOnEventResize({\n eventId: state.eventId,\n newStart,\n newEnd,\n event: state.event,\n });\n }\n }\n resizeRef.current = null;\n setResizeState(null);\n justResizedRef.current = true;\n requestAnimationFrame(() => {\n justResizedRef.current = false;\n });\n };\n\n document.addEventListener('pointermove', handlePointerMove);\n document.addEventListener('pointerup', handlePointerUp);\n\n return () => {\n document.body.style.userSelect = savedUserSelect;\n document.removeEventListener('pointermove', handlePointerMove);\n document.removeEventListener('pointerup', handlePointerUp);\n };\n }, [isResizing]);\n\n const getResizePosition = useCallback(\n (eventId: string | number) => {\n if (!resizeState || resizeState.eventId !== eventId) {\n return null;\n }\n return { top: resizeState.currentTop, height: resizeState.currentHeight };\n },\n [resizeState]\n );\n\n const isResizableEvent = useCallback(\n (event: ScheduleEventData) => {\n if (!enabled || mode === 'static' || event.display === 'background') {\n return false;\n }\n return canResizeEvent ? canResizeEvent(event) : true;\n },\n [enabled, mode, canResizeEvent]\n );\n\n const wasResizing = useCallback(() => justResizedRef.current, []);\n\n return {\n handleResizeStart,\n isResizing,\n resizingEventId: resizeState?.eventId ?? null,\n getResizePosition,\n isResizableEvent,\n wasResizing,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { validateEvent } from "../validate-event/validate-event.mjs";
|
|
3
3
|
import dayjs from "dayjs";
|
|
4
|
-
import
|
|
4
|
+
import * as rruleAll from "rrule";
|
|
5
5
|
//#region packages/@mantine/schedule/src/utils/expand-recurring-events/expand-recurring-events.ts
|
|
6
|
+
const RRule = "default" in rruleAll ? rruleAll.default.RRule : rruleAll.RRule;
|
|
6
7
|
function getRecurrenceKey(recurringEventId, recurrenceId) {
|
|
7
8
|
return `${recurringEventId}::${recurrenceId}`;
|
|
8
9
|
}
|
|
@@ -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
|
|
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":"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"}
|
|
@@ -3,10 +3,11 @@ import dayjs from "dayjs";
|
|
|
3
3
|
//#region packages/@mantine/schedule/src/utils/is-all-day-event/is-all-day-event.ts
|
|
4
4
|
function isAllDayEvent({ event, date }) {
|
|
5
5
|
const dayStart = dayjs(date).startOf("day");
|
|
6
|
-
const
|
|
6
|
+
const nextDayStart = dayStart.add(1, "day");
|
|
7
|
+
const sameDayEnd = nextDayStart.subtract(1, "second");
|
|
7
8
|
const startTime = dayjs(event.start);
|
|
8
9
|
const endTime = dayjs(event.end);
|
|
9
|
-
return startTime.isSame(dayStart) && endTime.isSame(
|
|
10
|
+
return startTime.isSame(dayStart) && (endTime.isSame(nextDayStart) || endTime.isSame(sameDayEnd));
|
|
10
11
|
}
|
|
11
12
|
//#endregion
|
|
12
13
|
export { isAllDayEvent };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"is-all-day-event.mjs","names":[],"sources":["../../../src/utils/is-all-day-event/is-all-day-event.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { AnyDateValue, ScheduleEventData } from '../../types';\n\ninterface IsAllDayEventInput {\n event: ScheduleEventData;\n date: AnyDateValue;\n}\n\nexport function isAllDayEvent({ event, date }: IsAllDayEventInput) {\n const dayStart = dayjs(date).startOf('day');\n const
|
|
1
|
+
{"version":3,"file":"is-all-day-event.mjs","names":[],"sources":["../../../src/utils/is-all-day-event/is-all-day-event.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { AnyDateValue, ScheduleEventData } from '../../types';\n\ninterface IsAllDayEventInput {\n event: ScheduleEventData;\n date: AnyDateValue;\n}\n\nexport function isAllDayEvent({ event, date }: IsAllDayEventInput) {\n const dayStart = dayjs(date).startOf('day');\n const nextDayStart = dayStart.add(1, 'day');\n const sameDayEnd = nextDayStart.subtract(1, 'second');\n const startTime = dayjs(event.start);\n const endTime = dayjs(event.end);\n\n return startTime.isSame(dayStart) && (endTime.isSame(nextDayStart) || endTime.isSame(sameDayEnd));\n}\n"],"mappings":";;;AAQA,SAAgB,cAAc,EAAE,OAAO,QAA4B;CACjE,MAAM,WAAW,MAAM,KAAK,CAAC,QAAQ,MAAM;CAC3C,MAAM,eAAe,SAAS,IAAI,GAAG,MAAM;CAC3C,MAAM,aAAa,aAAa,SAAS,GAAG,SAAS;CACrD,MAAM,YAAY,MAAM,MAAM,MAAM;CACpC,MAAM,UAAU,MAAM,MAAM,IAAI;AAEhC,QAAO,UAAU,OAAO,SAAS,KAAK,QAAQ,OAAO,aAAa,IAAI,QAAQ,OAAO,WAAW"}
|
|
@@ -2,8 +2,15 @@
|
|
|
2
2
|
import { isMultidayEvent } from "../is-multiday-event/is-multiday-event.mjs";
|
|
3
3
|
import dayjs from "dayjs";
|
|
4
4
|
//#region packages/@mantine/schedule/src/utils/is-event-in-time-range/is-event-in-time-range.ts
|
|
5
|
+
function spansFullDay(event) {
|
|
6
|
+
const start = dayjs(event.start);
|
|
7
|
+
const end = dayjs(event.end);
|
|
8
|
+
if (!start.isSame(start.startOf("day"))) return false;
|
|
9
|
+
const nextDayStart = start.startOf("day").add(1, "day");
|
|
10
|
+
return end.isSame(nextDayStart) || end.isSame(nextDayStart.subtract(1, "second"));
|
|
11
|
+
}
|
|
5
12
|
function isEventInTimeRange({ event, startTime, endTime }) {
|
|
6
|
-
if (isMultidayEvent(event)) return true;
|
|
13
|
+
if (isMultidayEvent(event) || spansFullDay(event)) return true;
|
|
7
14
|
if (!startTime || !endTime) return true;
|
|
8
15
|
const [startHour, startMinute] = startTime.split(":").map(Number);
|
|
9
16
|
const [endHour, endMinute] = endTime.split(":").map(Number);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"is-event-in-time-range.mjs","names":[],"sources":["../../../src/utils/is-event-in-time-range/is-event-in-time-range.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { ScheduleEventData } from '../../types';\nimport { isMultidayEvent } from '../is-multiday-event/is-multiday-event';\n\ninterface IsEventInTimeRangeInput {\n event: ScheduleEventData;\n startTime?: string;\n endTime?: string;\n}\n\nexport function isEventInTimeRange({ event, startTime, endTime }: IsEventInTimeRangeInput) {\n if (isMultidayEvent(event)) {\n return true;\n }\n\n if (!startTime || !endTime) {\n return true;\n }\n\n const [startHour, startMinute] = startTime.split(':').map(Number);\n const [endHour, endMinute] = endTime.split(':').map(Number);\n const timeWindowStartMinutes = startHour * 60 + startMinute;\n const timeWindowEndMinutes = endHour * 60 + endMinute;\n\n const eventStart = dayjs(event.start);\n const eventEnd = dayjs(event.end);\n const eventStartMinutes = eventStart.hour() * 60 + eventStart.minute();\n const eventEndMinutes = eventEnd.hour() * 60 + eventEnd.minute();\n\n return !(eventEndMinutes <= timeWindowStartMinutes || eventStartMinutes >= timeWindowEndMinutes);\n}\n"],"mappings":";;;;AAUA,SAAgB,mBAAmB,EAAE,OAAO,WAAW,WAAoC;AACzF,KAAI,gBAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"is-event-in-time-range.mjs","names":[],"sources":["../../../src/utils/is-event-in-time-range/is-event-in-time-range.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { ScheduleEventData } from '../../types';\nimport { isMultidayEvent } from '../is-multiday-event/is-multiday-event';\n\ninterface IsEventInTimeRangeInput {\n event: ScheduleEventData;\n startTime?: string;\n endTime?: string;\n}\n\nfunction spansFullDay(event: ScheduleEventData) {\n const start = dayjs(event.start);\n const end = dayjs(event.end);\n if (!start.isSame(start.startOf('day'))) {\n return false;\n }\n const nextDayStart = start.startOf('day').add(1, 'day');\n return end.isSame(nextDayStart) || end.isSame(nextDayStart.subtract(1, 'second'));\n}\n\nexport function isEventInTimeRange({ event, startTime, endTime }: IsEventInTimeRangeInput) {\n if (isMultidayEvent(event) || spansFullDay(event)) {\n return true;\n }\n\n if (!startTime || !endTime) {\n return true;\n }\n\n const [startHour, startMinute] = startTime.split(':').map(Number);\n const [endHour, endMinute] = endTime.split(':').map(Number);\n const timeWindowStartMinutes = startHour * 60 + startMinute;\n const timeWindowEndMinutes = endHour * 60 + endMinute;\n\n const eventStart = dayjs(event.start);\n const eventEnd = dayjs(event.end);\n const eventStartMinutes = eventStart.hour() * 60 + eventStart.minute();\n const eventEndMinutes = eventEnd.hour() * 60 + eventEnd.minute();\n\n return !(eventEndMinutes <= timeWindowStartMinutes || eventStartMinutes >= timeWindowEndMinutes);\n}\n"],"mappings":";;;;AAUA,SAAS,aAAa,OAA0B;CAC9C,MAAM,QAAQ,MAAM,MAAM,MAAM;CAChC,MAAM,MAAM,MAAM,MAAM,IAAI;AAC5B,KAAI,CAAC,MAAM,OAAO,MAAM,QAAQ,MAAM,CAAC,CACrC,QAAO;CAET,MAAM,eAAe,MAAM,QAAQ,MAAM,CAAC,IAAI,GAAG,MAAM;AACvD,QAAO,IAAI,OAAO,aAAa,IAAI,IAAI,OAAO,aAAa,SAAS,GAAG,SAAS,CAAC;;AAGnF,SAAgB,mBAAmB,EAAE,OAAO,WAAW,WAAoC;AACzF,KAAI,gBAAgB,MAAM,IAAI,aAAa,MAAM,CAC/C,QAAO;AAGT,KAAI,CAAC,aAAa,CAAC,QACjB,QAAO;CAGT,MAAM,CAAC,WAAW,eAAe,UAAU,MAAM,IAAI,CAAC,IAAI,OAAO;CACjE,MAAM,CAAC,SAAS,aAAa,QAAQ,MAAM,IAAI,CAAC,IAAI,OAAO;CAC3D,MAAM,yBAAyB,YAAY,KAAK;CAChD,MAAM,uBAAuB,UAAU,KAAK;CAE5C,MAAM,aAAa,MAAM,MAAM,MAAM;CACrC,MAAM,WAAW,MAAM,MAAM,IAAI;CACjC,MAAM,oBAAoB,WAAW,MAAM,GAAG,KAAK,WAAW,QAAQ;AAGtE,QAAO,EAFiB,SAAS,MAAM,GAAG,KAAK,SAAS,QAAQ,IAEpC,0BAA0B,qBAAqB"}
|
|
@@ -3,7 +3,8 @@ import dayjs from "dayjs";
|
|
|
3
3
|
//#region packages/@mantine/schedule/src/utils/is-multiday-event/is-multiday-event.ts
|
|
4
4
|
function isMultidayEvent(event) {
|
|
5
5
|
const startDate = dayjs(event.start).startOf("day");
|
|
6
|
-
|
|
6
|
+
const rawEnd = dayjs(event.end);
|
|
7
|
+
return (rawEnd.hour() === 0 && rawEnd.minute() === 0 && rawEnd.second() === 0 ? rawEnd.startOf("day").subtract(1, "day") : rawEnd.startOf("day")).isAfter(startDate);
|
|
7
8
|
}
|
|
8
9
|
//#endregion
|
|
9
10
|
export { isMultidayEvent };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"is-multiday-event.mjs","names":[],"sources":["../../../src/utils/is-multiday-event/is-multiday-event.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { ScheduleEventData } from '../../types';\n\nexport function isMultidayEvent(event: ScheduleEventData) {\n const startDate = dayjs(event.start).startOf('day');\n const
|
|
1
|
+
{"version":3,"file":"is-multiday-event.mjs","names":[],"sources":["../../../src/utils/is-multiday-event/is-multiday-event.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { ScheduleEventData } from '../../types';\n\nexport function isMultidayEvent(event: ScheduleEventData) {\n const startDate = dayjs(event.start).startOf('day');\n const rawEnd = dayjs(event.end);\n const endAtMidnight = rawEnd.hour() === 0 && rawEnd.minute() === 0 && rawEnd.second() === 0;\n const endDate = endAtMidnight ? rawEnd.startOf('day').subtract(1, 'day') : rawEnd.startOf('day');\n return endDate.isAfter(startDate);\n}\n"],"mappings":";;;AAGA,SAAgB,gBAAgB,OAA0B;CACxD,MAAM,YAAY,MAAM,MAAM,MAAM,CAAC,QAAQ,MAAM;CACnD,MAAM,SAAS,MAAM,MAAM,IAAI;AAG/B,SAFsB,OAAO,MAAM,KAAK,KAAK,OAAO,QAAQ,KAAK,KAAK,OAAO,QAAQ,KAAK,IAC1D,OAAO,QAAQ,MAAM,CAAC,SAAS,GAAG,MAAM,GAAG,OAAO,QAAQ,MAAM,EACjF,QAAQ,UAAU"}
|
|
@@ -13,7 +13,7 @@ export interface ScheduleEventProps extends BoxProps, StylesApiProps<ScheduleEve
|
|
|
13
13
|
__staticSelector?: string;
|
|
14
14
|
/** Event to display */
|
|
15
15
|
event: ScheduleEventData;
|
|
16
|
-
/** Key of `theme.radius` or any valid CSS value to set border-radius @default
|
|
16
|
+
/** Key of `theme.radius` or any valid CSS value to set border-radius @default 'sm' */
|
|
17
17
|
radius?: MantineRadius;
|
|
18
18
|
/** If set, event has `white-space: nowrap` @default false */
|
|
19
19
|
nowrap?: boolean;
|
|
@@ -45,6 +45,8 @@ export interface WeekViewProps extends BoxProps, StylesApiProps<WeekViewFactory>
|
|
|
45
45
|
withCurrentTimeIndicator?: boolean;
|
|
46
46
|
/** If set, the time indicator displays the current time in the bubble @default true */
|
|
47
47
|
withCurrentTimeBubble?: boolean;
|
|
48
|
+
/** If set, displays the current time indicator on the same day of week even when viewing a different week @default false */
|
|
49
|
+
forceCurrentTimeIndicator?: boolean;
|
|
48
50
|
/** If set, displays all-day slots at the top of the view @default true */
|
|
49
51
|
withAllDaySlots?: boolean;
|
|
50
52
|
/** If set, the header is displayed @default true */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mantine/schedule",
|
|
3
|
-
"version": "9.0
|
|
3
|
+
"version": "9.1.0",
|
|
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.0
|
|
39
|
-
"@mantine/dates": "9.0
|
|
40
|
-
"@mantine/hooks": "9.0
|
|
38
|
+
"@mantine/core": "9.1.0",
|
|
39
|
+
"@mantine/dates": "9.1.0",
|
|
40
|
+
"@mantine/hooks": "9.1.0",
|
|
41
41
|
"react": "^19.2.0",
|
|
42
42
|
"react-dom": "^19.2.0"
|
|
43
43
|
},
|