@mantine/schedule 9.6.3 → 9.7.1

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.
Files changed (101) hide show
  1. package/cjs/components/DayView/DayView.cjs +46 -31
  2. package/cjs/components/DayView/DayView.cjs.map +1 -1
  3. package/cjs/components/DayView/DayView.module.cjs.map +1 -1
  4. package/cjs/components/MonthView/MonthView.cjs +11 -4
  5. package/cjs/components/MonthView/MonthView.cjs.map +1 -1
  6. package/cjs/components/MonthView/get-month-view-events/get-month-positioned-events.cjs +10 -8
  7. package/cjs/components/MonthView/get-month-view-events/get-month-positioned-events.cjs.map +1 -1
  8. package/cjs/components/MonthView/get-month-view-events/reserve-row.cjs +12 -0
  9. package/cjs/components/MonthView/get-month-view-events/reserve-row.cjs.map +1 -0
  10. package/cjs/components/ResourcesDayView/ResourcesDayView.cjs +75 -60
  11. package/cjs/components/ResourcesDayView/ResourcesDayView.cjs.map +1 -1
  12. package/cjs/components/ResourcesDayView/ResourcesDayView.module.cjs.map +1 -1
  13. package/cjs/components/ResourcesMonthView/ResourcesMonthView.cjs +19 -7
  14. package/cjs/components/ResourcesMonthView/ResourcesMonthView.cjs.map +1 -1
  15. package/cjs/components/ResourcesSchedule/ResourcesSchedule.cjs +6 -1
  16. package/cjs/components/ResourcesSchedule/ResourcesSchedule.cjs.map +1 -1
  17. package/cjs/components/ResourcesWeekView/ResourcesWeekView.cjs +85 -58
  18. package/cjs/components/ResourcesWeekView/ResourcesWeekView.cjs.map +1 -1
  19. package/cjs/components/ResourcesWeekView/ResourcesWeekView.module.cjs.map +1 -1
  20. package/cjs/components/Schedule/Schedule.cjs +14 -1
  21. package/cjs/components/Schedule/Schedule.cjs.map +1 -1
  22. package/cjs/components/ScheduleEvent/ScheduleEvent.module.cjs.map +1 -1
  23. package/cjs/components/WeekView/WeekView.cjs +70 -46
  24. package/cjs/components/WeekView/WeekView.cjs.map +1 -1
  25. package/cjs/components/WeekView/WeekView.module.cjs.map +1 -1
  26. package/cjs/hooks/use-day-grid-event-resize.cjs +38 -8
  27. package/cjs/hooks/use-day-grid-event-resize.cjs.map +1 -1
  28. package/cjs/hooks/use-drag-drop-handlers.cjs +126 -10
  29. package/cjs/hooks/use-drag-drop-handlers.cjs.map +1 -1
  30. package/cjs/hooks/use-event-resize.cjs +36 -12
  31. package/cjs/hooks/use-event-resize.cjs.map +1 -1
  32. package/cjs/hooks/use-horizontal-event-resize.cjs +37 -12
  33. package/cjs/hooks/use-horizontal-event-resize.cjs.map +1 -1
  34. package/cjs/hooks/validate-event-resize.cjs +37 -0
  35. package/cjs/hooks/validate-event-resize.cjs.map +1 -0
  36. package/cjs/index.cjs +2 -0
  37. package/cjs/utils/is-event-placement-allowed/is-event-placement-allowed.cjs +52 -0
  38. package/cjs/utils/is-event-placement-allowed/is-event-placement-allowed.cjs.map +1 -0
  39. package/esm/components/DayView/DayView.mjs +46 -31
  40. package/esm/components/DayView/DayView.mjs.map +1 -1
  41. package/esm/components/DayView/DayView.module.mjs.map +1 -1
  42. package/esm/components/MonthView/MonthView.mjs +11 -4
  43. package/esm/components/MonthView/MonthView.mjs.map +1 -1
  44. package/esm/components/MonthView/get-month-view-events/get-month-positioned-events.mjs +10 -8
  45. package/esm/components/MonthView/get-month-view-events/get-month-positioned-events.mjs.map +1 -1
  46. package/esm/components/MonthView/get-month-view-events/reserve-row.mjs +12 -0
  47. package/esm/components/MonthView/get-month-view-events/reserve-row.mjs.map +1 -0
  48. package/esm/components/ResourcesDayView/ResourcesDayView.mjs +75 -60
  49. package/esm/components/ResourcesDayView/ResourcesDayView.mjs.map +1 -1
  50. package/esm/components/ResourcesDayView/ResourcesDayView.module.mjs.map +1 -1
  51. package/esm/components/ResourcesMonthView/ResourcesMonthView.mjs +19 -7
  52. package/esm/components/ResourcesMonthView/ResourcesMonthView.mjs.map +1 -1
  53. package/esm/components/ResourcesSchedule/ResourcesSchedule.mjs +6 -1
  54. package/esm/components/ResourcesSchedule/ResourcesSchedule.mjs.map +1 -1
  55. package/esm/components/ResourcesWeekView/ResourcesWeekView.mjs +85 -58
  56. package/esm/components/ResourcesWeekView/ResourcesWeekView.mjs.map +1 -1
  57. package/esm/components/ResourcesWeekView/ResourcesWeekView.module.mjs.map +1 -1
  58. package/esm/components/Schedule/Schedule.mjs +14 -1
  59. package/esm/components/Schedule/Schedule.mjs.map +1 -1
  60. package/esm/components/ScheduleEvent/ScheduleEvent.module.mjs.map +1 -1
  61. package/esm/components/WeekView/WeekView.mjs +70 -46
  62. package/esm/components/WeekView/WeekView.mjs.map +1 -1
  63. package/esm/components/WeekView/WeekView.module.mjs.map +1 -1
  64. package/esm/hooks/use-day-grid-event-resize.mjs +38 -8
  65. package/esm/hooks/use-day-grid-event-resize.mjs.map +1 -1
  66. package/esm/hooks/use-drag-drop-handlers.mjs +126 -10
  67. package/esm/hooks/use-drag-drop-handlers.mjs.map +1 -1
  68. package/esm/hooks/use-event-resize.mjs +36 -12
  69. package/esm/hooks/use-event-resize.mjs.map +1 -1
  70. package/esm/hooks/use-horizontal-event-resize.mjs +37 -12
  71. package/esm/hooks/use-horizontal-event-resize.mjs.map +1 -1
  72. package/esm/hooks/validate-event-resize.mjs +37 -0
  73. package/esm/hooks/validate-event-resize.mjs.map +1 -0
  74. package/esm/index.mjs +2 -1
  75. package/esm/utils/is-event-placement-allowed/is-event-placement-allowed.mjs +50 -0
  76. package/esm/utils/is-event-placement-allowed/is-event-placement-allowed.mjs.map +1 -0
  77. package/lib/components/DayView/DayView.d.ts +11 -1
  78. package/lib/components/MonthView/MonthView.d.ts +9 -1
  79. package/lib/components/MonthView/get-month-view-events/reserve-row.d.ts +7 -0
  80. package/lib/components/ResourcesDayView/ResourcesDayView.d.ts +11 -1
  81. package/lib/components/ResourcesMonthView/ResourcesMonthView.d.ts +11 -1
  82. package/lib/components/ResourcesSchedule/ResourcesSchedule.d.ts +12 -2
  83. package/lib/components/ResourcesWeekView/ResourcesWeekView.d.ts +11 -1
  84. package/lib/components/Schedule/Schedule.d.ts +12 -2
  85. package/lib/components/WeekView/WeekView.d.ts +11 -1
  86. package/lib/hooks/use-day-grid-event-resize.d.ts +4 -2
  87. package/lib/hooks/use-drag-drop-handlers.d.ts +17 -1
  88. package/lib/hooks/use-event-resize.d.ts +4 -2
  89. package/lib/hooks/use-horizontal-event-resize.d.ts +4 -2
  90. package/lib/hooks/validate-event-resize.d.ts +33 -0
  91. package/lib/types.d.ts +65 -0
  92. package/lib/utils/index.d.ts +2 -0
  93. package/lib/utils/is-event-placement-allowed/is-event-placement-allowed.d.ts +22 -0
  94. package/package.json +6 -6
  95. package/styles.css +29 -0
  96. package/styles.layer.css +29 -0
  97. package/cjs/components/MonthView/get-month-view-events/find-available-row.cjs +0 -18
  98. package/cjs/components/MonthView/get-month-view-events/find-available-row.cjs.map +0 -1
  99. package/esm/components/MonthView/get-month-view-events/find-available-row.mjs +0 -16
  100. package/esm/components/MonthView/get-month-view-events/find-available-row.mjs.map +0 -1
  101. package/lib/components/MonthView/get-month-view-events/find-available-row.d.ts +0 -10
@@ -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)) stableOnEventResize({
78
- eventId: state.eventId,
79
- newStart: state.start,
80
- newEnd: state.end,
81
- event: state.event
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 eventId: state.eventId,\n newStart: state.start,\n newEnd: state.end,\n event: state.event,\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 };\n}\n"],"mappings":"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"}
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 { start, end } = calculateDropTarget(target, dragState.state.draggedEvent);
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: (0, dayjs.default)(start).format("YYYY-MM-DD HH:mm:ss"),
73
- newEnd: (0, dayjs.default)(end).format("YYYY-MM-DD HH:mm:ss"),
74
- event: dragState.state.draggedEvent
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
- stableOnExternalDrop(event, target);
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
- let newStart;
109
- let newEnd;
110
- if (state.edge === "top") {
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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"}