@mantine/schedule 9.6.3 → 9.7.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 +45 -30
- package/cjs/components/DayView/DayView.cjs.map +1 -1
- package/cjs/components/DayView/DayView.module.cjs.map +1 -1
- package/cjs/components/MonthView/MonthView.cjs +11 -4
- package/cjs/components/MonthView/MonthView.cjs.map +1 -1
- package/cjs/components/ResourcesDayView/ResourcesDayView.cjs +74 -59
- package/cjs/components/ResourcesDayView/ResourcesDayView.cjs.map +1 -1
- package/cjs/components/ResourcesDayView/ResourcesDayView.module.cjs.map +1 -1
- package/cjs/components/ResourcesMonthView/ResourcesMonthView.cjs +18 -6
- package/cjs/components/ResourcesMonthView/ResourcesMonthView.cjs.map +1 -1
- package/cjs/components/ResourcesSchedule/ResourcesSchedule.cjs +6 -1
- package/cjs/components/ResourcesSchedule/ResourcesSchedule.cjs.map +1 -1
- package/cjs/components/ResourcesWeekView/ResourcesWeekView.cjs +84 -57
- package/cjs/components/ResourcesWeekView/ResourcesWeekView.cjs.map +1 -1
- package/cjs/components/ResourcesWeekView/ResourcesWeekView.module.cjs.map +1 -1
- package/cjs/components/Schedule/Schedule.cjs +14 -1
- package/cjs/components/Schedule/Schedule.cjs.map +1 -1
- package/cjs/components/ScheduleEvent/ScheduleEvent.module.cjs.map +1 -1
- package/cjs/components/WeekView/WeekView.cjs +67 -45
- package/cjs/components/WeekView/WeekView.cjs.map +1 -1
- package/cjs/components/WeekView/WeekView.module.cjs.map +1 -1
- package/cjs/hooks/use-day-grid-event-resize.cjs +38 -8
- package/cjs/hooks/use-day-grid-event-resize.cjs.map +1 -1
- package/cjs/hooks/use-drag-drop-handlers.cjs +126 -10
- package/cjs/hooks/use-drag-drop-handlers.cjs.map +1 -1
- package/cjs/hooks/use-event-resize.cjs +36 -12
- package/cjs/hooks/use-event-resize.cjs.map +1 -1
- package/cjs/hooks/use-horizontal-event-resize.cjs +37 -12
- package/cjs/hooks/use-horizontal-event-resize.cjs.map +1 -1
- package/cjs/hooks/validate-event-resize.cjs +37 -0
- package/cjs/hooks/validate-event-resize.cjs.map +1 -0
- package/cjs/index.cjs +2 -0
- package/cjs/utils/is-event-placement-allowed/is-event-placement-allowed.cjs +52 -0
- package/cjs/utils/is-event-placement-allowed/is-event-placement-allowed.cjs.map +1 -0
- package/esm/components/DayView/DayView.mjs +45 -30
- package/esm/components/DayView/DayView.mjs.map +1 -1
- package/esm/components/DayView/DayView.module.mjs.map +1 -1
- package/esm/components/MonthView/MonthView.mjs +11 -4
- package/esm/components/MonthView/MonthView.mjs.map +1 -1
- package/esm/components/ResourcesDayView/ResourcesDayView.mjs +74 -59
- package/esm/components/ResourcesDayView/ResourcesDayView.mjs.map +1 -1
- package/esm/components/ResourcesDayView/ResourcesDayView.module.mjs.map +1 -1
- package/esm/components/ResourcesMonthView/ResourcesMonthView.mjs +18 -6
- package/esm/components/ResourcesMonthView/ResourcesMonthView.mjs.map +1 -1
- package/esm/components/ResourcesSchedule/ResourcesSchedule.mjs +6 -1
- package/esm/components/ResourcesSchedule/ResourcesSchedule.mjs.map +1 -1
- package/esm/components/ResourcesWeekView/ResourcesWeekView.mjs +84 -57
- package/esm/components/ResourcesWeekView/ResourcesWeekView.mjs.map +1 -1
- package/esm/components/ResourcesWeekView/ResourcesWeekView.module.mjs.map +1 -1
- package/esm/components/Schedule/Schedule.mjs +14 -1
- package/esm/components/Schedule/Schedule.mjs.map +1 -1
- package/esm/components/ScheduleEvent/ScheduleEvent.module.mjs.map +1 -1
- package/esm/components/WeekView/WeekView.mjs +67 -45
- package/esm/components/WeekView/WeekView.mjs.map +1 -1
- package/esm/components/WeekView/WeekView.module.mjs.map +1 -1
- package/esm/hooks/use-day-grid-event-resize.mjs +38 -8
- package/esm/hooks/use-day-grid-event-resize.mjs.map +1 -1
- package/esm/hooks/use-drag-drop-handlers.mjs +126 -10
- package/esm/hooks/use-drag-drop-handlers.mjs.map +1 -1
- package/esm/hooks/use-event-resize.mjs +36 -12
- package/esm/hooks/use-event-resize.mjs.map +1 -1
- package/esm/hooks/use-horizontal-event-resize.mjs +37 -12
- package/esm/hooks/use-horizontal-event-resize.mjs.map +1 -1
- package/esm/hooks/validate-event-resize.mjs +37 -0
- package/esm/hooks/validate-event-resize.mjs.map +1 -0
- package/esm/index.mjs +2 -1
- package/esm/utils/is-event-placement-allowed/is-event-placement-allowed.mjs +50 -0
- package/esm/utils/is-event-placement-allowed/is-event-placement-allowed.mjs.map +1 -0
- package/lib/components/DayView/DayView.d.ts +11 -1
- package/lib/components/MonthView/MonthView.d.ts +9 -1
- package/lib/components/ResourcesDayView/ResourcesDayView.d.ts +11 -1
- package/lib/components/ResourcesMonthView/ResourcesMonthView.d.ts +11 -1
- package/lib/components/ResourcesSchedule/ResourcesSchedule.d.ts +12 -2
- package/lib/components/ResourcesWeekView/ResourcesWeekView.d.ts +11 -1
- package/lib/components/Schedule/Schedule.d.ts +12 -2
- package/lib/components/WeekView/WeekView.d.ts +11 -1
- package/lib/hooks/use-day-grid-event-resize.d.ts +4 -2
- package/lib/hooks/use-drag-drop-handlers.d.ts +17 -1
- package/lib/hooks/use-event-resize.d.ts +4 -2
- package/lib/hooks/use-horizontal-event-resize.d.ts +4 -2
- package/lib/hooks/validate-event-resize.d.ts +33 -0
- package/lib/types.d.ts +65 -0
- package/lib/utils/index.d.ts +2 -0
- package/lib/utils/is-event-placement-allowed/is-event-placement-allowed.d.ts +22 -0
- package/package.json +6 -6
- package/styles.css +29 -0
- package/styles.layer.css +29 -0
|
@@ -2,14 +2,27 @@
|
|
|
2
2
|
import { clampIntervalMinutes } from "../utils/clamp-interval-minutes/clamp-interval-minutes.mjs";
|
|
3
3
|
import { parseTimeString } from "../utils/parse-time-string/parse-time-string.mjs";
|
|
4
4
|
import { getDayRelativePercent } from "../utils/get-day-relative-percent/get-day-relative-percent.mjs";
|
|
5
|
+
import { validateEventResize } from "./validate-event-resize.mjs";
|
|
5
6
|
import dayjs from "dayjs";
|
|
6
7
|
import { useCallback, useEffect, useEffectEvent, useRef, useState } from "react";
|
|
7
8
|
//#region packages/@mantine/schedule/src/hooks/use-horizontal-event-resize.ts
|
|
8
|
-
function useHorizontalEventResize({ enabled = false, mode = "default", startTime, endTime, intervalMinutes, resizeIntervalMinutes, onEventResize, canResizeEvent, withBackgroundEvents = false }) {
|
|
9
|
+
function useHorizontalEventResize({ enabled = false, mode = "default", startTime, endTime, intervalMinutes, resizeIntervalMinutes, onEventResize, canResizeEvent, events, preventEventOverlap, canResizeEventTo, onEventPlacementRejected, withBackgroundEvents = false }) {
|
|
9
10
|
const [resizeState, setResizeState] = useState(null);
|
|
11
|
+
const [resizeValid, setResizeValid] = useState(true);
|
|
10
12
|
const resizeRef = useRef(null);
|
|
11
13
|
const justResizedRef = useRef(false);
|
|
12
14
|
const stableOnEventResize = useEffectEvent(onEventResize || (() => {}));
|
|
15
|
+
const stableOnEventPlacementRejected = useEffectEvent(onEventPlacementRejected || (() => {}));
|
|
16
|
+
const validateResize = useEffectEvent((event, start, end, edge) => validateEventResize({
|
|
17
|
+
event,
|
|
18
|
+
start,
|
|
19
|
+
end,
|
|
20
|
+
edge,
|
|
21
|
+
events,
|
|
22
|
+
preventEventOverlap,
|
|
23
|
+
canResizeEventTo,
|
|
24
|
+
resourceId: event.resourceId
|
|
25
|
+
}));
|
|
13
26
|
const parsedStartTime = parseTimeString(startTime);
|
|
14
27
|
const parsedEndTime = parseTimeString(endTime);
|
|
15
28
|
const startMinutes = parsedStartTime.hours * 60 + parsedStartTime.minutes;
|
|
@@ -70,7 +83,12 @@ function useHorizontalEventResize({ enabled = false, mode = "default", startTime
|
|
|
70
83
|
};
|
|
71
84
|
resizeRef.current = state;
|
|
72
85
|
setResizeState(state);
|
|
86
|
+
setResizeValid(true);
|
|
73
87
|
}, [enabled, mode]);
|
|
88
|
+
const getResizeRange = useCallback((state) => ({
|
|
89
|
+
start: state.edge === "start" ? percentToDateTime(state.currentLeft, state.eventDate) : state.originalStart,
|
|
90
|
+
end: state.edge === "start" ? state.originalEnd : percentToDateTime(state.currentLeft + state.currentWidth, state.eventDate)
|
|
91
|
+
}), [percentToDateTime]);
|
|
74
92
|
const isResizing = resizeState !== null;
|
|
75
93
|
useEffect(() => {
|
|
76
94
|
if (!isResizing) return;
|
|
@@ -108,30 +126,36 @@ function useHorizontalEventResize({ enabled = false, mode = "default", startTime
|
|
|
108
126
|
currentWidth: newWidth
|
|
109
127
|
};
|
|
110
128
|
setResizeState(resizeRef.current);
|
|
129
|
+
const range = getResizeRange(resizeRef.current);
|
|
130
|
+
setResizeValid(validateResize(state.event, range.start, range.end, state.edge).valid);
|
|
111
131
|
};
|
|
112
132
|
const handlePointerUp = () => {
|
|
113
133
|
const state = resizeRef.current;
|
|
114
134
|
if (state) {
|
|
115
135
|
if (state.currentLeft !== state.originalLeft || state.currentWidth !== state.originalWidth) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (
|
|
119
|
-
newStart = percentToDateTime(state.currentLeft, state.eventDate);
|
|
120
|
-
newEnd = state.originalEnd;
|
|
121
|
-
} else {
|
|
122
|
-
newStart = state.originalStart;
|
|
123
|
-
newEnd = percentToDateTime(state.currentLeft + state.currentWidth, state.eventDate);
|
|
124
|
-
}
|
|
125
|
-
stableOnEventResize({
|
|
136
|
+
const { start: newStart, end: newEnd } = getResizeRange(state);
|
|
137
|
+
const { valid, conflicts, reason } = validateResize(state.event, newStart, newEnd, state.edge);
|
|
138
|
+
if (valid) stableOnEventResize({
|
|
126
139
|
eventId: state.eventId,
|
|
127
140
|
newStart,
|
|
128
141
|
newEnd,
|
|
129
142
|
event: state.event
|
|
130
143
|
});
|
|
144
|
+
else stableOnEventPlacementRejected({
|
|
145
|
+
action: "resize",
|
|
146
|
+
event: state.event,
|
|
147
|
+
start: newStart,
|
|
148
|
+
end: newEnd,
|
|
149
|
+
edge: state.edge,
|
|
150
|
+
resourceId: state.event.resourceId,
|
|
151
|
+
conflicts,
|
|
152
|
+
reason
|
|
153
|
+
});
|
|
131
154
|
}
|
|
132
155
|
}
|
|
133
156
|
resizeRef.current = null;
|
|
134
157
|
setResizeState(null);
|
|
158
|
+
setResizeValid(true);
|
|
135
159
|
justResizedRef.current = true;
|
|
136
160
|
requestAnimationFrame(() => {
|
|
137
161
|
justResizedRef.current = false;
|
|
@@ -172,7 +196,8 @@ function useHorizontalEventResize({ enabled = false, mode = "default", startTime
|
|
|
172
196
|
resizingEdge: resizeState?.edge ?? null,
|
|
173
197
|
getResizePosition,
|
|
174
198
|
isResizableEvent,
|
|
175
|
-
wasResizing
|
|
199
|
+
wasResizing,
|
|
200
|
+
resizeValid
|
|
176
201
|
};
|
|
177
202
|
}
|
|
178
203
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-horizontal-event-resize.mjs","names":[],"sources":["../../src/hooks/use-horizontal-event-resize.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { useCallback, useEffect, useEffectEvent, useRef, useState } from 'react';\nimport { DateTimeStringValue, ScheduleEventData, ScheduleMode } from '../types';\nimport { clampIntervalMinutes } from '../utils/clamp-interval-minutes/clamp-interval-minutes';\nimport { getDayRelativePercent } from '../utils/get-day-relative-percent/get-day-relative-percent';\nimport { parseTimeString } from '../utils/parse-time-string/parse-time-string';\n\ntype ResizeEdge = 'start' | 'end';\n\ninterface ResizeState {\n eventId: string | number;\n event: ScheduleEventData;\n edge: ResizeEdge;\n container: HTMLElement;\n originalLeft: number;\n originalWidth: number;\n currentLeft: number;\n currentWidth: number;\n eventDate: string;\n originalStart: DateTimeStringValue;\n originalEnd: DateTimeStringValue;\n dayIndex: number;\n dayCount: number;\n}\n\nexport interface UseHorizontalEventResizeInput {\n enabled?: boolean;\n mode?: ScheduleMode;\n startTime: string;\n endTime: string;\n intervalMinutes: number;\n resizeIntervalMinutes?: 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 withBackgroundEvents?: boolean;\n}\n\nexport function useHorizontalEventResize({\n enabled = false,\n mode = 'default',\n startTime,\n endTime,\n intervalMinutes,\n resizeIntervalMinutes,\n onEventResize,\n canResizeEvent,\n withBackgroundEvents = false,\n}: UseHorizontalEventResizeInput) {\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 clampedInterval = clampIntervalMinutes(intervalMinutes);\n const clampedResizeInterval = clampIntervalMinutes(resizeIntervalMinutes ?? intervalMinutes);\n const literalRange = endMinutes - startMinutes;\n const totalMinutes = Math.ceil(literalRange / clampedInterval) * clampedInterval;\n\n // Snapped in absolute minutes from midnight so resized edges land on the same grid as\n // dragged events – see the matching comment in `use-event-resize`.\n const clampAndSnap = useCallback(\n (minutes: number): number => {\n const snapped =\n Math.round((startMinutes + minutes) / clampedResizeInterval) * clampedResizeInterval;\n return Math.max(0, Math.min(literalRange, snapped - startMinutes));\n },\n [literalRange, clampedResizeInterval, startMinutes]\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 snapEdgeMinutes = useCallback(\n (minutes: number, direction: 'up' | 'down'): number => {\n const absolute = startMinutes + minutes;\n const snapped =\n direction === 'up'\n ? Math.ceil(absolute / clampedResizeInterval) * clampedResizeInterval\n : Math.floor(absolute / clampedResizeInterval) * clampedResizeInterval;\n return Math.max(0, Math.min(literalRange, snapped - startMinutes));\n },\n [literalRange, clampedResizeInterval, startMinutes]\n );\n\n const percentToMinutes = useCallback(\n (percent: number): number => (percent / 100) * totalMinutes,\n [totalMinutes]\n );\n\n const minutesToPercent = useCallback(\n (minutes: number): number => (minutes / totalMinutes) * 100,\n [totalMinutes]\n );\n\n const handleResizeStart = useCallback(\n ({\n event,\n edge,\n container,\n originalLeft,\n originalWidth,\n eventDate,\n dayIndex = 0,\n dayCount = 1,\n pointerEvent,\n }: {\n event: ScheduleEventData;\n edge: ResizeEdge;\n container: HTMLElement;\n originalLeft: number;\n originalWidth: number;\n eventDate: string;\n dayIndex?: number;\n dayCount?: number;\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 originalLeft,\n originalWidth,\n currentLeft: originalLeft,\n currentWidth: originalWidth,\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 dayIndex,\n dayCount,\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 const savedCursor = document.body.style.cursor;\n document.body.style.userSelect = 'none';\n document.body.style.cursor = 'ew-resize';\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 rawPercent = getDayRelativePercent({\n clientX: e.clientX,\n containerLeft: containerRect.left,\n containerWidth: containerRect.width,\n dayIndex: state.dayIndex,\n dayCount: state.dayCount,\n });\n const draggedMinutes = clampAndSnap(percentToMinutes(rawPercent));\n\n let newLeft = state.originalLeft;\n let newWidth = state.originalWidth;\n\n if (state.edge === 'end') {\n const leftMinutes = percentToMinutes(state.originalLeft);\n const minEndMinutes = snapEdgeMinutes(leftMinutes + clampedResizeInterval, 'up');\n const endMinutes = Math.max(draggedMinutes, minEndMinutes);\n newWidth = Math.max(0, minutesToPercent(endMinutes) - state.originalLeft);\n } else {\n const rightMinutes = percentToMinutes(state.originalLeft + state.originalWidth);\n const maxStartMinutes = snapEdgeMinutes(rightMinutes - clampedResizeInterval, 'down');\n const startMinutesValue = Math.min(draggedMinutes, maxStartMinutes);\n newLeft = minutesToPercent(startMinutesValue);\n newWidth = Math.max(0, state.originalLeft + state.originalWidth - newLeft);\n }\n\n resizeRef.current = { ...state, currentLeft: newLeft, currentWidth: newWidth };\n setResizeState(resizeRef.current);\n };\n\n const handlePointerUp = () => {\n const state = resizeRef.current;\n if (state) {\n if (\n state.currentLeft !== state.originalLeft ||\n state.currentWidth !== state.originalWidth\n ) {\n let newStart: DateTimeStringValue;\n let newEnd: DateTimeStringValue;\n\n if (state.edge === 'start') {\n newStart = percentToDateTime(state.currentLeft, state.eventDate);\n newEnd = state.originalEnd;\n } else {\n newStart = state.originalStart;\n newEnd = percentToDateTime(state.currentLeft + state.currentWidth, 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.body.style.cursor = savedCursor;\n document.removeEventListener('pointermove', handlePointerMove);\n document.removeEventListener('pointerup', handlePointerUp);\n };\n }, [isResizing]);\n\n const getResizePosition = useCallback(\n (eventId: string | number, eventDate?: string) => {\n if (!resizeState || resizeState.eventId !== eventId) {\n return null;\n }\n if (eventDate !== undefined && resizeState.eventDate !== eventDate) {\n return null;\n }\n return { left: resizeState.currentLeft, width: resizeState.currentWidth };\n },\n [resizeState]\n );\n\n const isResizableEvent = useCallback(\n (event: ScheduleEventData) => {\n if (!enabled || mode === 'static') {\n return false;\n }\n if (event.display === 'background' && !withBackgroundEvents) {\n return false;\n }\n return canResizeEvent ? canResizeEvent(event) : true;\n },\n [enabled, mode, canResizeEvent, withBackgroundEvents]\n );\n\n const wasResizing = useCallback(() => justResizedRef.current, []);\n\n return {\n handleResizeStart,\n isResizing,\n resizingEventId: resizeState?.eventId ?? null,\n resizingEdge: resizeState?.edge ?? null,\n getResizePosition,\n isResizableEvent,\n wasResizing,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-horizontal-event-resize.mjs","names":[],"sources":["../../src/hooks/use-horizontal-event-resize.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { useCallback, useEffect, useEffectEvent, useRef, useState } from 'react';\nimport { DateTimeStringValue, ScheduleEventData, ScheduleMode } from '../types';\nimport { clampIntervalMinutes } from '../utils/clamp-interval-minutes/clamp-interval-minutes';\nimport { getDayRelativePercent } from '../utils/get-day-relative-percent/get-day-relative-percent';\nimport { parseTimeString } from '../utils/parse-time-string/parse-time-string';\nimport { EventResizeValidationOptions, validateEventResize } from './validate-event-resize';\n\ntype ResizeEdge = 'start' | 'end';\n\ninterface ResizeState {\n eventId: string | number;\n event: ScheduleEventData;\n edge: ResizeEdge;\n container: HTMLElement;\n originalLeft: number;\n originalWidth: number;\n currentLeft: number;\n currentWidth: number;\n eventDate: string;\n originalStart: DateTimeStringValue;\n originalEnd: DateTimeStringValue;\n dayIndex: number;\n dayCount: number;\n}\n\nexport interface UseHorizontalEventResizeInput extends EventResizeValidationOptions {\n enabled?: boolean;\n mode?: ScheduleMode;\n startTime: string;\n endTime: string;\n intervalMinutes: number;\n resizeIntervalMinutes?: 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 withBackgroundEvents?: boolean;\n}\n\nexport function useHorizontalEventResize({\n enabled = false,\n mode = 'default',\n startTime,\n endTime,\n intervalMinutes,\n resizeIntervalMinutes,\n onEventResize,\n canResizeEvent,\n events,\n preventEventOverlap,\n canResizeEventTo,\n onEventPlacementRejected,\n withBackgroundEvents = false,\n}: UseHorizontalEventResizeInput) {\n const [resizeState, setResizeState] = useState<ResizeState | null>(null);\n const [resizeValid, setResizeValid] = useState(true);\n const resizeRef = useRef<ResizeState | null>(null);\n const justResizedRef = useRef(false);\n const stableOnEventResize = useEffectEvent(onEventResize || (() => {}));\n const stableOnEventPlacementRejected = useEffectEvent(onEventPlacementRejected || (() => {}));\n\n const validateResize = useEffectEvent(\n (\n event: ScheduleEventData,\n start: DateTimeStringValue,\n end: DateTimeStringValue,\n edge: 'start' | 'end'\n ) =>\n validateEventResize({\n event,\n start,\n end,\n edge,\n events,\n preventEventOverlap,\n canResizeEventTo,\n resourceId: event.resourceId,\n })\n );\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 clampedInterval = clampIntervalMinutes(intervalMinutes);\n const clampedResizeInterval = clampIntervalMinutes(resizeIntervalMinutes ?? intervalMinutes);\n const literalRange = endMinutes - startMinutes;\n const totalMinutes = Math.ceil(literalRange / clampedInterval) * clampedInterval;\n\n // Snapped in absolute minutes from midnight so resized edges land on the same grid as\n // dragged events – see the matching comment in `use-event-resize`.\n const clampAndSnap = useCallback(\n (minutes: number): number => {\n const snapped =\n Math.round((startMinutes + minutes) / clampedResizeInterval) * clampedResizeInterval;\n return Math.max(0, Math.min(literalRange, snapped - startMinutes));\n },\n [literalRange, clampedResizeInterval, startMinutes]\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 snapEdgeMinutes = useCallback(\n (minutes: number, direction: 'up' | 'down'): number => {\n const absolute = startMinutes + minutes;\n const snapped =\n direction === 'up'\n ? Math.ceil(absolute / clampedResizeInterval) * clampedResizeInterval\n : Math.floor(absolute / clampedResizeInterval) * clampedResizeInterval;\n return Math.max(0, Math.min(literalRange, snapped - startMinutes));\n },\n [literalRange, clampedResizeInterval, startMinutes]\n );\n\n const percentToMinutes = useCallback(\n (percent: number): number => (percent / 100) * totalMinutes,\n [totalMinutes]\n );\n\n const minutesToPercent = useCallback(\n (minutes: number): number => (minutes / totalMinutes) * 100,\n [totalMinutes]\n );\n\n const handleResizeStart = useCallback(\n ({\n event,\n edge,\n container,\n originalLeft,\n originalWidth,\n eventDate,\n dayIndex = 0,\n dayCount = 1,\n pointerEvent,\n }: {\n event: ScheduleEventData;\n edge: ResizeEdge;\n container: HTMLElement;\n originalLeft: number;\n originalWidth: number;\n eventDate: string;\n dayIndex?: number;\n dayCount?: number;\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 originalLeft,\n originalWidth,\n currentLeft: originalLeft,\n currentWidth: originalWidth,\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 dayIndex,\n dayCount,\n };\n\n resizeRef.current = state;\n setResizeState(state);\n setResizeValid(true);\n },\n [enabled, mode]\n );\n\n const getResizeRange = useCallback(\n (state: ResizeState) => ({\n start:\n state.edge === 'start'\n ? percentToDateTime(state.currentLeft, state.eventDate)\n : state.originalStart,\n end:\n state.edge === 'start'\n ? state.originalEnd\n : percentToDateTime(state.currentLeft + state.currentWidth, state.eventDate),\n }),\n [percentToDateTime]\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 const savedCursor = document.body.style.cursor;\n document.body.style.userSelect = 'none';\n document.body.style.cursor = 'ew-resize';\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 rawPercent = getDayRelativePercent({\n clientX: e.clientX,\n containerLeft: containerRect.left,\n containerWidth: containerRect.width,\n dayIndex: state.dayIndex,\n dayCount: state.dayCount,\n });\n const draggedMinutes = clampAndSnap(percentToMinutes(rawPercent));\n\n let newLeft = state.originalLeft;\n let newWidth = state.originalWidth;\n\n if (state.edge === 'end') {\n const leftMinutes = percentToMinutes(state.originalLeft);\n const minEndMinutes = snapEdgeMinutes(leftMinutes + clampedResizeInterval, 'up');\n const endMinutes = Math.max(draggedMinutes, minEndMinutes);\n newWidth = Math.max(0, minutesToPercent(endMinutes) - state.originalLeft);\n } else {\n const rightMinutes = percentToMinutes(state.originalLeft + state.originalWidth);\n const maxStartMinutes = snapEdgeMinutes(rightMinutes - clampedResizeInterval, 'down');\n const startMinutesValue = Math.min(draggedMinutes, maxStartMinutes);\n newLeft = minutesToPercent(startMinutesValue);\n newWidth = Math.max(0, state.originalLeft + state.originalWidth - newLeft);\n }\n\n resizeRef.current = { ...state, currentLeft: newLeft, currentWidth: newWidth };\n setResizeState(resizeRef.current);\n\n const range = getResizeRange(resizeRef.current);\n setResizeValid(validateResize(state.event, range.start, range.end, state.edge).valid);\n };\n\n const handlePointerUp = () => {\n const state = resizeRef.current;\n if (state) {\n if (\n state.currentLeft !== state.originalLeft ||\n state.currentWidth !== state.originalWidth\n ) {\n const { start: newStart, end: newEnd } = getResizeRange(state);\n const { valid, conflicts, reason } = validateResize(\n state.event,\n newStart,\n newEnd,\n state.edge\n );\n\n if (valid) {\n stableOnEventResize({\n eventId: state.eventId,\n newStart,\n newEnd,\n event: state.event,\n });\n } else {\n stableOnEventPlacementRejected({\n action: 'resize',\n event: state.event,\n start: newStart,\n end: newEnd,\n edge: state.edge,\n resourceId: state.event.resourceId,\n conflicts,\n reason: reason!,\n });\n }\n }\n }\n resizeRef.current = null;\n setResizeState(null);\n setResizeValid(true);\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.body.style.cursor = savedCursor;\n document.removeEventListener('pointermove', handlePointerMove);\n document.removeEventListener('pointerup', handlePointerUp);\n };\n }, [isResizing]);\n\n const getResizePosition = useCallback(\n (eventId: string | number, eventDate?: string) => {\n if (!resizeState || resizeState.eventId !== eventId) {\n return null;\n }\n if (eventDate !== undefined && resizeState.eventDate !== eventDate) {\n return null;\n }\n return { left: resizeState.currentLeft, width: resizeState.currentWidth };\n },\n [resizeState]\n );\n\n const isResizableEvent = useCallback(\n (event: ScheduleEventData) => {\n if (!enabled || mode === 'static') {\n return false;\n }\n if (event.display === 'background' && !withBackgroundEvents) {\n return false;\n }\n return canResizeEvent ? canResizeEvent(event) : true;\n },\n [enabled, mode, canResizeEvent, withBackgroundEvents]\n );\n\n const wasResizing = useCallback(() => justResizedRef.current, []);\n\n return {\n handleResizeStart,\n isResizing,\n resizingEventId: resizeState?.eventId ?? null,\n resizingEdge: resizeState?.edge ?? null,\n getResizePosition,\n isResizableEvent,\n wasResizing,\n resizeValid,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { isEventPlacementAllowed } from "../utils/is-event-placement-allowed/is-event-placement-allowed.mjs";
|
|
3
|
+
//#region packages/@mantine/schedule/src/hooks/validate-event-resize.ts
|
|
4
|
+
function validateEventResize({ event, start, end, edge, events, preventEventOverlap, canResizeEventTo, resourceId }) {
|
|
5
|
+
const { allowed, conflicts } = isEventPlacementAllowed({
|
|
6
|
+
event,
|
|
7
|
+
start,
|
|
8
|
+
end,
|
|
9
|
+
events: events || [],
|
|
10
|
+
preventEventOverlap,
|
|
11
|
+
resourceId
|
|
12
|
+
});
|
|
13
|
+
if (!allowed) return {
|
|
14
|
+
valid: false,
|
|
15
|
+
conflicts,
|
|
16
|
+
reason: "overlap"
|
|
17
|
+
};
|
|
18
|
+
if (canResizeEventTo && canResizeEventTo({
|
|
19
|
+
event,
|
|
20
|
+
start,
|
|
21
|
+
end,
|
|
22
|
+
edge
|
|
23
|
+
}) === false) return {
|
|
24
|
+
valid: false,
|
|
25
|
+
conflicts: [],
|
|
26
|
+
reason: "rejected"
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
valid: true,
|
|
30
|
+
conflicts: [],
|
|
31
|
+
reason: null
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { validateEventResize };
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=validate-event-resize.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-event-resize.mjs","names":[],"sources":["../../src/hooks/validate-event-resize.ts"],"sourcesContent":["import {\n DateTimeStringValue,\n PreventEventOverlap,\n ScheduleCanResizeEventToData,\n ScheduleEventData,\n} from '../types';\nimport { isEventPlacementAllowed } from '../utils/is-event-placement-allowed/is-event-placement-allowed';\n\nexport interface EventResizeValidationOptions {\n /** Events the candidate range is checked against when `preventEventOverlap` is set */\n events?: ScheduleEventData[];\n\n /** If set, resizes that would make the event overlap another event are rejected */\n preventEventOverlap?: PreventEventOverlap;\n\n /** Called on every pointer move to compute live feedback and again before the resize is committed, return `false` to reject the new size */\n canResizeEventTo?: (data: ScheduleCanResizeEventToData) => boolean;\n\n /** Called when a resize is rejected */\n onEventPlacementRejected?: (data: {\n action: 'resize';\n event: ScheduleEventData;\n start: DateTimeStringValue;\n end: DateTimeStringValue;\n edge: 'start' | 'end';\n resourceId?: string | number;\n conflicts: ScheduleEventData[];\n reason: 'overlap' | 'rejected';\n }) => void;\n}\n\nexport interface ValidateEventResizeInput extends EventResizeValidationOptions {\n event: ScheduleEventData;\n start: DateTimeStringValue;\n end: DateTimeStringValue;\n edge: 'start' | 'end';\n resourceId?: string | number;\n}\n\nexport interface ValidateEventResizeResult {\n valid: boolean;\n conflicts: ScheduleEventData[];\n reason: 'overlap' | 'rejected' | null;\n}\n\nexport function validateEventResize({\n event,\n start,\n end,\n edge,\n events,\n preventEventOverlap,\n canResizeEventTo,\n resourceId,\n}: ValidateEventResizeInput): ValidateEventResizeResult {\n const { allowed, conflicts } = isEventPlacementAllowed({\n event,\n start,\n end,\n events: events || [],\n preventEventOverlap,\n resourceId,\n });\n\n if (!allowed) {\n return { valid: false, conflicts, reason: 'overlap' };\n }\n\n if (canResizeEventTo && canResizeEventTo({ event, start, end, edge }) === false) {\n return { valid: false, conflicts: [], reason: 'rejected' };\n }\n\n return { valid: true, conflicts: [], reason: null };\n}\n"],"mappings":";;;AA6CA,SAAgB,oBAAoB,EAClC,OACA,OACA,KACA,MACA,QACA,qBACA,kBACA,cACsD;CACtD,MAAM,EAAE,SAAS,cAAc,wBAAwB;EACrD;EACA;EACA;EACA,QAAQ,UAAU,CAAC;EACnB;EACA;CACF,CAAC;CAED,IAAI,CAAC,SACH,OAAO;EAAE,OAAO;EAAO;EAAW,QAAQ;CAAU;CAGtD,IAAI,oBAAoB,iBAAiB;EAAE;EAAO;EAAO;EAAK;CAAK,CAAC,MAAM,OACxE,OAAO;EAAE,OAAO;EAAO,WAAW,CAAC;EAAG,QAAQ;CAAW;CAG3D,OAAO;EAAE,OAAO;EAAM,WAAW,CAAC;EAAG,QAAQ;CAAK;AACpD"}
|
package/esm/index.mjs
CHANGED
|
@@ -43,6 +43,7 @@ import { getIndexFromDragPoint } from "./utils/get-index-from-drag-point/get-ind
|
|
|
43
43
|
import { getDayRelativePercent } from "./utils/get-day-relative-percent/get-day-relative-percent.mjs";
|
|
44
44
|
import { getOverlapClusters } from "./utils/get-overlap-clusters/get-overlap-clusters.mjs";
|
|
45
45
|
import { applyCascadeLayout } from "./utils/apply-cascade-layout/apply-cascade-layout.mjs";
|
|
46
|
+
import { isEventPlacementAllowed } from "./utils/is-event-placement-allowed/is-event-placement-allowed.mjs";
|
|
46
47
|
import { AgendaView } from "./components/AgendaView/AgendaView.mjs";
|
|
47
48
|
import { CurrentTimeIndicator } from "./components/CurrentTimeIndicator/CurrentTimeIndicator.mjs";
|
|
48
49
|
import { DragContext } from "./components/DragContext/DragContext.mjs";
|
|
@@ -60,4 +61,4 @@ import { ResourcesDayView } from "./components/ResourcesDayView/ResourcesDayView
|
|
|
60
61
|
import { ResourcesWeekView } from "./components/ResourcesWeekView/ResourcesWeekView.mjs";
|
|
61
62
|
import { ResourcesMonthView } from "./components/ResourcesMonthView/ResourcesMonthView.mjs";
|
|
62
63
|
import { ResourcesSchedule } from "./components/ResourcesSchedule/ResourcesSchedule.mjs";
|
|
63
|
-
export { AgendaView, CurrentTimeIndicator, DEFAULT_SCHEDULE_LABELS, DayView, DragContext, MobileMonthView, MonthView, MoreEvents, ResourcesDayView, ResourcesMonthView, ResourcesSchedule, ResourcesWeekView, Schedule, ScheduleBackgroundEvent, ScheduleEvent, ScheduleHeader, WeekView, YearView, applyCascadeLayout, calculateDropTime, calculateMonthDropDate, clampIntervalMinutes, expandRecurringEvents, formatDate, getBusinessHoursMod, getCurrentTimePosition, getDayPosition, getDayRelativePercent, getDayTimeIntervals, getEndOfWeek, getGroupToResourceIdMap, getIndexFromDragPoint, getLabel, getMonthDays, getMonthRange, getMonthsByQuarter, getMonthsList, getOrderedResources, getOverlapClusters, getStartOfWeek, getTimeAxisEventStyle, getVisibleEvents, getWeekDays, getWeekNumber, getWeekdaysNames, getYearsList, handleResourcesGridKeyDown, isAfterMinDate, isAllDayEvent, isBeforeMaxDate, isEventInTimeRange, isEventsOverlap, isInTimeRange, isMultidayEvent, isSameMonth, isWithinWeek, nextWeek, parseTimeString, previousWeek, sortEvents, toDateString, toTimeString, validateEvent };
|
|
64
|
+
export { AgendaView, CurrentTimeIndicator, DEFAULT_SCHEDULE_LABELS, DayView, DragContext, MobileMonthView, MonthView, MoreEvents, ResourcesDayView, ResourcesMonthView, ResourcesSchedule, ResourcesWeekView, Schedule, ScheduleBackgroundEvent, ScheduleEvent, ScheduleHeader, WeekView, YearView, applyCascadeLayout, calculateDropTime, calculateMonthDropDate, clampIntervalMinutes, expandRecurringEvents, formatDate, getBusinessHoursMod, getCurrentTimePosition, getDayPosition, getDayRelativePercent, getDayTimeIntervals, getEndOfWeek, getGroupToResourceIdMap, getIndexFromDragPoint, getLabel, getMonthDays, getMonthRange, getMonthsByQuarter, getMonthsList, getOrderedResources, getOverlapClusters, getStartOfWeek, getTimeAxisEventStyle, getVisibleEvents, getWeekDays, getWeekNumber, getWeekdaysNames, getYearsList, handleResourcesGridKeyDown, isAfterMinDate, isAllDayEvent, isBeforeMaxDate, isEventInTimeRange, isEventPlacementAllowed, isEventsOverlap, isInTimeRange, isMultidayEvent, isSameMonth, isWithinWeek, nextWeek, parseTimeString, previousWeek, sortEvents, toDateString, toTimeString, validateEvent };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { isEventsOverlap } from "../is-events-overlap/is-events-overlap.mjs";
|
|
3
|
+
import dayjs from "dayjs";
|
|
4
|
+
//#region packages/@mantine/schedule/src/utils/is-event-placement-allowed/is-event-placement-allowed.ts
|
|
5
|
+
function isSameEvent(event, other) {
|
|
6
|
+
if (event.id === other.id) return true;
|
|
7
|
+
const eventSeriesId = event.recurringEventId ?? event.recurringInstance?.recurringEventId;
|
|
8
|
+
const otherSeriesId = other.recurringEventId ?? other.recurringInstance?.recurringEventId;
|
|
9
|
+
if (eventSeriesId === void 0 || otherSeriesId === void 0) return false;
|
|
10
|
+
const eventOccurrenceId = event.recurrenceId ?? event.recurringInstance?.recurrenceId;
|
|
11
|
+
const otherOccurrenceId = other.recurrenceId ?? other.recurringInstance?.recurrenceId;
|
|
12
|
+
return eventSeriesId === otherSeriesId && eventOccurrenceId !== void 0 && eventOccurrenceId === otherOccurrenceId;
|
|
13
|
+
}
|
|
14
|
+
function isMidnight(value) {
|
|
15
|
+
return value.hour() === 0 && value.minute() === 0 && value.second() === 0;
|
|
16
|
+
}
|
|
17
|
+
function spansWholeDays(event) {
|
|
18
|
+
const start = dayjs(event.start);
|
|
19
|
+
const end = dayjs(event.end);
|
|
20
|
+
if (!isMidnight(start)) return false;
|
|
21
|
+
return isMidnight(end) || end.hour() === 23 && end.minute() === 59 && end.second() === 59;
|
|
22
|
+
}
|
|
23
|
+
function isEventPlacementAllowed({ event, start, end, events, preventEventOverlap, resourceId }) {
|
|
24
|
+
if (!preventEventOverlap) return {
|
|
25
|
+
allowed: true,
|
|
26
|
+
conflicts: []
|
|
27
|
+
};
|
|
28
|
+
const candidate = {
|
|
29
|
+
...event,
|
|
30
|
+
start,
|
|
31
|
+
end,
|
|
32
|
+
...resourceId !== void 0 ? { resourceId } : {}
|
|
33
|
+
};
|
|
34
|
+
const candidateIsAllDay = spansWholeDays(candidate);
|
|
35
|
+
const conflicts = events.filter((other) => {
|
|
36
|
+
if (other.display === "background" || isSameEvent(event, other)) return false;
|
|
37
|
+
if (resourceId !== void 0 && other.resourceId !== resourceId) return false;
|
|
38
|
+
if (spansWholeDays(other) !== candidateIsAllDay) return false;
|
|
39
|
+
if (!isEventsOverlap(other, candidate)) return false;
|
|
40
|
+
return typeof preventEventOverlap === "function" ? preventEventOverlap(other, candidate) : true;
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
allowed: conflicts.length === 0,
|
|
44
|
+
conflicts
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { isEventPlacementAllowed };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=is-event-placement-allowed.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-event-placement-allowed.mjs","names":[],"sources":["../../../src/utils/is-event-placement-allowed/is-event-placement-allowed.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { DateTimeStringValue, PreventEventOverlap, ScheduleEventData } from '../../types';\nimport { isEventsOverlap } from '../is-events-overlap/is-events-overlap';\n\nexport interface IsEventPlacementAllowedInput {\n /** Event that is being moved or resized */\n event: ScheduleEventData;\n\n /** Candidate start of the event */\n start: DateTimeStringValue;\n\n /** Candidate end of the event */\n end: DateTimeStringValue;\n\n /** Events the candidate range is checked against */\n events: ScheduleEventData[];\n\n /** If set, placements that overlap another event are rejected */\n preventEventOverlap?: PreventEventOverlap;\n\n /** Target resource, limits the check to events of that resource */\n resourceId?: string | number;\n}\n\nexport interface IsEventPlacementAllowedResult {\n /** True if the candidate range does not conflict with any event */\n allowed: boolean;\n\n /** Events the candidate range collided with */\n conflicts: ScheduleEventData[];\n}\n\nfunction isSameEvent(event: ScheduleEventData, other: ScheduleEventData) {\n if (event.id === other.id) {\n return true;\n }\n\n const eventSeriesId = event.recurringEventId ?? event.recurringInstance?.recurringEventId;\n const otherSeriesId = other.recurringEventId ?? other.recurringInstance?.recurringEventId;\n\n if (eventSeriesId === undefined || otherSeriesId === undefined) {\n return false;\n }\n\n const eventOccurrenceId = event.recurrenceId ?? event.recurringInstance?.recurrenceId;\n const otherOccurrenceId = other.recurrenceId ?? other.recurringInstance?.recurrenceId;\n\n return (\n eventSeriesId === otherSeriesId &&\n eventOccurrenceId !== undefined &&\n eventOccurrenceId === otherOccurrenceId\n );\n}\n\nfunction isMidnight(value: dayjs.Dayjs) {\n return value.hour() === 0 && value.minute() === 0 && value.second() === 0;\n}\n\nfunction spansWholeDays(event: ScheduleEventData) {\n const start = dayjs(event.start);\n const end = dayjs(event.end);\n\n if (!isMidnight(start)) {\n return false;\n }\n\n return isMidnight(end) || (end.hour() === 23 && end.minute() === 59 && end.second() === 59);\n}\n\nexport function isEventPlacementAllowed({\n event,\n start,\n end,\n events,\n preventEventOverlap,\n resourceId,\n}: IsEventPlacementAllowedInput): IsEventPlacementAllowedResult {\n if (!preventEventOverlap) {\n return { allowed: true, conflicts: [] };\n }\n\n const candidate = {\n ...event,\n start,\n end,\n ...(resourceId !== undefined ? { resourceId } : {}),\n } as ScheduleEventData;\n const candidateIsAllDay = spansWholeDays(candidate);\n const conflicts = events.filter((other) => {\n if (other.display === 'background' || isSameEvent(event, other)) {\n return false;\n }\n\n if (resourceId !== undefined && other.resourceId !== resourceId) {\n return false;\n }\n\n if (spansWholeDays(other) !== candidateIsAllDay) {\n return false;\n }\n\n if (!isEventsOverlap(other, candidate)) {\n return false;\n }\n\n return typeof preventEventOverlap === 'function' ? preventEventOverlap(other, candidate) : true;\n });\n\n return { allowed: conflicts.length === 0, conflicts };\n}\n"],"mappings":";;;;AAgCA,SAAS,YAAY,OAA0B,OAA0B;CACvE,IAAI,MAAM,OAAO,MAAM,IACrB,OAAO;CAGT,MAAM,gBAAgB,MAAM,oBAAoB,MAAM,mBAAmB;CACzE,MAAM,gBAAgB,MAAM,oBAAoB,MAAM,mBAAmB;CAEzE,IAAI,kBAAkB,KAAA,KAAa,kBAAkB,KAAA,GACnD,OAAO;CAGT,MAAM,oBAAoB,MAAM,gBAAgB,MAAM,mBAAmB;CACzE,MAAM,oBAAoB,MAAM,gBAAgB,MAAM,mBAAmB;CAEzE,OACE,kBAAkB,iBAClB,sBAAsB,KAAA,KACtB,sBAAsB;AAE1B;AAEA,SAAS,WAAW,OAAoB;CACtC,OAAO,MAAM,KAAK,MAAM,KAAK,MAAM,OAAO,MAAM,KAAK,MAAM,OAAO,MAAM;AAC1E;AAEA,SAAS,eAAe,OAA0B;CAChD,MAAM,QAAQ,MAAM,MAAM,KAAK;CAC/B,MAAM,MAAM,MAAM,MAAM,GAAG;CAE3B,IAAI,CAAC,WAAW,KAAK,GACnB,OAAO;CAGT,OAAO,WAAW,GAAG,KAAM,IAAI,KAAK,MAAM,MAAM,IAAI,OAAO,MAAM,MAAM,IAAI,OAAO,MAAM;AAC1F;AAEA,SAAgB,wBAAwB,EACtC,OACA,OACA,KACA,QACA,qBACA,cAC8D;CAC9D,IAAI,CAAC,qBACH,OAAO;EAAE,SAAS;EAAM,WAAW,CAAC;CAAE;CAGxC,MAAM,YAAY;EAChB,GAAG;EACH;EACA;EACA,GAAI,eAAe,KAAA,IAAY,EAAE,WAAW,IAAI,CAAC;CACnD;CACA,MAAM,oBAAoB,eAAe,SAAS;CAClD,MAAM,YAAY,OAAO,QAAQ,UAAU;EACzC,IAAI,MAAM,YAAY,gBAAgB,YAAY,OAAO,KAAK,GAC5D,OAAO;EAGT,IAAI,eAAe,KAAA,KAAa,MAAM,eAAe,YACnD,OAAO;EAGT,IAAI,eAAe,KAAK,MAAM,mBAC5B,OAAO;EAGT,IAAI,CAAC,gBAAgB,OAAO,SAAS,GACnC,OAAO;EAGT,OAAO,OAAO,wBAAwB,aAAa,oBAAoB,OAAO,SAAS,IAAI;CAC7F,CAAC;CAED,OAAO;EAAE,SAAS,UAAU,WAAW;EAAG;CAAU;AACtD"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BoxProps, DataAttributes, ElementProps, Factory, MantineRadius, ScrollAreaAutosizeProps, StylesApiProps } from '@mantine/core';
|
|
2
2
|
import { ScheduleLabelsOverride } from '../../labels';
|
|
3
|
-
import { AnyDateValue, DateLabelFormat, DateStringValue, DateTimeStringValue, ScheduleEventData, ScheduleEventOverlapMode, ScheduleMode, ScheduleViewLevel } from '../../types';
|
|
3
|
+
import { AnyDateValue, DateLabelFormat, DateStringValue, DateTimeStringValue, PreventEventOverlap, ScheduleCanDropEventData, ScheduleCanDropExternalEventData, ScheduleCanResizeEventToData, ScheduleEventData, ScheduleEventOverlapMode, ScheduleEventPlacementRejectedData, ScheduleMode, ScheduleViewLevel } from '../../types';
|
|
4
4
|
import { AgendaViewStylesNames } from '../AgendaView/AgendaView';
|
|
5
5
|
import { CurrentTimeIndicatorStylesNames } from '../CurrentTimeIndicator/CurrentTimeIndicator';
|
|
6
6
|
import { MoreEventsProps, MoreEventsStylesNames } from '../MoreEvents/MoreEvents';
|
|
@@ -128,6 +128,16 @@ export interface DayViewProps extends BoxProps, StylesApiProps<DayViewFactory>,
|
|
|
128
128
|
}) => void;
|
|
129
129
|
/** Function to determine if event can be resized */
|
|
130
130
|
canResizeEvent?: (event: ScheduleEventData) => boolean;
|
|
131
|
+
/** Called while an event is dragged over a target to compute live feedback and again before the drop is committed, return `false` to reject the drop. Must be pure and cheap. */
|
|
132
|
+
canDropEvent?: (data: ScheduleCanDropEventData) => boolean;
|
|
133
|
+
/** Called while an external item is dragged over a target to compute live feedback and again before the drop is committed, return `false` to reject the drop. Must be pure and cheap. Only `dataTransfer.types` can be read while the drag is in progress. */
|
|
134
|
+
canDropExternalEvent?: (data: ScheduleCanDropExternalEventData) => boolean;
|
|
135
|
+
/** Called on every pointer move while an event is resized to compute live feedback and again before the resize is committed, return `false` to reject the new size. Must be pure and cheap. */
|
|
136
|
+
canResizeEventTo?: (data: ScheduleCanResizeEventToData) => boolean;
|
|
137
|
+
/** If set, drops and resizes that would make the event overlap another event are rejected. Pass a function to decide per pair of events: return `true` to forbid the overlap. The function runs while dragging and resizing to compute live feedback, it must be pure and cheap. @default false */
|
|
138
|
+
preventEventOverlap?: PreventEventOverlap;
|
|
139
|
+
/** Called when a drop or resize is rejected */
|
|
140
|
+
onEventPlacementRejected?: (data: ScheduleEventPlacementRejectedData) => void;
|
|
131
141
|
/** Snap step for resizing events, in minutes. Must divide evenly into an hour (e.g. `15`, `30`) or be a whole number of hours. When not set, `intervalMinutes` is used. @default intervalMinutes */
|
|
132
142
|
eventResizeInterval?: number;
|
|
133
143
|
/** Max number of generated recurring instances per recurring series @default 2000 */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BoxProps, DataAttributes, ElementProps, Factory, MantineRadius, ScrollAreaProps, StylesApiProps } from '@mantine/core';
|
|
2
2
|
import { ScheduleLabelsOverride } from '../../labels';
|
|
3
|
-
import { DateLabelFormat, DateStringValue, DateTimeStringValue, DayOfWeek, ScheduleEventData, ScheduleMode, ScheduleViewLevel } from '../../types';
|
|
3
|
+
import { DateLabelFormat, DateStringValue, DateTimeStringValue, DayOfWeek, PreventEventOverlap, ScheduleCanDropEventData, ScheduleCanDropExternalEventData, ScheduleEventData, ScheduleEventPlacementRejectedData, ScheduleMode, ScheduleViewLevel } from '../../types';
|
|
4
4
|
import { AgendaViewStylesNames } from '../AgendaView/AgendaView';
|
|
5
5
|
import { MoreEventsProps } from '../MoreEvents/MoreEvents';
|
|
6
6
|
import { RenderEvent, RenderEventBody } from '../ScheduleEvent/ScheduleEvent';
|
|
@@ -80,6 +80,14 @@ export interface MonthViewProps extends BoxProps, StylesApiProps<MonthViewFactor
|
|
|
80
80
|
}) => void;
|
|
81
81
|
/** Function to determine if event can be dragged */
|
|
82
82
|
canDragEvent?: (event: ScheduleEventData) => boolean;
|
|
83
|
+
/** Called while an event is dragged over a target to compute live feedback and again before the drop is committed, return `false` to reject the drop. Must be pure and cheap. */
|
|
84
|
+
canDropEvent?: (data: ScheduleCanDropEventData) => boolean;
|
|
85
|
+
/** Called while an external item is dragged over a target to compute live feedback and again before the drop is committed, return `false` to reject the drop. Must be pure and cheap. Only `dataTransfer.types` can be read while the drag is in progress. */
|
|
86
|
+
canDropExternalEvent?: (data: ScheduleCanDropExternalEventData) => boolean;
|
|
87
|
+
/** If set, drops that would make the event overlap another event are rejected. Pass a function to decide per pair of events: return `true` to forbid the overlap. The function runs while dragging to compute live feedback, it must be pure and cheap. @default false */
|
|
88
|
+
preventEventOverlap?: PreventEventOverlap;
|
|
89
|
+
/** Called when a drop is rejected */
|
|
90
|
+
onEventPlacementRejected?: (data: ScheduleEventPlacementRejectedData) => void;
|
|
83
91
|
/** Called when any event drag starts */
|
|
84
92
|
onEventDragStart?: (event: ScheduleEventData) => void;
|
|
85
93
|
/** Called when any event drag ends */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BoxProps, DataAttributes, ElementProps, Factory, MantineRadius, ScrollAreaProps, StylesApiProps } from '@mantine/core';
|
|
2
2
|
import { ScheduleLabelsOverride } from '../../labels';
|
|
3
|
-
import { AnyDateValue, DateLabelFormat, DateStringValue, DateTimeStringValue, ScheduleEventData, ScheduleMode, ScheduleResourceData, ScheduleResourceGroup, ScheduleViewLevel } from '../../types';
|
|
3
|
+
import { AnyDateValue, DateLabelFormat, DateStringValue, DateTimeStringValue, PreventEventOverlap, ScheduleCanDropEventData, ScheduleCanDropExternalEventData, ScheduleCanResizeEventToData, ScheduleEventData, ScheduleEventPlacementRejectedData, ScheduleMode, ScheduleResourceData, ScheduleResourceGroup, ScheduleViewLevel } from '../../types';
|
|
4
4
|
import { MoreEventsProps, MoreEventsStylesNames } from '../MoreEvents/MoreEvents';
|
|
5
5
|
import { RenderEvent, RenderEventBody } from '../ScheduleEvent/ScheduleEvent';
|
|
6
6
|
import { CombinedScheduleHeaderStylesNames } from '../ScheduleHeader/ScheduleHeader';
|
|
@@ -135,6 +135,16 @@ export interface ResourcesDayViewProps extends BoxProps, StylesApiProps<Resource
|
|
|
135
135
|
}) => void;
|
|
136
136
|
/** Function to determine if event can be resized */
|
|
137
137
|
canResizeEvent?: (event: ScheduleEventData) => boolean;
|
|
138
|
+
/** Called while an event is dragged over a target to compute live feedback and again before the drop is committed, return `false` to reject the drop. Must be pure and cheap. */
|
|
139
|
+
canDropEvent?: (data: ScheduleCanDropEventData) => boolean;
|
|
140
|
+
/** Called while an external item is dragged over a target to compute live feedback and again before the drop is committed, return `false` to reject the drop. Must be pure and cheap. Only `dataTransfer.types` can be read while the drag is in progress. */
|
|
141
|
+
canDropExternalEvent?: (data: ScheduleCanDropExternalEventData) => boolean;
|
|
142
|
+
/** Called on every pointer move while an event is resized to compute live feedback and again before the resize is committed, return `false` to reject the new size. Must be pure and cheap. */
|
|
143
|
+
canResizeEventTo?: (data: ScheduleCanResizeEventToData) => boolean;
|
|
144
|
+
/** If set, drops and resizes that would make the event overlap another event are rejected. Pass a function to decide per pair of events: return `true` to forbid the overlap. The function runs while dragging and resizing to compute live feedback, it must be pure and cheap. @default false */
|
|
145
|
+
preventEventOverlap?: PreventEventOverlap;
|
|
146
|
+
/** Called when a drop or resize is rejected */
|
|
147
|
+
onEventPlacementRejected?: (data: ScheduleEventPlacementRejectedData) => void;
|
|
138
148
|
/** Snap step for resizing events, in minutes. Must divide evenly into an hour (e.g. `15`, `30`) or be a whole number of hours. When not set, `intervalMinutes` is used. @default intervalMinutes */
|
|
139
149
|
eventResizeInterval?: number;
|
|
140
150
|
/** Max number of generated recurring instances per recurring series @default 2000 */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BoxProps, DataAttributes, ElementProps, Factory, MantineRadius, ScrollAreaProps, StylesApiProps } from '@mantine/core';
|
|
2
2
|
import { ScheduleLabelsOverride } from '../../labels';
|
|
3
|
-
import { DateLabelFormat, DateStringValue, DateTimeStringValue, DayOfWeek, ScheduleEventData, ScheduleMode, ScheduleResourceData, ScheduleResourceGroup, ScheduleViewLevel } from '../../types';
|
|
3
|
+
import { DateLabelFormat, DateStringValue, DateTimeStringValue, DayOfWeek, PreventEventOverlap, ScheduleCanDropEventData, ScheduleCanDropExternalEventData, ScheduleCanResizeEventToData, ScheduleEventData, ScheduleEventPlacementRejectedData, ScheduleMode, ScheduleResourceData, ScheduleResourceGroup, ScheduleViewLevel } from '../../types';
|
|
4
4
|
import { MoreEventsProps, MoreEventsStylesNames } from '../MoreEvents/MoreEvents';
|
|
5
5
|
import { RenderEvent, RenderEventBody } from '../ScheduleEvent/ScheduleEvent';
|
|
6
6
|
import { MonthYearSelectProps } from '../ScheduleHeader/MonthYearSelect/MonthYearSelect';
|
|
@@ -97,6 +97,16 @@ export interface ResourcesMonthViewProps extends BoxProps, StylesApiProps<Resour
|
|
|
97
97
|
}) => void;
|
|
98
98
|
/** Function to determine if event can be resized */
|
|
99
99
|
canResizeEvent?: (event: ScheduleEventData) => boolean;
|
|
100
|
+
/** Called while an event is dragged over a target to compute live feedback and again before the drop is committed, return `false` to reject the drop. Must be pure and cheap. */
|
|
101
|
+
canDropEvent?: (data: ScheduleCanDropEventData) => boolean;
|
|
102
|
+
/** Called while an external item is dragged over a target to compute live feedback and again before the drop is committed, return `false` to reject the drop. Must be pure and cheap. Only `dataTransfer.types` can be read while the drag is in progress. */
|
|
103
|
+
canDropExternalEvent?: (data: ScheduleCanDropExternalEventData) => boolean;
|
|
104
|
+
/** Called on every pointer move while an event is resized to compute live feedback and again before the resize is committed, return `false` to reject the new size. Must be pure and cheap. */
|
|
105
|
+
canResizeEventTo?: (data: ScheduleCanResizeEventToData) => boolean;
|
|
106
|
+
/** If set, drops and resizes that would make the event overlap another event are rejected. Pass a function to decide per pair of events: return `true` to forbid the overlap. The function runs while dragging and resizing to compute live feedback, it must be pure and cheap. @default false */
|
|
107
|
+
preventEventOverlap?: PreventEventOverlap;
|
|
108
|
+
/** Called when a drop or resize is rejected */
|
|
109
|
+
onEventPlacementRejected?: (data: ScheduleEventPlacementRejectedData) => void;
|
|
100
110
|
/** Called when event is clicked */
|
|
101
111
|
onEventClick?: (event: ScheduleEventData, e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
102
112
|
/** If set, enables drag-to-select day ranges @default false */
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { BoxProps, ElementProps, Factory, MantineRadius, StylesApiProps } from '@mantine/core';
|
|
2
2
|
import { ScheduleLabelsOverride } from '../../labels';
|
|
3
|
-
import { DateStringValue, DateTimeStringValue, ScheduleEventData, ScheduleMode, ScheduleResourceData } from '../../types';
|
|
3
|
+
import { DateStringValue, DateTimeStringValue, PreventEventOverlap, ScheduleCanDropEventData, ScheduleCanDropExternalEventData, ScheduleCanResizeEventToData, ScheduleEventData, ScheduleEventPlacementRejectedData, ScheduleMode, ScheduleResourceData } from '../../types';
|
|
4
4
|
import { ResourcesDayViewProps, ResourcesDayViewStylesNames } from '../ResourcesDayView/ResourcesDayView';
|
|
5
5
|
import { ResourcesMonthViewProps, ResourcesMonthViewStylesNames } from '../ResourcesMonthView/ResourcesMonthView';
|
|
6
6
|
import { ResourcesWeekViewProps, ResourcesWeekViewStylesNames } from '../ResourcesWeekView/ResourcesWeekView';
|
|
7
7
|
import { RenderEventBody } from '../ScheduleEvent/ScheduleEvent';
|
|
8
8
|
export type ResourcesScheduleStylesNames = 'root' | ResourcesDayViewStylesNames | ResourcesWeekViewStylesNames | ResourcesMonthViewStylesNames;
|
|
9
9
|
export type ResourcesScheduleViewLevel = 'day' | 'week' | 'month';
|
|
10
|
-
type ResourcesScheduleCommonProps = 'date' | 'onDateChange' | 'resources' | 'events' | 'locale' | 'radius' | 'labels' | 'renderEventBody' | 'renderResourceLabel' | 'withEventsDragAndDrop' | 'onEventDrop' | 'canDragEvent' | 'onEventDragStart' | 'onEventDragEnd' | 'onTimeSlotClick' | 'onEventClick' | 'onDayClick' | 'withDragSlotSelect' | 'onSlotDragEnd' | 'view' | 'onViewChange' | 'mode' | 'onExternalEventDrop' | 'withEventResize' | 'onEventResize' | 'canResizeEvent' | 'recurrenceExpansionLimit' | 'withInteractiveBackgroundEvents';
|
|
10
|
+
type ResourcesScheduleCommonProps = 'date' | 'onDateChange' | 'resources' | 'events' | 'locale' | 'radius' | 'labels' | 'renderEventBody' | 'renderResourceLabel' | 'withEventsDragAndDrop' | 'onEventDrop' | 'canDragEvent' | 'onEventDragStart' | 'onEventDragEnd' | 'onTimeSlotClick' | 'onEventClick' | 'onDayClick' | 'withDragSlotSelect' | 'onSlotDragEnd' | 'view' | 'onViewChange' | 'mode' | 'onExternalEventDrop' | 'withEventResize' | 'onEventResize' | 'canResizeEvent' | 'canDropEvent' | 'canDropExternalEvent' | 'canResizeEventTo' | 'preventEventOverlap' | 'onEventPlacementRejected' | 'recurrenceExpansionLimit' | 'withInteractiveBackgroundEvents';
|
|
11
11
|
type ResourcesScheduleViewProps<T> = Partial<Omit<T, ResourcesScheduleCommonProps>>;
|
|
12
12
|
export interface ResourcesScheduleProps extends BoxProps, StylesApiProps<ResourcesScheduleFactory>, ElementProps<'div'> {
|
|
13
13
|
__staticSelector?: string;
|
|
@@ -95,6 +95,16 @@ export interface ResourcesScheduleProps extends BoxProps, StylesApiProps<Resourc
|
|
|
95
95
|
}) => void;
|
|
96
96
|
/** Function to determine if event can be resized */
|
|
97
97
|
canResizeEvent?: (event: ScheduleEventData) => boolean;
|
|
98
|
+
/** Called while an event is dragged over a target to compute live feedback and again before the drop is committed, return `false` to reject the drop. Must be pure and cheap. */
|
|
99
|
+
canDropEvent?: (data: ScheduleCanDropEventData) => boolean;
|
|
100
|
+
/** Called while an external item is dragged over a target to compute live feedback and again before the drop is committed, return `false` to reject the drop. Must be pure and cheap. Only `dataTransfer.types` can be read while the drag is in progress. */
|
|
101
|
+
canDropExternalEvent?: (data: ScheduleCanDropExternalEventData) => boolean;
|
|
102
|
+
/** Called on every pointer move while an event is resized to compute live feedback and again before the resize is committed, return `false` to reject the new size. Must be pure and cheap. */
|
|
103
|
+
canResizeEventTo?: (data: ScheduleCanResizeEventToData) => boolean;
|
|
104
|
+
/** If set, drops and resizes that would make the event overlap another event are rejected. Pass a function to decide per pair of events: return `true` to forbid the overlap. The function runs while dragging and resizing to compute live feedback, it must be pure and cheap. @default false */
|
|
105
|
+
preventEventOverlap?: PreventEventOverlap;
|
|
106
|
+
/** Called when a drop or resize is rejected */
|
|
107
|
+
onEventPlacementRejected?: (data: ScheduleEventPlacementRejectedData) => void;
|
|
98
108
|
/** If set, background events (`display: 'background'`) can be clicked and trigger `onEventClick`. Applies to `day` and `week` views only – `ResourcesMonthView` does not render background events separately. Combined with `withEventResize`, timed background events can also be resized by dragging their edges. @default false */
|
|
99
109
|
withInteractiveBackgroundEvents?: boolean;
|
|
100
110
|
/** Max number of generated recurring instances @default 2000 */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BoxProps, DataAttributes, ElementProps, Factory, MantineRadius, ScrollAreaProps, StylesApiProps } from '@mantine/core';
|
|
2
2
|
import { ScheduleLabelsOverride } from '../../labels';
|
|
3
|
-
import { AnyDateValue, DateLabelFormat, DateStringValue, DateTimeStringValue, DayOfWeek, ScheduleEventData, ScheduleMode, ScheduleResourceData, ScheduleResourceGroup, ScheduleViewLevel } from '../../types';
|
|
3
|
+
import { AnyDateValue, DateLabelFormat, DateStringValue, DateTimeStringValue, DayOfWeek, PreventEventOverlap, ScheduleCanDropEventData, ScheduleCanDropExternalEventData, ScheduleCanResizeEventToData, ScheduleEventData, ScheduleEventPlacementRejectedData, ScheduleMode, ScheduleResourceData, ScheduleResourceGroup, ScheduleViewLevel } from '../../types';
|
|
4
4
|
import { MoreEventsProps, MoreEventsStylesNames } from '../MoreEvents/MoreEvents';
|
|
5
5
|
import { RenderEvent, RenderEventBody } from '../ScheduleEvent/ScheduleEvent';
|
|
6
6
|
import { CombinedScheduleHeaderStylesNames } from '../ScheduleHeader/ScheduleHeader';
|
|
@@ -101,6 +101,16 @@ export interface ResourcesWeekViewProps extends BoxProps, StylesApiProps<Resourc
|
|
|
101
101
|
}) => void;
|
|
102
102
|
/** Function to determine if event can be resized */
|
|
103
103
|
canResizeEvent?: (event: ScheduleEventData) => boolean;
|
|
104
|
+
/** Called while an event is dragged over a target to compute live feedback and again before the drop is committed, return `false` to reject the drop. Must be pure and cheap. */
|
|
105
|
+
canDropEvent?: (data: ScheduleCanDropEventData) => boolean;
|
|
106
|
+
/** Called while an external item is dragged over a target to compute live feedback and again before the drop is committed, return `false` to reject the drop. Must be pure and cheap. Only `dataTransfer.types` can be read while the drag is in progress. */
|
|
107
|
+
canDropExternalEvent?: (data: ScheduleCanDropExternalEventData) => boolean;
|
|
108
|
+
/** Called on every pointer move while an event is resized to compute live feedback and again before the resize is committed, return `false` to reject the new size. Must be pure and cheap. */
|
|
109
|
+
canResizeEventTo?: (data: ScheduleCanResizeEventToData) => boolean;
|
|
110
|
+
/** If set, drops and resizes that would make the event overlap another event are rejected. Pass a function to decide per pair of events: return `true` to forbid the overlap. The function runs while dragging and resizing to compute live feedback, it must be pure and cheap. @default false */
|
|
111
|
+
preventEventOverlap?: PreventEventOverlap;
|
|
112
|
+
/** Called when a drop or resize is rejected */
|
|
113
|
+
onEventPlacementRejected?: (data: ScheduleEventPlacementRejectedData) => void;
|
|
104
114
|
/** Snap step for resizing events, in minutes. Must divide evenly into an hour (e.g. `15`, `30`) or be a whole number of hours. When not set, `intervalMinutes` is used. @default intervalMinutes */
|
|
105
115
|
eventResizeInterval?: number;
|
|
106
116
|
recurrenceExpansionLimit?: number;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BoxProps, ElementProps, Factory, MantineRadius, StylesApiProps } from '@mantine/core';
|
|
2
2
|
import { ScheduleLabelsOverride } from '../../labels';
|
|
3
|
-
import { DateStringValue, DateTimeStringValue, ScheduleEventData, ScheduleMode, ScheduleViewLevel } from '../../types';
|
|
3
|
+
import { DateStringValue, DateTimeStringValue, PreventEventOverlap, ScheduleCanDropEventData, ScheduleCanDropExternalEventData, ScheduleCanResizeEventToData, ScheduleEventData, ScheduleEventPlacementRejectedData, ScheduleMode, ScheduleViewLevel } from '../../types';
|
|
4
4
|
import { DayViewProps, DayViewStylesNames } from '../DayView/DayView';
|
|
5
5
|
import { MobileMonthViewProps, MobileMonthViewStylesNames } from '../MobileMonthView/MobileMonthView';
|
|
6
6
|
import { MonthViewProps, MonthViewStylesNames } from '../MonthView/MonthView';
|
|
@@ -9,7 +9,7 @@ import { WeekViewProps, WeekViewStylesNames } from '../WeekView/WeekView';
|
|
|
9
9
|
import { YearViewProps, YearViewStylesNames } from '../YearView/YearView';
|
|
10
10
|
export type ScheduleStylesNames = 'root' | 'desktopView' | 'mobileView' | DayViewStylesNames | WeekViewStylesNames | MonthViewStylesNames | YearViewStylesNames | MobileMonthViewStylesNames;
|
|
11
11
|
export type ScheduleLayout = 'default' | 'responsive';
|
|
12
|
-
type ScheduleCommonProps = 'date' | 'onDateChange' | 'events' | 'locale' | 'radius' | 'labels' | 'renderEventBody' | 'withEventsDragAndDrop' | 'onEventDrop' | 'canDragEvent' | 'onEventDragStart' | 'onEventDragEnd' | 'onTimeSlotClick' | 'onAllDaySlotClick' | 'onEventClick' | 'onDayClick' | 'onMonthClick' | 'withDragSlotSelect' | 'onSlotDragEnd' | 'view' | 'onViewChange' | 'mode' | 'withAgenda' | 'onExternalEventDrop' | 'withEventResize' | 'onEventResize' | 'canResizeEvent' | 'recurrenceExpansionLimit' | 'withInteractiveBackgroundEvents';
|
|
12
|
+
type ScheduleCommonProps = 'date' | 'onDateChange' | 'events' | 'locale' | 'radius' | 'labels' | 'renderEventBody' | 'withEventsDragAndDrop' | 'onEventDrop' | 'canDragEvent' | 'onEventDragStart' | 'onEventDragEnd' | 'onTimeSlotClick' | 'onAllDaySlotClick' | 'onEventClick' | 'onDayClick' | 'onMonthClick' | 'withDragSlotSelect' | 'onSlotDragEnd' | 'view' | 'onViewChange' | 'mode' | 'withAgenda' | 'onExternalEventDrop' | 'withEventResize' | 'onEventResize' | 'canResizeEvent' | 'canDropEvent' | 'canDropExternalEvent' | 'canResizeEventTo' | 'preventEventOverlap' | 'onEventPlacementRejected' | 'recurrenceExpansionLimit' | 'withInteractiveBackgroundEvents';
|
|
13
13
|
type ScheduleViewProps<T> = Partial<Omit<T, ScheduleCommonProps>>;
|
|
14
14
|
export interface ScheduleProps extends BoxProps, StylesApiProps<ScheduleFactory>, ElementProps<'div'> {
|
|
15
15
|
__staticSelector?: string;
|
|
@@ -84,6 +84,16 @@ export interface ScheduleProps extends BoxProps, StylesApiProps<ScheduleFactory>
|
|
|
84
84
|
}) => void;
|
|
85
85
|
/** Function to determine if event can be resized */
|
|
86
86
|
canResizeEvent?: (event: ScheduleEventData) => boolean;
|
|
87
|
+
/** Called while an event is dragged over a target to compute live feedback and again before the drop is committed, return `false` to reject the drop. Must be pure and cheap. */
|
|
88
|
+
canDropEvent?: (data: ScheduleCanDropEventData) => boolean;
|
|
89
|
+
/** Called while an external item is dragged over a target to compute live feedback and again before the drop is committed, return `false` to reject the drop. Must be pure and cheap. Only `dataTransfer.types` can be read while the drag is in progress. */
|
|
90
|
+
canDropExternalEvent?: (data: ScheduleCanDropExternalEventData) => boolean;
|
|
91
|
+
/** Called on every pointer move while an event is resized to compute live feedback and again before the resize is committed, return `false` to reject the new size. Must be pure and cheap. */
|
|
92
|
+
canResizeEventTo?: (data: ScheduleCanResizeEventToData) => boolean;
|
|
93
|
+
/** If set, drops and resizes that would make the event overlap another event are rejected. Pass a function to decide per pair of events: return `true` to forbid the overlap. The function runs while dragging and resizing to compute live feedback, it must be pure and cheap. @default false */
|
|
94
|
+
preventEventOverlap?: PreventEventOverlap;
|
|
95
|
+
/** Called when a drop or resize is rejected */
|
|
96
|
+
onEventPlacementRejected?: (data: ScheduleEventPlacementRejectedData) => void;
|
|
87
97
|
/** If set, background events (`display: 'background'`) can be clicked and trigger `onEventClick`. Applies to `day`, `week` and `month` views only – `YearView` and the `MobileMonthView` used by `layout="responsive"` on small screens do not render background events at all. Combined with `withEventResize`, timed background events in `day` and `week` views can also be resized by dragging their edges. @default false */
|
|
88
98
|
withInteractiveBackgroundEvents?: boolean;
|
|
89
99
|
/** Max number of generated recurring instances per recurring series @default 2000 */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BoxProps, DataAttributes, ElementProps, Factory, MantineRadius, ScrollAreaAutosizeProps, StylesApiProps } from '@mantine/core';
|
|
2
2
|
import { ScheduleLabelsOverride } from '../../labels';
|
|
3
|
-
import { AnyDateValue, DateLabelFormat, DateStringValue, DateTimeStringValue, DayOfWeek, ScheduleEventData, ScheduleEventOverlapMode, ScheduleMode, ScheduleViewLevel } from '../../types';
|
|
3
|
+
import { AnyDateValue, DateLabelFormat, DateStringValue, DateTimeStringValue, DayOfWeek, PreventEventOverlap, ScheduleCanDropEventData, ScheduleCanDropExternalEventData, ScheduleCanResizeEventToData, ScheduleEventData, ScheduleEventOverlapMode, ScheduleEventPlacementRejectedData, ScheduleMode, ScheduleViewLevel } from '../../types';
|
|
4
4
|
import { BusinessHoursValue } from '../../utils';
|
|
5
5
|
import { AgendaViewStylesNames } from '../AgendaView/AgendaView';
|
|
6
6
|
import { CurrentTimeIndicatorStylesNames } from '../CurrentTimeIndicator/CurrentTimeIndicator';
|
|
@@ -144,6 +144,16 @@ export interface WeekViewProps extends BoxProps, StylesApiProps<WeekViewFactory>
|
|
|
144
144
|
}) => void;
|
|
145
145
|
/** Function to determine if event can be resized */
|
|
146
146
|
canResizeEvent?: (event: ScheduleEventData) => boolean;
|
|
147
|
+
/** Called while an event is dragged over a target to compute live feedback and again before the drop is committed, return `false` to reject the drop. Must be pure and cheap. */
|
|
148
|
+
canDropEvent?: (data: ScheduleCanDropEventData) => boolean;
|
|
149
|
+
/** Called while an external item is dragged over a target to compute live feedback and again before the drop is committed, return `false` to reject the drop. Must be pure and cheap. Only `dataTransfer.types` can be read while the drag is in progress. */
|
|
150
|
+
canDropExternalEvent?: (data: ScheduleCanDropExternalEventData) => boolean;
|
|
151
|
+
/** Called on every pointer move while an event is resized to compute live feedback and again before the resize is committed, return `false` to reject the new size. Must be pure and cheap. */
|
|
152
|
+
canResizeEventTo?: (data: ScheduleCanResizeEventToData) => boolean;
|
|
153
|
+
/** If set, drops and resizes that would make the event overlap another event are rejected. Pass a function to decide per pair of events: return `true` to forbid the overlap. The function runs while dragging and resizing to compute live feedback, it must be pure and cheap. @default false */
|
|
154
|
+
preventEventOverlap?: PreventEventOverlap;
|
|
155
|
+
/** Called when a drop or resize is rejected */
|
|
156
|
+
onEventPlacementRejected?: (data: ScheduleEventPlacementRejectedData) => void;
|
|
147
157
|
/** Snap step for resizing events, in minutes. Must divide evenly into an hour (e.g. `15`, `30`) or be a whole number of hours. When not set, `intervalMinutes` is used. @default intervalMinutes */
|
|
148
158
|
eventResizeInterval?: number;
|
|
149
159
|
/** Max number of generated recurring instances per recurring series @default 2000 */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DateStringValue, DateTimeStringValue, ScheduleEventData, ScheduleMode } from '../types';
|
|
2
2
|
import { MonthResizeEdge } from '../utils/calculate-month-resize-dates/calculate-month-resize-dates';
|
|
3
|
-
|
|
3
|
+
import { EventResizeValidationOptions } from './validate-event-resize';
|
|
4
|
+
export interface UseDayGridEventResizeInput extends EventResizeValidationOptions {
|
|
4
5
|
enabled?: boolean;
|
|
5
6
|
mode?: ScheduleMode;
|
|
6
7
|
onEventResize?: (data: {
|
|
@@ -23,7 +24,7 @@ export interface DayGridResizeStartInput {
|
|
|
23
24
|
* The target day is resolved by hit-testing `cells` with the pointer position, so
|
|
24
25
|
* the hook works for any layout that renders one element per day in a single row.
|
|
25
26
|
*/
|
|
26
|
-
export declare function useDayGridEventResize({ enabled, mode, onEventResize, canResizeEvent, }: UseDayGridEventResizeInput): {
|
|
27
|
+
export declare function useDayGridEventResize({ enabled, mode, onEventResize, canResizeEvent, events, preventEventOverlap, canResizeEventTo, onEventPlacementRejected, }: UseDayGridEventResizeInput): {
|
|
27
28
|
handleResizeStart: ({ event, edge, cells, days, pointerEvent }: DayGridResizeStartInput) => void;
|
|
28
29
|
isResizing: boolean;
|
|
29
30
|
resizingEventId: string | number | null;
|
|
@@ -32,4 +33,5 @@ export declare function useDayGridEventResize({ enabled, mode, onEventResize, ca
|
|
|
32
33
|
previewEnd: string | null;
|
|
33
34
|
isResizableEvent: (event: ScheduleEventData) => boolean;
|
|
34
35
|
wasResizing: () => boolean;
|
|
36
|
+
resizeValid: boolean;
|
|
35
37
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DragContextValue } from '../components/DragContext/DragContext';
|
|
2
|
-
import { DateTimeStringValue, ScheduleEventData, ScheduleMode } from '../types';
|
|
2
|
+
import { DateTimeStringValue, PreventEventOverlap, ScheduleCanDropEventData, ScheduleCanDropExternalEventData, ScheduleEventData, ScheduleEventPlacementRejectedData, ScheduleMode } from '../types';
|
|
3
3
|
export interface DragPreview<T = any> {
|
|
4
4
|
start: DateTimeStringValue;
|
|
5
5
|
end: DateTimeStringValue;
|
|
@@ -33,6 +33,20 @@ export interface UseDragDropHandlersOptions<T = any> {
|
|
|
33
33
|
};
|
|
34
34
|
/** Called when an external item is dropped onto the schedule */
|
|
35
35
|
onExternalDrop?: (e: React.DragEvent, target: T) => void;
|
|
36
|
+
/** Events the candidate range is checked against when `preventEventOverlap` is set */
|
|
37
|
+
events?: ScheduleEventData[];
|
|
38
|
+
/** If set, drops that would make the event overlap another event are rejected */
|
|
39
|
+
preventEventOverlap?: PreventEventOverlap;
|
|
40
|
+
/** Called on every `dragover` to compute live feedback and again before the drop is committed, return `false` to reject the drop */
|
|
41
|
+
canDropEvent?: (data: ScheduleCanDropEventData) => boolean;
|
|
42
|
+
/** Called on every `dragover` of an external item to compute live feedback and again before the drop is committed, return `false` to reject the drop */
|
|
43
|
+
canDropExternalEvent?: (data: ScheduleCanDropExternalEventData) => boolean;
|
|
44
|
+
/** Called when a drop is rejected */
|
|
45
|
+
onEventPlacementRejected?: (data: ScheduleEventPlacementRejectedData) => void;
|
|
46
|
+
/** Resolves the target resource from the drop target, used by `Resources*` views */
|
|
47
|
+
getTargetResourceId?: (target: T) => string | number | undefined;
|
|
48
|
+
/** Resolves the datetime an external item would be dropped at, `null` when the target has no datetime */
|
|
49
|
+
getExternalDropDateTime?: (target: T) => DateTimeStringValue | null;
|
|
36
50
|
}
|
|
37
51
|
export interface DragDropHandlers<T = any> {
|
|
38
52
|
/** Context value for DragContext.Provider */
|
|
@@ -57,6 +71,8 @@ export interface DragDropHandlers<T = any> {
|
|
|
57
71
|
dragPreview: DragPreview<T> | null;
|
|
58
72
|
/** Sets the current drag preview */
|
|
59
73
|
setDragPreview: (preview: DragPreview<T> | null) => void;
|
|
74
|
+
/** False while the pointer is over a target that would be rejected */
|
|
75
|
+
dropValid: boolean;
|
|
60
76
|
}
|
|
61
77
|
/**
|
|
62
78
|
* Hook that provides unified drag-drop handlers for Schedule views.
|