@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,1170 @@
1
+ import { filterEventsForDay, sortEvents, eventsOverlap, getEventStartMinutes, getEventEndMinutes } from './chunk-2B3YLWHA.mjs';
2
+ import { isSchedulerAutoTextColor, markSchedulerAutoTextColor, filterEventsByCategory, getEventCategoryStyle, calculateContrastTextColor } from './chunk-C7ADJGNV.mjs';
3
+ import { resolveSchedulerEventSurfaceColor } from './chunk-5KORIWDT.mjs';
4
+ import { parseRRule, serializeRRule, expandRRule } from './chunk-FIBAZFC4.mjs';
5
+ import { getLocalTimezone, convertEventsToTimezone } from './chunk-QDMZBJDV.mjs';
6
+
7
+ // src/event/event-list.ts
8
+ function upsertEventById(eventList, event) {
9
+ const index = eventList.findIndex((item) => item.id === event.id);
10
+ if (index === -1) {
11
+ return [...eventList, event];
12
+ }
13
+ const next = [...eventList];
14
+ next[index] = event;
15
+ return next;
16
+ }
17
+ function removeEventById(eventList, eventId) {
18
+ const index = eventList.findIndex((event) => event.id === eventId);
19
+ if (index === -1) {
20
+ return eventList;
21
+ }
22
+ return eventList.filter((event) => event.id !== eventId);
23
+ }
24
+
25
+ // src/event/event.positioning.ts
26
+ function timeStringToMinutes(time) {
27
+ const [hours, minutes] = time.split(":").map(Number);
28
+ return hours * 60 + (minutes || 0);
29
+ }
30
+ var DEFAULT_TITLE_HEIGHT_MINUTES = 45;
31
+ function findOverlapGroups(events) {
32
+ if (events.length === 0) return [];
33
+ const sorted = [...events].sort((a, b) => {
34
+ const startDiff = a.start.getTime() - b.start.getTime();
35
+ if (startDiff !== 0) return startDiff;
36
+ return b.durationMinutes - a.durationMinutes;
37
+ });
38
+ const groups = [];
39
+ const assigned = /* @__PURE__ */ new Set();
40
+ for (const event of sorted) {
41
+ if (assigned.has(event)) continue;
42
+ const group = [event];
43
+ assigned.add(event);
44
+ let changed = true;
45
+ while (changed) {
46
+ changed = false;
47
+ for (const other of sorted) {
48
+ if (assigned.has(other)) continue;
49
+ const overlapsWithGroup = group.some((groupEvent) => eventsOverlap(groupEvent, other));
50
+ if (overlapsWithGroup) {
51
+ group.push(other);
52
+ assigned.add(other);
53
+ changed = true;
54
+ }
55
+ }
56
+ }
57
+ groups.push(group);
58
+ }
59
+ return groups;
60
+ }
61
+ function assignColumnsWithinGroup(events, titleHeightMinutes) {
62
+ const result = /* @__PURE__ */ new Map();
63
+ if (events.length === 0) return result;
64
+ const sorted = [...events].sort((a, b) => {
65
+ const startDiff = a.start.getTime() - b.start.getTime();
66
+ if (startDiff !== 0) return startDiff;
67
+ return b.durationMinutes - a.durationMinutes;
68
+ });
69
+ const columns = [];
70
+ const processedEvents = [];
71
+ const eventColumnMap = /* @__PURE__ */ new Map();
72
+ for (const event of sorted) {
73
+ const eventStart = getEventStartMinutes(event);
74
+ const eventEnd = getEventEndMinutes(event);
75
+ const eventTitleEnd = eventStart + titleHeightMinutes;
76
+ let assignedColumn = -1;
77
+ for (let colIndex = 0; colIndex < columns.length; colIndex++) {
78
+ const column = columns[colIndex];
79
+ let canFit = true;
80
+ for (const slot of column) {
81
+ const timesOverlap = eventStart < slot.endMinutes && eventEnd > slot.startMinutes;
82
+ if (!timesOverlap) continue;
83
+ const titleOverlap = eventStart < slot.titleEndMinutes && eventTitleEnd > slot.startMinutes;
84
+ if (titleOverlap) {
85
+ canFit = false;
86
+ break;
87
+ }
88
+ }
89
+ if (canFit) {
90
+ assignedColumn = colIndex;
91
+ break;
92
+ }
93
+ }
94
+ if (assignedColumn === -1) {
95
+ assignedColumn = columns.length;
96
+ columns.push([]);
97
+ }
98
+ let nestingDepth = 0;
99
+ for (const processed of processedEvents) {
100
+ const otherStart = getEventStartMinutes(processed.event);
101
+ const otherEnd = getEventEndMinutes(processed.event);
102
+ const otherTitleEnd = otherStart + titleHeightMinutes;
103
+ const timesOverlap = eventStart < otherEnd && eventEnd > otherStart;
104
+ if (!timesOverlap) continue;
105
+ const titleOverlap = eventStart < otherTitleEnd && eventTitleEnd > otherStart;
106
+ if (!titleOverlap) {
107
+ nestingDepth = Math.max(nestingDepth, processed.nestingDepth + 1);
108
+ }
109
+ }
110
+ columns[assignedColumn].push({
111
+ event,
112
+ startMinutes: eventStart,
113
+ endMinutes: eventEnd,
114
+ titleEndMinutes: eventTitleEnd
115
+ });
116
+ processedEvents.push({ event, nestingDepth, column: assignedColumn });
117
+ eventColumnMap.set(event, assignedColumn);
118
+ result.set(event, { column: assignedColumn, totalColumns: 0, nestingDepth });
119
+ }
120
+ for (const event of sorted) {
121
+ const assignment = result.get(event);
122
+ const eventStart = getEventStartMinutes(event);
123
+ const eventEnd = getEventEndMinutes(event);
124
+ const eventTitleEnd = eventStart + titleHeightMinutes;
125
+ const columnsUsed = /* @__PURE__ */ new Set();
126
+ columnsUsed.add(assignment.column);
127
+ for (const other of sorted) {
128
+ if (other === event) continue;
129
+ const otherStart = getEventStartMinutes(other);
130
+ const otherEnd = getEventEndMinutes(other);
131
+ const otherTitleEnd = otherStart + titleHeightMinutes;
132
+ const timesOverlap = eventStart < otherEnd && eventEnd > otherStart;
133
+ if (!timesOverlap) continue;
134
+ const titleOverlap = eventStart < otherTitleEnd && eventTitleEnd > otherStart;
135
+ if (titleOverlap) {
136
+ columnsUsed.add(eventColumnMap.get(other));
137
+ }
138
+ }
139
+ assignment.totalColumns = columnsUsed.size;
140
+ }
141
+ return result;
142
+ }
143
+ var NESTING_INDENT_PERCENT = 5;
144
+ function calculateEventPositionGoogle(event, column, totalColumns, nestingDepth, options) {
145
+ const { slotMinTime, slotMaxTime, slotDuration, slotHeight } = options;
146
+ const minMinutes = timeStringToMinutes(slotMinTime);
147
+ const maxMinutes = timeStringToMinutes(slotMaxTime);
148
+ const totalMinutes = maxMinutes - minMinutes;
149
+ let eventStartMinutes = event.start.getHours() * 60 + event.start.getMinutes();
150
+ let eventEndMinutes = event.end.getHours() * 60 + event.end.getMinutes();
151
+ if (eventEndMinutes === 0) eventEndMinutes = 24 * 60;
152
+ eventStartMinutes = Math.max(eventStartMinutes, minMinutes);
153
+ eventEndMinutes = Math.min(eventEndMinutes, maxMinutes);
154
+ const totalSlots = totalMinutes / slotDuration;
155
+ const totalHeight = totalSlots * slotHeight;
156
+ const top = (eventStartMinutes - minMinutes) / totalMinutes * totalHeight;
157
+ const height = Math.max((eventEndMinutes - eventStartMinutes) / totalMinutes * totalHeight, slotHeight / 2);
158
+ const nestingIndent = nestingDepth * NESTING_INDENT_PERCENT;
159
+ const availableWidth = 100 - nestingIndent;
160
+ const columnWidthPercent = availableWidth / totalColumns;
161
+ const left = nestingIndent + column * columnWidthPercent;
162
+ const width = columnWidthPercent;
163
+ return {
164
+ top: Math.round(top * 100) / 100,
165
+ height: Math.round(height * 100) / 100,
166
+ left,
167
+ width: Math.max(width, 10)
168
+ };
169
+ }
170
+ function positionEventsForDay(events, date, options) {
171
+ const dayEvents = filterEventsForDay(events, date);
172
+ const timedEvents = dayEvents.filter((e) => !e.allDay && !e.isMultiDay);
173
+ const sorted = sortEvents(timedEvents);
174
+ const titleHeightMinutes = options.titleHeight ?? DEFAULT_TITLE_HEIGHT_MINUTES;
175
+ const groups = findOverlapGroups(sorted);
176
+ const positioned = [];
177
+ for (const group of groups) {
178
+ const columnAssignments = assignColumnsWithinGroup(group, titleHeightMinutes);
179
+ for (const event of group) {
180
+ const assignment = columnAssignments.get(event);
181
+ if (!assignment) continue;
182
+ const { column, totalColumns, nestingDepth } = assignment;
183
+ const position = calculateEventPositionGoogle(event, column, totalColumns, nestingDepth, options);
184
+ positioned.push({
185
+ ...event,
186
+ column,
187
+ totalColumns,
188
+ zIndex: 1,
189
+ ...position
190
+ });
191
+ }
192
+ }
193
+ const stackingOrder = [...positioned].sort((a, b) => {
194
+ const topDiff = b.top - a.top;
195
+ if (Math.abs(topDiff) > 0.01) {
196
+ return topDiff;
197
+ }
198
+ const leftDiff = b.left - a.left;
199
+ if (Math.abs(leftDiff) > 0.01) {
200
+ return leftDiff;
201
+ }
202
+ const widthDiff = a.width - b.width;
203
+ if (Math.abs(widthDiff) > 0.01) {
204
+ return widthDiff;
205
+ }
206
+ return a.height - b.height;
207
+ });
208
+ stackingOrder.forEach((event, index) => {
209
+ event.zIndex = stackingOrder.length - index;
210
+ });
211
+ return positioned;
212
+ }
213
+ function calculateEventPosition(event, column, totalColumns, options, nestingDepth = 0) {
214
+ return calculateEventPositionGoogle(event, column, totalColumns, nestingDepth, options);
215
+ }
216
+ function getAllDayEventsForDay(events, date) {
217
+ const dayEvents = filterEventsForDay(events, date);
218
+ return sortEvents(dayEvents.filter((e) => e.allDay || e.isMultiDay));
219
+ }
220
+ function positionAllDayEventsForWeek(events, weekStart, weekEnd) {
221
+ const positioned = [];
222
+ const allDayEvents = events.filter((event) => {
223
+ const overlapsWeek = event.start <= weekEnd && event.end >= weekStart;
224
+ return overlapsWeek && (event.allDay || event.isMultiDay);
225
+ }).map((event) => {
226
+ const eventStart = event.start < weekStart ? weekStart : event.start;
227
+ const eventEnd = event.end > weekEnd ? weekEnd : event.end;
228
+ const startCol = Math.floor((eventStart.getTime() - weekStart.getTime()) / (1e3 * 60 * 60 * 24));
229
+ const endCol = Math.floor((eventEnd.getTime() - weekStart.getTime()) / (1e3 * 60 * 60 * 24));
230
+ return {
231
+ event,
232
+ eventStart,
233
+ eventEnd,
234
+ startCol,
235
+ endCol
236
+ };
237
+ });
238
+ const sorted = [...allDayEvents].sort((a, b) => {
239
+ if (a.startCol !== b.startCol) return a.startCol - b.startCol;
240
+ const spanA = a.endCol - a.startCol;
241
+ const spanB = b.endCol - b.startCol;
242
+ if (spanA !== spanB) return spanB - spanA;
243
+ const startDiff = a.event.start.getTime() - b.event.start.getTime();
244
+ if (startDiff !== 0) return startDiff;
245
+ const durationDiff = b.event.durationMinutes - a.event.durationMinutes;
246
+ if (durationDiff !== 0) return durationDiff;
247
+ return String(a.event.id ?? "").localeCompare(String(b.event.id ?? ""));
248
+ });
249
+ const rowEndDates = [];
250
+ for (const eventPosition of sorted) {
251
+ let assignedRow = -1;
252
+ for (let row = 0; row < rowEndDates.length; row++) {
253
+ if (rowEndDates[row] < eventPosition.eventStart) {
254
+ assignedRow = row;
255
+ rowEndDates[row] = eventPosition.eventEnd;
256
+ break;
257
+ }
258
+ }
259
+ if (assignedRow === -1) {
260
+ assignedRow = rowEndDates.length;
261
+ rowEndDates.push(eventPosition.eventEnd);
262
+ }
263
+ positioned.push({
264
+ event: eventPosition.event,
265
+ row: assignedRow,
266
+ startCol: eventPosition.startCol,
267
+ endCol: eventPosition.endCol,
268
+ isStart: eventPosition.event.start >= weekStart,
269
+ isEnd: eventPosition.event.end <= weekEnd
270
+ });
271
+ }
272
+ return positioned;
273
+ }
274
+ function getTimedEventsForDay(events, date) {
275
+ const dayStart = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
276
+ const dayEnd = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);
277
+ return events.filter((event) => {
278
+ if (event.allDay || event.isMultiDay) return false;
279
+ return event.start >= dayStart && event.start <= dayEnd;
280
+ }).sort((a, b) => a.start.getTime() - b.start.getTime());
281
+ }
282
+ function positionEventsForWeekRow(events, weekStart, weekEnd, maxEventsPerDay = 3) {
283
+ const positioned = positionAllDayEventsForWeek(events, weekStart, weekEnd);
284
+ const timedEventsByDay = /* @__PURE__ */ new Map();
285
+ for (let dayIndex = 0; dayIndex < 7; dayIndex++) {
286
+ const dayDate = new Date(weekStart.getTime() + dayIndex * 24 * 60 * 60 * 1e3);
287
+ const timedEvents = getTimedEventsForDay(events, dayDate);
288
+ timedEventsByDay.set(dayIndex, timedEvents);
289
+ }
290
+ const overflow = /* @__PURE__ */ new Map();
291
+ const rowsUsedByDay = /* @__PURE__ */ new Map();
292
+ for (const pos of positioned) {
293
+ for (let col = pos.startCol; col <= pos.endCol; col++) {
294
+ if (!rowsUsedByDay.has(col)) {
295
+ rowsUsedByDay.set(col, /* @__PURE__ */ new Set());
296
+ }
297
+ rowsUsedByDay.get(col).add(pos.row);
298
+ }
299
+ }
300
+ for (let dayIndex = 0; dayIndex < 7; dayIndex++) {
301
+ const rowsUsed = rowsUsedByDay.get(dayIndex)?.size || 0;
302
+ const timedEvents = timedEventsByDay.get(dayIndex) || [];
303
+ const timedCount = timedEvents.length;
304
+ const totalItems = rowsUsed + timedCount;
305
+ const availableSlots = maxEventsPerDay;
306
+ if (totalItems > availableSlots) {
307
+ const overflowCount = totalItems - availableSlots + 1;
308
+ overflow.set(dayIndex, overflowCount);
309
+ }
310
+ }
311
+ return { positioned, timedEventsByDay, overflow };
312
+ }
313
+
314
+ // src/event/pipeline.ts
315
+ function toDate(value) {
316
+ if (!value) return null;
317
+ return value instanceof Date ? value : new Date(value);
318
+ }
319
+ function getFallbackEnd(start, event) {
320
+ if (typeof event.duration === "number" && Number.isFinite(event.duration) && event.duration > 0) {
321
+ return new Date(start.getTime() + event.duration);
322
+ }
323
+ return new Date(start.getTime() + 60 * 60 * 1e3);
324
+ }
325
+ function normalizeSchedulerEventToDisplayTimezone(event, schedulerTimezone, displayTimezone) {
326
+ const start = toDate(event.start);
327
+ if (!start) {
328
+ return event;
329
+ }
330
+ const end = toDate(event.end) ?? getFallbackEnd(start, event);
331
+ const sourceTimezone = event.timezone ?? schedulerTimezone ?? getLocalTimezone();
332
+ const normalized = {
333
+ ...event,
334
+ start,
335
+ end,
336
+ timezone: sourceTimezone
337
+ };
338
+ if (sourceTimezone === displayTimezone) {
339
+ const nextEvent2 = {
340
+ ...normalized,
341
+ timezone: displayTimezone
342
+ };
343
+ return isSchedulerAutoTextColor(event) ? markSchedulerAutoTextColor(nextEvent2) : nextEvent2;
344
+ }
345
+ const [converted] = convertEventsToTimezone([normalized], displayTimezone);
346
+ const nextEvent = {
347
+ ...converted,
348
+ timezone: displayTimezone
349
+ };
350
+ return isSchedulerAutoTextColor(event) ? markSchedulerAutoTextColor(nextEvent) : nextEvent;
351
+ }
352
+ function normalizeSchedulerEventsToDisplayTimezone(events, schedulerTimezone, resolvedDisplayTimezone) {
353
+ if (!resolvedDisplayTimezone) {
354
+ return events;
355
+ }
356
+ return events.map((event) => normalizeSchedulerEventToDisplayTimezone(event, schedulerTimezone, resolvedDisplayTimezone));
357
+ }
358
+ function hasRecurrenceRule(event) {
359
+ return event.rrule != null;
360
+ }
361
+ function expandSchedulerEvents(events, dateRange) {
362
+ const out = [];
363
+ for (const event of events) {
364
+ if (!hasRecurrenceRule(event)) {
365
+ out.push(event);
366
+ continue;
367
+ }
368
+ try {
369
+ const expanded = expandRRule(event, dateRange.start, dateRange.end);
370
+ if (Array.isArray(expanded) && expanded.length > 0) {
371
+ for (const occurrence of expanded) {
372
+ out.push(occurrence);
373
+ }
374
+ } else {
375
+ out.push(event);
376
+ }
377
+ } catch {
378
+ out.push(event);
379
+ }
380
+ }
381
+ return out;
382
+ }
383
+ function countEventsByCategory(events, categoryField) {
384
+ const counts = {};
385
+ for (const event of events) {
386
+ const rawEvent = event;
387
+ const categoryId = rawEvent[categoryField] ?? event.metadata?.[categoryField];
388
+ if (categoryId !== void 0 && categoryId !== null) {
389
+ counts[categoryId] = (counts[categoryId] ?? 0) + 1;
390
+ }
391
+ }
392
+ return counts;
393
+ }
394
+ function enrichEventCategoryStyles(events, categories, categoryField, categoryAutoTextColor, eventShell) {
395
+ if (categories.length === 0) {
396
+ return events;
397
+ }
398
+ const preserveCategoryTextColor = eventShell === "none";
399
+ return events.map((event) => {
400
+ const categoryId = event[categoryField];
401
+ if (categoryId == null) return event;
402
+ const category = categories.find((item) => item.id === categoryId);
403
+ if (!category) return event;
404
+ const style = getEventCategoryStyle(event, categories, categoryField);
405
+ const color = event.color ?? style.background ?? category.color;
406
+ let textColor = event.textColor;
407
+ let isAutoTextColor = false;
408
+ if (!textColor && preserveCategoryTextColor) {
409
+ textColor = style.color ?? category.textColor;
410
+ if (!textColor && categoryAutoTextColor && (color || category.color)) {
411
+ try {
412
+ textColor = calculateContrastTextColor(color || category.color);
413
+ isAutoTextColor = true;
414
+ } catch {
415
+ textColor = void 0;
416
+ }
417
+ }
418
+ }
419
+ if (!textColor) {
420
+ return { ...event, color };
421
+ }
422
+ const nextEvent = { ...event, color, textColor };
423
+ return isAutoTextColor ? markSchedulerAutoTextColor(nextEvent) : nextEvent;
424
+ });
425
+ }
426
+ function buildEventSurfaceColors(events, context) {
427
+ const colors = /* @__PURE__ */ new Map();
428
+ for (const event of events) {
429
+ colors.set(event.id, resolveSchedulerEventSurfaceColor(event, context));
430
+ }
431
+ return colors;
432
+ }
433
+ function getRangeTime(value) {
434
+ return value.getTime();
435
+ }
436
+ function normalizeActiveCategoryIdsKey(activeCategoryIds) {
437
+ if (!activeCategoryIds || activeCategoryIds.length === 0) {
438
+ return "";
439
+ }
440
+ const normalized = new Set(activeCategoryIds.map((id) => `${typeof id}:${String(id)}`));
441
+ return JSON.stringify([...normalized].sort());
442
+ }
443
+ function createSchedulerEventPipelineMemoKey(input) {
444
+ return {
445
+ events: input.events,
446
+ dateRangeStart: getRangeTime(input.dateRange.start),
447
+ dateRangeEnd: getRangeTime(input.dateRange.end),
448
+ categories: input.categories,
449
+ categoryField: input.categoryField ?? "categoryId",
450
+ categoryAutoTextColor: input.categoryAutoTextColor ?? false,
451
+ eventShell: input.eventShell ?? "default",
452
+ activeCategoryIdsKey: normalizeActiveCategoryIdsKey(input.activeCategoryIds),
453
+ resources: input.resources,
454
+ schedulerTimezone: input.schedulerTimezone,
455
+ resolvedDisplayTimezone: input.resolvedDisplayTimezone
456
+ };
457
+ }
458
+ function areSchedulerEventPipelineMemoKeysEqual(a, b) {
459
+ return a.events === b.events && Object.is(a.dateRangeStart, b.dateRangeStart) && Object.is(a.dateRangeEnd, b.dateRangeEnd) && a.categories === b.categories && a.categoryField === b.categoryField && a.categoryAutoTextColor === b.categoryAutoTextColor && a.eventShell === b.eventShell && a.activeCategoryIdsKey === b.activeCategoryIdsKey && a.resources === b.resources && a.schedulerTimezone === b.schedulerTimezone && a.resolvedDisplayTimezone === b.resolvedDisplayTimezone;
460
+ }
461
+ function buildSchedulerEventPipeline(input) {
462
+ const rawEvents = input.events;
463
+ const categories = input.categories ?? [];
464
+ const categoryField = input.categoryField ?? "categoryId";
465
+ const activeCategoryIds = input.activeCategoryIds ?? [];
466
+ const expandedEvents = expandSchedulerEvents(rawEvents, input.dateRange);
467
+ const filteredEvents = activeCategoryIds.length > 0 && categories.length > 0 ? filterEventsByCategory(expandedEvents, activeCategoryIds, categories, categoryField, false) : expandedEvents;
468
+ const categoryFilteredEvents = enrichEventCategoryStyles(filteredEvents, categories, categoryField, input.categoryAutoTextColor ?? false, input.eventShell ?? "default");
469
+ const displayTimezoneEvents = normalizeSchedulerEventsToDisplayTimezone(categoryFilteredEvents, input.schedulerTimezone, input.resolvedDisplayTimezone);
470
+ const categoryCounts = countEventsByCategory(rawEvents, categoryField);
471
+ const eventSurfaceContext = {
472
+ resources: input.resources,
473
+ categories,
474
+ categoryField
475
+ };
476
+ return {
477
+ rawEvents,
478
+ expandedEvents,
479
+ categoryFilteredEvents,
480
+ displayTimezoneEvents,
481
+ categoryCounts,
482
+ eventSurfaceContext,
483
+ eventSurfaceColors: buildEventSurfaceColors(categoryFilteredEvents, eventSurfaceContext)
484
+ };
485
+ }
486
+ function createSchedulerEventPipelineMemo() {
487
+ let previous = null;
488
+ return (input) => {
489
+ const key = createSchedulerEventPipelineMemoKey(input);
490
+ if (previous && areSchedulerEventPipelineMemoKeysEqual(previous.key, key)) {
491
+ return previous.result;
492
+ }
493
+ const result = buildSchedulerEventPipeline(input);
494
+ previous = { key, result };
495
+ return result;
496
+ };
497
+ }
498
+
499
+ // src/event/recurrence-edit.ts
500
+ var DEFAULT_RECURRENCE_EDIT_OPTIONS = {
501
+ editModes: ["this", "all", "future"],
502
+ deleteModes: ["this", "all", "future"],
503
+ dragMode: "ask",
504
+ resizeMode: "ask",
505
+ confirmDialog: true,
506
+ allowExceptions: true
507
+ };
508
+ function normalizeRecurrenceEditOptions(options) {
509
+ if (options === false || options === void 0) {
510
+ return { ...DEFAULT_RECURRENCE_EDIT_OPTIONS, editModes: [], deleteModes: [] };
511
+ }
512
+ if (options === true) {
513
+ return { ...DEFAULT_RECURRENCE_EDIT_OPTIONS };
514
+ }
515
+ return { ...DEFAULT_RECURRENCE_EDIT_OPTIONS, ...options };
516
+ }
517
+ function dateToKey(date) {
518
+ const d = typeof date === "string" ? new Date(date) : date;
519
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
520
+ }
521
+ function isSameDay(date1, date2) {
522
+ return dateToKey(date1) === dateToKey(date2);
523
+ }
524
+ function toDate2(date) {
525
+ return typeof date === "string" ? new Date(date) : new Date(date);
526
+ }
527
+ function editThisOnly(series, occurrence, changes) {
528
+ const originalStart = toDate2(occurrence.start);
529
+ const existingExceptions = series.exceptions || [];
530
+ const existingIndex = existingExceptions.findIndex((ex) => isSameDay(ex.originalStart, originalStart));
531
+ let exception;
532
+ if (existingIndex >= 0) {
533
+ exception = {
534
+ ...existingExceptions[existingIndex],
535
+ event: {
536
+ ...existingExceptions[existingIndex].event,
537
+ ...changes
538
+ }
539
+ };
540
+ } else {
541
+ exception = {
542
+ recurrenceId: originalStart,
543
+ originalStart,
544
+ event: {
545
+ id: `${series.id}_exc_${originalStart.getTime()}`,
546
+ title: series.title,
547
+ start: occurrence.start,
548
+ end: occurrence.end,
549
+ duration: series.duration,
550
+ allDay: series.allDay,
551
+ color: series.color,
552
+ textColor: series.textColor,
553
+ className: series.className,
554
+ editable: series.editable,
555
+ draggable: series.draggable,
556
+ resizable: series.resizable,
557
+ metadata: series.metadata,
558
+ ...changes
559
+ }
560
+ };
561
+ }
562
+ const newExceptions = existingIndex >= 0 ? existingExceptions.map((ex, i) => i === existingIndex ? exception : ex) : [...existingExceptions, exception];
563
+ return {
564
+ series: {
565
+ ...series,
566
+ exceptions: newExceptions
567
+ },
568
+ exception
569
+ };
570
+ }
571
+ function editAll(series, changes) {
572
+ const { start: changesStart, end: changesEnd, ...otherChanges } = changes;
573
+ const updatedSeries = {
574
+ ...series,
575
+ ...otherChanges
576
+ };
577
+ if (changesStart !== void 0) {
578
+ const originalStart = toDate2(series.start);
579
+ const newStart = toDate2(changesStart);
580
+ const updatedStart = new Date(originalStart);
581
+ updatedStart.setHours(newStart.getHours(), newStart.getMinutes(), newStart.getSeconds(), newStart.getMilliseconds());
582
+ updatedSeries.start = updatedStart;
583
+ }
584
+ if (changesEnd !== void 0 && series.end !== void 0) {
585
+ const originalEnd = toDate2(series.end);
586
+ const newEnd = toDate2(changesEnd);
587
+ const updatedEnd = new Date(originalEnd);
588
+ updatedEnd.setHours(newEnd.getHours(), newEnd.getMinutes(), newEnd.getSeconds(), newEnd.getMilliseconds());
589
+ updatedSeries.end = updatedEnd;
590
+ }
591
+ if (otherChanges.rrule !== void 0) {
592
+ updatedSeries.rrule = otherChanges.rrule;
593
+ }
594
+ return { series: updatedSeries };
595
+ }
596
+ function editThisAndFuture(series, occurrence, changes) {
597
+ const splitDate = toDate2(occurrence.start);
598
+ const dayBefore = new Date(splitDate);
599
+ dayBefore.setDate(dayBefore.getDate() - 1);
600
+ const originalRRule = typeof series.rrule === "string" ? parseRRule(series.rrule) : series.rrule;
601
+ if (!originalRRule) {
602
+ throw new Error("Cannot split series: invalid RRULE");
603
+ }
604
+ const truncatedRRule = {
605
+ ...originalRRule,
606
+ until: dayBefore
607
+ };
608
+ delete truncatedRRule.count;
609
+ const originalExceptions = series.exceptions || [];
610
+ const pastExceptions = originalExceptions.filter((ex) => toDate2(ex.originalStart) < splitDate);
611
+ const originalExdates = series.exdate || [];
612
+ const pastExdates = originalExdates.filter((exd) => toDate2(exd) < splitDate);
613
+ const originalRdates = series.rdate || [];
614
+ const pastRdates = originalRdates.filter((rd) => toDate2(rd) < splitDate);
615
+ const originalSeries = {
616
+ ...series,
617
+ rrule: truncatedRRule,
618
+ exdate: pastExdates.length > 0 ? pastExdates : void 0,
619
+ rdate: pastRdates.length > 0 ? pastRdates : void 0,
620
+ exceptions: pastExceptions.length > 0 ? pastExceptions : void 0
621
+ };
622
+ const newSeriesRRule = changes.rrule ? typeof changes.rrule === "string" ? parseRRule(changes.rrule) || originalRRule : changes.rrule : { ...originalRRule };
623
+ if (newSeriesRRule.until) {
624
+ const originalUntil = toDate2(newSeriesRRule.until);
625
+ if (originalUntil < splitDate) {
626
+ delete newSeriesRRule.until;
627
+ }
628
+ }
629
+ const newSeriesId = `${series.id}_split_${splitDate.getTime()}`;
630
+ const newSeries = {
631
+ id: newSeriesId,
632
+ title: changes.title ?? series.title,
633
+ start: changes.start ?? occurrence.start,
634
+ end: changes.end,
635
+ duration: changes.duration ?? series.duration,
636
+ allDay: changes.allDay ?? series.allDay,
637
+ color: changes.color ?? series.color,
638
+ textColor: changes.textColor ?? series.textColor,
639
+ className: changes.className ?? series.className,
640
+ editable: changes.editable ?? series.editable,
641
+ draggable: changes.draggable ?? series.draggable,
642
+ resizable: changes.resizable ?? series.resizable,
643
+ metadata: changes.metadata ?? series.metadata,
644
+ rrule: newSeriesRRule,
645
+ timezone: series.timezone,
646
+ resourceId: changes.resourceId ?? series.resourceId,
647
+ resourceIds: changes.resourceIds ?? series.resourceIds
648
+ };
649
+ return {
650
+ originalSeries,
651
+ newSeries
652
+ };
653
+ }
654
+ function deleteThisOnly(series, occurrence) {
655
+ const occurrenceStart = toDate2(occurrence.start);
656
+ const existingExdates = series.exdate || [];
657
+ const alreadyExcluded = existingExdates.some((exd) => isSameDay(exd, occurrenceStart));
658
+ if (alreadyExcluded) {
659
+ return { series };
660
+ }
661
+ const existingExceptions = series.exceptions || [];
662
+ const remainingExceptions = existingExceptions.filter((ex) => !isSameDay(ex.originalStart, occurrenceStart));
663
+ return {
664
+ series: {
665
+ ...series,
666
+ exdate: [...existingExdates, occurrenceStart],
667
+ exceptions: remainingExceptions.length > 0 ? remainingExceptions : void 0
668
+ }
669
+ };
670
+ }
671
+ function deleteAll() {
672
+ return { deleted: true };
673
+ }
674
+ function deleteThisAndFuture(series, occurrence) {
675
+ const splitDate = toDate2(occurrence.start);
676
+ const seriesStart = toDate2(series.start);
677
+ if (isSameDay(splitDate, seriesStart)) {
678
+ return { series: null };
679
+ }
680
+ const dayBefore = new Date(splitDate);
681
+ dayBefore.setDate(dayBefore.getDate() - 1);
682
+ const originalRRule = typeof series.rrule === "string" ? parseRRule(series.rrule) : series.rrule;
683
+ if (!originalRRule) {
684
+ return { series: null };
685
+ }
686
+ const truncatedRRule = {
687
+ ...originalRRule,
688
+ until: dayBefore
689
+ };
690
+ delete truncatedRRule.count;
691
+ const originalExceptions = series.exceptions || [];
692
+ const pastExceptions = originalExceptions.filter((ex) => toDate2(ex.originalStart) < splitDate);
693
+ const originalExdates = series.exdate || [];
694
+ const pastExdates = originalExdates.filter((exd) => toDate2(exd) < splitDate);
695
+ const originalRdates = series.rdate || [];
696
+ const pastRdates = originalRdates.filter((rd) => toDate2(rd) < splitDate);
697
+ return {
698
+ series: {
699
+ ...series,
700
+ rrule: truncatedRRule,
701
+ exdate: pastExdates.length > 0 ? pastExdates : void 0,
702
+ rdate: pastRdates.length > 0 ? pastRdates : void 0,
703
+ exceptions: pastExceptions.length > 0 ? pastExceptions : void 0
704
+ }
705
+ };
706
+ }
707
+ function applyRecurrenceEdit(series, occurrence, changes, scope) {
708
+ switch (scope) {
709
+ case "this": {
710
+ const result = editThisOnly(series, occurrence, changes);
711
+ return { series: result.series };
712
+ }
713
+ case "all": {
714
+ const result = editAll(series, changes);
715
+ return { series: result.series };
716
+ }
717
+ case "future": {
718
+ const result = editThisAndFuture(series, occurrence, changes);
719
+ return {
720
+ series: result.originalSeries,
721
+ newSeries: result.newSeries
722
+ };
723
+ }
724
+ }
725
+ }
726
+ function applyRecurrenceDelete(series, occurrence, scope) {
727
+ switch (scope) {
728
+ case "this": {
729
+ const result = deleteThisOnly(series, occurrence);
730
+ return { series: result.series };
731
+ }
732
+ case "all": {
733
+ return { series: null, deleted: true };
734
+ }
735
+ case "future": {
736
+ const result = deleteThisAndFuture(series, occurrence);
737
+ return { series: result.series };
738
+ }
739
+ }
740
+ }
741
+ function addRDate(series, date) {
742
+ const existingRdates = series.rdate || [];
743
+ const newDate = toDate2(date);
744
+ const alreadyExists = existingRdates.some((rd) => isSameDay(rd, newDate));
745
+ if (alreadyExists) {
746
+ return series;
747
+ }
748
+ return {
749
+ ...series,
750
+ rdate: [...existingRdates, newDate]
751
+ };
752
+ }
753
+ function removeRDate(series, date) {
754
+ const existingRdates = series.rdate || [];
755
+ const targetDate = toDate2(date);
756
+ return {
757
+ ...series,
758
+ rdate: existingRdates.filter((rd) => !isSameDay(rd, targetDate))
759
+ };
760
+ }
761
+ function addException(series, exception) {
762
+ const existingExceptions = series.exceptions || [];
763
+ const existingIndex = existingExceptions.findIndex((ex) => isSameDay(ex.originalStart, exception.originalStart));
764
+ if (existingIndex >= 0) {
765
+ return {
766
+ ...series,
767
+ exceptions: existingExceptions.map((ex, i) => i === existingIndex ? exception : ex)
768
+ };
769
+ }
770
+ return {
771
+ ...series,
772
+ exceptions: [...existingExceptions, exception]
773
+ };
774
+ }
775
+ function removeException(series, originalStart) {
776
+ const existingExceptions = series.exceptions || [];
777
+ return {
778
+ ...series,
779
+ exceptions: existingExceptions.filter((ex) => !isSameDay(ex.originalStart, originalStart))
780
+ };
781
+ }
782
+ function getExceptionForDate(series, date) {
783
+ const exceptions = series.exceptions || [];
784
+ return exceptions.find((ex) => isSameDay(ex.originalStart, date));
785
+ }
786
+ function hasExceptionForDate(series, date) {
787
+ return getExceptionForDate(series, date) !== void 0;
788
+ }
789
+ function isOccurrenceExcluded(series, date) {
790
+ const exdates = series.exdate || [];
791
+ return exdates.some((exd) => isSameDay(exd, date));
792
+ }
793
+ function getRecurrenceDescription(scope) {
794
+ switch (scope) {
795
+ case "this":
796
+ return "This event only";
797
+ case "all":
798
+ return "All events in the series";
799
+ case "future":
800
+ return "This and all future events";
801
+ }
802
+ }
803
+ function getDeleteDescription(scope) {
804
+ switch (scope) {
805
+ case "this":
806
+ return "Delete this event only";
807
+ case "all":
808
+ return "Delete all events in the series";
809
+ case "future":
810
+ return "Delete this and all future events";
811
+ }
812
+ }
813
+ function isRecurringSeries(event) {
814
+ return event.rrule !== void 0 && !("recurrenceId" in event && event.recurrenceId !== void 0);
815
+ }
816
+ function convertToSeries(event) {
817
+ if (!event.rrule) {
818
+ return null;
819
+ }
820
+ return {
821
+ id: event.id,
822
+ title: event.title,
823
+ start: event.start,
824
+ end: event.end,
825
+ duration: event.duration,
826
+ allDay: event.allDay,
827
+ color: event.color,
828
+ textColor: event.textColor,
829
+ className: event.className,
830
+ editable: event.editable,
831
+ draggable: event.draggable,
832
+ resizable: event.resizable,
833
+ metadata: event.metadata,
834
+ rrule: event.rrule,
835
+ exdate: event.exdate,
836
+ rdate: event.rdate,
837
+ timezone: event.timezone,
838
+ resourceId: event.resourceId,
839
+ resourceIds: event.resourceIds
840
+ };
841
+ }
842
+ function serializeSeries(series) {
843
+ const rrule = typeof series.rrule === "string" ? series.rrule : serializeRRule(series.rrule);
844
+ const parts = [`RRULE:${rrule}`];
845
+ if (series.exdate && series.exdate.length > 0) {
846
+ const exdates = series.exdate.map((d) => formatICalDate(toDate2(d))).join(",");
847
+ parts.push(`EXDATE:${exdates}`);
848
+ }
849
+ if (series.rdate && series.rdate.length > 0) {
850
+ const rdates = series.rdate.map((d) => formatICalDate(toDate2(d))).join(",");
851
+ parts.push(`RDATE:${rdates}`);
852
+ }
853
+ return parts.join("\n");
854
+ }
855
+ function formatICalDate(date) {
856
+ const year = date.getFullYear();
857
+ const month = String(date.getMonth() + 1).padStart(2, "0");
858
+ const day = String(date.getDate()).padStart(2, "0");
859
+ const hour = String(date.getHours()).padStart(2, "0");
860
+ const minute = String(date.getMinutes()).padStart(2, "0");
861
+ const second = String(date.getSeconds()).padStart(2, "0");
862
+ return `${year}${month}${day}T${hour}${minute}${second}`;
863
+ }
864
+ function parseICalDate(dateStr) {
865
+ if (dateStr.length === 8) {
866
+ const year = parseInt(dateStr.slice(0, 4), 10);
867
+ const month = parseInt(dateStr.slice(4, 6), 10) - 1;
868
+ const day = parseInt(dateStr.slice(6, 8), 10);
869
+ return new Date(year, month, day);
870
+ }
871
+ if (dateStr.includes("T")) {
872
+ const datePart = dateStr.slice(0, 8);
873
+ const timePart = dateStr.slice(9, 15);
874
+ const year = parseInt(datePart.slice(0, 4), 10);
875
+ const month = parseInt(datePart.slice(4, 6), 10) - 1;
876
+ const day = parseInt(datePart.slice(6, 8), 10);
877
+ const hour = parseInt(timePart.slice(0, 2), 10);
878
+ const minute = parseInt(timePart.slice(2, 4), 10);
879
+ const second = parseInt(timePart.slice(4, 6), 10);
880
+ if (dateStr.endsWith("Z")) {
881
+ return new Date(Date.UTC(year, month, day, hour, minute, second));
882
+ }
883
+ return new Date(year, month, day, hour, minute, second);
884
+ }
885
+ return new Date(dateStr);
886
+ }
887
+ function parseExDates(exdateStr) {
888
+ const cleaned = exdateStr.replace(/^EXDATE[;:]/i, "").trim();
889
+ return cleaned.split(",").map((d) => parseICalDate(d.trim()));
890
+ }
891
+ function parseRDates(rdateStr) {
892
+ const cleaned = rdateStr.replace(/^RDATE[;:]/i, "").trim();
893
+ return cleaned.split(",").map((d) => parseICalDate(d.trim()));
894
+ }
895
+
896
+ // src/event/recurrence-runtime.ts
897
+ var toDateValue = (value, fallback) => {
898
+ if (value instanceof Date) {
899
+ return new Date(value);
900
+ }
901
+ if (typeof value === "string") {
902
+ const parsed = new Date(value);
903
+ if (!Number.isNaN(parsed.getTime())) {
904
+ return parsed;
905
+ }
906
+ }
907
+ return /* @__PURE__ */ new Date();
908
+ };
909
+ var toEventDurationMinutes = (event, fallback) => {
910
+ if (typeof event.duration === "number" && Number.isFinite(event.duration) && event.duration > 0) {
911
+ return event.duration;
912
+ }
913
+ const start = toDateValue(event.start);
914
+ if (event.end) {
915
+ const end = toDateValue(event.end);
916
+ const diff = Math.round((end.getTime() - start.getTime()) / 6e4);
917
+ if (diff > 0) {
918
+ return diff;
919
+ }
920
+ }
921
+ return fallback;
922
+ };
923
+ function resolveSeriesIdFromOccurrence(event) {
924
+ const recurringEvent = event;
925
+ if (recurringEvent.recurringEventId !== void 0 && recurringEvent.recurringEventId !== null) {
926
+ return recurringEvent.recurringEventId;
927
+ }
928
+ if (recurringEvent.originalId !== void 0 && recurringEvent.originalId !== null) {
929
+ return recurringEvent.originalId;
930
+ }
931
+ const metadataRecurringId = recurringEvent.metadata?.recurringEventId;
932
+ if (typeof metadataRecurringId === "string" || typeof metadataRecurringId === "number") {
933
+ return metadataRecurringId;
934
+ }
935
+ if (typeof event.id === "string") {
936
+ const match = event.id.match(/^(.*)_\d+$/);
937
+ if (match && match[1]) {
938
+ return match[1];
939
+ }
940
+ }
941
+ return null;
942
+ }
943
+ function isRecurringOccurrenceEvent(event) {
944
+ const recurringEvent = event;
945
+ if (recurringEvent.isRecurring) {
946
+ return true;
947
+ }
948
+ if (recurringEvent.recurringEventId !== void 0 && recurringEvent.recurringEventId !== null) {
949
+ return true;
950
+ }
951
+ if (recurringEvent.originalId !== void 0 && recurringEvent.originalId !== null) {
952
+ return true;
953
+ }
954
+ return Boolean(event.recurrenceId && !event.rrule);
955
+ }
956
+ function resolveRecurringContext(event, baseEvents, durationFallbackMinutes = 60) {
957
+ if (!isRecurringOccurrenceEvent(event)) {
958
+ return null;
959
+ }
960
+ const seriesId = resolveSeriesIdFromOccurrence(event);
961
+ if (seriesId === null) {
962
+ return null;
963
+ }
964
+ const sourceSeries = baseEvents.find((candidate) => String(candidate.id) === String(seriesId) && Boolean(candidate.rrule) && !candidate.recurrenceId) ?? (event.rrule && !event.recurrenceId ? event : null);
965
+ if (!sourceSeries) {
966
+ return null;
967
+ }
968
+ const series = convertToSeries(sourceSeries);
969
+ if (!series) {
970
+ return null;
971
+ }
972
+ const start = toDateValue(event.start);
973
+ const duration = toEventDurationMinutes(event, typeof series.duration === "number" ? series.duration : durationFallbackMinutes);
974
+ const end = event.end ? toDateValue(event.end) : new Date(start.getTime() + duration * 6e4);
975
+ const recurringEvent = event;
976
+ const occurrence = {
977
+ id: String(event.id),
978
+ originalId: recurringEvent.originalId ?? series.id,
979
+ title: event.title,
980
+ start,
981
+ end,
982
+ allDay: event.allDay,
983
+ color: event.color,
984
+ textColor: event.textColor,
985
+ className: event.className,
986
+ editable: event.editable,
987
+ draggable: event.draggable,
988
+ resizable: event.resizable,
989
+ metadata: event.metadata,
990
+ isRecurring: true,
991
+ recurringEventId: series.id,
992
+ recurrenceId: recurringEvent.recurrenceId ?? recurringEvent.originalStart ?? start,
993
+ isException: recurringEvent.isException,
994
+ originalStart: recurringEvent.originalStart ? toDateValue(recurringEvent.originalStart) : start,
995
+ timezone: event.timezone,
996
+ resourceId: event.resourceId,
997
+ resourceIds: event.resourceIds,
998
+ categoryId: event.categoryId,
999
+ categoryIds: event.categoryIds
1000
+ };
1001
+ return { series, occurrence };
1002
+ }
1003
+ function resolveDefaultEditScope(modes) {
1004
+ if (modes.length === 0) {
1005
+ return null;
1006
+ }
1007
+ return modes.includes("this") ? "this" : modes[0];
1008
+ }
1009
+ function resolveDefaultDeleteScope(modes) {
1010
+ if (modes.length === 0) {
1011
+ return null;
1012
+ }
1013
+ return modes.includes("this") ? "this" : modes[0];
1014
+ }
1015
+ function resolveRecurrenceEditScope(action, options, scopeSelector) {
1016
+ if (options.editModes.length === 0) {
1017
+ return null;
1018
+ }
1019
+ const fallback = resolveDefaultEditScope(options.editModes);
1020
+ if (!fallback) {
1021
+ return null;
1022
+ }
1023
+ if (action === "drag" && options.dragMode === "this") {
1024
+ return fallback;
1025
+ }
1026
+ if (action === "resize" && options.resizeMode === "this") {
1027
+ return fallback;
1028
+ }
1029
+ if (!scopeSelector) {
1030
+ return fallback;
1031
+ }
1032
+ return scopeSelector({
1033
+ action,
1034
+ modes: options.editModes,
1035
+ fallback
1036
+ });
1037
+ }
1038
+ function resolveRecurrenceDeleteScope(options, scopeSelector) {
1039
+ if (options.deleteModes.length === 0) {
1040
+ return null;
1041
+ }
1042
+ const fallback = resolveDefaultDeleteScope(options.deleteModes);
1043
+ if (!fallback) {
1044
+ return null;
1045
+ }
1046
+ if (!scopeSelector) {
1047
+ return fallback;
1048
+ }
1049
+ return scopeSelector({
1050
+ modes: options.deleteModes,
1051
+ fallback
1052
+ });
1053
+ }
1054
+ function applyRecurringEditChange(input) {
1055
+ const context = resolveRecurringContext(input.event, input.baseEvents, input.durationFallbackMinutes);
1056
+ if (!context || input.editOptions.editModes.length === 0) {
1057
+ return { handled: false };
1058
+ }
1059
+ const scope = resolveRecurrenceEditScope(input.action, input.editOptions, input.scopeSelector);
1060
+ if (!scope) {
1061
+ return { handled: true, canceled: true };
1062
+ }
1063
+ const result = applyRecurrenceEdit(context.series, context.occurrence, input.changes, scope);
1064
+ return {
1065
+ handled: true,
1066
+ scope,
1067
+ context,
1068
+ result
1069
+ };
1070
+ }
1071
+ function applyRecurringDeleteChange(input) {
1072
+ const context = resolveRecurringContext(input.event, input.baseEvents, input.durationFallbackMinutes);
1073
+ if (!context || input.editOptions.deleteModes.length === 0) {
1074
+ return { handled: false };
1075
+ }
1076
+ const scope = resolveRecurrenceDeleteScope(input.editOptions, input.scopeSelector);
1077
+ if (!scope) {
1078
+ return { handled: true, canceled: true };
1079
+ }
1080
+ const result = applyRecurrenceDelete(context.series, context.occurrence, scope);
1081
+ return {
1082
+ handled: true,
1083
+ scope,
1084
+ context,
1085
+ result
1086
+ };
1087
+ }
1088
+
1089
+ // src/event/recurrence.controller.ts
1090
+ function processRecurrenceEdit(input) {
1091
+ const state = applyRecurringEditChange(input);
1092
+ if (!state.handled || state.canceled || !state.scope || !state.context || !state.result) {
1093
+ return state;
1094
+ }
1095
+ const emits = {
1096
+ recurrenceEdit: {
1097
+ source: input.source,
1098
+ scope: state.scope,
1099
+ occurrence: state.context.occurrence,
1100
+ series: state.context.series,
1101
+ changes: input.changes,
1102
+ result: state.result
1103
+ },
1104
+ eventChange: {
1105
+ event: state.result.series,
1106
+ oldEvent: state.context.series
1107
+ }
1108
+ };
1109
+ if (state.result.newSeries) {
1110
+ emits.eventAdd = state.result.newSeries;
1111
+ }
1112
+ return { ...state, emits };
1113
+ }
1114
+ function processRecurrenceDelete(input) {
1115
+ const state = applyRecurringDeleteChange(input);
1116
+ if (!state.handled || state.canceled || !state.scope || !state.context || !state.result) {
1117
+ return state;
1118
+ }
1119
+ const emits = {
1120
+ recurrenceDelete: {
1121
+ source: input.source,
1122
+ scope: state.scope,
1123
+ occurrence: state.context.occurrence,
1124
+ series: state.context.series,
1125
+ result: state.result
1126
+ }
1127
+ };
1128
+ if (state.result.series) {
1129
+ emits.eventChange = {
1130
+ event: state.result.series,
1131
+ oldEvent: state.context.series
1132
+ };
1133
+ } else {
1134
+ emits.eventRemove = {
1135
+ event: state.context.series
1136
+ };
1137
+ }
1138
+ return { ...state, emits };
1139
+ }
1140
+ function resolveRecurrenceEditContext(input) {
1141
+ const context = resolveRecurringContext(input.event, input.baseEvents, input.durationFallbackMinutes);
1142
+ if (!context || input.editOptions.editModes.length === 0) {
1143
+ return { resolved: false };
1144
+ }
1145
+ const scope = resolveRecurrenceEditScope("edit", input.editOptions, input.scopeSelector);
1146
+ if (!scope) {
1147
+ return { resolved: true, canceled: true };
1148
+ }
1149
+ return { resolved: true, scope, context };
1150
+ }
1151
+ function getRecurrenceEditScopeMessage(action) {
1152
+ if (action === "drag") return "Apply drag changes to recurring event:";
1153
+ if (action === "resize") return "Apply resize changes to recurring event:";
1154
+ return "Apply edit changes to recurring event:";
1155
+ }
1156
+ function getRecurrenceDeleteScopeMessage() {
1157
+ return "Delete recurring event scope:";
1158
+ }
1159
+ function getEditScopeLabel(scope, index) {
1160
+ if (scope === "this") return `${index}. This event only`;
1161
+ if (scope === "all") return `${index}. All events in series`;
1162
+ return `${index}. This and future events`;
1163
+ }
1164
+ function getDeleteScopeLabel(scope, index) {
1165
+ if (scope === "this") return `${index}. Delete this event only`;
1166
+ if (scope === "all") return `${index}. Delete all events in series`;
1167
+ return `${index}. Delete this and future events`;
1168
+ }
1169
+
1170
+ export { DEFAULT_RECURRENCE_EDIT_OPTIONS, addException, addRDate, applyRecurrenceDelete, applyRecurrenceEdit, applyRecurringDeleteChange, applyRecurringEditChange, buildSchedulerEventPipeline, calculateEventPosition, convertToSeries, createSchedulerEventPipelineMemo, deleteAll, deleteThisAndFuture, deleteThisOnly, editAll, editThisAndFuture, editThisOnly, getAllDayEventsForDay, getDeleteDescription, getDeleteScopeLabel, getEditScopeLabel, getExceptionForDate, getRecurrenceDeleteScopeMessage, getRecurrenceDescription, getRecurrenceEditScopeMessage, getTimedEventsForDay, hasExceptionForDate, isOccurrenceExcluded, isRecurringOccurrenceEvent, isRecurringSeries, normalizeRecurrenceEditOptions, normalizeSchedulerEventToDisplayTimezone, normalizeSchedulerEventsToDisplayTimezone, parseExDates, parseICalDate, parseRDates, positionAllDayEventsForWeek, positionEventsForDay, positionEventsForWeekRow, processRecurrenceDelete, processRecurrenceEdit, removeEventById, removeException, removeRDate, resolveDefaultDeleteScope, resolveDefaultEditScope, resolveRecurrenceDeleteScope, resolveRecurrenceEditContext, resolveRecurrenceEditScope, resolveRecurringContext, resolveSeriesIdFromOccurrence, serializeSeries, upsertEventById };