@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,440 @@
1
+ import { isRangeWithinBusinessHours } from './chunk-HPC5B3AR.mjs';
2
+ import { addDays, isSameDay, startOfDay } from './chunk-WFUJWDST.mjs';
3
+
4
+ // src/utils/resource-constraints.ts
5
+ var DEFAULT_CONFLICT_OPTIONS = {
6
+ enabled: false,
7
+ mode: "highlight",
8
+ checkCapacity: true,
9
+ checkOverlap: true,
10
+ checkAvailability: true,
11
+ checkDailyHours: true,
12
+ checkEventDuration: true
13
+ };
14
+ function normalizeConflictOptions(options) {
15
+ if (options === false || options === void 0) {
16
+ return { ...DEFAULT_CONFLICT_OPTIONS, enabled: false };
17
+ }
18
+ if (options === true) {
19
+ return { ...DEFAULT_CONFLICT_OPTIONS, enabled: true };
20
+ }
21
+ return { ...DEFAULT_CONFLICT_OPTIONS, ...options, enabled: options.enabled ?? true };
22
+ }
23
+ function getEventDates(event) {
24
+ const start = typeof event.start === "string" ? new Date(event.start) : new Date(event.start);
25
+ const end = event.end ? typeof event.end === "string" ? new Date(event.end) : new Date(event.end) : new Date(start.getTime() + (event.duration ?? 30) * 60 * 1e3);
26
+ return { start, end };
27
+ }
28
+ function getEventDuration(event) {
29
+ const { start, end } = getEventDates(event);
30
+ return (end.getTime() - start.getTime()) / (1e3 * 60);
31
+ }
32
+ function getEventResourceIds(event) {
33
+ if (Array.isArray(event.resourceIds) && event.resourceIds.length > 0) {
34
+ return event.resourceIds;
35
+ }
36
+ if (event.resourceId !== void 0) {
37
+ return [event.resourceId];
38
+ }
39
+ return [];
40
+ }
41
+ function eventBelongsToResource(event, resourceId) {
42
+ return getEventResourceIds(event).includes(resourceId);
43
+ }
44
+ function getDurationHoursInRange(start, end, rangeStart, rangeEnd) {
45
+ const effectiveStart = start.getTime() < rangeStart.getTime() ? rangeStart : start;
46
+ const effectiveEnd = end.getTime() > rangeEnd.getTime() ? rangeEnd : end;
47
+ const ms = effectiveEnd.getTime() - effectiveStart.getTime();
48
+ if (ms <= 0) {
49
+ return 0;
50
+ }
51
+ return ms / (1e3 * 60 * 60);
52
+ }
53
+ function getOverlappingDayStarts(start, end) {
54
+ if (end.getTime() <= start.getTime()) {
55
+ return [];
56
+ }
57
+ const days = [];
58
+ let cursor = startOfDay(start);
59
+ while (cursor.getTime() < end.getTime()) {
60
+ days.push(new Date(cursor));
61
+ cursor = addDays(cursor, 1);
62
+ }
63
+ return days;
64
+ }
65
+ function getEventsForResourceOnDate(resourceId, date, allEvents) {
66
+ const dayStart = startOfDay(date);
67
+ const dayEnd = addDays(dayStart, 1);
68
+ return allEvents.filter((event) => {
69
+ if (!eventBelongsToResource(event, resourceId)) return false;
70
+ const { start, end } = getEventDates(event);
71
+ return start < dayEnd && end > dayStart;
72
+ });
73
+ }
74
+ function getEventsOverlappingTimeRange(resourceId, start, end, allEvents, excludeEventId) {
75
+ return allEvents.filter((event) => {
76
+ if (excludeEventId && event.id === excludeEventId) return false;
77
+ if (!eventBelongsToResource(event, resourceId)) return false;
78
+ const eventDates = getEventDates(event);
79
+ return eventDates.start < end && eventDates.end > start;
80
+ });
81
+ }
82
+ function checkCapacityConstraint(resource, event, allEvents) {
83
+ const capacity = resource.constraints?.capacity;
84
+ if (capacity === void 0) return null;
85
+ const { start, end } = getEventDates(event);
86
+ const overlapping = getEventsOverlappingTimeRange(resource.id, start, end, allEvents, event.id);
87
+ if (overlapping.length >= capacity) {
88
+ return {
89
+ type: "capacity",
90
+ resourceId: resource.id,
91
+ eventId: event.id,
92
+ message: `Resource "${resource.title}" has reached its capacity of ${capacity} concurrent events`,
93
+ severity: "error"
94
+ };
95
+ }
96
+ return null;
97
+ }
98
+ function checkDailyHoursConstraint(resource, event, allEvents) {
99
+ const maxHours = resource.constraints?.maxHoursPerDay;
100
+ if (maxHours === void 0) return null;
101
+ const { start: eventStart, end: eventEnd } = getEventDates(event);
102
+ for (const dayStart of getOverlappingDayStarts(eventStart, eventEnd)) {
103
+ const dayEnd = addDays(dayStart, 1);
104
+ const dayEvents = getEventsForResourceOnDate(resource.id, dayStart, allEvents).filter((e) => e.id !== event.id);
105
+ const existingHours = dayEvents.reduce((sum, dayEvent) => {
106
+ const { start, end } = getEventDates(dayEvent);
107
+ return sum + getDurationHoursInRange(start, end, dayStart, dayEnd);
108
+ }, 0);
109
+ const candidateHours = getDurationHoursInRange(eventStart, eventEnd, dayStart, dayEnd);
110
+ if (existingHours + candidateHours > maxHours) {
111
+ const dayLabel = dayStart.toISOString().slice(0, 10);
112
+ return {
113
+ type: "hours-limit",
114
+ resourceId: resource.id,
115
+ eventId: event.id,
116
+ message: `Adding this event would exceed ${maxHours} hours on ${dayLabel} for "${resource.title}"`,
117
+ severity: "error"
118
+ };
119
+ }
120
+ }
121
+ return null;
122
+ }
123
+ function checkDailyEventsConstraint(resource, event, allEvents) {
124
+ const maxEvents = resource.constraints?.maxEventsPerDay;
125
+ if (maxEvents === void 0) return null;
126
+ const { start, end } = getEventDates(event);
127
+ for (const dayStart of getOverlappingDayStarts(start, end)) {
128
+ const dayEvents = getEventsForResourceOnDate(resource.id, dayStart, allEvents).filter((e) => e.id !== event.id);
129
+ if (dayEvents.length + 1 > maxEvents) {
130
+ const dayLabel = dayStart.toISOString().slice(0, 10);
131
+ return {
132
+ type: "capacity",
133
+ resourceId: resource.id,
134
+ eventId: event.id,
135
+ message: `Resource "${resource.title}" has reached its daily limit of ${maxEvents} events on ${dayLabel}`,
136
+ severity: "error"
137
+ };
138
+ }
139
+ }
140
+ return null;
141
+ }
142
+ function checkOverlapConstraint(resource, event, allEvents) {
143
+ const { start, end } = getEventDates(event);
144
+ const overlapping = getEventsOverlappingTimeRange(resource.id, start, end, allEvents, event.id);
145
+ if (overlapping.length > 0) {
146
+ return {
147
+ type: "overlap",
148
+ resourceId: resource.id,
149
+ eventId: event.id,
150
+ message: `Resource "${resource.title}" already has ${overlapping.length} overlapping event(s)`,
151
+ severity: "error"
152
+ };
153
+ }
154
+ return null;
155
+ }
156
+ function checkAvailabilityConstraint(resource, event) {
157
+ if (event.allDay) {
158
+ return null;
159
+ }
160
+ if (!resource.businessHours) {
161
+ return null;
162
+ }
163
+ const { start, end } = getEventDates(event);
164
+ if (!isRangeWithinBusinessHours(start, end, resource.businessHours)) {
165
+ return {
166
+ type: "availability",
167
+ resourceId: resource.id,
168
+ eventId: event.id,
169
+ message: `Event is outside availability hours for "${resource.title}"`,
170
+ severity: "error"
171
+ };
172
+ }
173
+ return null;
174
+ }
175
+ function checkEventDurationConstraint(resource, event) {
176
+ const constraints = resource.constraints;
177
+ if (!constraints) return null;
178
+ const duration = getEventDuration(event);
179
+ if (constraints.minEventDuration !== void 0 && duration < constraints.minEventDuration) {
180
+ return {
181
+ type: "duration",
182
+ resourceId: resource.id,
183
+ eventId: event.id,
184
+ message: `Event duration (${duration} min) is below minimum (${constraints.minEventDuration} min) for "${resource.title}"`,
185
+ severity: "error"
186
+ };
187
+ }
188
+ if (constraints.maxEventDuration !== void 0 && duration > constraints.maxEventDuration) {
189
+ return {
190
+ type: "duration",
191
+ resourceId: resource.id,
192
+ eventId: event.id,
193
+ message: `Event duration (${duration} min) exceeds maximum (${constraints.maxEventDuration} min) for "${resource.title}"`,
194
+ severity: "error"
195
+ };
196
+ }
197
+ return null;
198
+ }
199
+ function checkSkillConstraint(resource, event) {
200
+ const resourceSkills = resource.constraints?.skills;
201
+ if (!resourceSkills || resourceSkills.length === 0) return null;
202
+ const requiredSkills = event.metadata?.requiredSkills;
203
+ if (!requiredSkills || requiredSkills.length === 0) return null;
204
+ const missingSkills = requiredSkills.filter((skill) => !resourceSkills.includes(skill));
205
+ if (missingSkills.length > 0) {
206
+ return {
207
+ type: "skill-mismatch",
208
+ resourceId: resource.id,
209
+ eventId: event.id,
210
+ message: `Resource "${resource.title}" is missing required skills: ${missingSkills.join(", ")}`,
211
+ severity: "warning"
212
+ };
213
+ }
214
+ return null;
215
+ }
216
+ function validateResourceConstraints(event, resource, allEvents, options) {
217
+ const conflicts = [];
218
+ if (!options.enabled) return conflicts;
219
+ if (options.checkCapacity) {
220
+ const capacityConflict = checkCapacityConstraint(resource, event, allEvents);
221
+ if (capacityConflict) conflicts.push(capacityConflict);
222
+ const dailyEventsConflict = checkDailyEventsConstraint(resource, event, allEvents);
223
+ if (dailyEventsConflict) conflicts.push(dailyEventsConflict);
224
+ }
225
+ if (options.checkOverlap) {
226
+ const overlapConflict = checkOverlapConstraint(resource, event, allEvents);
227
+ if (overlapConflict) conflicts.push(overlapConflict);
228
+ }
229
+ if (options.checkAvailability) {
230
+ const availabilityConflict = checkAvailabilityConstraint(resource, event);
231
+ if (availabilityConflict) conflicts.push(availabilityConflict);
232
+ }
233
+ if (options.checkDailyHours) {
234
+ const hoursConflict = checkDailyHoursConstraint(resource, event, allEvents);
235
+ if (hoursConflict) conflicts.push(hoursConflict);
236
+ }
237
+ if (options.checkEventDuration) {
238
+ const durationConflict = checkEventDurationConstraint(resource, event);
239
+ if (durationConflict) conflicts.push(durationConflict);
240
+ }
241
+ const skillConflict = checkSkillConstraint(resource, event);
242
+ if (skillConflict) conflicts.push(skillConflict);
243
+ return conflicts;
244
+ }
245
+ function getResourceCapacity(resource, date, allEvents) {
246
+ const capacity = resource.constraints?.capacity ?? Infinity;
247
+ const maxEvents = resource.constraints?.maxEventsPerDay;
248
+ if (capacity === Infinity && maxEvents === void 0) {
249
+ return { current: 0, max: Infinity, available: Infinity, percentage: 0 };
250
+ }
251
+ const dayEvents = getEventsForResourceOnDate(resource.id, date, allEvents);
252
+ if (maxEvents !== void 0) {
253
+ const current = dayEvents.length;
254
+ return {
255
+ current,
256
+ max: maxEvents,
257
+ available: Math.max(0, maxEvents - current),
258
+ percentage: maxEvents > 0 ? current / maxEvents * 100 : 0
259
+ };
260
+ }
261
+ let maxConcurrent = 0;
262
+ const timePoints = /* @__PURE__ */ new Set();
263
+ dayEvents.forEach((event) => {
264
+ const { start, end } = getEventDates(event);
265
+ timePoints.add(start.getTime());
266
+ timePoints.add(end.getTime());
267
+ });
268
+ const sortedPoints = Array.from(timePoints).sort((a, b) => a - b);
269
+ for (const point of sortedPoints) {
270
+ const concurrent = dayEvents.filter((event) => {
271
+ const { start, end } = getEventDates(event);
272
+ return start.getTime() <= point && end.getTime() > point;
273
+ }).length;
274
+ maxConcurrent = Math.max(maxConcurrent, concurrent);
275
+ }
276
+ return {
277
+ current: maxConcurrent,
278
+ max: capacity,
279
+ available: Math.max(0, capacity - maxConcurrent),
280
+ percentage: capacity > 0 ? maxConcurrent / capacity * 100 : 0
281
+ };
282
+ }
283
+ function getCapacityStatus(info) {
284
+ if (info.max === Infinity) return "available";
285
+ if (info.current >= info.max) return "full";
286
+ if (info.percentage >= 80) return "near-full";
287
+ return "available";
288
+ }
289
+ function canDropEventWithResourceConstraints(event, resource, allEvents, options) {
290
+ if (!resource || !options.enabled) {
291
+ return { canDrop: true, conflicts: [] };
292
+ }
293
+ const conflicts = validateResourceConstraints(event, resource, allEvents, options);
294
+ if (conflicts.length === 0) {
295
+ return { canDrop: true, conflicts: [] };
296
+ }
297
+ if (options.mode === "prevent") {
298
+ const hasErrors = conflicts.some((c) => c.severity === "error");
299
+ return { canDrop: !hasErrors, conflicts };
300
+ }
301
+ if (options.onConflict) {
302
+ for (const conflict of conflicts) {
303
+ const result = options.onConflict(conflict);
304
+ if (result === "prevent") {
305
+ return { canDrop: false, conflicts };
306
+ }
307
+ }
308
+ }
309
+ return { canDrop: true, conflicts };
310
+ }
311
+
312
+ // src/interaction/selection.ts
313
+ function handleDateSelection(date, mode, currentState) {
314
+ if (mode === "none") {
315
+ return {
316
+ selectedDates: currentState.selectedDates,
317
+ selectedRange: currentState.selectedRange,
318
+ selectedTimeRange: currentState.selectedTimeRange,
319
+ changed: false
320
+ };
321
+ }
322
+ if (mode === "single") {
323
+ return {
324
+ selectedDates: [date],
325
+ selectedRange: null,
326
+ selectedTimeRange: null,
327
+ changed: true
328
+ };
329
+ }
330
+ if (mode === "multiple") {
331
+ const existingIndex = currentState.selectedDates.findIndex((d) => d.getTime() === date.getTime());
332
+ if (existingIndex >= 0) {
333
+ return {
334
+ selectedDates: currentState.selectedDates.filter((_, i) => i !== existingIndex),
335
+ selectedRange: null,
336
+ selectedTimeRange: null,
337
+ changed: true
338
+ };
339
+ }
340
+ return {
341
+ selectedDates: [...currentState.selectedDates, date],
342
+ selectedRange: null,
343
+ selectedTimeRange: null,
344
+ changed: true
345
+ };
346
+ }
347
+ if (mode === "range") {
348
+ return {
349
+ selectedDates: [],
350
+ selectedRange: { start: date, end: date },
351
+ selectedTimeRange: null,
352
+ changed: true
353
+ };
354
+ }
355
+ return {
356
+ selectedDates: currentState.selectedDates,
357
+ selectedRange: currentState.selectedRange,
358
+ selectedTimeRange: currentState.selectedTimeRange,
359
+ changed: false
360
+ };
361
+ }
362
+ function handleTimeSelection(start, end, mode) {
363
+ if (mode === "none") {
364
+ return {
365
+ selectedDates: [],
366
+ selectedRange: null,
367
+ selectedTimeRange: null,
368
+ changed: false
369
+ };
370
+ }
371
+ return {
372
+ selectedDates: [],
373
+ selectedRange: null,
374
+ selectedTimeRange: { start, end },
375
+ changed: true
376
+ };
377
+ }
378
+ function startDragSelection(date) {
379
+ return {
380
+ isDragging: true,
381
+ dragStartDate: date,
382
+ previewRange: { start: date, end: date }
383
+ };
384
+ }
385
+ function calculateDragRange(startDate, currentDate) {
386
+ if (startDate <= currentDate) {
387
+ return { start: startDate, end: currentDate };
388
+ }
389
+ return { start: currentDate, end: startDate };
390
+ }
391
+ function updateDragPreview(startDate, currentDate) {
392
+ return {
393
+ previewRange: calculateDragRange(startDate, currentDate)
394
+ };
395
+ }
396
+ function finalizeDragSelection(startDate, endDate) {
397
+ const range = calculateDragRange(startDate, endDate);
398
+ return {
399
+ selectedDates: [],
400
+ selectedRange: range,
401
+ selectedTimeRange: null,
402
+ isDragging: false,
403
+ dragStartDate: null,
404
+ previewRange: null
405
+ };
406
+ }
407
+ function clearSelection() {
408
+ return {
409
+ selectedDates: [],
410
+ selectedRange: null,
411
+ selectedTimeRange: null
412
+ };
413
+ }
414
+ function hasSelection(state) {
415
+ return state.selectedDates.length > 0 || state.selectedRange !== null || state.selectedTimeRange !== null;
416
+ }
417
+ function isDateSelected(date, selectedDates) {
418
+ return selectedDates.some((d) => isSameDay(d, date));
419
+ }
420
+ function isDateInSelectionRange(date, range) {
421
+ if (!range) return false;
422
+ const time = date.getTime();
423
+ return time >= range.start.getTime() && time <= range.end.getTime();
424
+ }
425
+ function isRangeStart(date, range) {
426
+ if (!range) return false;
427
+ return isSameDay(date, range.start);
428
+ }
429
+ function isRangeEnd(date, range) {
430
+ if (!range) return false;
431
+ return isSameDay(date, range.end);
432
+ }
433
+ function getActiveRange(selectedRange, previewRange, isDragging) {
434
+ if (isDragging && previewRange) {
435
+ return previewRange;
436
+ }
437
+ return selectedRange;
438
+ }
439
+
440
+ export { DEFAULT_CONFLICT_OPTIONS, calculateDragRange, canDropEventWithResourceConstraints, checkAvailabilityConstraint, checkCapacityConstraint, checkDailyEventsConstraint, checkDailyHoursConstraint, checkEventDurationConstraint, checkOverlapConstraint, checkSkillConstraint, clearSelection, finalizeDragSelection, getActiveRange, getCapacityStatus, getResourceCapacity, handleDateSelection, handleTimeSelection, hasSelection, isDateInSelectionRange, isDateSelected, isRangeEnd, isRangeStart, normalizeConflictOptions, startDragSelection, updateDragPreview, validateResourceConstraints };