@primeui/scheduler-core 0.0.1-alpha.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 (63) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +1 -0
  3. package/dist/calendar/index.d.mts +57 -0
  4. package/dist/calendar/index.mjs +1 -0
  5. package/dist/chunk-2B3YLWHA.mjs +196 -0
  6. package/dist/chunk-2THQAZ26.mjs +1669 -0
  7. package/dist/chunk-5KORIWDT.mjs +41 -0
  8. package/dist/chunk-5N4ZOBJV.mjs +866 -0
  9. package/dist/chunk-6OZAPQZ5.mjs +229 -0
  10. package/dist/chunk-6PK5WSKT.mjs +369 -0
  11. package/dist/chunk-6VYWVIGM.mjs +1170 -0
  12. package/dist/chunk-AAVM7UCG.mjs +100 -0
  13. package/dist/chunk-C7ADJGNV.mjs +157 -0
  14. package/dist/chunk-DYW6WUHE.mjs +277 -0
  15. package/dist/chunk-F5W5HD7S.mjs +285 -0
  16. package/dist/chunk-FIBAZFC4.mjs +871 -0
  17. package/dist/chunk-HPC5B3AR.mjs +558 -0
  18. package/dist/chunk-KQGRXTP5.mjs +650 -0
  19. package/dist/chunk-NMX4BW42.mjs +672 -0
  20. package/dist/chunk-NX46LPLF.mjs +440 -0
  21. package/dist/chunk-NZGJN7HG.mjs +314 -0
  22. package/dist/chunk-QDMZBJDV.mjs +251 -0
  23. package/dist/chunk-QR2SVYAD.mjs +1144 -0
  24. package/dist/chunk-SYJ5O4KH.mjs +136 -0
  25. package/dist/chunk-TNKJPFGI.mjs +569 -0
  26. package/dist/chunk-UMAMDBU4.mjs +1 -0
  27. package/dist/chunk-W2SJW3QQ.mjs +3925 -0
  28. package/dist/chunk-WFUJWDST.mjs +352 -0
  29. package/dist/chunk-XUBQ2IQS.mjs +1 -0
  30. package/dist/chunk-ZUKUKGNK.mjs +613 -0
  31. package/dist/controllers/index.d.mts +384 -0
  32. package/dist/controllers/index.mjs +13 -0
  33. package/dist/date-D_CjQPmM.d.mts +74 -0
  34. package/dist/display-format-CLVvRt4I.d.mts +57 -0
  35. package/dist/event/index.d.mts +267 -0
  36. package/dist/event/index.mjs +8 -0
  37. package/dist/event-surface-_R_LHD95.d.mts +21 -0
  38. package/dist/event.positioning-BdzAVPk7.d.mts +51 -0
  39. package/dist/event.utils-QSNdd-3W.d.mts +35 -0
  40. package/dist/index.d.mts +1128 -0
  41. package/dist/index.mjs +1022 -0
  42. package/dist/interaction/index.d.mts +442 -0
  43. package/dist/interaction/index.mjs +9 -0
  44. package/dist/month/index.d.mts +104 -0
  45. package/dist/month/index.mjs +6 -0
  46. package/dist/overlay-BYM9B6nC.d.mts +64 -0
  47. package/dist/resource/index.d.mts +172 -0
  48. package/dist/resource/index.mjs +1 -0
  49. package/dist/selection-CO_98HdS.d.mts +56 -0
  50. package/dist/time-grid/index.d.mts +92 -0
  51. package/dist/time-grid/index.mjs +13 -0
  52. package/dist/timeline/index.d.mts +165 -0
  53. package/dist/timeline/index.mjs +6 -0
  54. package/dist/touch-BhsMWsjf.d.mts +69 -0
  55. package/dist/utils/index.d.mts +494 -0
  56. package/dist/utils/index.mjs +17 -0
  57. package/dist/views/index.d.mts +51 -0
  58. package/dist/views/index.mjs +8 -0
  59. package/dist/views/timeline/index.d.mts +37 -0
  60. package/dist/views/timeline/index.mjs +4 -0
  61. package/dist/year/index.d.mts +70 -0
  62. package/dist/year/index.mjs +6 -0
  63. package/package.json +58 -0
@@ -0,0 +1,1669 @@
1
+ import { canDropEventWithResourceConstraints, getResourceCapacity } from './chunk-NX46LPLF.mjs';
2
+ import { getEventStart, moveEventTo, getEventEnd, resizeEventBy } from './chunk-2B3YLWHA.mjs';
3
+ import { addDays, addYears, addMonths, parseTimeString, timeStringToMinutes, isSameDay, startOfWeek } from './chunk-WFUJWDST.mjs';
4
+ import { formatTime, formatTimeRange, createSchedulerDateFormatter } from './chunk-TNKJPFGI.mjs';
5
+ import { differenceInCalendarDays, addCalendarDaysToDate, resolveSchedulerCalendarModeForView, getSchedulerCalendarAdapter } from './chunk-DYW6WUHE.mjs';
6
+ import { findResourceById } from './chunk-QR2SVYAD.mjs';
7
+
8
+ // src/interaction/drag.ts
9
+ var DEFAULT_DRAG_MIN_DISTANCE = 5;
10
+ var DEFAULT_SNAP_DURATION = 15;
11
+ function createDragState(event, startX, startY, offsetX, offsetY) {
12
+ return {
13
+ event: { ...event },
14
+ originalEvent: { ...event },
15
+ startX,
16
+ startY,
17
+ currentX: startX,
18
+ currentY: startY,
19
+ offsetX,
20
+ offsetY,
21
+ delta: { days: 0, milliseconds: 0 },
22
+ isDragging: false,
23
+ hasMovedMinDistance: false
24
+ };
25
+ }
26
+ function updateDragState(state, currentX, currentY, minDistance = DEFAULT_DRAG_MIN_DISTANCE) {
27
+ const dx = currentX - state.startX;
28
+ const dy = currentY - state.startY;
29
+ const distance = Math.sqrt(dx * dx + dy * dy);
30
+ return {
31
+ ...state,
32
+ currentX,
33
+ currentY,
34
+ hasMovedMinDistance: state.hasMovedMinDistance || distance >= minDistance,
35
+ isDragging: state.hasMovedMinDistance || distance >= minDistance
36
+ };
37
+ }
38
+ function snapToSlot(minutes, slotDuration) {
39
+ return Math.round(minutes / slotDuration) * slotDuration;
40
+ }
41
+ function snapTime(date, slotDuration) {
42
+ const totalMinutes = date.getHours() * 60 + date.getMinutes();
43
+ const snappedMinutes = snapToSlot(totalMinutes, slotDuration);
44
+ const result = new Date(date);
45
+ result.setHours(Math.floor(snappedMinutes / 60), snappedMinutes % 60, 0, 0);
46
+ return result;
47
+ }
48
+ function calculateTimeDelta(pixelDelta, slotHeight, slotDuration, snapDuration = slotDuration) {
49
+ const minutesDelta = pixelDelta / slotHeight * slotDuration;
50
+ return snapToSlot(minutesDelta, snapDuration) * 60 * 1e3;
51
+ }
52
+ function getSchedulerTimedInteractionDeltaMs(input) {
53
+ return calculateTimeDelta(input.pixelDelta, input.slotHeight, input.slotDuration, input.snapDuration);
54
+ }
55
+ function getSchedulerEventDropDelta(sourceStart, nextStart, options = {}) {
56
+ const days = differenceInCalendarDays(sourceStart, nextStart, options);
57
+ if (days === 0) {
58
+ return { days, milliseconds: nextStart.getTime() - sourceStart.getTime() };
59
+ }
60
+ const dayAdjustedStart = addCalendarDaysToDate(sourceStart, days, options);
61
+ return {
62
+ days,
63
+ milliseconds: nextStart.getTime() - dayAdjustedStart.getTime()
64
+ };
65
+ }
66
+ function createSchedulerEventDropCandidate({ source, date, resourceId, preserveTime = true, calendar, locale, view }) {
67
+ const sourceStart = getEventStart(source);
68
+ const target = new Date(date);
69
+ if (preserveTime) {
70
+ target.setHours(sourceStart.getHours(), sourceStart.getMinutes(), sourceStart.getSeconds(), sourceStart.getMilliseconds());
71
+ }
72
+ const targetResourceId = resourceId ?? source.resourceId;
73
+ const next = {
74
+ ...moveEventTo(source, target),
75
+ resourceId: targetResourceId
76
+ };
77
+ const nextStart = getEventStart(next);
78
+ const nextEnd = getEventEnd(next);
79
+ const delta = getSchedulerEventDropDelta(sourceStart, nextStart, { calendar, locale, view });
80
+ return { next, nextStart, nextEnd, sourceStart, targetResourceId, delta };
81
+ }
82
+ function calculateDayDelta(pixelDelta, columnWidth) {
83
+ return Math.round(pixelDelta / columnWidth);
84
+ }
85
+ function shouldApplyCalendarDayDelta(calendarOptions) {
86
+ if (!calendarOptions) {
87
+ return false;
88
+ }
89
+ const mode = resolveSchedulerCalendarModeForView(calendarOptions.calendar ?? "gregory", calendarOptions.view);
90
+ if (mode === "gregory") {
91
+ return false;
92
+ }
93
+ return getSchedulerCalendarAdapter(mode, { locale: calendarOptions.locale }).id !== "gregory";
94
+ }
95
+ function applyDeltaToEvent(event, delta, calendarOptions) {
96
+ const start = typeof event.start === "string" ? new Date(event.start) : new Date(event.start);
97
+ const end = event.end ? typeof event.end === "string" ? new Date(event.end) : new Date(event.end) : null;
98
+ const hasCalendarDayDelta = shouldApplyCalendarDayDelta(calendarOptions) && delta.days !== 0;
99
+ if (hasCalendarDayDelta) {
100
+ const calendarStart = addCalendarDaysToDate(start, delta.days, calendarOptions);
101
+ const newStart2 = new Date(calendarStart.getTime() + delta.milliseconds);
102
+ const totalDelta2 = newStart2.getTime() - start.getTime();
103
+ const newEnd2 = end ? new Date(end.getTime() + totalDelta2) : void 0;
104
+ return {
105
+ ...event,
106
+ start: newStart2,
107
+ end: newEnd2
108
+ };
109
+ }
110
+ const dayMillis = delta.days * 24 * 60 * 60 * 1e3;
111
+ const totalDelta = dayMillis + delta.milliseconds;
112
+ const newStart = new Date(start.getTime() + totalDelta);
113
+ const newEnd = end ? new Date(end.getTime() + totalDelta) : void 0;
114
+ return {
115
+ ...event,
116
+ start: newStart,
117
+ end: newEnd
118
+ };
119
+ }
120
+ function validateDragPosition(event, delta, constraints, allEvents) {
121
+ const newEvent = applyDeltaToEvent(event, delta);
122
+ const newStart = typeof newEvent.start === "string" ? new Date(newEvent.start) : newEvent.start;
123
+ const newEnd = newEvent.end ? typeof newEvent.end === "string" ? new Date(newEvent.end) : newEvent.end : newStart;
124
+ if (constraints.minDate && newStart < constraints.minDate) {
125
+ return { valid: false, reason: "Event would be before minimum date" };
126
+ }
127
+ if (constraints.maxDate && newEnd > constraints.maxDate) {
128
+ return { valid: false, reason: "Event would be after maximum date" };
129
+ }
130
+ if (constraints.allowOverlap === false) {
131
+ const overlapping = findOverlappingEvents(newEvent, allEvents);
132
+ if (overlapping.length > 0) {
133
+ return { valid: false, reason: "Event would overlap with another event" };
134
+ }
135
+ }
136
+ return { valid: true };
137
+ }
138
+ function findOverlappingEvents(event, allEvents) {
139
+ const eventStart = typeof event.start === "string" ? new Date(event.start) : event.start;
140
+ const eventEnd = event.end ? typeof event.end === "string" ? new Date(event.end) : event.end : new Date(eventStart.getTime() + 30 * 60 * 1e3);
141
+ return allEvents.filter((other) => {
142
+ if (other.id === event.id) return false;
143
+ const otherStart = typeof other.start === "string" ? new Date(other.start) : other.start;
144
+ const otherEnd = other.end ? typeof other.end === "string" ? new Date(other.end) : other.end : new Date(otherStart.getTime() + 30 * 60 * 1e3);
145
+ return eventStart < otherEnd && eventEnd > otherStart;
146
+ });
147
+ }
148
+ function isEventDraggable(event, globalEditable = false, eventStartEditable = true) {
149
+ if (event.draggable === false) return false;
150
+ if (event.editable === false) return false;
151
+ if (event.draggable === true) return true;
152
+ if (event.editable === true) return eventStartEditable;
153
+ return globalEditable && eventStartEditable;
154
+ }
155
+ function getTimeFromYPosition(yPosition, date, slotMinTime, slotDuration, slotHeight) {
156
+ const [minHours, minMinutes] = slotMinTime.split(":").map(Number);
157
+ const minTimeInMinutes = minHours * 60 + (minMinutes || 0);
158
+ const minutesPerPixel = slotDuration / slotHeight;
159
+ const minutesFromTop = yPosition * minutesPerPixel;
160
+ const totalMinutes = minTimeInMinutes + minutesFromTop;
161
+ const snappedMinutes = snapToSlot(totalMinutes, slotDuration);
162
+ const hours = Math.floor(snappedMinutes / 60);
163
+ const minutes = snappedMinutes % 60;
164
+ const result = new Date(date);
165
+ result.setHours(hours, minutes, 0, 0);
166
+ return result;
167
+ }
168
+ function getYPositionFromTime(date, slotMinTime, slotDuration, slotHeight) {
169
+ const [minHours, minMinutes] = slotMinTime.split(":").map(Number);
170
+ const minTimeInMinutes = minHours * 60 + (minMinutes || 0);
171
+ const timeInMinutes = date.getHours() * 60 + date.getMinutes();
172
+ const minutesFromMin = timeInMinutes - minTimeInMinutes;
173
+ return minutesFromMin / slotDuration * slotHeight;
174
+ }
175
+ function getDayIndexFromX(x, columnWidth, numColumns) {
176
+ const index = Math.floor(x / columnWidth);
177
+ return Math.max(0, Math.min(index, numColumns - 1));
178
+ }
179
+ function clampTime(date, slotMinTime, slotMaxTime) {
180
+ const [minHours, minMinutes] = slotMinTime.split(":").map(Number);
181
+ const [maxHours, maxMinutes] = slotMaxTime.split(":").map(Number);
182
+ const minTimeInMinutes = minHours * 60 + (minMinutes || 0);
183
+ const maxTimeInMinutes = maxHours * 60 + (maxMinutes || 0);
184
+ const currentMinutes = date.getHours() * 60 + date.getMinutes();
185
+ const clampedMinutes = Math.max(minTimeInMinutes, Math.min(currentMinutes, maxTimeInMinutes));
186
+ const result = new Date(date);
187
+ result.setHours(Math.floor(clampedMinutes / 60), clampedMinutes % 60, 0, 0);
188
+ return result;
189
+ }
190
+ function calculateDragDelta(state, columnWidth, slotHeight, slotDuration) {
191
+ const dxPixels = state.currentX - state.startX;
192
+ const dyPixels = state.currentY - state.startY;
193
+ const days = calculateDayDelta(dxPixels, columnWidth);
194
+ const milliseconds = calculateTimeDelta(dyPixels, slotHeight, slotDuration);
195
+ return { days, milliseconds };
196
+ }
197
+
198
+ // src/interaction/auto-scroll.ts
199
+ var DEFAULT_THRESHOLD = 48;
200
+ var DEFAULT_MAX_SPEED = 18;
201
+ var DEFAULT_INTERVAL = 16;
202
+ function resolveEdgeSpeed(distanceIntoEdge, threshold, maxSpeed) {
203
+ if (distanceIntoEdge <= 0) return 0;
204
+ const ratio = Math.min(1, distanceIntoEdge / threshold);
205
+ return Math.max(1, Math.round(maxSpeed * ratio));
206
+ }
207
+ function resolveAutoScrollDelta(input) {
208
+ const { pointerX, pointerY, rect, canScrollX, canScrollY, threshold, maxSpeed } = input;
209
+ let x = 0;
210
+ let y = 0;
211
+ if (canScrollX && pointerY >= rect.top && pointerY <= rect.bottom) {
212
+ if (pointerX >= rect.left && pointerX <= rect.left + threshold) {
213
+ x = -resolveEdgeSpeed(rect.left + threshold - pointerX, threshold, maxSpeed);
214
+ } else if (pointerX <= rect.right && pointerX >= rect.right - threshold) {
215
+ x = resolveEdgeSpeed(pointerX - (rect.right - threshold), threshold, maxSpeed);
216
+ }
217
+ }
218
+ if (canScrollY && pointerX >= rect.left && pointerX <= rect.right) {
219
+ if (pointerY >= rect.top && pointerY <= rect.top + threshold) {
220
+ y = -resolveEdgeSpeed(rect.top + threshold - pointerY, threshold, maxSpeed);
221
+ } else if (pointerY <= rect.bottom && pointerY >= rect.bottom - threshold) {
222
+ y = resolveEdgeSpeed(pointerY - (rect.bottom - threshold), threshold, maxSpeed);
223
+ }
224
+ }
225
+ return { x, y };
226
+ }
227
+ function createDragAutoScroller(options) {
228
+ const threshold = options.threshold ?? DEFAULT_THRESHOLD;
229
+ const maxSpeed = options.maxSpeed ?? DEFAULT_MAX_SPEED;
230
+ const interval = options.interval ?? DEFAULT_INTERVAL;
231
+ let point = null;
232
+ let timer = null;
233
+ const isEnabled = () => (typeof options.enabled === "function" ? options.enabled() : options.enabled) !== false;
234
+ const stop = () => {
235
+ if (timer !== null) {
236
+ clearInterval(timer);
237
+ timer = null;
238
+ }
239
+ point = null;
240
+ };
241
+ const tick = () => {
242
+ if (!point || !isEnabled()) {
243
+ stop();
244
+ return;
245
+ }
246
+ let scrolled = false;
247
+ for (const target of options.getContainers(point)) {
248
+ if (!target) continue;
249
+ const rect = target.measure();
250
+ const delta = resolveAutoScrollDelta({
251
+ pointerX: point.clientX,
252
+ pointerY: point.clientY,
253
+ rect,
254
+ canScrollX: isScrollContainerScrollable(target, "x"),
255
+ canScrollY: isScrollContainerScrollable(target, "y"),
256
+ threshold,
257
+ maxSpeed
258
+ });
259
+ scrolled = scrollTarget(target, delta) || scrolled;
260
+ if (scrolled) break;
261
+ }
262
+ if (!scrolled) {
263
+ stop();
264
+ }
265
+ };
266
+ const update = (nextPoint) => {
267
+ if (!isEnabled()) {
268
+ stop();
269
+ return;
270
+ }
271
+ point = nextPoint;
272
+ tick();
273
+ if (point && timer === null) {
274
+ timer = setInterval(tick, interval);
275
+ }
276
+ };
277
+ return {
278
+ update,
279
+ stop,
280
+ destroy: stop,
281
+ isActive: () => timer !== null
282
+ };
283
+ }
284
+ function isScrollContainerScrollable(target, axis) {
285
+ if (axis === "x") {
286
+ return target.scrollWidth > target.clientWidth;
287
+ }
288
+ return target.scrollHeight > target.clientHeight;
289
+ }
290
+ function scrollTarget(target, delta) {
291
+ if (delta.x === 0 && delta.y === 0) return false;
292
+ const beforeLeft = target.scrollLeft;
293
+ const beforeTop = target.scrollTop;
294
+ if (delta.x !== 0) {
295
+ target.scrollLeft += delta.x;
296
+ }
297
+ if (delta.y !== 0) {
298
+ target.scrollTop += delta.y;
299
+ }
300
+ return target.scrollLeft !== beforeLeft || target.scrollTop !== beforeTop;
301
+ }
302
+
303
+ // src/interaction/event-selection.ts
304
+ var RESOURCE_VIEWS = ["resourceDay", "resourceWeek", "resourceMonth", "resourceTimelineDay", "resourceTimelineWeek", "resourceTimelineMonth"];
305
+ function isResourceView(view) {
306
+ return RESOURCE_VIEWS.includes(view);
307
+ }
308
+ function eventBelongsToResource(event, resourceId) {
309
+ if (event.resourceId === resourceId) return true;
310
+ if (Array.isArray(event.resourceIds) && event.resourceIds.includes(resourceId)) return true;
311
+ return false;
312
+ }
313
+ function filterVisibleIdsByResource(visibleIds, events, resourceId, currentView) {
314
+ if (!isResourceView(currentView) || resourceId === void 0) {
315
+ return visibleIds;
316
+ }
317
+ const eventMap = new Map(events.map((e) => [e.id, e]));
318
+ return visibleIds.filter((id) => {
319
+ const event = eventMap.get(id);
320
+ if (!event) return false;
321
+ return eventBelongsToResource(event, resourceId);
322
+ });
323
+ }
324
+ var DEFAULT_OPTIONS = {
325
+ multiple: true,
326
+ selectMode: "click",
327
+ preserveOnNavigation: false,
328
+ maxSelection: void 0
329
+ };
330
+ function normalizeEventSelectionOptions(options) {
331
+ if (!options) {
332
+ return { ...DEFAULT_OPTIONS, enabled: false };
333
+ }
334
+ if (options === true) {
335
+ return { ...DEFAULT_OPTIONS, enabled: true };
336
+ }
337
+ return {
338
+ enabled: true,
339
+ multiple: options.multiple ?? DEFAULT_OPTIONS.multiple,
340
+ selectMode: options.selectMode ?? DEFAULT_OPTIONS.selectMode,
341
+ preserveOnNavigation: options.preserveOnNavigation ?? DEFAULT_OPTIONS.preserveOnNavigation,
342
+ maxSelection: options.maxSelection
343
+ };
344
+ }
345
+ var applyMaxSelection = (ids, maxSelection) => {
346
+ if (!maxSelection || maxSelection <= 0) {
347
+ return { ids, limitReached: false };
348
+ }
349
+ if (ids.length <= maxSelection) {
350
+ return { ids, limitReached: false };
351
+ }
352
+ return { ids: ids.slice(0, maxSelection), limitReached: true };
353
+ };
354
+ var buildRange = (anchorId, targetId, visibleIds) => {
355
+ const anchorIndex = visibleIds.indexOf(anchorId);
356
+ const targetIndex = visibleIds.indexOf(targetId);
357
+ if (anchorIndex === -1 || targetIndex === -1) {
358
+ return [targetId];
359
+ }
360
+ const start = Math.min(anchorIndex, targetIndex);
361
+ const end = Math.max(anchorIndex, targetIndex);
362
+ return visibleIds.slice(start, end + 1);
363
+ };
364
+ function applyEventSelection(params) {
365
+ const { eventId, visibleIds, modifiers, options, state } = params;
366
+ const selectedSet = new Set(state.selectedEventIds);
367
+ const isSelected = selectedSet.has(eventId);
368
+ if (!options.enabled) {
369
+ return { ...state, changed: false };
370
+ }
371
+ if (!options.multiple) {
372
+ return {
373
+ selectedEventIds: [eventId],
374
+ lastSelected: eventId,
375
+ selectionAnchor: eventId,
376
+ selectionModeActive: state.selectionModeActive,
377
+ changed: state.selectedEventIds.length !== 1 || state.selectedEventIds[0] !== eventId
378
+ };
379
+ }
380
+ const wantsRange = Boolean(modifiers.shiftKey && state.selectionAnchor);
381
+ const wantsToggle = Boolean(modifiers.ctrlKey || modifiers.metaKey || modifiers.toggle);
382
+ if (wantsRange && state.selectionAnchor !== null) {
383
+ const range = buildRange(state.selectionAnchor, eventId, visibleIds);
384
+ const { ids: nextIds, limitReached } = applyMaxSelection(range, options.maxSelection);
385
+ return {
386
+ selectedEventIds: nextIds,
387
+ lastSelected: eventId,
388
+ selectionAnchor: state.selectionAnchor,
389
+ selectionModeActive: state.selectionModeActive,
390
+ changed: true,
391
+ limitReached
392
+ };
393
+ }
394
+ if (wantsToggle) {
395
+ if (isSelected) {
396
+ const nextIds2 = state.selectedEventIds.filter((id) => id !== eventId);
397
+ const nextAnchor = state.selectionAnchor === eventId ? nextIds2[0] ?? null : state.selectionAnchor;
398
+ const nextLastSelected = nextIds2.length ? state.lastSelected : null;
399
+ return {
400
+ selectedEventIds: nextIds2,
401
+ lastSelected: nextLastSelected,
402
+ selectionAnchor: nextAnchor,
403
+ selectionModeActive: state.selectionModeActive,
404
+ changed: true
405
+ };
406
+ }
407
+ if (options.maxSelection && state.selectedEventIds.length >= options.maxSelection) {
408
+ return { ...state, changed: false, limitReached: true };
409
+ }
410
+ const nextIds = [...state.selectedEventIds, eventId];
411
+ return {
412
+ selectedEventIds: nextIds,
413
+ lastSelected: eventId,
414
+ selectionAnchor: state.selectionAnchor ?? eventId,
415
+ selectionModeActive: state.selectionModeActive,
416
+ changed: true
417
+ };
418
+ }
419
+ return {
420
+ selectedEventIds: [eventId],
421
+ lastSelected: eventId,
422
+ selectionAnchor: eventId,
423
+ selectionModeActive: state.selectionModeActive,
424
+ changed: state.selectedEventIds.length !== 1 || state.selectedEventIds[0] !== eventId
425
+ };
426
+ }
427
+ function selectAllVisibleEvents(visibleIds, options) {
428
+ if (!options.enabled) {
429
+ return {
430
+ selectedEventIds: [],
431
+ lastSelected: null,
432
+ selectionAnchor: null,
433
+ selectionModeActive: false,
434
+ changed: false
435
+ };
436
+ }
437
+ const { ids: nextIds, limitReached } = options.multiple ? applyMaxSelection(visibleIds, options.maxSelection) : { ids: visibleIds.slice(0, 1), limitReached: false };
438
+ const lastSelected = nextIds.length ? nextIds[nextIds.length - 1] : null;
439
+ return {
440
+ selectedEventIds: nextIds,
441
+ lastSelected,
442
+ selectionAnchor: nextIds[0] ?? null,
443
+ selectionModeActive: true,
444
+ changed: true,
445
+ limitReached
446
+ };
447
+ }
448
+ function invertSelection(currentIds, visibleIds, options) {
449
+ if (!options.enabled) {
450
+ return {
451
+ selectedEventIds: [],
452
+ lastSelected: null,
453
+ selectionAnchor: null,
454
+ selectionModeActive: false,
455
+ changed: false
456
+ };
457
+ }
458
+ const currentSet = new Set(currentIds);
459
+ const inverted = visibleIds.filter((id) => !currentSet.has(id));
460
+ const { ids: nextIds, limitReached } = options.multiple ? applyMaxSelection(inverted, options.maxSelection) : { ids: inverted.slice(0, 1), limitReached: false };
461
+ const lastSelected = nextIds.length ? nextIds[nextIds.length - 1] : null;
462
+ return {
463
+ selectedEventIds: nextIds,
464
+ lastSelected,
465
+ selectionAnchor: nextIds[0] ?? null,
466
+ selectionModeActive: nextIds.length > 0,
467
+ changed: true,
468
+ limitReached
469
+ };
470
+ }
471
+ function clearEventSelection() {
472
+ return {
473
+ selectedEventIds: [],
474
+ lastSelected: null,
475
+ selectionAnchor: null,
476
+ selectionModeActive: false
477
+ };
478
+ }
479
+
480
+ // src/interaction/resize.ts
481
+ var RESIZE_HANDLE_SIZE = 8;
482
+ function createResizeState(event, edge, startY) {
483
+ return {
484
+ event: { ...event },
485
+ originalEvent: { ...event },
486
+ edge,
487
+ startY,
488
+ currentY: startY,
489
+ startDelta: 0,
490
+ endDelta: 0,
491
+ isResizing: false
492
+ };
493
+ }
494
+ function updateResizeState(state, currentY, slotHeight, slotDuration, snapDuration = slotDuration) {
495
+ const dyPixels = currentY - state.startY;
496
+ const minutesDelta = dyPixels / slotHeight * slotDuration;
497
+ const snappedMinutes = snapToSlot(minutesDelta, snapDuration);
498
+ const deltaMs = snappedMinutes * 60 * 1e3;
499
+ let startDelta = 0;
500
+ let endDelta = 0;
501
+ if (state.edge === "top") {
502
+ startDelta = deltaMs;
503
+ } else {
504
+ endDelta = deltaMs;
505
+ }
506
+ return {
507
+ ...state,
508
+ currentY,
509
+ startDelta,
510
+ endDelta,
511
+ isResizing: Math.abs(dyPixels) > 2
512
+ };
513
+ }
514
+ function applyResizeToEvent(event, startDelta, endDelta) {
515
+ const start = typeof event.start === "string" ? new Date(event.start) : new Date(event.start);
516
+ const end = event.end ? typeof event.end === "string" ? new Date(event.end) : new Date(event.end) : new Date(start.getTime() + 30 * 60 * 1e3);
517
+ const newStart = new Date(start.getTime() + startDelta);
518
+ const newEnd = new Date(end.getTime() + endDelta);
519
+ if (newStart > newEnd) {
520
+ return event;
521
+ }
522
+ return {
523
+ ...event,
524
+ start: newStart,
525
+ end: newEnd
526
+ };
527
+ }
528
+ function getSchedulerTimedResizePreview(input) {
529
+ const minDuration = input.snapMinutes * 6e4;
530
+ const nextStartTime = input.edge === "top" ? Math.min(input.start.getTime() + input.deltaMinutes * 6e4, input.end.getTime() - minDuration) : input.start.getTime();
531
+ const nextEndTime = input.edge === "bottom" ? Math.max(input.end.getTime() + input.deltaMinutes * 6e4, input.start.getTime() + minDuration) : input.end.getTime();
532
+ const event = input.edge === "top" ? { ...input.event, start: new Date(nextStartTime) } : resizeEventBy(input.event, (nextEndTime - input.end.getTime()) / 6e4);
533
+ return {
534
+ edge: input.edge,
535
+ event,
536
+ startDelta: getEventStart(event).getTime() - input.start.getTime(),
537
+ endDelta: getEventEnd(event).getTime() - input.end.getTime()
538
+ };
539
+ }
540
+ function getSchedulerTimelineResizePreview(input) {
541
+ const minDuration = input.snapMinutes * 6e4;
542
+ const nextStartTime = input.edge === "start" ? Math.min(input.start.getTime() + input.deltaMs, input.end.getTime() - minDuration) : input.start.getTime();
543
+ const nextEndTime = input.edge === "end" ? Math.max(input.end.getTime() + input.deltaMs, input.start.getTime() + minDuration) : input.end.getTime();
544
+ const event = input.edge === "start" ? { ...input.event, start: new Date(nextStartTime) } : resizeEventBy(input.event, (nextEndTime - input.end.getTime()) / 6e4);
545
+ return {
546
+ edge: input.edge,
547
+ event,
548
+ startDelta: getEventStart(event).getTime() - input.start.getTime(),
549
+ endDelta: getEventEnd(event).getTime() - input.end.getTime()
550
+ };
551
+ }
552
+ function validateResize(event, startDelta, endDelta, constraints, allEvents, minDuration = 15) {
553
+ const newEvent = applyResizeToEvent(event, startDelta, endDelta);
554
+ const newStart = typeof newEvent.start === "string" ? new Date(newEvent.start) : newEvent.start;
555
+ const newEnd = newEvent.end ? typeof newEvent.end === "string" ? new Date(newEvent.end) : newEvent.end : newStart;
556
+ const durationMs = newEnd.getTime() - newStart.getTime();
557
+ const minDurationMs = minDuration * 60 * 1e3;
558
+ if (durationMs < minDurationMs) {
559
+ return { valid: false, reason: `Event must be at least ${minDuration} minutes` };
560
+ }
561
+ if (constraints.minDate && newStart < constraints.minDate) {
562
+ return { valid: false, reason: "Event would start before minimum date" };
563
+ }
564
+ if (constraints.maxDate && newEnd > constraints.maxDate) {
565
+ return { valid: false, reason: "Event would end after maximum date" };
566
+ }
567
+ if (constraints.allowOverlap === false) {
568
+ const overlapping = findOverlappingEvents(newEvent, allEvents);
569
+ if (overlapping.length > 0) {
570
+ return { valid: false, reason: "Event would overlap with another event" };
571
+ }
572
+ }
573
+ return { valid: true };
574
+ }
575
+ function isEventResizable(event, globalEditable = false, eventDurationEditable = true) {
576
+ if (event.resizable === false) return false;
577
+ if (event.editable === false) return false;
578
+ if (event.resizable === true) return true;
579
+ if (event.editable === true) return eventDurationEditable;
580
+ return globalEditable && eventDurationEditable;
581
+ }
582
+ function detectResizeEdge(mouseY, eventTop, eventHeight) {
583
+ const relativeY = mouseY - eventTop;
584
+ if (relativeY <= RESIZE_HANDLE_SIZE) {
585
+ return "top";
586
+ }
587
+ if (relativeY >= eventHeight - RESIZE_HANDLE_SIZE) {
588
+ return "bottom";
589
+ }
590
+ return null;
591
+ }
592
+ function getResizeCursor(edge) {
593
+ if (edge === "top" || edge === "bottom") {
594
+ return "ns-resize";
595
+ }
596
+ return "grab";
597
+ }
598
+ function calculateNewEventTimes(state, slotMinTime, slotMaxTime) {
599
+ const event = state.originalEvent;
600
+ const start = typeof event.start === "string" ? new Date(event.start) : new Date(event.start);
601
+ const end = event.end ? typeof event.end === "string" ? new Date(event.end) : new Date(event.end) : new Date(start.getTime() + 30 * 60 * 1e3);
602
+ const newStart = new Date(start.getTime() + state.startDelta);
603
+ const newEnd = new Date(end.getTime() + state.endDelta);
604
+ const [minHours, minMinutes] = slotMinTime.split(":").map(Number);
605
+ const [maxHours, maxMinutes] = slotMaxTime.split(":").map(Number);
606
+ const minTimeMinutes = minHours * 60 + (minMinutes || 0);
607
+ const maxTimeMinutes = maxHours * 60 + (maxMinutes || 0);
608
+ const newStartMinutes = newStart.getHours() * 60 + newStart.getMinutes();
609
+ const newEndMinutes = newEnd.getHours() * 60 + newEnd.getMinutes();
610
+ if (newStartMinutes < minTimeMinutes) {
611
+ newStart.setHours(minHours, minMinutes || 0, 0, 0);
612
+ }
613
+ if (newEndMinutes > maxTimeMinutes || maxTimeMinutes === 24 * 60 && newEndMinutes === 0) {
614
+ if (maxTimeMinutes === 24 * 60) {
615
+ newEnd.setHours(23, 59, 59, 999);
616
+ } else {
617
+ newEnd.setHours(maxHours, maxMinutes || 0, 0, 0);
618
+ }
619
+ }
620
+ if (newStart > newEnd) {
621
+ return null;
622
+ }
623
+ return { start: newStart, end: newEnd };
624
+ }
625
+
626
+ // src/interaction/keyboard.ts
627
+ var KEY_TO_ACTION_MAP = {
628
+ ArrowLeft: "prev-day",
629
+ ArrowRight: "next-day",
630
+ ArrowUp: "prev-week",
631
+ ArrowDown: "next-week",
632
+ Home: "start-of-week",
633
+ End: "end-of-week",
634
+ PageUp: "prev-month",
635
+ PageDown: "next-month",
636
+ Enter: "select",
637
+ " ": "select",
638
+ Escape: "escape",
639
+ Tab: "tab"
640
+ };
641
+ var TIME_VIEW_KEY_OVERRIDES = {
642
+ ArrowUp: "prev-time-slot",
643
+ ArrowDown: "next-time-slot"
644
+ };
645
+ var YEAR_VIEW_KEY_OVERRIDES = {
646
+ PageUp: "prev-year",
647
+ PageDown: "next-year"
648
+ };
649
+ var AGENDA_VIEW_KEYS = /* @__PURE__ */ new Set(["Escape", "Tab"]);
650
+ function getKeyboardAction(key, viewType) {
651
+ if (viewType === "agenda" && !AGENDA_VIEW_KEYS.has(key)) {
652
+ return null;
653
+ }
654
+ if (viewType === "week" || viewType === "day") {
655
+ const override = TIME_VIEW_KEY_OVERRIDES[key];
656
+ if (override) return override;
657
+ }
658
+ if (viewType === "year") {
659
+ const override = YEAR_VIEW_KEY_OVERRIDES[key];
660
+ if (override) return override;
661
+ }
662
+ return KEY_TO_ACTION_MAP[key] || null;
663
+ }
664
+ function getStartOfWeek(date, firstDayOfWeek = 0) {
665
+ const result = new Date(date);
666
+ const day = result.getDay();
667
+ const diff = (day - firstDayOfWeek + 7) % 7;
668
+ result.setDate(result.getDate() - diff);
669
+ result.setHours(0, 0, 0, 0);
670
+ return result;
671
+ }
672
+ function getEndOfWeek(date, firstDayOfWeek = 0) {
673
+ const start = getStartOfWeek(date, firstDayOfWeek);
674
+ return addDays(start, 6);
675
+ }
676
+ function addMinutesToDate(date, minutes) {
677
+ const result = new Date(date);
678
+ result.setMinutes(result.getMinutes() + minutes);
679
+ return result;
680
+ }
681
+ function calculateNewFocusedDate(action, options) {
682
+ const { currentDate, firstDayOfWeek = 0, slotDuration = 30 } = options;
683
+ switch (action) {
684
+ case "prev-day":
685
+ return addDays(currentDate, -1);
686
+ case "next-day":
687
+ return addDays(currentDate, 1);
688
+ case "prev-week":
689
+ return addDays(currentDate, -7);
690
+ case "next-week":
691
+ return addDays(currentDate, 7);
692
+ case "start-of-week":
693
+ return getStartOfWeek(currentDate, firstDayOfWeek);
694
+ case "end-of-week":
695
+ return getEndOfWeek(currentDate, firstDayOfWeek);
696
+ case "prev-month":
697
+ return addMonths(currentDate, -1);
698
+ case "next-month":
699
+ return addMonths(currentDate, 1);
700
+ case "prev-year":
701
+ return addYears(currentDate, -1);
702
+ case "next-year":
703
+ return addYears(currentDate, 1);
704
+ case "prev-time-slot":
705
+ return addMinutesToDate(currentDate, -slotDuration);
706
+ case "next-time-slot":
707
+ return addMinutesToDate(currentDate, slotDuration);
708
+ case "select":
709
+ case "escape":
710
+ case "tab":
711
+ return null;
712
+ default:
713
+ return null;
714
+ }
715
+ }
716
+ function handleKeyboardNavigation(input, viewType, currentFocusedDate, options) {
717
+ const action = getKeyboardAction(input.key, viewType);
718
+ if (!action) return null;
719
+ const effectiveCurrentDate = currentFocusedDate || /* @__PURE__ */ new Date();
720
+ const fullOptions = {
721
+ ...options,
722
+ currentDate: effectiveCurrentDate
723
+ };
724
+ const result = {
725
+ action,
726
+ newFocusedDate: null,
727
+ shouldPreventDefault: true,
728
+ shouldNavigate: null,
729
+ shouldSelect: false,
730
+ shouldClearFocus: false
731
+ };
732
+ switch (action) {
733
+ case "prev-month":
734
+ result.shouldNavigate = "prev";
735
+ result.newFocusedDate = calculateNewFocusedDate(action, fullOptions);
736
+ break;
737
+ case "next-month":
738
+ result.shouldNavigate = "next";
739
+ result.newFocusedDate = calculateNewFocusedDate(action, fullOptions);
740
+ break;
741
+ case "prev-year":
742
+ result.shouldNavigate = "prev";
743
+ result.newFocusedDate = calculateNewFocusedDate(action, fullOptions);
744
+ break;
745
+ case "next-year":
746
+ result.shouldNavigate = "next";
747
+ result.newFocusedDate = calculateNewFocusedDate(action, fullOptions);
748
+ break;
749
+ case "select":
750
+ if (currentFocusedDate) {
751
+ result.shouldSelect = true;
752
+ result.shouldClearFocus = true;
753
+ }
754
+ break;
755
+ case "escape":
756
+ result.shouldClearFocus = true;
757
+ break;
758
+ case "tab":
759
+ result.shouldPreventDefault = false;
760
+ break;
761
+ default:
762
+ result.newFocusedDate = calculateNewFocusedDate(action, fullOptions);
763
+ break;
764
+ }
765
+ return result;
766
+ }
767
+ function getPeriodNavigationDirection(input) {
768
+ if (input.key === "ArrowLeft" || input.key === "PageUp") {
769
+ return "prev";
770
+ }
771
+ if (input.key === "ArrowRight" || input.key === "PageDown") {
772
+ return "next";
773
+ }
774
+ return null;
775
+ }
776
+
777
+ // src/interaction/focus.ts
778
+ function snapTimeToSlot(date, slotMinTime, slotDuration) {
779
+ const { hours: minHours, minutes: minMinutes } = parseTimeString(slotMinTime);
780
+ const minTimeInMinutes = minHours * 60 + minMinutes;
781
+ const timeInMinutes = date.getHours() * 60 + date.getMinutes();
782
+ const minutesFromMin = timeInMinutes - minTimeInMinutes;
783
+ const snappedMinutesFromMin = Math.floor(minutesFromMin / slotDuration) * slotDuration;
784
+ const snappedTotalMinutes = minTimeInMinutes + snappedMinutesFromMin;
785
+ const result = new Date(date);
786
+ result.setHours(Math.floor(snappedTotalMinutes / 60), snappedTotalMinutes % 60, 0, 0);
787
+ return result;
788
+ }
789
+ function clampTimeToSlotBounds(date, slotMinTime, slotMaxTime, slotDuration) {
790
+ const minTimeInMinutes = timeStringToMinutes(slotMinTime);
791
+ const maxTimeInMinutes = timeStringToMinutes(slotMaxTime);
792
+ const timeInMinutes = date.getHours() * 60 + date.getMinutes();
793
+ const result = new Date(date);
794
+ if (timeInMinutes < minTimeInMinutes) {
795
+ const { hours, minutes } = parseTimeString(slotMinTime);
796
+ result.setHours(hours, minutes, 0, 0);
797
+ } else if (timeInMinutes >= maxTimeInMinutes) {
798
+ const lastSlotMinutes = maxTimeInMinutes - slotDuration;
799
+ result.setHours(Math.floor(lastSlotMinutes / 60), lastSlotMinutes % 60, 0, 0);
800
+ }
801
+ return result;
802
+ }
803
+ function getDefaultFocusDateForMonth(grid) {
804
+ const today = /* @__PURE__ */ new Date();
805
+ for (const week of grid) {
806
+ for (const cell of week) {
807
+ if (cell.isCurrentMonth && cell.isToday) {
808
+ return cell.date;
809
+ }
810
+ }
811
+ }
812
+ for (const week of grid) {
813
+ for (const cell of week) {
814
+ if (cell.isCurrentMonth) {
815
+ return cell.date;
816
+ }
817
+ }
818
+ }
819
+ return today;
820
+ }
821
+ function getDefaultFocusDateForWeek(columns, slotMinTime, slotDuration) {
822
+ const now = /* @__PURE__ */ new Date();
823
+ const todayColumn = columns.find((col) => col.isToday);
824
+ if (todayColumn) {
825
+ const result = new Date(todayColumn.date);
826
+ result.setHours(now.getHours(), now.getMinutes(), 0, 0);
827
+ return clampTimeToSlotBounds(snapTimeToSlot(result, slotMinTime, slotDuration), slotMinTime, "24:00", slotDuration);
828
+ }
829
+ if (columns.length > 0) {
830
+ const { hours, minutes } = parseTimeString(slotMinTime);
831
+ const result = new Date(columns[0].date);
832
+ result.setHours(hours, minutes, 0, 0);
833
+ return result;
834
+ }
835
+ return now;
836
+ }
837
+ function getDefaultFocusDateForDay(date, isToday, slotMinTime, slotDuration) {
838
+ const now = /* @__PURE__ */ new Date();
839
+ if (isToday) {
840
+ const result2 = new Date(date);
841
+ result2.setHours(now.getHours(), now.getMinutes(), 0, 0);
842
+ return clampTimeToSlotBounds(snapTimeToSlot(result2, slotMinTime, slotDuration), slotMinTime, "24:00", slotDuration);
843
+ }
844
+ const { hours, minutes } = parseTimeString(slotMinTime);
845
+ const result = new Date(date);
846
+ result.setHours(hours, minutes, 0, 0);
847
+ return result;
848
+ }
849
+ function getDefaultFocusDateForYear(displayedYear) {
850
+ const today = /* @__PURE__ */ new Date();
851
+ if (today.getFullYear() === displayedYear) {
852
+ return today;
853
+ }
854
+ return new Date(displayedYear, 0, 1);
855
+ }
856
+ function getDefaultFocusDate(context) {
857
+ switch (context.viewType) {
858
+ case "month":
859
+ return getDefaultFocusDateForMonth(context.grid);
860
+ case "week":
861
+ return getDefaultFocusDateForWeek(context.columns, context.slotMinTime ?? "00:00", context.slotDuration ?? 30);
862
+ case "day":
863
+ return getDefaultFocusDateForDay(context.currentDate, context.isToday, context.slotMinTime ?? "00:00", context.slotDuration ?? 30);
864
+ case "year":
865
+ return getDefaultFocusDateForYear(context.displayedYear);
866
+ default:
867
+ return /* @__PURE__ */ new Date();
868
+ }
869
+ }
870
+ function isDateInMonthView(date, grid) {
871
+ for (const week of grid) {
872
+ for (const cell of week) {
873
+ if (isSameDay(cell.date, date)) {
874
+ return true;
875
+ }
876
+ }
877
+ }
878
+ return false;
879
+ }
880
+ function isDateInWeekView(date, columns) {
881
+ return columns.some((col) => isSameDay(col.date, date));
882
+ }
883
+ function isDateInDayView(date, viewDate) {
884
+ return isSameDay(date, viewDate);
885
+ }
886
+ function isDateInYearView(date, displayedYear) {
887
+ return date.getFullYear() === displayedYear;
888
+ }
889
+ function getNavigationDirectionForMonth(targetDate, grid) {
890
+ let displayedMonth = -1;
891
+ let displayedYear = -1;
892
+ for (const week of grid) {
893
+ for (const cell of week) {
894
+ if (cell.isCurrentMonth) {
895
+ displayedMonth = cell.date.getMonth();
896
+ displayedYear = cell.date.getFullYear();
897
+ break;
898
+ }
899
+ }
900
+ if (displayedMonth >= 0) break;
901
+ }
902
+ if (displayedMonth < 0) {
903
+ const middleWeek = grid[Math.floor(grid.length / 2)];
904
+ const middleCell = middleWeek[Math.floor(middleWeek.length / 2)];
905
+ displayedMonth = middleCell.date.getMonth();
906
+ displayedYear = middleCell.date.getFullYear();
907
+ }
908
+ const targetMonth = targetDate.getMonth();
909
+ const targetYear = targetDate.getFullYear();
910
+ if (targetYear < displayedYear) return "prev";
911
+ if (targetYear > displayedYear) return "next";
912
+ if (targetMonth < displayedMonth) return "prev";
913
+ if (targetMonth > displayedMonth) return "next";
914
+ return null;
915
+ }
916
+ function getNavigationDirectionForWeek(targetDate, columns) {
917
+ if (columns.length === 0) return null;
918
+ const firstDay = columns[0].date;
919
+ const lastDay = columns[columns.length - 1].date;
920
+ if (targetDate < firstDay && !isSameDay(targetDate, firstDay)) return "prev";
921
+ if (targetDate > lastDay && !isSameDay(targetDate, lastDay)) return "next";
922
+ return null;
923
+ }
924
+ function getNavigationDirectionForDay(targetDate, viewDate) {
925
+ if (targetDate < viewDate && !isSameDay(targetDate, viewDate)) return "prev";
926
+ if (targetDate > viewDate && !isSameDay(targetDate, viewDate)) return "next";
927
+ return null;
928
+ }
929
+ function getNavigationDirectionForYear(targetDate, displayedYear) {
930
+ if (targetDate.getFullYear() < displayedYear) return "prev";
931
+ if (targetDate.getFullYear() > displayedYear) return "next";
932
+ return null;
933
+ }
934
+ function findCellForDate(date, grid) {
935
+ for (const week of grid) {
936
+ for (const cell of week) {
937
+ if (isSameDay(cell.date, date)) {
938
+ return cell;
939
+ }
940
+ }
941
+ }
942
+ return null;
943
+ }
944
+ function getDisplayedMonthFromGrid(grid) {
945
+ for (const week of grid) {
946
+ for (const cell of week) {
947
+ if (cell.isCurrentMonth) {
948
+ return { month: cell.date.getMonth(), year: cell.date.getFullYear() };
949
+ }
950
+ }
951
+ }
952
+ const middleWeek = grid[Math.floor(grid.length / 2)];
953
+ const middleCell = middleWeek[Math.floor(middleWeek.length / 2)];
954
+ return { month: middleCell.date.getMonth(), year: middleCell.date.getFullYear() };
955
+ }
956
+ function getStartOfWeekForFocus(date, firstDayOfWeek = 0) {
957
+ return startOfWeek(date, firstDayOfWeek);
958
+ }
959
+ function getEndOfWeekForFocus(date, firstDayOfWeek = 0) {
960
+ const start = startOfWeek(date, firstDayOfWeek);
961
+ return addDays(start, 6);
962
+ }
963
+
964
+ // src/interaction/popover.ts
965
+ function formatEventTimeRange(event, locale = "en", options, timeZone, displayConfig) {
966
+ if (event.allDay) {
967
+ return "All day";
968
+ }
969
+ const startDate = event.start instanceof Date ? event.start : new Date(event.start);
970
+ const endDate = event.end ? event.end instanceof Date ? event.end : new Date(event.end) : null;
971
+ if (!endDate) {
972
+ return formatTime(startDate, locale, options, timeZone);
973
+ }
974
+ const isSameDay2 = startDate.getFullYear() === endDate.getFullYear() && startDate.getMonth() === endDate.getMonth() && startDate.getDate() === endDate.getDate();
975
+ if (isSameDay2) {
976
+ return formatTimeRange(startDate, endDate, locale, options, timeZone);
977
+ }
978
+ const includeYear = startDate.getFullYear() !== endDate.getFullYear();
979
+ const dateOptions = includeYear ? { month: "short", day: "numeric", year: "numeric" } : { month: "short", day: "numeric" };
980
+ const formatter = createSchedulerDateFormatter({ ...displayConfig, locale, timeZone });
981
+ const startDateTime = `${formatter.format(startDate, dateOptions)} ${formatTime(startDate, locale, options, timeZone)}`;
982
+ const endDateTime = `${formatter.format(endDate, dateOptions)} ${formatTime(endDate, locale, options, timeZone)}`;
983
+ return `${startDateTime} - ${endDateTime}`;
984
+ }
985
+ function formatEventDate(event, locale = "en", displayConfig) {
986
+ const startDate = event.start instanceof Date ? event.start : new Date(event.start);
987
+ const endDate = event.end ? event.end instanceof Date ? event.end : new Date(event.end) : null;
988
+ const formatter = createSchedulerDateFormatter({ ...displayConfig, locale });
989
+ const dateFormat = {
990
+ weekday: "long",
991
+ month: "long",
992
+ day: "numeric",
993
+ year: "numeric"
994
+ };
995
+ const startDateStr = formatter.format(startDate, dateFormat);
996
+ if (!endDate) {
997
+ return startDateStr;
998
+ }
999
+ const isSameDay2 = startDate.getFullYear() === endDate.getFullYear() && startDate.getMonth() === endDate.getMonth() && startDate.getDate() === endDate.getDate();
1000
+ if (isSameDay2) {
1001
+ return startDateStr;
1002
+ }
1003
+ const endDateStr = formatter.format(endDate, dateFormat);
1004
+ return `${startDateStr} - ${endDateStr}`;
1005
+ }
1006
+ function formatEventDuration(event) {
1007
+ if (event.allDay) {
1008
+ const startDate2 = event.start instanceof Date ? event.start : new Date(event.start);
1009
+ const endDate2 = event.end ? event.end instanceof Date ? event.end : new Date(event.end) : startDate2;
1010
+ const startDay = new Date(startDate2.getFullYear(), startDate2.getMonth(), startDate2.getDate());
1011
+ const endDay = new Date(endDate2.getFullYear(), endDate2.getMonth(), endDate2.getDate());
1012
+ const days = Math.round((endDay.getTime() - startDay.getTime()) / (1e3 * 60 * 60 * 24)) + 1;
1013
+ if (days === 1) {
1014
+ return "1 day";
1015
+ }
1016
+ return `${days} days`;
1017
+ }
1018
+ const startDate = event.start instanceof Date ? event.start : new Date(event.start);
1019
+ const endDate = event.end ? event.end instanceof Date ? event.end : new Date(event.end) : null;
1020
+ if (!endDate) {
1021
+ return "";
1022
+ }
1023
+ const durationMs = endDate.getTime() - startDate.getTime();
1024
+ const durationMinutes = Math.round(durationMs / (1e3 * 60));
1025
+ if (durationMinutes < 60) {
1026
+ return `${durationMinutes} min`;
1027
+ }
1028
+ const hours = Math.floor(durationMinutes / 60);
1029
+ const minutes = durationMinutes % 60;
1030
+ if (minutes === 0) {
1031
+ return hours === 1 ? "1 hour" : `${hours} hours`;
1032
+ }
1033
+ if (hours === 1) {
1034
+ return `1 hour ${minutes} min`;
1035
+ }
1036
+ return `${hours} hours ${minutes} min`;
1037
+ }
1038
+
1039
+ // src/interaction/timeline-virtual.ts
1040
+ var DEFAULT_OVERSCAN = 2;
1041
+ var VIRTUAL_THRESHOLD = 100;
1042
+ function shouldUseVirtualScrolling(totalCells, threshold = VIRTUAL_THRESHOLD) {
1043
+ return totalCells > threshold;
1044
+ }
1045
+ function createVirtualScrollState(scrollLeft, containerWidth, totalWidth, overscan = DEFAULT_OVERSCAN) {
1046
+ return {
1047
+ scrollLeft,
1048
+ containerWidth,
1049
+ totalWidth,
1050
+ overscan
1051
+ };
1052
+ }
1053
+ function calculateVirtualRange(state, cells) {
1054
+ if (cells.length === 0) {
1055
+ return {
1056
+ startIndex: 0,
1057
+ endIndex: 0,
1058
+ startOffset: 0,
1059
+ visibleCells: []
1060
+ };
1061
+ }
1062
+ const { scrollLeft, containerWidth, overscan } = state;
1063
+ const viewportEnd = scrollLeft + containerWidth;
1064
+ let startIndex = 0;
1065
+ let endIndex = cells.length - 1;
1066
+ let accumulated = 0;
1067
+ for (let i = 0; i < cells.length; i++) {
1068
+ const cellEnd = accumulated + cells[i].width;
1069
+ if (cellEnd > scrollLeft) {
1070
+ startIndex = Math.max(0, i - overscan);
1071
+ break;
1072
+ }
1073
+ accumulated = cellEnd;
1074
+ }
1075
+ accumulated = 0;
1076
+ for (let i = 0; i < cells.length; i++) {
1077
+ accumulated += cells[i].width;
1078
+ if (accumulated >= viewportEnd) {
1079
+ endIndex = Math.min(cells.length - 1, i + overscan);
1080
+ break;
1081
+ }
1082
+ }
1083
+ let startOffset = 0;
1084
+ for (let i = 0; i < startIndex; i++) {
1085
+ startOffset += cells[i].width;
1086
+ }
1087
+ const visibleCells = cells.slice(startIndex, endIndex + 1);
1088
+ return {
1089
+ startIndex,
1090
+ endIndex,
1091
+ startOffset,
1092
+ visibleCells
1093
+ };
1094
+ }
1095
+ function calculateVisibleEvents(events, scrollLeft, containerWidth, bufferWidth = 100) {
1096
+ const viewportStart = scrollLeft - bufferWidth;
1097
+ const viewportEnd = scrollLeft + containerWidth + bufferWidth;
1098
+ const visibleEvents = [];
1099
+ const leftBuffer = [];
1100
+ const rightBuffer = [];
1101
+ for (const event of events) {
1102
+ const eventStart = event.left;
1103
+ const eventEnd = event.left + event.width;
1104
+ if (eventEnd < viewportStart - bufferWidth) {
1105
+ leftBuffer.push(event);
1106
+ } else if (eventStart > viewportEnd + bufferWidth) {
1107
+ rightBuffer.push(event);
1108
+ } else if (eventEnd >= viewportStart && eventStart <= viewportEnd) {
1109
+ visibleEvents.push(event);
1110
+ }
1111
+ }
1112
+ return {
1113
+ visibleEvents,
1114
+ leftBuffer,
1115
+ rightBuffer
1116
+ };
1117
+ }
1118
+ function updateVirtualScrollState(state, scrollLeft) {
1119
+ return {
1120
+ ...state,
1121
+ scrollLeft
1122
+ };
1123
+ }
1124
+ function getScrollPositionForDate(targetDate, viewStart, totalWidth, totalDuration, containerWidth, centerInView = true) {
1125
+ const targetMs = targetDate.getTime() - viewStart.getTime();
1126
+ let scrollPosition = targetMs / totalDuration * totalWidth;
1127
+ if (centerInView) {
1128
+ scrollPosition -= containerWidth / 2;
1129
+ }
1130
+ return Math.max(0, Math.min(scrollPosition, totalWidth - containerWidth));
1131
+ }
1132
+ function getScrollPositionForNow(viewStart, viewEnd, totalWidth, containerWidth) {
1133
+ const now = /* @__PURE__ */ new Date();
1134
+ if (now < viewStart || now > viewEnd) {
1135
+ return null;
1136
+ }
1137
+ const totalDuration = viewEnd.getTime() - viewStart.getTime();
1138
+ return getScrollPositionForDate(now, viewStart, totalWidth, totalDuration, containerWidth, true);
1139
+ }
1140
+ function isDateInVisibleRange(date, viewStart, scrollLeft, containerWidth, totalWidth, totalDuration) {
1141
+ const dateOffset = (date.getTime() - viewStart.getTime()) / totalDuration * totalWidth;
1142
+ return dateOffset >= scrollLeft && dateOffset <= scrollLeft + containerWidth;
1143
+ }
1144
+ function createChunkedCells(cells, chunkSize = 20) {
1145
+ const chunks = [];
1146
+ let currentChunk = [];
1147
+ let chunkWidth = 0;
1148
+ for (const cell of cells) {
1149
+ currentChunk.push(cell);
1150
+ if (currentChunk.length >= chunkSize) {
1151
+ chunks.push(currentChunk);
1152
+ currentChunk = [];
1153
+ }
1154
+ }
1155
+ if (currentChunk.length > 0) {
1156
+ chunks.push(currentChunk);
1157
+ }
1158
+ if (chunks.length > 0 && chunks[0].length > 0) {
1159
+ chunkWidth = chunks[0].reduce((sum, cell) => sum + cell.width, 0);
1160
+ }
1161
+ return {
1162
+ chunks,
1163
+ chunkWidth,
1164
+ totalChunks: chunks.length
1165
+ };
1166
+ }
1167
+ function getVisibleChunks(chunkedCells, scrollLeft, containerWidth, overscan = 1) {
1168
+ const { chunks, chunkWidth } = chunkedCells;
1169
+ if (chunks.length === 0) {
1170
+ return { startChunk: 0, endChunk: 0, visibleCells: [] };
1171
+ }
1172
+ const startChunk = Math.max(0, Math.floor(scrollLeft / chunkWidth) - overscan);
1173
+ const endChunk = Math.min(chunks.length - 1, Math.ceil((scrollLeft + containerWidth) / chunkWidth) + overscan);
1174
+ const visibleCells = [];
1175
+ for (let i = startChunk; i <= endChunk; i++) {
1176
+ visibleCells.push(...chunks[i]);
1177
+ }
1178
+ return { startChunk, endChunk, visibleCells };
1179
+ }
1180
+ function calculateTotalWidth(cells) {
1181
+ return cells.reduce((sum, cell) => sum + cell.width, 0);
1182
+ }
1183
+ function getEstimatedScrollPosition(targetIndex, averageCellWidth) {
1184
+ return targetIndex * averageCellWidth;
1185
+ }
1186
+ function getCellAtPosition(scrollLeft, cells) {
1187
+ let accumulated = 0;
1188
+ for (let i = 0; i < cells.length; i++) {
1189
+ accumulated += cells[i].width;
1190
+ if (accumulated > scrollLeft) {
1191
+ return { cell: cells[i], index: i };
1192
+ }
1193
+ }
1194
+ return cells.length > 0 ? { cell: cells[cells.length - 1], index: cells.length - 1 } : null;
1195
+ }
1196
+
1197
+ // src/interaction/quick-info.ts
1198
+ var DEFAULT_OPTIONS2 = {
1199
+ enabled: false,
1200
+ trigger: "click",
1201
+ editorTrigger: "button",
1202
+ showDelete: true,
1203
+ showEdit: true,
1204
+ autoClose: 0
1205
+ };
1206
+ function normalizeQuickInfoOptions(options) {
1207
+ if (!options) {
1208
+ return { ...DEFAULT_OPTIONS2, enabled: false };
1209
+ }
1210
+ if (options === true) {
1211
+ return { ...DEFAULT_OPTIONS2, enabled: true };
1212
+ }
1213
+ return {
1214
+ enabled: true,
1215
+ trigger: options.trigger ?? DEFAULT_OPTIONS2.trigger,
1216
+ editorTrigger: options.editorTrigger ?? DEFAULT_OPTIONS2.editorTrigger,
1217
+ showDelete: options.showDelete ?? DEFAULT_OPTIONS2.showDelete,
1218
+ showEdit: options.showEdit ?? DEFAULT_OPTIONS2.showEdit,
1219
+ autoClose: options.autoClose ?? DEFAULT_OPTIONS2.autoClose
1220
+ };
1221
+ }
1222
+
1223
+ // src/interaction/inline-edit.ts
1224
+ var DEFAULT_OPTIONS3 = {
1225
+ enabled: false,
1226
+ trigger: "click",
1227
+ fields: ["title"],
1228
+ saveOnBlur: true,
1229
+ saveOnEnter: true,
1230
+ cancelOnEscape: true
1231
+ };
1232
+ function resolveInlineEditTrigger(trigger) {
1233
+ return trigger === "dblclick" ? "dblclick" : "click";
1234
+ }
1235
+ function normalizeInlineEditOptions(options) {
1236
+ if (!options) {
1237
+ return { ...DEFAULT_OPTIONS3, enabled: false };
1238
+ }
1239
+ if (options === true) {
1240
+ return { ...DEFAULT_OPTIONS3, enabled: true };
1241
+ }
1242
+ return {
1243
+ enabled: true,
1244
+ trigger: resolveInlineEditTrigger(options.trigger),
1245
+ fields: options.fields ?? DEFAULT_OPTIONS3.fields,
1246
+ saveOnBlur: options.saveOnBlur ?? DEFAULT_OPTIONS3.saveOnBlur,
1247
+ saveOnEnter: options.saveOnEnter ?? DEFAULT_OPTIONS3.saveOnEnter,
1248
+ cancelOnEscape: options.cancelOnEscape ?? DEFAULT_OPTIONS3.cancelOnEscape
1249
+ };
1250
+ }
1251
+
1252
+ // src/interaction/inline-edit-time.ts
1253
+ function toValidDate(value) {
1254
+ if (!value) return null;
1255
+ const date = value instanceof Date ? new Date(value) : new Date(value);
1256
+ if (Number.isNaN(date.getTime())) {
1257
+ return null;
1258
+ }
1259
+ return date;
1260
+ }
1261
+ function formatTimeToken(date) {
1262
+ const hours = String(date.getHours()).padStart(2, "0");
1263
+ const minutes = String(date.getMinutes()).padStart(2, "0");
1264
+ return `${hours}:${minutes}`;
1265
+ }
1266
+ function parseInlineTimeToken(input) {
1267
+ const normalized = input.trim().toLowerCase().replace(/\./g, "");
1268
+ if (!normalized) return null;
1269
+ const meridiemMatch = normalized.match(/(am?|pm?)$/i);
1270
+ const meridiem = meridiemMatch ? meridiemMatch[1].toLowerCase() : null;
1271
+ const baseTime = meridiem ? normalized.slice(0, -meridiem.length).trim() : normalized;
1272
+ let hours;
1273
+ let minutes;
1274
+ if (baseTime.includes(":")) {
1275
+ const [rawHours, rawMinutes = "0"] = baseTime.split(":");
1276
+ hours = Number.parseInt(rawHours, 10);
1277
+ minutes = Number.parseInt(rawMinutes, 10);
1278
+ } else if (/^\d{3,4}$/.test(baseTime)) {
1279
+ const cut = baseTime.length - 2;
1280
+ hours = Number.parseInt(baseTime.slice(0, cut), 10);
1281
+ minutes = Number.parseInt(baseTime.slice(cut), 10);
1282
+ } else if (/^\d{1,2}$/.test(baseTime)) {
1283
+ hours = Number.parseInt(baseTime, 10);
1284
+ minutes = 0;
1285
+ } else {
1286
+ return null;
1287
+ }
1288
+ if (!Number.isFinite(hours) || !Number.isFinite(minutes) || minutes < 0 || minutes > 59) {
1289
+ return null;
1290
+ }
1291
+ if (meridiem) {
1292
+ if (hours < 1 || hours > 12) {
1293
+ return null;
1294
+ }
1295
+ if (meridiem.startsWith("p") && hours < 12) {
1296
+ hours += 12;
1297
+ } else if (meridiem.startsWith("a") && hours === 12) {
1298
+ hours = 0;
1299
+ }
1300
+ } else if (hours < 0 || hours > 23) {
1301
+ return null;
1302
+ }
1303
+ return { hours, minutes };
1304
+ }
1305
+ function getInlineEditTimeValue(event) {
1306
+ if (event.allDay) {
1307
+ return "";
1308
+ }
1309
+ const startDate = toValidDate(event.start);
1310
+ if (!startDate) {
1311
+ return "";
1312
+ }
1313
+ const endDate = toValidDate(event.end);
1314
+ if (!endDate) {
1315
+ return formatTimeToken(startDate);
1316
+ }
1317
+ return `${formatTimeToken(startDate)} - ${formatTimeToken(endDate)}`;
1318
+ }
1319
+ function parseInlineEditTimeValue(input, event) {
1320
+ if (event.allDay) {
1321
+ return null;
1322
+ }
1323
+ const startReference = toValidDate(event.start);
1324
+ if (!startReference) {
1325
+ return null;
1326
+ }
1327
+ const endReference = toValidDate(event.end);
1328
+ const trimmed = input.trim();
1329
+ if (!trimmed) {
1330
+ return null;
1331
+ }
1332
+ const rangeMatch = trimmed.match(/^(.+?)\s*(?:-|–|—|to)\s*(.+)$/i);
1333
+ const startToken = rangeMatch ? rangeMatch[1] : trimmed;
1334
+ const endToken = rangeMatch ? rangeMatch[2] : null;
1335
+ const parsedStart = parseInlineTimeToken(startToken);
1336
+ if (!parsedStart) {
1337
+ return null;
1338
+ }
1339
+ const nextStart = new Date(startReference);
1340
+ nextStart.setHours(parsedStart.hours, parsedStart.minutes, 0, 0);
1341
+ let nextEnd;
1342
+ if (endToken) {
1343
+ const parsedEnd = parseInlineTimeToken(endToken);
1344
+ if (!parsedEnd) {
1345
+ return null;
1346
+ }
1347
+ const endBase = endReference ?? startReference;
1348
+ nextEnd = new Date(endBase);
1349
+ nextEnd.setHours(parsedEnd.hours, parsedEnd.minutes, 0, 0);
1350
+ if (nextEnd.getTime() <= nextStart.getTime()) {
1351
+ nextEnd = addDays(nextEnd, 1);
1352
+ }
1353
+ } else if (endReference) {
1354
+ const duration = endReference.getTime() - startReference.getTime();
1355
+ if (duration > 0) {
1356
+ nextEnd = new Date(nextStart.getTime() + duration);
1357
+ }
1358
+ }
1359
+ return {
1360
+ start: nextStart,
1361
+ end: nextEnd,
1362
+ normalizedValue: nextEnd ? `${formatTimeToken(nextStart)} - ${formatTimeToken(nextEnd)}` : formatTimeToken(nextStart)
1363
+ };
1364
+ }
1365
+ function hasInlineTimeChanged(event, start, end) {
1366
+ const previousStart = toValidDate(event.start);
1367
+ const previousEnd = toValidDate(event.end);
1368
+ if (!previousStart) {
1369
+ return true;
1370
+ }
1371
+ const previousEndTime = previousEnd ? previousEnd.getTime() : null;
1372
+ const nextEndTime = end ? end.getTime() : null;
1373
+ return previousStart.getTime() !== start.getTime() || previousEndTime !== nextEndTime;
1374
+ }
1375
+
1376
+ // src/interaction/touch.ts
1377
+ function getPointerCoords(input) {
1378
+ return { clientX: input.clientX, clientY: input.clientY };
1379
+ }
1380
+
1381
+ // src/interaction/resource-conflict.ts
1382
+ function getEventResourceIds(event) {
1383
+ if (event.resourceIds && event.resourceIds.length > 0) {
1384
+ return event.resourceIds;
1385
+ }
1386
+ if (event.resourceId !== void 0) {
1387
+ return [event.resourceId];
1388
+ }
1389
+ return [];
1390
+ }
1391
+ function dedupeConflicts(conflicts) {
1392
+ const seen = /* @__PURE__ */ new Set();
1393
+ const output = [];
1394
+ for (const conflict of conflicts) {
1395
+ const key = `${conflict.type}:${String(conflict.resourceId)}:${conflict.message}`;
1396
+ if (seen.has(key)) {
1397
+ continue;
1398
+ }
1399
+ seen.add(key);
1400
+ output.push(conflict);
1401
+ }
1402
+ return output;
1403
+ }
1404
+ function projectEventsWithCandidate(events, event) {
1405
+ const index = events.findIndex((item) => item.id === event.id);
1406
+ if (index === -1) {
1407
+ return [...events, event];
1408
+ }
1409
+ const projected = [...events];
1410
+ projected[index] = event;
1411
+ return projected;
1412
+ }
1413
+ function updateConflictedEventIds(conflictedEventIds, eventId, hasConflict) {
1414
+ const hasExisting = conflictedEventIds.includes(eventId);
1415
+ if (hasConflict && !hasExisting) {
1416
+ return [...conflictedEventIds, eventId];
1417
+ }
1418
+ if (!hasConflict && hasExisting) {
1419
+ return conflictedEventIds.filter((id) => id !== eventId);
1420
+ }
1421
+ return conflictedEventIds;
1422
+ }
1423
+ function evaluateResourceConflictDecision(input) {
1424
+ if (!input.options.enabled) {
1425
+ return { canProceed: true, conflicts: [] };
1426
+ }
1427
+ const resourceIds = input.targetResourceId !== void 0 ? [input.targetResourceId] : getEventResourceIds(input.event);
1428
+ if (resourceIds.length === 0) {
1429
+ return { canProceed: true, conflicts: [] };
1430
+ }
1431
+ let canProceed = true;
1432
+ const conflicts = [];
1433
+ for (const resourceId of resourceIds) {
1434
+ const resource = findResourceById(input.resources, resourceId);
1435
+ if (!resource) {
1436
+ continue;
1437
+ }
1438
+ const result = canDropEventWithResourceConstraints(input.event, resource, input.events, input.options);
1439
+ if (!result.canDrop) {
1440
+ canProceed = false;
1441
+ }
1442
+ conflicts.push(...result.conflicts);
1443
+ }
1444
+ return {
1445
+ canProceed,
1446
+ conflicts: dedupeConflicts(conflicts)
1447
+ };
1448
+ }
1449
+ function resolveCapacityExceededPayloads(event, conflicts, resources, events, currentDate) {
1450
+ const projectedEvents = projectEventsWithCandidate(events, event);
1451
+ const resourceIds = new Set(conflicts.filter((conflict) => conflict.type === "capacity").map((conflict) => conflict.resourceId));
1452
+ const payloads = [];
1453
+ for (const resourceId of resourceIds) {
1454
+ const resource = findResourceById(resources, resourceId);
1455
+ if (!resource) {
1456
+ continue;
1457
+ }
1458
+ const capacity = getResourceCapacity(resource, currentDate, projectedEvents);
1459
+ if (capacity.max === Infinity) {
1460
+ continue;
1461
+ }
1462
+ payloads.push({
1463
+ resourceId: resource.id,
1464
+ current: capacity.current,
1465
+ max: capacity.max
1466
+ });
1467
+ }
1468
+ return payloads;
1469
+ }
1470
+ function resolveResourceConflictRuntime(input) {
1471
+ if (!input.options.enabled) {
1472
+ return {
1473
+ conflictedEventIds: updateConflictedEventIds(input.conflictedEventIds, input.event.id, false),
1474
+ capacityExceededPayloads: []
1475
+ };
1476
+ }
1477
+ const hasConflict = input.decision.conflicts.length > 0;
1478
+ const conflictedEventIds = updateConflictedEventIds(input.conflictedEventIds, input.event.id, hasConflict);
1479
+ if (!hasConflict) {
1480
+ return {
1481
+ conflictedEventIds,
1482
+ capacityExceededPayloads: []
1483
+ };
1484
+ }
1485
+ return {
1486
+ conflictedEventIds,
1487
+ conflictDetectedPayload: {
1488
+ conflicts: input.decision.conflicts,
1489
+ event: input.event,
1490
+ canProceed: input.decision.canProceed
1491
+ },
1492
+ capacityExceededPayloads: resolveCapacityExceededPayloads(input.event, input.decision.conflicts, input.resources, input.events, input.currentDate)
1493
+ };
1494
+ }
1495
+
1496
+ // src/interaction/conflict.controller.ts
1497
+ function processConflictDecision(input) {
1498
+ const decision = evaluateResourceConflictDecision({
1499
+ event: input.event,
1500
+ options: input.options,
1501
+ resources: input.resources,
1502
+ events: input.events,
1503
+ targetResourceId: input.targetResourceId
1504
+ });
1505
+ const runtime = resolveResourceConflictRuntime({
1506
+ event: input.event,
1507
+ options: input.options,
1508
+ resources: input.resources,
1509
+ events: input.events,
1510
+ currentDate: input.currentDate,
1511
+ conflictedEventIds: input.conflictedEventIds,
1512
+ decision
1513
+ });
1514
+ return {
1515
+ decision,
1516
+ conflictedEventIds: runtime.conflictedEventIds,
1517
+ emits: {
1518
+ conflictDetected: runtime.conflictDetectedPayload,
1519
+ capacityExceeded: runtime.capacityExceededPayloads
1520
+ }
1521
+ };
1522
+ }
1523
+ function evaluateDropConflict(input) {
1524
+ if (!input.options.enabled) {
1525
+ return true;
1526
+ }
1527
+ const projectedEvent = {
1528
+ ...input.event,
1529
+ start: input.dropStart,
1530
+ end: input.dropEnd
1531
+ };
1532
+ const decision = evaluateResourceConflictDecision({
1533
+ event: projectedEvent,
1534
+ options: input.options,
1535
+ resources: input.resources,
1536
+ events: input.events,
1537
+ targetResourceId: input.dropResourceId
1538
+ });
1539
+ return decision.canProceed;
1540
+ }
1541
+ function buildResourceCapacityMap(input) {
1542
+ if (!input.options.enabled || !input.options.checkCapacity) {
1543
+ return {};
1544
+ }
1545
+ const output = {};
1546
+ for (const resource of input.flatResources) {
1547
+ if (resource.constraints?.capacity === void 0 && resource.constraints?.maxEventsPerDay === void 0) {
1548
+ continue;
1549
+ }
1550
+ output[String(resource.id)] = getResourceCapacity(resource, input.currentDate, input.events);
1551
+ }
1552
+ return output;
1553
+ }
1554
+
1555
+ // src/interaction/scheduler-actions.controller.ts
1556
+ function resolveGlobalKeyAction(ctx) {
1557
+ if (ctx.key === "Escape") {
1558
+ if (ctx.morePopoverVisible) return { type: "dismiss-more-popover" };
1559
+ if (ctx.popoverVisible) return { type: "dismiss-popover" };
1560
+ if (ctx.quickInfoVisible) return { type: "dismiss-quickinfo" };
1561
+ }
1562
+ if (!ctx.isInsideRoot) return { type: "none" };
1563
+ if (ctx.focusedEventId && (ctx.key === "Enter" || ctx.key === " ")) {
1564
+ return { type: "activate-event", eventId: ctx.focusedEventId };
1565
+ }
1566
+ if (ctx.focusedEventId && ctx.key === "Escape") {
1567
+ return { type: "escape-event-focus" };
1568
+ }
1569
+ if (ctx.isEditableElement) return { type: "none" };
1570
+ if ((ctx.key === "Delete" || ctx.key === "Backspace") && ctx.selectedEventCount > 0) {
1571
+ return { type: "delete-events" };
1572
+ }
1573
+ if (ctx.altKey && ctx.key >= "1" && ctx.key <= "9") {
1574
+ const index = parseInt(ctx.key) - 1;
1575
+ if (index < ctx.availableViewCount) {
1576
+ return { type: "switch-view", viewIndex: index };
1577
+ }
1578
+ return { type: "none" };
1579
+ }
1580
+ const hasModifier = ctx.ctrlKey || ctx.metaKey;
1581
+ if (hasModifier && ctx.key === "ArrowLeft") return { type: "navigate", direction: "prev" };
1582
+ if (hasModifier && ctx.key === "ArrowRight") return { type: "navigate", direction: "next" };
1583
+ if (hasModifier && ctx.key.toLowerCase() === "t") return { type: "go-today" };
1584
+ if (!ctx.eventSelectionEnabled) return { type: "none" };
1585
+ if (hasModifier && ctx.key.toLowerCase() === "a") return { type: "select-all" };
1586
+ if (ctx.key === "Escape" && ctx.selectedEventCount > 0) {
1587
+ return { type: "clear-event-selection" };
1588
+ }
1589
+ return { type: "none" };
1590
+ }
1591
+ function resolveOutsideClickAction(ctx) {
1592
+ if (!ctx.isOutside || ctx.isDragging) {
1593
+ return { clearFocus: false, clearDateSelection: false, clearEventSelection: false };
1594
+ }
1595
+ return {
1596
+ clearFocus: ctx.hasFocus,
1597
+ clearDateSelection: ctx.unselectOnOutsideClick && ctx.hasDateSelection,
1598
+ clearEventSelection: ctx.unselectOnOutsideClick && ctx.eventSelectionEnabled && ctx.hasEventSelection
1599
+ };
1600
+ }
1601
+
1602
+ // src/interaction/multi-drag.ts
1603
+ function getTimeOfDayMs(date) {
1604
+ return (date.getHours() * 3600 + date.getMinutes() * 60 + date.getSeconds()) * 1e3;
1605
+ }
1606
+ function getDayOffset(anchor, target) {
1607
+ const a = new Date(anchor);
1608
+ a.setHours(0, 0, 0, 0);
1609
+ const b = new Date(target);
1610
+ b.setHours(0, 0, 0, 0);
1611
+ return Math.round((b.getTime() - a.getTime()) / 864e5);
1612
+ }
1613
+ function getSelectedEventsForMultiDrag(selectedIds, events) {
1614
+ const idSet = new Set(selectedIds);
1615
+ return events.filter((e) => idSet.has(e.id));
1616
+ }
1617
+ function isMultiDragCandidate(selectedIds, anchorId) {
1618
+ return selectedIds.length > 1 && selectedIds.includes(anchorId);
1619
+ }
1620
+ function computeTimedMultiDragOffsets(anchorStart, selectedEvents) {
1621
+ const anchorMs = anchorStart.getTime();
1622
+ const offsets = /* @__PURE__ */ new Map();
1623
+ for (const event of selectedEvents) {
1624
+ const start = new Date(event.start);
1625
+ offsets.set(event.id, start.getTime() - anchorMs);
1626
+ }
1627
+ return offsets;
1628
+ }
1629
+ function computeColumnTimedMultiDragOffsets(anchorStart, anchorColIndex, selectedEvents, getColumnIndex) {
1630
+ const anchorTimeMs = getTimeOfDayMs(anchorStart);
1631
+ const offsets = /* @__PURE__ */ new Map();
1632
+ for (const event of selectedEvents) {
1633
+ const start = new Date(event.start);
1634
+ const colIndex = getColumnIndex(start);
1635
+ offsets.set(event.id, {
1636
+ colIndexOffset: colIndex - anchorColIndex,
1637
+ milliseconds: getTimeOfDayMs(start) - anchorTimeMs
1638
+ });
1639
+ }
1640
+ return offsets;
1641
+ }
1642
+ function computeDayMultiDragOffsets(anchorStart, selectedEvents) {
1643
+ const offsets = /* @__PURE__ */ new Map();
1644
+ for (const event of selectedEvents) {
1645
+ const start = new Date(event.start);
1646
+ offsets.set(event.id, getDayOffset(anchorStart, start));
1647
+ }
1648
+ return offsets;
1649
+ }
1650
+
1651
+ // src/interaction/permissions.ts
1652
+ function isPermissionAllowed(resolver, ctx) {
1653
+ if (!resolver) return true;
1654
+ return resolver(ctx);
1655
+ }
1656
+ function filterEventsByPermission(events, action, resolver, view) {
1657
+ if (!resolver) return events;
1658
+ return events.filter((event) => resolver({ action, event, view }));
1659
+ }
1660
+ function isEventDraggableWithPermission(event, globalEditable, eventStartEditable, resolver, view) {
1661
+ if (!isEventDraggable(event, globalEditable, eventStartEditable)) return false;
1662
+ return isPermissionAllowed(resolver, { action: "drag", event, view });
1663
+ }
1664
+ function isEventResizableWithPermission(event, globalEditable, eventDurationEditable, resolver, view) {
1665
+ if (!isEventResizable(event, globalEditable, eventDurationEditable)) return false;
1666
+ return isPermissionAllowed(resolver, { action: "resize", event, view });
1667
+ }
1668
+
1669
+ export { DEFAULT_DRAG_MIN_DISTANCE, DEFAULT_OVERSCAN, DEFAULT_SNAP_DURATION, RESIZE_HANDLE_SIZE, RESOURCE_VIEWS, VIRTUAL_THRESHOLD, applyDeltaToEvent, applyEventSelection, applyResizeToEvent, buildResourceCapacityMap, calculateDayDelta, calculateDragDelta, calculateNewEventTimes, calculateNewFocusedDate, calculateTimeDelta, calculateTotalWidth, calculateVirtualRange, calculateVisibleEvents, clampTime, clampTimeToSlotBounds, clearEventSelection, computeColumnTimedMultiDragOffsets, computeDayMultiDragOffsets, computeTimedMultiDragOffsets, createChunkedCells, createDragAutoScroller, createDragState, createResizeState, createSchedulerEventDropCandidate, createVirtualScrollState, dedupeConflicts, detectResizeEdge, evaluateDropConflict, evaluateResourceConflictDecision, eventBelongsToResource, filterEventsByPermission, filterVisibleIdsByResource, findCellForDate, findOverlappingEvents, formatEventDate, formatEventDuration, formatEventTimeRange, formatTimeToken, getCellAtPosition, getDayIndexFromX, getDayOffset, getDefaultFocusDate, getDefaultFocusDateForDay, getDefaultFocusDateForMonth, getDefaultFocusDateForWeek, getDefaultFocusDateForYear, getDisplayedMonthFromGrid, getEndOfWeek, getEndOfWeekForFocus, getEstimatedScrollPosition, getInlineEditTimeValue, getKeyboardAction, getNavigationDirectionForDay, getNavigationDirectionForMonth, getNavigationDirectionForWeek, getNavigationDirectionForYear, getPeriodNavigationDirection, getPointerCoords, getResizeCursor, getSchedulerEventDropDelta, getSchedulerTimedInteractionDeltaMs, getSchedulerTimedResizePreview, getSchedulerTimelineResizePreview, getScrollPositionForDate, getScrollPositionForNow, getSelectedEventsForMultiDrag, getStartOfWeek, getStartOfWeekForFocus, getTimeFromYPosition, getTimeOfDayMs, getVisibleChunks, getYPositionFromTime, handleKeyboardNavigation, hasInlineTimeChanged, invertSelection, isDateInDayView, isDateInMonthView, isDateInVisibleRange, isDateInWeekView, isDateInYearView, isEventDraggable, isEventDraggableWithPermission, isEventResizable, isEventResizableWithPermission, isMultiDragCandidate, isPermissionAllowed, isResourceView, normalizeEventSelectionOptions, normalizeInlineEditOptions, normalizeQuickInfoOptions, parseInlineEditTimeValue, parseInlineTimeToken, processConflictDecision, projectEventsWithCandidate, resolveAutoScrollDelta, resolveCapacityExceededPayloads, resolveGlobalKeyAction, resolveOutsideClickAction, resolveResourceConflictRuntime, selectAllVisibleEvents, shouldUseVirtualScrolling, snapTime, snapTimeToSlot, snapToSlot, toValidDate, updateConflictedEventIds, updateDragState, updateResizeState, updateVirtualScrollState, validateDragPosition, validateResize };