@mantine/schedule 9.6.2 → 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/MobileMonthView/get-mobile-month-view-events.cjs +10 -2
- package/cjs/components/MobileMonthView/get-mobile-month-view-events.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/MobileMonthView/get-mobile-month-view-events.mjs +10 -2
- package/esm/components/MobileMonthView/get-mobile-month-view-events.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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
3
3
|
const require_get_index_from_drag_point = require("../utils/get-index-from-drag-point/get-index-from-drag-point.cjs");
|
|
4
|
+
const require_validate_event_resize = require("./validate-event-resize.cjs");
|
|
4
5
|
const require_calculate_month_resize_dates = require("../utils/calculate-month-resize-dates/calculate-month-resize-dates.cjs");
|
|
5
6
|
let dayjs = require("dayjs");
|
|
6
7
|
dayjs = require_runtime.__toESM(dayjs);
|
|
@@ -11,11 +12,23 @@ let react = require("react");
|
|
|
11
12
|
* The target day is resolved by hit-testing `cells` with the pointer position, so
|
|
12
13
|
* the hook works for any layout that renders one element per day in a single row.
|
|
13
14
|
*/
|
|
14
|
-
function useDayGridEventResize({ enabled = false, mode = "default", onEventResize, canResizeEvent }) {
|
|
15
|
+
function useDayGridEventResize({ enabled = false, mode = "default", onEventResize, canResizeEvent, events, preventEventOverlap, canResizeEventTo, onEventPlacementRejected }) {
|
|
15
16
|
const [resizeState, setResizeState] = (0, react.useState)(null);
|
|
17
|
+
const [resizeValid, setResizeValid] = (0, react.useState)(true);
|
|
16
18
|
const resizeRef = (0, react.useRef)(null);
|
|
17
19
|
const justResizedRef = (0, react.useRef)(false);
|
|
18
20
|
const stableOnEventResize = (0, react.useEffectEvent)(onEventResize || (() => {}));
|
|
21
|
+
const stableOnEventPlacementRejected = (0, react.useEffectEvent)(onEventPlacementRejected || (() => {}));
|
|
22
|
+
const validateResize = (0, react.useEffectEvent)((state) => require_validate_event_resize.validateEventResize({
|
|
23
|
+
event: state.event,
|
|
24
|
+
start: state.start,
|
|
25
|
+
end: state.end,
|
|
26
|
+
edge: state.edge,
|
|
27
|
+
events,
|
|
28
|
+
preventEventOverlap,
|
|
29
|
+
canResizeEventTo,
|
|
30
|
+
resourceId: state.event.resourceId
|
|
31
|
+
}));
|
|
19
32
|
const handleResizeStart = (0, react.useCallback)(({ event, edge, cells, days, pointerEvent }) => {
|
|
20
33
|
if (!enabled || mode === "static") return;
|
|
21
34
|
pointerEvent.preventDefault();
|
|
@@ -36,6 +49,7 @@ function useDayGridEventResize({ enabled = false, mode = "default", onEventResiz
|
|
|
36
49
|
};
|
|
37
50
|
resizeRef.current = state;
|
|
38
51
|
setResizeState(state);
|
|
52
|
+
setResizeValid(true);
|
|
39
53
|
}, [enabled, mode]);
|
|
40
54
|
const isResizing = resizeState !== null;
|
|
41
55
|
(0, react.useEffect)(() => {
|
|
@@ -63,10 +77,12 @@ function useDayGridEventResize({ enabled = false, mode = "default", onEventResiz
|
|
|
63
77
|
end
|
|
64
78
|
};
|
|
65
79
|
setResizeState(resizeRef.current);
|
|
80
|
+
setResizeValid(validateResize(resizeRef.current).valid);
|
|
66
81
|
};
|
|
67
82
|
const endGesture = () => {
|
|
68
83
|
resizeRef.current = null;
|
|
69
84
|
setResizeState(null);
|
|
85
|
+
setResizeValid(true);
|
|
70
86
|
justResizedRef.current = true;
|
|
71
87
|
requestAnimationFrame(() => {
|
|
72
88
|
justResizedRef.current = false;
|
|
@@ -74,12 +90,25 @@ function useDayGridEventResize({ enabled = false, mode = "default", onEventResiz
|
|
|
74
90
|
};
|
|
75
91
|
const handlePointerUp = () => {
|
|
76
92
|
const state = resizeRef.current;
|
|
77
|
-
if (state && (state.start !== state.originalStart || state.end !== state.originalEnd))
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
93
|
+
if (state && (state.start !== state.originalStart || state.end !== state.originalEnd)) {
|
|
94
|
+
const { valid, conflicts, reason } = validateResize(state);
|
|
95
|
+
if (valid) stableOnEventResize({
|
|
96
|
+
eventId: state.eventId,
|
|
97
|
+
newStart: state.start,
|
|
98
|
+
newEnd: state.end,
|
|
99
|
+
event: state.event
|
|
100
|
+
});
|
|
101
|
+
else stableOnEventPlacementRejected({
|
|
102
|
+
action: "resize",
|
|
103
|
+
event: state.event,
|
|
104
|
+
start: state.start,
|
|
105
|
+
end: state.end,
|
|
106
|
+
edge: state.edge,
|
|
107
|
+
resourceId: state.event.resourceId,
|
|
108
|
+
conflicts,
|
|
109
|
+
reason
|
|
110
|
+
});
|
|
111
|
+
}
|
|
83
112
|
endGesture();
|
|
84
113
|
};
|
|
85
114
|
document.addEventListener("pointermove", handlePointerMove);
|
|
@@ -110,7 +139,8 @@ function useDayGridEventResize({ enabled = false, mode = "default", onEventResiz
|
|
|
110
139
|
previewStart: resizeState?.start ?? null,
|
|
111
140
|
previewEnd: resizeState?.end ?? null,
|
|
112
141
|
isResizableEvent,
|
|
113
|
-
wasResizing
|
|
142
|
+
wasResizing,
|
|
143
|
+
resizeValid
|
|
114
144
|
};
|
|
115
145
|
}
|
|
116
146
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-day-grid-event-resize.cjs","names":["useState","useRef","useEffectEvent","useCallback","getIndexFromDragPoint","calculateMonthResizeDates"],"sources":["../../src/hooks/use-day-grid-event-resize.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { useCallback, useEffect, useEffectEvent, useRef, useState } from 'react';\nimport { DateStringValue, DateTimeStringValue, ScheduleEventData, ScheduleMode } from '../types';\nimport {\n calculateMonthResizeDates,\n MonthResizeEdge,\n} from '../utils/calculate-month-resize-dates/calculate-month-resize-dates';\nimport { getIndexFromDragPoint } from '../utils/get-index-from-drag-point/get-index-from-drag-point';\n\ninterface ResizeState {\n eventId: string | number;\n event: ScheduleEventData;\n edge: MonthResizeEdge;\n cells: (HTMLElement | null)[];\n days: DateStringValue[];\n dayIndex: number;\n originalStart: DateTimeStringValue;\n originalEnd: DateTimeStringValue;\n start: DateTimeStringValue;\n end: DateTimeStringValue;\n}\n\nexport interface UseDayGridEventResizeInput {\n enabled?: boolean;\n mode?: ScheduleMode;\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 interface DayGridResizeStartInput {\n event: ScheduleEventData;\n edge: MonthResizeEdge;\n cells: (HTMLElement | null)[];\n days: DateStringValue[];\n pointerEvent: React.PointerEvent;\n}\n\n/**\n * Resizes an event by dragging its start or end edge across a strip of day cells.\n * The target day is resolved by hit-testing `cells` with the pointer position, so\n * the hook works for any layout that renders one element per day in a single row.\n */\nexport function useDayGridEventResize({\n enabled = false,\n mode = 'default',\n onEventResize,\n canResizeEvent,\n}: UseDayGridEventResizeInput) {\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 handleResizeStart = useCallback(\n ({ event, edge, cells, days, pointerEvent }: DayGridResizeStartInput) => {\n if (!enabled || mode === 'static') {\n return;\n }\n\n pointerEvent.preventDefault();\n pointerEvent.stopPropagation();\n\n const originalStart = dayjs(event.start).format('YYYY-MM-DD HH:mm:ss');\n const originalEnd = dayjs(event.end).format('YYYY-MM-DD HH:mm:ss');\n\n const state: ResizeState = {\n eventId: event.id,\n event,\n edge,\n cells,\n days,\n dayIndex: -1,\n originalStart,\n originalEnd,\n start: originalStart,\n end: originalEnd,\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 dayIndex = getIndexFromDragPoint(state.cells, e.clientX);\n if (dayIndex === null || dayIndex === state.dayIndex) {\n return;\n }\n\n const targetDay = state.days[dayIndex];\n if (!targetDay) {\n return;\n }\n\n const { start, end } = calculateMonthResizeDates({\n event: state.event,\n edge: state.edge,\n targetDay,\n });\n\n resizeRef.current = { ...state, dayIndex, start, end };\n setResizeState(resizeRef.current);\n };\n\n const endGesture = () => {\n resizeRef.current = null;\n setResizeState(null);\n justResizedRef.current = true;\n requestAnimationFrame(() => {\n justResizedRef.current = false;\n });\n };\n\n const handlePointerUp = () => {\n const state = resizeRef.current;\n if (state && (state.start !== state.originalStart || state.end !== state.originalEnd)) {\n stableOnEventResize({\n
|
|
1
|
+
{"version":3,"file":"use-day-grid-event-resize.cjs","names":["useState","useRef","useEffectEvent","validateEventResize","useCallback","getIndexFromDragPoint","calculateMonthResizeDates"],"sources":["../../src/hooks/use-day-grid-event-resize.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { useCallback, useEffect, useEffectEvent, useRef, useState } from 'react';\nimport { DateStringValue, DateTimeStringValue, ScheduleEventData, ScheduleMode } from '../types';\nimport {\n calculateMonthResizeDates,\n MonthResizeEdge,\n} from '../utils/calculate-month-resize-dates/calculate-month-resize-dates';\nimport { getIndexFromDragPoint } from '../utils/get-index-from-drag-point/get-index-from-drag-point';\nimport { EventResizeValidationOptions, validateEventResize } from './validate-event-resize';\n\ninterface ResizeState {\n eventId: string | number;\n event: ScheduleEventData;\n edge: MonthResizeEdge;\n cells: (HTMLElement | null)[];\n days: DateStringValue[];\n dayIndex: number;\n originalStart: DateTimeStringValue;\n originalEnd: DateTimeStringValue;\n start: DateTimeStringValue;\n end: DateTimeStringValue;\n}\n\nexport interface UseDayGridEventResizeInput extends EventResizeValidationOptions {\n enabled?: boolean;\n mode?: ScheduleMode;\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 interface DayGridResizeStartInput {\n event: ScheduleEventData;\n edge: MonthResizeEdge;\n cells: (HTMLElement | null)[];\n days: DateStringValue[];\n pointerEvent: React.PointerEvent;\n}\n\n/**\n * Resizes an event by dragging its start or end edge across a strip of day cells.\n * The target day is resolved by hit-testing `cells` with the pointer position, so\n * the hook works for any layout that renders one element per day in a single row.\n */\nexport function useDayGridEventResize({\n enabled = false,\n mode = 'default',\n onEventResize,\n canResizeEvent,\n events,\n preventEventOverlap,\n canResizeEventTo,\n onEventPlacementRejected,\n}: UseDayGridEventResizeInput) {\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((state: ResizeState) =>\n validateEventResize({\n event: state.event,\n start: state.start,\n end: state.end,\n edge: state.edge,\n events,\n preventEventOverlap,\n canResizeEventTo,\n resourceId: state.event.resourceId,\n })\n );\n\n const handleResizeStart = useCallback(\n ({ event, edge, cells, days, pointerEvent }: DayGridResizeStartInput) => {\n if (!enabled || mode === 'static') {\n return;\n }\n\n pointerEvent.preventDefault();\n pointerEvent.stopPropagation();\n\n const originalStart = dayjs(event.start).format('YYYY-MM-DD HH:mm:ss');\n const originalEnd = dayjs(event.end).format('YYYY-MM-DD HH:mm:ss');\n\n const state: ResizeState = {\n eventId: event.id,\n event,\n edge,\n cells,\n days,\n dayIndex: -1,\n originalStart,\n originalEnd,\n start: originalStart,\n end: originalEnd,\n };\n\n resizeRef.current = state;\n setResizeState(state);\n setResizeValid(true);\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 dayIndex = getIndexFromDragPoint(state.cells, e.clientX);\n if (dayIndex === null || dayIndex === state.dayIndex) {\n return;\n }\n\n const targetDay = state.days[dayIndex];\n if (!targetDay) {\n return;\n }\n\n const { start, end } = calculateMonthResizeDates({\n event: state.event,\n edge: state.edge,\n targetDay,\n });\n\n resizeRef.current = { ...state, dayIndex, start, end };\n setResizeState(resizeRef.current);\n setResizeValid(validateResize(resizeRef.current).valid);\n };\n\n const endGesture = () => {\n resizeRef.current = null;\n setResizeState(null);\n setResizeValid(true);\n justResizedRef.current = true;\n requestAnimationFrame(() => {\n justResizedRef.current = false;\n });\n };\n\n const handlePointerUp = () => {\n const state = resizeRef.current;\n if (state && (state.start !== state.originalStart || state.end !== state.originalEnd)) {\n const { valid, conflicts, reason } = validateResize(state);\n\n if (valid) {\n stableOnEventResize({\n eventId: state.eventId,\n newStart: state.start,\n newEnd: state.end,\n event: state.event,\n });\n } else {\n stableOnEventPlacementRejected({\n action: 'resize',\n event: state.event,\n start: state.start,\n end: state.end,\n edge: state.edge,\n resourceId: state.event.resourceId,\n conflicts,\n reason: reason!,\n });\n }\n }\n\n endGesture();\n };\n\n document.addEventListener('pointermove', handlePointerMove);\n document.addEventListener('pointerup', handlePointerUp);\n document.addEventListener('pointercancel', endGesture);\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 document.removeEventListener('pointercancel', endGesture);\n };\n }, [isResizing]);\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 resizingEdge: resizeState?.edge ?? null,\n previewStart: resizeState?.start ?? null,\n previewEnd: resizeState?.end ?? null,\n isResizableEvent,\n wasResizing,\n resizeValid,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
const require_is_event_placement_allowed = require("../utils/is-event-placement-allowed/is-event-placement-allowed.cjs");
|
|
3
4
|
const require_use_drag_state = require("./use-drag-state.cjs");
|
|
4
5
|
let dayjs = require("dayjs");
|
|
5
6
|
dayjs = require_runtime.__toESM(dayjs);
|
|
6
7
|
let react = require("react");
|
|
7
8
|
//#region packages/@mantine/schedule/src/hooks/use-drag-drop-handlers.ts
|
|
9
|
+
const DATE_TIME_FORMAT = "YYYY-MM-DD HH:mm:ss";
|
|
8
10
|
/**
|
|
9
11
|
* Hook that provides unified drag-drop handlers for Schedule views.
|
|
10
12
|
* Handles drag state management and event drops across Day, Week, and Month views.
|
|
@@ -12,18 +14,97 @@ let react = require("react");
|
|
|
12
14
|
* @template T - Type of the drop target (e.g., slot index, day string, etc.)
|
|
13
15
|
*/
|
|
14
16
|
function useDragDropHandlers(options) {
|
|
15
|
-
const { enabled, mode, onEventDrop, canDragEvent, onEventDragStart, onEventDragEnd, calculateDropTarget, onExternalDrop } = options;
|
|
17
|
+
const { enabled, mode, onEventDrop, canDragEvent, onEventDragStart, onEventDragEnd, calculateDropTarget, onExternalDrop, events, preventEventOverlap, canDropEvent, canDropExternalEvent, onEventPlacementRejected, getTargetResourceId, getExternalDropDateTime } = options;
|
|
16
18
|
const stableOnEventDrop = (0, react.useEffectEvent)(onEventDrop || (() => {}));
|
|
19
|
+
const stableOnEventPlacementRejected = (0, react.useEffectEvent)(onEventPlacementRejected || (() => {}));
|
|
17
20
|
const stableOnEventDragStart = (0, react.useEffectEvent)(onEventDragStart || (() => {}));
|
|
18
21
|
const stableOnEventDragEnd = (0, react.useEffectEvent)(onEventDragEnd || (() => {}));
|
|
19
22
|
const stableOnExternalDrop = (0, react.useEffectEvent)(onExternalDrop || (() => {}));
|
|
20
23
|
const dragState = require_use_drag_state.useDragState();
|
|
21
24
|
const [dropTarget, setDropTarget] = (0, react.useState)(null);
|
|
22
25
|
const [dragPreview, setDragPreview] = (0, react.useState)(null);
|
|
26
|
+
const [dropValid, setDropValid] = (0, react.useState)(true);
|
|
27
|
+
const validateDrop = (0, react.useCallback)((target, draggedEvent) => {
|
|
28
|
+
const range = calculateDropTarget(target, draggedEvent);
|
|
29
|
+
const start = (0, dayjs.default)(range.start).format(DATE_TIME_FORMAT);
|
|
30
|
+
const end = (0, dayjs.default)(range.end).format(DATE_TIME_FORMAT);
|
|
31
|
+
const resourceId = getTargetResourceId?.(target);
|
|
32
|
+
const { allowed, conflicts } = require_is_event_placement_allowed.isEventPlacementAllowed({
|
|
33
|
+
event: draggedEvent,
|
|
34
|
+
start,
|
|
35
|
+
end,
|
|
36
|
+
events: events || [],
|
|
37
|
+
preventEventOverlap,
|
|
38
|
+
resourceId
|
|
39
|
+
});
|
|
40
|
+
if (!allowed) return {
|
|
41
|
+
valid: false,
|
|
42
|
+
start,
|
|
43
|
+
end,
|
|
44
|
+
resourceId,
|
|
45
|
+
conflicts,
|
|
46
|
+
reason: "overlap"
|
|
47
|
+
};
|
|
48
|
+
if (canDropEvent && canDropEvent({
|
|
49
|
+
event: draggedEvent,
|
|
50
|
+
start,
|
|
51
|
+
end,
|
|
52
|
+
resourceId
|
|
53
|
+
}) === false) return {
|
|
54
|
+
valid: false,
|
|
55
|
+
start,
|
|
56
|
+
end,
|
|
57
|
+
resourceId,
|
|
58
|
+
conflicts: [],
|
|
59
|
+
reason: "rejected"
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
valid: true,
|
|
63
|
+
start,
|
|
64
|
+
end,
|
|
65
|
+
resourceId,
|
|
66
|
+
conflicts: [],
|
|
67
|
+
reason: null
|
|
68
|
+
};
|
|
69
|
+
}, [
|
|
70
|
+
calculateDropTarget,
|
|
71
|
+
getTargetResourceId,
|
|
72
|
+
events,
|
|
73
|
+
preventEventOverlap,
|
|
74
|
+
canDropEvent
|
|
75
|
+
]);
|
|
76
|
+
const validateExternalDrop = (0, react.useCallback)((target, dataTransfer) => {
|
|
77
|
+
if (!canDropExternalEvent || !getExternalDropDateTime) return {
|
|
78
|
+
valid: true,
|
|
79
|
+
start: void 0,
|
|
80
|
+
resourceId: void 0
|
|
81
|
+
};
|
|
82
|
+
const start = getExternalDropDateTime(target);
|
|
83
|
+
if (start === null) return {
|
|
84
|
+
valid: true,
|
|
85
|
+
start: void 0,
|
|
86
|
+
resourceId: void 0
|
|
87
|
+
};
|
|
88
|
+
const resourceId = getTargetResourceId?.(target);
|
|
89
|
+
return {
|
|
90
|
+
valid: canDropExternalEvent({
|
|
91
|
+
dataTransfer,
|
|
92
|
+
start,
|
|
93
|
+
resourceId
|
|
94
|
+
}) !== false,
|
|
95
|
+
start,
|
|
96
|
+
resourceId
|
|
97
|
+
};
|
|
98
|
+
}, [
|
|
99
|
+
canDropExternalEvent,
|
|
100
|
+
getExternalDropDateTime,
|
|
101
|
+
getTargetResourceId
|
|
102
|
+
]);
|
|
23
103
|
const handleDragEnd = (0, react.useCallback)(() => {
|
|
24
104
|
dragState.endDrag();
|
|
25
105
|
setDropTarget(null);
|
|
26
106
|
setDragPreview(null);
|
|
107
|
+
setDropValid(true);
|
|
27
108
|
stableOnEventDragEnd();
|
|
28
109
|
}, [dragState]);
|
|
29
110
|
const handleDragStart = (0, react.useCallback)((event) => {
|
|
@@ -45,14 +126,20 @@ function useDragDropHandlers(options) {
|
|
|
45
126
|
if (isInternalDrag && !enabled) return;
|
|
46
127
|
if (!isInternalDrag && !onExternalDrop) return;
|
|
47
128
|
event.preventDefault();
|
|
129
|
+
const draggedEvent = dragState.state.draggedEvent;
|
|
130
|
+
const valid = isInternalDrag && draggedEvent ? validateDrop(target, draggedEvent).valid : validateExternalDrop(target, event.dataTransfer).valid;
|
|
131
|
+
setDropValid(valid);
|
|
48
132
|
event.dataTransfer.dropEffect = isInternalDrag ? "move" : "copy";
|
|
49
133
|
setDropTarget(target);
|
|
50
134
|
}, [
|
|
51
135
|
enabled,
|
|
52
136
|
mode,
|
|
53
137
|
dragState.state.isDragging,
|
|
138
|
+
dragState.state.draggedEvent,
|
|
54
139
|
onExternalDrop,
|
|
55
|
-
handleDragEnd
|
|
140
|
+
handleDragEnd,
|
|
141
|
+
validateDrop,
|
|
142
|
+
validateExternalDrop
|
|
56
143
|
]);
|
|
57
144
|
const handleDragLeave = (0, react.useCallback)((event) => {
|
|
58
145
|
if (event?.currentTarget) {
|
|
@@ -61,39 +148,67 @@ function useDragDropHandlers(options) {
|
|
|
61
148
|
}
|
|
62
149
|
setDropTarget(null);
|
|
63
150
|
setDragPreview(null);
|
|
151
|
+
setDropValid(true);
|
|
64
152
|
}, []);
|
|
65
153
|
const handleDrop = (0, react.useCallback)((event, target) => {
|
|
66
154
|
event.preventDefault();
|
|
67
155
|
const isInternalDrag = dragState.state.isDragging && event.dataTransfer.types.includes("application/json");
|
|
68
156
|
if (isInternalDrag && enabled && dragState.state.draggedEvent && onEventDrop) {
|
|
69
|
-
const
|
|
157
|
+
const draggedEvent = dragState.state.draggedEvent;
|
|
158
|
+
const { valid, start, end, resourceId, conflicts, reason } = validateDrop(target, draggedEvent);
|
|
159
|
+
if (!valid) {
|
|
160
|
+
stableOnEventPlacementRejected({
|
|
161
|
+
action: "drop",
|
|
162
|
+
event: draggedEvent,
|
|
163
|
+
start,
|
|
164
|
+
end,
|
|
165
|
+
resourceId,
|
|
166
|
+
conflicts,
|
|
167
|
+
reason
|
|
168
|
+
});
|
|
169
|
+
handleDragEnd();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
70
172
|
stableOnEventDrop({
|
|
71
173
|
eventId: dragState.state.draggedEventId,
|
|
72
|
-
newStart:
|
|
73
|
-
newEnd:
|
|
74
|
-
event:
|
|
174
|
+
newStart: start,
|
|
175
|
+
newEnd: end,
|
|
176
|
+
event: draggedEvent
|
|
75
177
|
});
|
|
76
178
|
handleDragEnd();
|
|
77
179
|
return;
|
|
78
180
|
}
|
|
79
181
|
if (!isInternalDrag && onExternalDrop) {
|
|
80
182
|
if (dragState.state.isDragging) handleDragEnd();
|
|
81
|
-
|
|
183
|
+
const external = validateExternalDrop(target, event.dataTransfer);
|
|
184
|
+
if (!external.valid) stableOnEventPlacementRejected({
|
|
185
|
+
action: "external-drop",
|
|
186
|
+
dataTransfer: event.dataTransfer,
|
|
187
|
+
start: external.start,
|
|
188
|
+
resourceId: external.resourceId,
|
|
189
|
+
conflicts: [],
|
|
190
|
+
reason: "rejected"
|
|
191
|
+
});
|
|
192
|
+
else stableOnExternalDrop(event, target);
|
|
82
193
|
setDropTarget(null);
|
|
83
194
|
setDragPreview(null);
|
|
195
|
+
setDropValid(true);
|
|
84
196
|
return;
|
|
85
197
|
}
|
|
86
198
|
setDropTarget(null);
|
|
87
199
|
setDragPreview(null);
|
|
200
|
+
setDropValid(true);
|
|
88
201
|
}, [
|
|
89
202
|
enabled,
|
|
90
203
|
dragState.state,
|
|
91
204
|
onEventDrop,
|
|
92
205
|
onExternalDrop,
|
|
93
|
-
calculateDropTarget,
|
|
94
206
|
handleDragEnd,
|
|
207
|
+
validateDrop,
|
|
208
|
+
validateExternalDrop,
|
|
95
209
|
stableOnEventDrop,
|
|
96
|
-
stableOnExternalDrop
|
|
210
|
+
stableOnExternalDrop,
|
|
211
|
+
stableOnEventPlacementRejected
|
|
97
212
|
]);
|
|
98
213
|
const isDraggableEvent = (0, react.useCallback)((event) => {
|
|
99
214
|
return enabled && mode !== "static" && event.display !== "background" && (canDragEvent ? canDragEvent(event) : true);
|
|
@@ -125,7 +240,8 @@ function useDragDropHandlers(options) {
|
|
|
125
240
|
isDraggableEvent,
|
|
126
241
|
isDropTarget,
|
|
127
242
|
dragPreview,
|
|
128
|
-
setDragPreview
|
|
243
|
+
setDragPreview,
|
|
244
|
+
dropValid
|
|
129
245
|
};
|
|
130
246
|
}
|
|
131
247
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-drag-drop-handlers.cjs","names":["useEffectEvent","useDragState","useState","useCallback"],"sources":["../../src/hooks/use-drag-drop-handlers.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { useCallback, useEffectEvent, useState } from 'react';\nimport { DragContextValue } from '../components/DragContext/DragContext';\nimport { DateTimeStringValue, ScheduleEventData, ScheduleMode } from '../types';\nimport { useDragState } from './use-drag-state';\n\nexport interface DragPreview<T = any> {\n start: DateTimeStringValue;\n end: DateTimeStringValue;\n target: T;\n}\n\nexport interface UseDragDropHandlersOptions<T = any> {\n /** Whether drag and drop is enabled */\n enabled: boolean;\n\n /** Schedule interaction mode */\n mode: ScheduleMode;\n\n /** Called when event is dropped at new location */\n onEventDrop?: (data: {\n eventId: string | number;\n newStart: DateTimeStringValue;\n newEnd: DateTimeStringValue;\n event: ScheduleEventData;\n }) => void;\n\n /** Function to determine if event can be dragged */\n canDragEvent?: (event: ScheduleEventData) => boolean;\n\n /** Called when any event drag starts */\n onEventDragStart?: (event: ScheduleEventData) => void;\n\n /** Called when any event drag ends */\n onEventDragEnd?: () => void;\n\n /**\n * Function to calculate drop target dates from drop location.\n * Receives the target location and the dragged event.\n */\n calculateDropTarget: (target: T, draggedEvent: ScheduleEventData) => { start: Date; end: Date };\n\n /** Called when an external item is dropped onto the schedule */\n onExternalDrop?: (e: React.DragEvent, target: T) => void;\n}\n\nexport interface DragDropHandlers<T = any> {\n /** Context value for DragContext.Provider */\n dragContextValue: DragContextValue;\n\n /** Current drop target */\n dropTarget: T | null;\n\n /** Handle drag start event */\n handleDragStart: (event: ScheduleEventData) => void;\n\n /** Handle drag end event */\n handleDragEnd: () => void;\n\n /** Handle drag over event */\n handleDragOver: (e: React.DragEvent, target: T) => void;\n\n /** Handle drag leave event */\n handleDragLeave: (event?: React.DragEvent) => void;\n\n /** Handle drop event */\n handleDrop: (e: React.DragEvent, target: T) => void;\n\n /** Check if event is draggable */\n isDraggableEvent: (event: ScheduleEventData) => boolean;\n\n /** Check if target is the current drop target */\n isDropTarget: (target: T) => boolean;\n\n /** Snapped drop preview used to render the drag ghost, or `null` when not dragging */\n dragPreview: DragPreview<T> | null;\n\n /** Sets the current drag preview */\n setDragPreview: (preview: DragPreview<T> | null) => void;\n}\n\n/**\n * Hook that provides unified drag-drop handlers for Schedule views.\n * Handles drag state management and event drops across Day, Week, and Month views.\n *\n * @template T - Type of the drop target (e.g., slot index, day string, etc.)\n */\nexport function useDragDropHandlers<T = any>(\n options: UseDragDropHandlersOptions<T>\n): DragDropHandlers<T> {\n const {\n enabled,\n mode,\n onEventDrop,\n canDragEvent,\n onEventDragStart,\n onEventDragEnd,\n calculateDropTarget,\n onExternalDrop,\n } = options;\n\n const stableOnEventDrop = useEffectEvent(onEventDrop || (() => {}));\n const stableOnEventDragStart = useEffectEvent(onEventDragStart || (() => {}));\n const stableOnEventDragEnd = useEffectEvent(onEventDragEnd || (() => {}));\n const stableOnExternalDrop = useEffectEvent(onExternalDrop || (() => {}));\n\n const dragState = useDragState();\n const [dropTarget, setDropTarget] = useState<T | null>(null);\n const [dragPreview, setDragPreview] = useState<DragPreview<T> | null>(null);\n\n const handleDragEnd = useCallback(() => {\n dragState.endDrag();\n setDropTarget(null);\n setDragPreview(null);\n stableOnEventDragEnd();\n }, [dragState]);\n\n const handleDragStart = useCallback(\n (event: ScheduleEventData) => {\n if (!enabled || mode === 'static') {\n return;\n }\n dragState.startDrag(event);\n stableOnEventDragStart(event);\n },\n [enabled, mode, dragState]\n );\n\n const handleDragOver = useCallback(\n (event: React.DragEvent, target: T) => {\n if (mode === 'static') {\n return;\n }\n\n let isInternalDrag = dragState.state.isDragging;\n\n if (isInternalDrag && !event.dataTransfer.types.includes('application/json')) {\n handleDragEnd();\n isInternalDrag = false;\n }\n\n if (isInternalDrag && !enabled) {\n return;\n }\n\n if (!isInternalDrag && !onExternalDrop) {\n return;\n }\n\n event.preventDefault();\n event.dataTransfer.dropEffect = isInternalDrag ? 'move' : 'copy';\n setDropTarget(target);\n },\n [enabled, mode, dragState.state.isDragging, onExternalDrop, handleDragEnd]\n );\n\n const handleDragLeave = useCallback((event?: React.DragEvent) => {\n if (event?.currentTarget) {\n const rect = event.currentTarget.getBoundingClientRect();\n const isInside =\n event.clientX >= rect.left &&\n event.clientX < rect.right &&\n event.clientY >= rect.top &&\n event.clientY < rect.bottom;\n if (isInside) {\n return;\n }\n }\n\n setDropTarget(null);\n setDragPreview(null);\n }, []);\n\n const handleDrop = useCallback(\n (event: React.DragEvent, target: T) => {\n event.preventDefault();\n\n const isInternalDrag =\n dragState.state.isDragging && event.dataTransfer.types.includes('application/json');\n\n if (isInternalDrag && enabled && dragState.state.draggedEvent && onEventDrop) {\n const { start, end } = calculateDropTarget(target, dragState.state.draggedEvent);\n stableOnEventDrop({\n eventId: dragState.state.draggedEventId!,\n newStart: dayjs(start).format('YYYY-MM-DD HH:mm:ss'),\n newEnd: dayjs(end).format('YYYY-MM-DD HH:mm:ss'),\n event: dragState.state.draggedEvent,\n });\n handleDragEnd();\n return;\n }\n\n if (!isInternalDrag && onExternalDrop) {\n if (dragState.state.isDragging) {\n handleDragEnd();\n }\n stableOnExternalDrop(event, target);\n setDropTarget(null);\n setDragPreview(null);\n return;\n }\n\n setDropTarget(null);\n setDragPreview(null);\n },\n [\n enabled,\n dragState.state,\n onEventDrop,\n onExternalDrop,\n calculateDropTarget,\n handleDragEnd,\n stableOnEventDrop,\n stableOnExternalDrop,\n ]\n );\n\n const isDraggableEvent = useCallback(\n (event: ScheduleEventData) => {\n return (\n enabled &&\n mode !== 'static' &&\n event.display !== 'background' &&\n (canDragEvent ? canDragEvent(event) : true)\n );\n },\n [enabled, mode, canDragEvent]\n );\n\n const isDropTarget = useCallback(\n (target: T) => {\n // Handle complex target comparison (for WeekView with day + slotIndex)\n if (dropTarget && typeof dropTarget === 'object' && typeof target === 'object') {\n return JSON.stringify(dropTarget) === JSON.stringify(target);\n }\n return dropTarget === target;\n },\n [dropTarget]\n );\n\n const dragContextValue: DragContextValue = {\n isDragging: dragState.state.isDragging,\n draggedEventId: dragState.state.draggedEventId,\n draggedEvent: dragState.state.draggedEvent,\n dropTarget: dragState.state.dropTarget,\n onDragStart: handleDragStart,\n onDragEnd: handleDragEnd,\n setDropTarget: dragState.setDropTarget,\n };\n\n return {\n dragContextValue,\n dropTarget,\n handleDragStart,\n handleDragEnd,\n handleDragOver,\n handleDragLeave,\n handleDrop,\n isDraggableEvent,\n isDropTarget,\n dragPreview,\n setDragPreview,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-drag-drop-handlers.cjs","names":["useEffectEvent","useDragState","useState","useCallback","isEventPlacementAllowed"],"sources":["../../src/hooks/use-drag-drop-handlers.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { useCallback, useEffectEvent, useState } from 'react';\nimport { DragContextValue } from '../components/DragContext/DragContext';\nimport {\n DateTimeStringValue,\n PreventEventOverlap,\n ScheduleCanDropEventData,\n ScheduleCanDropExternalEventData,\n ScheduleEventData,\n ScheduleEventPlacementRejectedData,\n ScheduleMode,\n} from '../types';\nimport { isEventPlacementAllowed } from '../utils/is-event-placement-allowed/is-event-placement-allowed';\nimport { useDragState } from './use-drag-state';\n\nconst DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';\n\nexport interface DragPreview<T = any> {\n start: DateTimeStringValue;\n end: DateTimeStringValue;\n target: T;\n}\n\nexport interface UseDragDropHandlersOptions<T = any> {\n /** Whether drag and drop is enabled */\n enabled: boolean;\n\n /** Schedule interaction mode */\n mode: ScheduleMode;\n\n /** Called when event is dropped at new location */\n onEventDrop?: (data: {\n eventId: string | number;\n newStart: DateTimeStringValue;\n newEnd: DateTimeStringValue;\n event: ScheduleEventData;\n }) => void;\n\n /** Function to determine if event can be dragged */\n canDragEvent?: (event: ScheduleEventData) => boolean;\n\n /** Called when any event drag starts */\n onEventDragStart?: (event: ScheduleEventData) => void;\n\n /** Called when any event drag ends */\n onEventDragEnd?: () => void;\n\n /**\n * Function to calculate drop target dates from drop location.\n * Receives the target location and the dragged event.\n */\n calculateDropTarget: (target: T, draggedEvent: ScheduleEventData) => { start: Date; end: Date };\n\n /** Called when an external item is dropped onto the schedule */\n onExternalDrop?: (e: React.DragEvent, target: T) => void;\n\n /** Events the candidate range is checked against when `preventEventOverlap` is set */\n events?: ScheduleEventData[];\n\n /** If set, drops that would make the event overlap another event are rejected */\n preventEventOverlap?: PreventEventOverlap;\n\n /** Called on every `dragover` to compute live feedback and again before the drop is committed, return `false` to reject the drop */\n canDropEvent?: (data: ScheduleCanDropEventData) => boolean;\n\n /** 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 */\n canDropExternalEvent?: (data: ScheduleCanDropExternalEventData) => boolean;\n\n /** Called when a drop is rejected */\n onEventPlacementRejected?: (data: ScheduleEventPlacementRejectedData) => void;\n\n /** Resolves the target resource from the drop target, used by `Resources*` views */\n getTargetResourceId?: (target: T) => string | number | undefined;\n\n /** Resolves the datetime an external item would be dropped at, `null` when the target has no datetime */\n getExternalDropDateTime?: (target: T) => DateTimeStringValue | null;\n}\n\nexport interface DragDropHandlers<T = any> {\n /** Context value for DragContext.Provider */\n dragContextValue: DragContextValue;\n\n /** Current drop target */\n dropTarget: T | null;\n\n /** Handle drag start event */\n handleDragStart: (event: ScheduleEventData) => void;\n\n /** Handle drag end event */\n handleDragEnd: () => void;\n\n /** Handle drag over event */\n handleDragOver: (e: React.DragEvent, target: T) => void;\n\n /** Handle drag leave event */\n handleDragLeave: (event?: React.DragEvent) => void;\n\n /** Handle drop event */\n handleDrop: (e: React.DragEvent, target: T) => void;\n\n /** Check if event is draggable */\n isDraggableEvent: (event: ScheduleEventData) => boolean;\n\n /** Check if target is the current drop target */\n isDropTarget: (target: T) => boolean;\n\n /** Snapped drop preview used to render the drag ghost, or `null` when not dragging */\n dragPreview: DragPreview<T> | null;\n\n /** Sets the current drag preview */\n setDragPreview: (preview: DragPreview<T> | null) => void;\n\n /** False while the pointer is over a target that would be rejected */\n dropValid: boolean;\n}\n\n/**\n * Hook that provides unified drag-drop handlers for Schedule views.\n * Handles drag state management and event drops across Day, Week, and Month views.\n *\n * @template T - Type of the drop target (e.g., slot index, day string, etc.)\n */\nexport function useDragDropHandlers<T = any>(\n options: UseDragDropHandlersOptions<T>\n): DragDropHandlers<T> {\n const {\n enabled,\n mode,\n onEventDrop,\n canDragEvent,\n onEventDragStart,\n onEventDragEnd,\n calculateDropTarget,\n onExternalDrop,\n events,\n preventEventOverlap,\n canDropEvent,\n canDropExternalEvent,\n onEventPlacementRejected,\n getTargetResourceId,\n getExternalDropDateTime,\n } = options;\n\n const stableOnEventDrop = useEffectEvent(onEventDrop || (() => {}));\n const stableOnEventPlacementRejected = useEffectEvent(onEventPlacementRejected || (() => {}));\n const stableOnEventDragStart = useEffectEvent(onEventDragStart || (() => {}));\n const stableOnEventDragEnd = useEffectEvent(onEventDragEnd || (() => {}));\n const stableOnExternalDrop = useEffectEvent(onExternalDrop || (() => {}));\n\n const dragState = useDragState();\n const [dropTarget, setDropTarget] = useState<T | null>(null);\n const [dragPreview, setDragPreview] = useState<DragPreview<T> | null>(null);\n const [dropValid, setDropValid] = useState(true);\n\n const validateDrop = useCallback(\n (target: T, draggedEvent: ScheduleEventData) => {\n const range = calculateDropTarget(target, draggedEvent);\n const start = dayjs(range.start).format(DATE_TIME_FORMAT);\n const end = dayjs(range.end).format(DATE_TIME_FORMAT);\n const resourceId = getTargetResourceId?.(target);\n\n const { allowed, conflicts } = isEventPlacementAllowed({\n event: draggedEvent,\n start,\n end,\n events: events || [],\n preventEventOverlap,\n resourceId,\n });\n\n if (!allowed) {\n return { valid: false, start, end, resourceId, conflicts, reason: 'overlap' as const };\n }\n\n if (canDropEvent && canDropEvent({ event: draggedEvent, start, end, resourceId }) === false) {\n return { valid: false, start, end, resourceId, conflicts: [], reason: 'rejected' as const };\n }\n\n return { valid: true, start, end, resourceId, conflicts: [], reason: null };\n },\n [calculateDropTarget, getTargetResourceId, events, preventEventOverlap, canDropEvent]\n );\n\n const validateExternalDrop = useCallback(\n (target: T, dataTransfer: DataTransfer) => {\n if (!canDropExternalEvent || !getExternalDropDateTime) {\n return { valid: true, start: undefined, resourceId: undefined };\n }\n\n const start = getExternalDropDateTime(target);\n\n if (start === null) {\n return { valid: true, start: undefined, resourceId: undefined };\n }\n\n const resourceId = getTargetResourceId?.(target);\n return {\n valid: canDropExternalEvent({ dataTransfer, start, resourceId }) !== false,\n start,\n resourceId,\n };\n },\n [canDropExternalEvent, getExternalDropDateTime, getTargetResourceId]\n );\n\n const handleDragEnd = useCallback(() => {\n dragState.endDrag();\n setDropTarget(null);\n setDragPreview(null);\n setDropValid(true);\n stableOnEventDragEnd();\n }, [dragState]);\n\n const handleDragStart = useCallback(\n (event: ScheduleEventData) => {\n if (!enabled || mode === 'static') {\n return;\n }\n dragState.startDrag(event);\n stableOnEventDragStart(event);\n },\n [enabled, mode, dragState]\n );\n\n const handleDragOver = useCallback(\n (event: React.DragEvent, target: T) => {\n if (mode === 'static') {\n return;\n }\n\n let isInternalDrag = dragState.state.isDragging;\n\n if (isInternalDrag && !event.dataTransfer.types.includes('application/json')) {\n handleDragEnd();\n isInternalDrag = false;\n }\n\n if (isInternalDrag && !enabled) {\n return;\n }\n\n if (!isInternalDrag && !onExternalDrop) {\n return;\n }\n\n event.preventDefault();\n\n const draggedEvent = dragState.state.draggedEvent;\n const valid =\n isInternalDrag && draggedEvent\n ? validateDrop(target, draggedEvent).valid\n : validateExternalDrop(target, event.dataTransfer).valid;\n\n setDropValid(valid);\n\n // The drop effect stays permissive even for an invalid target: setting it to 'none' makes the\n // browser end the drag with dragleave/dragend and never fire `drop`, which would stop\n // `handleDrop` from rejecting the placement and reporting it through onEventPlacementRejected.\n // Invalid targets are signalled with `data-invalid` on the drag preview instead.\n event.dataTransfer.dropEffect = isInternalDrag ? 'move' : 'copy';\n setDropTarget(target);\n },\n [\n enabled,\n mode,\n dragState.state.isDragging,\n dragState.state.draggedEvent,\n onExternalDrop,\n handleDragEnd,\n validateDrop,\n validateExternalDrop,\n ]\n );\n\n const handleDragLeave = useCallback((event?: React.DragEvent) => {\n if (event?.currentTarget) {\n const rect = event.currentTarget.getBoundingClientRect();\n const isInside =\n event.clientX >= rect.left &&\n event.clientX < rect.right &&\n event.clientY >= rect.top &&\n event.clientY < rect.bottom;\n if (isInside) {\n return;\n }\n }\n\n setDropTarget(null);\n setDragPreview(null);\n setDropValid(true);\n }, []);\n\n const handleDrop = useCallback(\n (event: React.DragEvent, target: T) => {\n event.preventDefault();\n\n const isInternalDrag =\n dragState.state.isDragging && event.dataTransfer.types.includes('application/json');\n\n if (isInternalDrag && enabled && dragState.state.draggedEvent && onEventDrop) {\n const draggedEvent = dragState.state.draggedEvent;\n const { valid, start, end, resourceId, conflicts, reason } = validateDrop(\n target,\n draggedEvent\n );\n\n if (!valid) {\n stableOnEventPlacementRejected({\n action: 'drop',\n event: draggedEvent,\n start,\n end,\n resourceId,\n conflicts,\n reason: reason!,\n });\n handleDragEnd();\n return;\n }\n\n stableOnEventDrop({\n eventId: dragState.state.draggedEventId!,\n newStart: start,\n newEnd: end,\n event: draggedEvent,\n });\n handleDragEnd();\n return;\n }\n\n if (!isInternalDrag && onExternalDrop) {\n if (dragState.state.isDragging) {\n handleDragEnd();\n }\n\n const external = validateExternalDrop(target, event.dataTransfer);\n\n if (!external.valid) {\n stableOnEventPlacementRejected({\n action: 'external-drop',\n dataTransfer: event.dataTransfer,\n start: external.start!,\n resourceId: external.resourceId,\n conflicts: [],\n reason: 'rejected',\n });\n } else {\n stableOnExternalDrop(event, target);\n }\n\n setDropTarget(null);\n setDragPreview(null);\n setDropValid(true);\n return;\n }\n\n setDropTarget(null);\n setDragPreview(null);\n setDropValid(true);\n },\n [\n enabled,\n dragState.state,\n onEventDrop,\n onExternalDrop,\n handleDragEnd,\n validateDrop,\n validateExternalDrop,\n stableOnEventDrop,\n stableOnExternalDrop,\n stableOnEventPlacementRejected,\n ]\n );\n\n const isDraggableEvent = useCallback(\n (event: ScheduleEventData) => {\n return (\n enabled &&\n mode !== 'static' &&\n event.display !== 'background' &&\n (canDragEvent ? canDragEvent(event) : true)\n );\n },\n [enabled, mode, canDragEvent]\n );\n\n const isDropTarget = useCallback(\n (target: T) => {\n // Handle complex target comparison (for WeekView with day + slotIndex)\n if (dropTarget && typeof dropTarget === 'object' && typeof target === 'object') {\n return JSON.stringify(dropTarget) === JSON.stringify(target);\n }\n return dropTarget === target;\n },\n [dropTarget]\n );\n\n const dragContextValue: DragContextValue = {\n isDragging: dragState.state.isDragging,\n draggedEventId: dragState.state.draggedEventId,\n draggedEvent: dragState.state.draggedEvent,\n dropTarget: dragState.state.dropTarget,\n onDragStart: handleDragStart,\n onDragEnd: handleDragEnd,\n setDropTarget: dragState.setDropTarget,\n };\n\n return {\n dragContextValue,\n dropTarget,\n handleDragStart,\n handleDragEnd,\n handleDragOver,\n handleDragLeave,\n handleDrop,\n isDraggableEvent,\n isDropTarget,\n dragPreview,\n setDragPreview,\n dropValid,\n };\n}\n"],"mappings":"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"}
|
|
@@ -2,15 +2,27 @@
|
|
|
2
2
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
3
3
|
const require_clamp_interval_minutes = require("../utils/clamp-interval-minutes/clamp-interval-minutes.cjs");
|
|
4
4
|
const require_parse_time_string = require("../utils/parse-time-string/parse-time-string.cjs");
|
|
5
|
+
const require_validate_event_resize = require("./validate-event-resize.cjs");
|
|
5
6
|
let dayjs = require("dayjs");
|
|
6
7
|
dayjs = require_runtime.__toESM(dayjs);
|
|
7
8
|
let react = require("react");
|
|
8
9
|
//#region packages/@mantine/schedule/src/hooks/use-event-resize.ts
|
|
9
|
-
function useEventResize({ enabled = false, mode = "default", startTime, endTime, intervalMinutes, resizeIntervalMinutes, onEventResize, canResizeEvent, withBackgroundEvents = false }) {
|
|
10
|
+
function useEventResize({ enabled = false, mode = "default", startTime, endTime, intervalMinutes, resizeIntervalMinutes, onEventResize, canResizeEvent, events, preventEventOverlap, canResizeEventTo, onEventPlacementRejected, withBackgroundEvents = false }) {
|
|
10
11
|
const [resizeState, setResizeState] = (0, react.useState)(null);
|
|
12
|
+
const [resizeValid, setResizeValid] = (0, react.useState)(true);
|
|
11
13
|
const resizeRef = (0, react.useRef)(null);
|
|
12
14
|
const justResizedRef = (0, react.useRef)(false);
|
|
13
15
|
const stableOnEventResize = (0, react.useEffectEvent)(onEventResize || (() => {}));
|
|
16
|
+
const stableOnEventPlacementRejected = (0, react.useEffectEvent)(onEventPlacementRejected || (() => {}));
|
|
17
|
+
const validateResize = (0, react.useEffectEvent)((event, start, end, edge) => require_validate_event_resize.validateEventResize({
|
|
18
|
+
event,
|
|
19
|
+
start,
|
|
20
|
+
end,
|
|
21
|
+
edge,
|
|
22
|
+
events,
|
|
23
|
+
preventEventOverlap,
|
|
24
|
+
canResizeEventTo
|
|
25
|
+
}));
|
|
14
26
|
const parsedStartTime = require_parse_time_string.parseTimeString(startTime);
|
|
15
27
|
const parsedEndTime = require_parse_time_string.parseTimeString(endTime);
|
|
16
28
|
const startMinutes = parsedStartTime.hours * 60 + parsedStartTime.minutes;
|
|
@@ -69,7 +81,13 @@ function useEventResize({ enabled = false, mode = "default", startTime, endTime,
|
|
|
69
81
|
};
|
|
70
82
|
resizeRef.current = state;
|
|
71
83
|
setResizeState(state);
|
|
84
|
+
setResizeValid(true);
|
|
72
85
|
}, [enabled, mode]);
|
|
86
|
+
const getResizeRange = (0, react.useCallback)((state) => ({
|
|
87
|
+
start: state.edge === "top" ? percentToDateTime(state.currentTop, state.eventDate) : state.originalStart,
|
|
88
|
+
end: state.edge === "top" ? state.originalEnd : percentToDateTime(state.currentTop + state.currentHeight, state.eventDate),
|
|
89
|
+
edge: state.edge === "top" ? "start" : "end"
|
|
90
|
+
}), [percentToDateTime]);
|
|
73
91
|
const isResizing = resizeState !== null;
|
|
74
92
|
(0, react.useEffect)(() => {
|
|
75
93
|
if (!isResizing) return;
|
|
@@ -100,30 +118,35 @@ function useEventResize({ enabled = false, mode = "default", startTime, endTime,
|
|
|
100
118
|
currentHeight: newHeight
|
|
101
119
|
};
|
|
102
120
|
setResizeState(resizeRef.current);
|
|
121
|
+
const range = getResizeRange(resizeRef.current);
|
|
122
|
+
setResizeValid(validateResize(state.event, range.start, range.end, range.edge).valid);
|
|
103
123
|
};
|
|
104
124
|
const handlePointerUp = () => {
|
|
105
125
|
const state = resizeRef.current;
|
|
106
126
|
if (state) {
|
|
107
127
|
if (state.currentTop !== state.originalTop || state.currentHeight !== state.originalHeight) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
newStart = percentToDateTime(state.currentTop, state.eventDate);
|
|
112
|
-
newEnd = state.originalEnd;
|
|
113
|
-
} else {
|
|
114
|
-
newStart = state.originalStart;
|
|
115
|
-
newEnd = percentToDateTime(state.currentTop + state.currentHeight, state.eventDate);
|
|
116
|
-
}
|
|
117
|
-
stableOnEventResize({
|
|
128
|
+
const { start: newStart, end: newEnd, edge } = getResizeRange(state);
|
|
129
|
+
const { valid, conflicts, reason } = validateResize(state.event, newStart, newEnd, edge);
|
|
130
|
+
if (valid) stableOnEventResize({
|
|
118
131
|
eventId: state.eventId,
|
|
119
132
|
newStart,
|
|
120
133
|
newEnd,
|
|
121
134
|
event: state.event
|
|
122
135
|
});
|
|
136
|
+
else stableOnEventPlacementRejected({
|
|
137
|
+
action: "resize",
|
|
138
|
+
event: state.event,
|
|
139
|
+
start: newStart,
|
|
140
|
+
end: newEnd,
|
|
141
|
+
edge,
|
|
142
|
+
conflicts,
|
|
143
|
+
reason
|
|
144
|
+
});
|
|
123
145
|
}
|
|
124
146
|
}
|
|
125
147
|
resizeRef.current = null;
|
|
126
148
|
setResizeState(null);
|
|
149
|
+
setResizeValid(true);
|
|
127
150
|
justResizedRef.current = true;
|
|
128
151
|
requestAnimationFrame(() => {
|
|
129
152
|
justResizedRef.current = false;
|
|
@@ -162,7 +185,8 @@ function useEventResize({ enabled = false, mode = "default", startTime, endTime,
|
|
|
162
185
|
resizingEventId: resizeState?.eventId ?? null,
|
|
163
186
|
getResizePosition,
|
|
164
187
|
isResizableEvent,
|
|
165
|
-
wasResizing
|
|
188
|
+
wasResizing,
|
|
189
|
+
resizeValid
|
|
166
190
|
};
|
|
167
191
|
}
|
|
168
192
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-event-resize.cjs","names":["useState","useRef","useEffectEvent","parseTimeString","clampIntervalMinutes","useCallback"],"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 { clampIntervalMinutes } from '../utils/clamp-interval-minutes/clamp-interval-minutes';\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 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 useEventResize({\n enabled = false,\n mode = 'default',\n startTime,\n endTime,\n intervalMinutes,\n resizeIntervalMinutes,\n onEventResize,\n canResizeEvent,\n withBackgroundEvents = false,\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 clampedInterval = clampIntervalMinutes(intervalMinutes);\n const clampedResizeInterval = clampIntervalMinutes(resizeIntervalMinutes ?? intervalMinutes);\n const literalRange = endMinutes - startMinutes;\n const totalMinutes = Math.ceil(literalRange / clampedInterval) * clampedInterval;\n\n // Snapping happens in absolute minutes from midnight – the same grid `calculateDropTime`\n // uses for drag and drop. Snapping relative to `startTime` would put resized edges on a\n // different grid than dragged events whenever `startTime` is not a multiple of the interval\n // (with `startTime=\"08:10\"` and a 30 minute step, drops land on :00/:30 but resizes on\n // :10/:40, so the two operations could never align an event).\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 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 draggedMinutes = clampAndSnap(percentToMinutes(rawPercent));\n\n let newTop = state.originalTop;\n let newHeight = state.originalHeight;\n\n if (state.edge === 'bottom') {\n const topMinutes = percentToMinutes(state.originalTop);\n const minEndMinutes = snapEdgeMinutes(topMinutes + clampedResizeInterval, 'up');\n const endMinutes = Math.max(draggedMinutes, minEndMinutes);\n newHeight = Math.max(0, minutesToPercent(endMinutes) - state.originalTop);\n } else {\n const bottomMinutes = percentToMinutes(state.originalTop + state.originalHeight);\n const maxStartMinutes = snapEdgeMinutes(bottomMinutes - clampedResizeInterval, 'down');\n const startMinutesValue = Math.min(draggedMinutes, maxStartMinutes);\n newTop = minutesToPercent(startMinutesValue);\n newHeight = Math.max(0, state.originalTop + state.originalHeight - 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, 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 { top: resizeState.currentTop, height: resizeState.currentHeight };\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 getResizePosition,\n isResizableEvent,\n wasResizing,\n };\n}\n"],"mappings":";;;;;;;;AAuCA,SAAgB,eAAe,EAC7B,UAAU,OACV,OAAO,WACP,WACA,SACA,iBACA,uBACA,eACA,gBACA,uBAAuB,SACD;CACtB,MAAM,CAAC,aAAa,mBAAA,GAAkBA,MAAAA,SAAAA,CAA6B,IAAI;CACvE,MAAM,aAAA,GAAYC,MAAAA,OAAAA,CAA2B,IAAI;CACjD,MAAM,kBAAA,GAAiBA,MAAAA,OAAAA,CAAO,KAAK;CACnC,MAAM,uBAAA,GAAsBC,MAAAA,eAAAA,CAAe,wBAAwB,CAAC,EAAE;CAEtE,MAAM,kBAAkBC,0BAAAA,gBAAgB,SAAS;CACjD,MAAM,gBAAgBA,0BAAAA,gBAAgB,OAAO;CAC7C,MAAM,eAAe,gBAAgB,QAAQ,KAAK,gBAAgB;CAClE,MAAM,aAAa,cAAc,QAAQ,KAAK,cAAc;CAC5D,MAAM,kBAAkBC,+BAAAA,qBAAqB,eAAe;CAC5D,MAAM,wBAAwBA,+BAAAA,qBAAqB,yBAAyB,eAAe;CAC3F,MAAM,eAAe,aAAa;CAClC,MAAM,eAAe,KAAK,KAAK,eAAe,eAAe,IAAI;CAOjE,MAAM,gBAAA,GAAeC,MAAAA,YAAAA,EAClB,YAA4B;EAC3B,MAAM,UACJ,KAAK,OAAO,eAAe,WAAW,qBAAqB,IAAI;EACjE,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,cAAc,UAAU,YAAY,CAAC;CACnE,GACA;EAAC;EAAc;EAAuB;CAAY,CACpD;CAEA,MAAM,qBAAA,GAAoBA,MAAAA,YAAAA,EACvB,SAAiB,cAA2C;EAC3D,MAAM,UAAW,UAAU,MAAO;EAClC,MAAM,iBAAiB,aAAa,OAAO;EAC3C,MAAM,YAAY,eAAe;EACjC,MAAM,QAAQ,KAAK,MAAM,YAAY,EAAE;EACvC,MAAM,OAAO,YAAY;EACzB,OAAO,GAAG,UAAU,GAAG,OAAO,KAAK,CAAC,CAAC,SAAS,GAAG,GAAG,EAAE,GAAG,OAAO,IAAI,CAAC,CAAC,SAAS,GAAG,GAAG,EAAE;CACzF,GACA;EAAC;EAAc;EAAc;CAAY,CAC3C;CAEA,MAAM,mBAAA,GAAkBA,MAAAA,YAAAA,EACrB,SAAiB,cAAqC;EACrD,MAAM,WAAW,eAAe;EAChC,MAAM,UACJ,cAAc,OACV,KAAK,KAAK,WAAW,qBAAqB,IAAI,wBAC9C,KAAK,MAAM,WAAW,qBAAqB,IAAI;EACrD,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,cAAc,UAAU,YAAY,CAAC;CACnE,GACA;EAAC;EAAc;EAAuB;CAAY,CACpD;CAEA,MAAM,oBAAA,GAAmBA,MAAAA,YAAAA,EACtB,YAA6B,UAAU,MAAO,cAC/C,CAAC,YAAY,CACf;CAEA,MAAM,oBAAA,GAAmBA,MAAAA,YAAAA,EACtB,YAA6B,UAAU,eAAgB,KACxD,CAAC,YAAY,CACf;CAEA,MAAM,qBAAA,GAAoBA,MAAAA,YAAAA,EACvB,EACC,OACA,MACA,WACA,aACA,gBACA,WACA,mBASI;EACJ,IAAI,CAAC,WAAW,SAAS,UACvB;EAGF,aAAa,eAAe;EAC5B,aAAa,gBAAgB;EAE7B,MAAM,QAAqB;GACzB,SAAS,MAAM;GACf;GACA;GACA;GACA;GACA;GACA,YAAY;GACZ,eAAe;GACf;GACA,gBAAA,GAAe,MAAA,QAAA,CAAM,MAAM,KAAK,CAAC,CAAC,OAAO,qBAAqB;GAC9D,cAAA,GAAa,MAAA,QAAA,CAAM,MAAM,GAAG,CAAC,CAAC,OAAO,qBAAqB;EAC5D;EAEA,UAAU,UAAU;EACpB,eAAe,KAAK;CACtB,GACA,CAAC,SAAS,IAAI,CAChB;CAEA,MAAM,aAAa,gBAAgB;CAEnC,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,IAAI,CAAC,YACH;EAGF,MAAM,kBAAkB,SAAS,KAAK,MAAM;EAC5C,SAAS,KAAK,MAAM,aAAa;EAEjC,MAAM,qBAAqB,MAAoB;GAC7C,MAAM,QAAQ,UAAU;GACxB,IAAI,CAAC,OACH;GAGF,MAAM,gBAAgB,MAAM,UAAU,sBAAsB;GAC5D,MAAM,YAAY,EAAE,UAAU,cAAc;GAC5C,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,IAAI,KAAM,YAAY,cAAc,SAAU,GAAG,CAAC;GACtF,MAAM,iBAAiB,aAAa,iBAAiB,UAAU,CAAC;GAEhE,IAAI,SAAS,MAAM;GACnB,IAAI,YAAY,MAAM;GAEtB,IAAI,MAAM,SAAS,UAAU;IAC3B,MAAM,aAAa,iBAAiB,MAAM,WAAW;IACrD,MAAM,gBAAgB,gBAAgB,aAAa,uBAAuB,IAAI;IAE9E,YAAY,KAAK,IAAI,GAAG,iBADL,KAAK,IAAI,gBAAgB,aACM,CAAC,IAAI,MAAM,WAAW;GAC1E,OAAO;IACL,MAAM,gBAAgB,iBAAiB,MAAM,cAAc,MAAM,cAAc;IAC/E,MAAM,kBAAkB,gBAAgB,gBAAgB,uBAAuB,MAAM;IAErF,SAAS,iBADiB,KAAK,IAAI,gBAAgB,eACT,CAAC;IAC3C,YAAY,KAAK,IAAI,GAAG,MAAM,cAAc,MAAM,iBAAiB,MAAM;GAC3E;GAEA,UAAU,UAAU;IAAE,GAAG;IAAO,YAAY;IAAQ,eAAe;GAAU;GAC7E,eAAe,UAAU,OAAO;EAClC;EAEA,MAAM,wBAAwB;GAC5B,MAAM,QAAQ,UAAU;GACxB,IAAI,OAEA;QAAA,MAAM,eAAe,MAAM,eAC3B,MAAM,kBAAkB,MAAM,gBAC9B;KACA,IAAI;KACJ,IAAI;KAEJ,IAAI,MAAM,SAAS,OAAO;MACxB,WAAW,kBAAkB,MAAM,YAAY,MAAM,SAAS;MAC9D,SAAS,MAAM;KACjB,OAAO;MACL,WAAW,MAAM;MACjB,SAAS,kBAAkB,MAAM,aAAa,MAAM,eAAe,MAAM,SAAS;KACpF;KAEA,oBAAoB;MAClB,SAAS,MAAM;MACf;MACA;MACA,OAAO,MAAM;KACf,CAAC;IACH;;GAEF,UAAU,UAAU;GACpB,eAAe,IAAI;GACnB,eAAe,UAAU;GACzB,4BAA4B;IAC1B,eAAe,UAAU;GAC3B,CAAC;EACH;EAEA,SAAS,iBAAiB,eAAe,iBAAiB;EAC1D,SAAS,iBAAiB,aAAa,eAAe;EAEtD,aAAa;GACX,SAAS,KAAK,MAAM,aAAa;GACjC,SAAS,oBAAoB,eAAe,iBAAiB;GAC7D,SAAS,oBAAoB,aAAa,eAAe;EAC3D;CACF,GAAG,CAAC,UAAU,CAAC;CAEf,MAAM,qBAAA,GAAoBA,MAAAA,YAAAA,EACvB,SAA0B,cAAuB;EAChD,IAAI,CAAC,eAAe,YAAY,YAAY,SAC1C,OAAO;EAET,IAAI,cAAc,KAAA,KAAa,YAAY,cAAc,WACvD,OAAO;EAET,OAAO;GAAE,KAAK,YAAY;GAAY,QAAQ,YAAY;EAAc;CAC1E,GACA,CAAC,WAAW,CACd;CAEA,MAAM,oBAAA,GAAmBA,MAAAA,YAAAA,EACtB,UAA6B;EAC5B,IAAI,CAAC,WAAW,SAAS,UACvB,OAAO;EAET,IAAI,MAAM,YAAY,gBAAgB,CAAC,sBACrC,OAAO;EAET,OAAO,iBAAiB,eAAe,KAAK,IAAI;CAClD,GACA;EAAC;EAAS;EAAM;EAAgB;CAAoB,CACtD;CAEA,MAAM,eAAA,GAAcA,MAAAA,YAAAA,OAAkB,eAAe,SAAS,CAAC,CAAC;CAEhE,OAAO;EACL;EACA;EACA,iBAAiB,aAAa,WAAW;EACzC;EACA;EACA;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"use-event-resize.cjs","names":["useState","useRef","useEffectEvent","validateEventResize","parseTimeString","clampIntervalMinutes","useCallback"],"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 { clampIntervalMinutes } from '../utils/clamp-interval-minutes/clamp-interval-minutes';\nimport { parseTimeString } from '../utils/parse-time-string/parse-time-string';\nimport { EventResizeValidationOptions, validateEventResize } from './validate-event-resize';\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 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 useEventResize({\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}: UseEventResizeInput) {\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 })\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 // Snapping happens in absolute minutes from midnight – the same grid `calculateDropTime`\n // uses for drag and drop. Snapping relative to `startTime` would put resized edges on a\n // different grid than dragged events whenever `startTime` is not a multiple of the interval\n // (with `startTime=\"08:10\"` and a 30 minute step, drops land on :00/:30 but resizes on\n // :10/:40, so the two operations could never align an event).\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 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 setResizeValid(true);\n },\n [enabled, mode]\n );\n\n const getResizeRange = useCallback(\n (state: ResizeState) => ({\n start:\n state.edge === 'top'\n ? percentToDateTime(state.currentTop, state.eventDate)\n : state.originalStart,\n end:\n state.edge === 'top'\n ? state.originalEnd\n : percentToDateTime(state.currentTop + state.currentHeight, state.eventDate),\n edge: (state.edge === 'top' ? 'start' : 'end') as 'start' | 'end',\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 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 draggedMinutes = clampAndSnap(percentToMinutes(rawPercent));\n\n let newTop = state.originalTop;\n let newHeight = state.originalHeight;\n\n if (state.edge === 'bottom') {\n const topMinutes = percentToMinutes(state.originalTop);\n const minEndMinutes = snapEdgeMinutes(topMinutes + clampedResizeInterval, 'up');\n const endMinutes = Math.max(draggedMinutes, minEndMinutes);\n newHeight = Math.max(0, minutesToPercent(endMinutes) - state.originalTop);\n } else {\n const bottomMinutes = percentToMinutes(state.originalTop + state.originalHeight);\n const maxStartMinutes = snapEdgeMinutes(bottomMinutes - clampedResizeInterval, 'down');\n const startMinutesValue = Math.min(draggedMinutes, maxStartMinutes);\n newTop = minutesToPercent(startMinutesValue);\n newHeight = Math.max(0, state.originalTop + state.originalHeight - newTop);\n }\n\n resizeRef.current = { ...state, currentTop: newTop, currentHeight: newHeight };\n setResizeState(resizeRef.current);\n\n const range = getResizeRange(resizeRef.current);\n setResizeValid(validateResize(state.event, range.start, range.end, range.edge).valid);\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 const { start: newStart, end: newEnd, edge } = getResizeRange(state);\n const { valid, conflicts, reason } = validateResize(state.event, newStart, newEnd, edge);\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,\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.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 { top: resizeState.currentTop, height: resizeState.currentHeight };\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 getResizePosition,\n isResizableEvent,\n wasResizing,\n resizeValid,\n };\n}\n"],"mappings":"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"}
|