@liteforge/calendar 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +395 -0
  3. package/dist/calendar.d.ts +6 -0
  4. package/dist/calendar.d.ts.map +1 -0
  5. package/dist/calendar.js +318 -0
  6. package/dist/calendar.js.map +1 -0
  7. package/dist/components/toolbar.d.ts +18 -0
  8. package/dist/components/toolbar.d.ts.map +1 -0
  9. package/dist/components/toolbar.js +84 -0
  10. package/dist/components/toolbar.js.map +1 -0
  11. package/dist/date-utils.d.ts +104 -0
  12. package/dist/date-utils.d.ts.map +1 -0
  13. package/dist/date-utils.js +323 -0
  14. package/dist/date-utils.js.map +1 -0
  15. package/dist/index.d.ts +12 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +14 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/interactions/drag-drop.d.ts +30 -0
  20. package/dist/interactions/drag-drop.d.ts.map +1 -0
  21. package/dist/interactions/drag-drop.js +207 -0
  22. package/dist/interactions/drag-drop.js.map +1 -0
  23. package/dist/interactions/index.d.ts +10 -0
  24. package/dist/interactions/index.d.ts.map +1 -0
  25. package/dist/interactions/index.js +7 -0
  26. package/dist/interactions/index.js.map +1 -0
  27. package/dist/interactions/resize.d.ts +25 -0
  28. package/dist/interactions/resize.d.ts.map +1 -0
  29. package/dist/interactions/resize.js +116 -0
  30. package/dist/interactions/resize.js.map +1 -0
  31. package/dist/interactions/slot-selection.d.ts +25 -0
  32. package/dist/interactions/slot-selection.d.ts.map +1 -0
  33. package/dist/interactions/slot-selection.js +151 -0
  34. package/dist/interactions/slot-selection.js.map +1 -0
  35. package/dist/recurring.d.ts +15 -0
  36. package/dist/recurring.d.ts.map +1 -0
  37. package/dist/recurring.js +158 -0
  38. package/dist/recurring.js.map +1 -0
  39. package/dist/styles.d.ts +6 -0
  40. package/dist/styles.d.ts.map +1 -0
  41. package/dist/styles.js +632 -0
  42. package/dist/styles.js.map +1 -0
  43. package/dist/types.d.ts +169 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +5 -0
  46. package/dist/types.js.map +1 -0
  47. package/dist/views/agenda-view.d.ts +19 -0
  48. package/dist/views/agenda-view.d.ts.map +1 -0
  49. package/dist/views/agenda-view.js +114 -0
  50. package/dist/views/agenda-view.js.map +1 -0
  51. package/dist/views/day-view.d.ts +30 -0
  52. package/dist/views/day-view.d.ts.map +1 -0
  53. package/dist/views/day-view.js +432 -0
  54. package/dist/views/day-view.js.map +1 -0
  55. package/dist/views/month-view.d.ts +17 -0
  56. package/dist/views/month-view.d.ts.map +1 -0
  57. package/dist/views/month-view.js +123 -0
  58. package/dist/views/month-view.js.map +1 -0
  59. package/dist/views/shared.d.ts +30 -0
  60. package/dist/views/shared.d.ts.map +1 -0
  61. package/dist/views/shared.js +281 -0
  62. package/dist/views/shared.js.map +1 -0
  63. package/dist/views/week-view.d.ts +24 -0
  64. package/dist/views/week-view.d.ts.map +1 -0
  65. package/dist/views/week-view.js +377 -0
  66. package/dist/views/week-view.js.map +1 -0
  67. package/package.json +61 -0
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @liteforge/calendar - Interactions Module
3
+ */
4
+ export { setupSlotSelection } from './slot-selection.js';
5
+ export { setupEventDrag } from './drag-drop.js';
6
+ export { setupEventResize } from './resize.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/interactions/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAGxD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAG/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @liteforge/calendar - Event Resize Interaction
3
+ *
4
+ * Enables resizing events by dragging the bottom edge.
5
+ */
6
+ import type { CalendarEvent, ResolvedTimeConfig } from '../types.js';
7
+ export interface ResizeOptions<T extends CalendarEvent> {
8
+ dayColumn: HTMLElement;
9
+ day: Date;
10
+ config: ResolvedTimeConfig;
11
+ onEventResize?: (event: T, newEnd: Date) => void;
12
+ }
13
+ export interface ResizeState<T extends CalendarEvent> {
14
+ isResizing: boolean;
15
+ event: T | null;
16
+ eventElement: HTMLElement | null;
17
+ originalEnd: Date | null;
18
+ startY: number;
19
+ cleanup: () => void;
20
+ }
21
+ /**
22
+ * Set up resize for an event element.
23
+ */
24
+ export declare function setupEventResize<T extends CalendarEvent>(eventElement: HTMLElement, resizeHandle: HTMLElement, event: T, options: ResizeOptions<T>): ResizeState<T>;
25
+ //# sourceMappingURL=resize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resize.d.ts","sourceRoot":"","sources":["../../src/interactions/resize.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAGpE,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,aAAa;IACpD,SAAS,EAAE,WAAW,CAAA;IACtB,GAAG,EAAE,IAAI,CAAA;IACT,MAAM,EAAE,kBAAkB,CAAA;IAC1B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,KAAK,IAAI,CAAA;CACjD;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,aAAa;IAClD,UAAU,EAAE,OAAO,CAAA;IACnB,KAAK,EAAE,CAAC,GAAG,IAAI,CAAA;IACf,YAAY,EAAE,WAAW,GAAG,IAAI,CAAA;IAChC,WAAW,EAAE,IAAI,GAAG,IAAI,CAAA;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAsCD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,aAAa,EACtD,YAAY,EAAE,WAAW,EACzB,YAAY,EAAE,WAAW,EACzB,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GACxB,WAAW,CAAC,CAAC,CAAC,CAsGhB"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * @liteforge/calendar - Event Resize Interaction
3
+ *
4
+ * Enables resizing events by dragging the bottom edge.
5
+ */
6
+ import { addMinutes } from '../date-utils.js';
7
+ /**
8
+ * Calculate new end time based on resize position.
9
+ */
10
+ function calculateNewEnd(y, dayColumn, day, config, eventStart) {
11
+ const rect = dayColumn.getBoundingClientRect();
12
+ const relativeY = y - rect.top;
13
+ const slotHeight = 40; // CSS variable --lf-cal-slot-height default
14
+ const totalSlots = (config.dayEnd - config.dayStart) * (60 / config.slotDuration);
15
+ // Calculate which slot index we're in
16
+ const slotIndex = Math.floor(relativeY / slotHeight);
17
+ const clampedIndex = Math.max(0, Math.min(slotIndex, totalSlots - 1));
18
+ // Convert to time
19
+ const minutesFromStart = (clampedIndex + 1) * config.slotDuration;
20
+ const hours = config.dayStart + Math.floor(minutesFromStart / 60);
21
+ const minutes = minutesFromStart % 60;
22
+ const newEnd = new Date(day);
23
+ newEnd.setHours(hours, minutes, 0, 0);
24
+ // Ensure minimum duration of 1 slot
25
+ const minEnd = addMinutes(eventStart, config.slotDuration);
26
+ if (newEnd < minEnd) {
27
+ return minEnd;
28
+ }
29
+ return newEnd;
30
+ }
31
+ /**
32
+ * Set up resize for an event element.
33
+ */
34
+ export function setupEventResize(eventElement, resizeHandle, event, options) {
35
+ const { dayColumn, day, config, onEventResize } = options;
36
+ const state = {
37
+ isResizing: false,
38
+ event: null,
39
+ eventElement: null,
40
+ originalEnd: null,
41
+ startY: 0,
42
+ cleanup: () => { },
43
+ };
44
+ let previewEndTime = null;
45
+ const handlePointerDown = (e) => {
46
+ // Only handle left click
47
+ if (e.button !== 0)
48
+ return;
49
+ e.stopPropagation();
50
+ e.preventDefault();
51
+ state.isResizing = true;
52
+ state.event = event;
53
+ state.eventElement = eventElement;
54
+ state.originalEnd = new Date(event.end);
55
+ state.startY = e.clientY;
56
+ // Mark as resizing
57
+ eventElement.classList.add('lf-cal-event--resizing');
58
+ // Prevent text selection
59
+ document.body.style.userSelect = 'none';
60
+ document.body.style.cursor = 'ns-resize';
61
+ // Add listeners
62
+ document.addEventListener('pointermove', handlePointerMove);
63
+ document.addEventListener('pointerup', handlePointerUp);
64
+ };
65
+ const handlePointerMove = (e) => {
66
+ if (!state.isResizing || !state.event || !state.eventElement)
67
+ return;
68
+ // Calculate new end time
69
+ previewEndTime = calculateNewEnd(e.clientY, dayColumn, day, config, state.event.start);
70
+ // Update visual height
71
+ const slotHeight = 40;
72
+ const startMinutes = (state.event.start.getHours() - config.dayStart) * 60 + state.event.start.getMinutes();
73
+ const endMinutes = (previewEndTime.getHours() - config.dayStart) * 60 + previewEndTime.getMinutes();
74
+ const durationMinutes = endMinutes - startMinutes;
75
+ const newHeight = (durationMinutes / config.slotDuration) * slotHeight;
76
+ state.eventElement.style.height = `${Math.max(newHeight, slotHeight / 2)}px`;
77
+ };
78
+ const handlePointerUp = (_e) => {
79
+ // Clean up listeners
80
+ document.removeEventListener('pointermove', handlePointerMove);
81
+ document.removeEventListener('pointerup', handlePointerUp);
82
+ if (!state.isResizing || !state.event || !previewEndTime) {
83
+ resetState();
84
+ return;
85
+ }
86
+ // Remove resizing class
87
+ if (state.eventElement) {
88
+ state.eventElement.classList.remove('lf-cal-event--resizing');
89
+ }
90
+ // Call resize handler
91
+ if (onEventResize && state.event) {
92
+ onEventResize(state.event, previewEndTime);
93
+ }
94
+ resetState();
95
+ };
96
+ const resetState = () => {
97
+ state.isResizing = false;
98
+ state.event = null;
99
+ state.eventElement = null;
100
+ state.originalEnd = null;
101
+ document.body.style.userSelect = '';
102
+ document.body.style.cursor = '';
103
+ previewEndTime = null;
104
+ };
105
+ // Attach listener to resize handle
106
+ resizeHandle.addEventListener('pointerdown', handlePointerDown);
107
+ // Store cleanup
108
+ state.cleanup = () => {
109
+ resizeHandle.removeEventListener('pointerdown', handlePointerDown);
110
+ document.removeEventListener('pointermove', handlePointerMove);
111
+ document.removeEventListener('pointerup', handlePointerUp);
112
+ resetState();
113
+ };
114
+ return state;
115
+ }
116
+ //# sourceMappingURL=resize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resize.js","sourceRoot":"","sources":["../../src/interactions/resize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @liteforge/calendar - Slot Selection Interaction
3
+ *
4
+ * Enables selecting time ranges by clicking and dragging on empty slots.
5
+ */
6
+ import type { ResolvedTimeConfig } from '../types.js';
7
+ export interface SlotSelectionOptions {
8
+ slotsContainer: HTMLElement;
9
+ day: Date;
10
+ config: ResolvedTimeConfig;
11
+ resourceId?: string | undefined;
12
+ onSlotClick?: ((start: Date, end: Date, resourceId?: string) => void) | undefined;
13
+ onSlotSelect?: ((start: Date, end: Date, resourceId?: string) => void) | undefined;
14
+ }
15
+ export interface SlotSelectionState {
16
+ isSelecting: boolean;
17
+ startSlot: Date | null;
18
+ currentSlot: Date | null;
19
+ cleanup: () => void;
20
+ }
21
+ /**
22
+ * Set up slot selection interaction on a slots container.
23
+ */
24
+ export declare function setupSlotSelection(options: SlotSelectionOptions): SlotSelectionState;
25
+ //# sourceMappingURL=slot-selection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slot-selection.d.ts","sourceRoot":"","sources":["../../src/interactions/slot-selection.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAGrD,MAAM,WAAW,oBAAoB;IACnC,cAAc,EAAE,WAAW,CAAA;IAC3B,GAAG,EAAE,IAAI,CAAA;IACT,MAAM,EAAE,kBAAkB,CAAA;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACjF,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;CACnF;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,OAAO,CAAA;IACpB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAA;IACtB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAA;IACxB,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAqED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,GAAG,kBAAkB,CAgHpF"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * @liteforge/calendar - Slot Selection Interaction
3
+ *
4
+ * Enables selecting time ranges by clicking and dragging on empty slots.
5
+ */
6
+ import { addMinutes } from '../date-utils.js';
7
+ /**
8
+ * Calculate slot time from Y position within the slots container.
9
+ */
10
+ function getSlotTimeFromY(y, containerRect, day, config) {
11
+ const relativeY = y - containerRect.top;
12
+ const slotHeight = 40; // CSS variable --lf-cal-slot-height default
13
+ const totalSlots = (config.dayEnd - config.dayStart) * (60 / config.slotDuration);
14
+ // Calculate which slot index we're in
15
+ const slotIndex = Math.floor(relativeY / slotHeight);
16
+ const clampedIndex = Math.max(0, Math.min(slotIndex, totalSlots - 1));
17
+ // Convert to time
18
+ const minutesFromStart = clampedIndex * config.slotDuration;
19
+ const hours = config.dayStart + Math.floor(minutesFromStart / 60);
20
+ const minutes = minutesFromStart % 60;
21
+ const result = new Date(day);
22
+ result.setHours(hours, minutes, 0, 0);
23
+ return result;
24
+ }
25
+ /**
26
+ * Highlight slots between start and current selection.
27
+ */
28
+ function highlightSlots(slotsContainer, startSlot, endSlot, config) {
29
+ // Clear existing highlights
30
+ clearHighlights(slotsContainer);
31
+ const slots = slotsContainer.children;
32
+ const startMinutes = (startSlot.getHours() - config.dayStart) * 60 + startSlot.getMinutes();
33
+ const endMinutes = (endSlot.getHours() - config.dayStart) * 60 + endSlot.getMinutes();
34
+ const minMinutes = Math.min(startMinutes, endMinutes);
35
+ const maxMinutes = Math.max(startMinutes, endMinutes);
36
+ const startIndex = Math.floor(minMinutes / config.slotDuration);
37
+ const endIndex = Math.floor(maxMinutes / config.slotDuration);
38
+ for (let i = startIndex; i <= endIndex && i < slots.length; i++) {
39
+ const slot = slots[i];
40
+ if (slot) {
41
+ slot.classList.add('lf-cal-time-slot--selected');
42
+ }
43
+ }
44
+ }
45
+ /**
46
+ * Clear all slot highlights.
47
+ */
48
+ function clearHighlights(slotsContainer) {
49
+ const slots = slotsContainer.querySelectorAll('.lf-cal-time-slot--selected');
50
+ slots.forEach((slot) => {
51
+ slot.classList.remove('lf-cal-time-slot--selected');
52
+ });
53
+ }
54
+ /**
55
+ * Set up slot selection interaction on a slots container.
56
+ */
57
+ export function setupSlotSelection(options) {
58
+ const { slotsContainer, day, config, resourceId, onSlotClick, onSlotSelect } = options;
59
+ const state = {
60
+ isSelecting: false,
61
+ startSlot: null,
62
+ currentSlot: null,
63
+ cleanup: () => { },
64
+ };
65
+ let containerRect = null;
66
+ const handlePointerDown = (e) => {
67
+ // Only handle left click
68
+ if (e.button !== 0)
69
+ return;
70
+ // Ignore clicks on events
71
+ const target = e.target;
72
+ if (target.closest('.lf-cal-event'))
73
+ return;
74
+ containerRect = slotsContainer.getBoundingClientRect();
75
+ const startTime = getSlotTimeFromY(e.clientY, containerRect, day, config);
76
+ state.isSelecting = true;
77
+ state.startSlot = startTime;
78
+ state.currentSlot = startTime;
79
+ // Highlight initial slot
80
+ highlightSlots(slotsContainer, startTime, startTime, config);
81
+ // Prevent text selection during drag
82
+ document.body.style.userSelect = 'none';
83
+ // Capture pointer for tracking (not available in all test environments)
84
+ if (target.setPointerCapture) {
85
+ target.setPointerCapture(e.pointerId);
86
+ }
87
+ };
88
+ const handlePointerMove = (e) => {
89
+ if (!state.isSelecting || !state.startSlot || !containerRect)
90
+ return;
91
+ const currentTime = getSlotTimeFromY(e.clientY, containerRect, day, config);
92
+ state.currentSlot = currentTime;
93
+ // Update highlight
94
+ highlightSlots(slotsContainer, state.startSlot, currentTime, config);
95
+ };
96
+ const handlePointerUp = (_e) => {
97
+ if (!state.isSelecting || !state.startSlot) {
98
+ state.isSelecting = false;
99
+ return;
100
+ }
101
+ const endTime = state.currentSlot ?? state.startSlot;
102
+ // Determine start and end (swap if needed)
103
+ let finalStart = state.startSlot;
104
+ let finalEnd = addMinutes(endTime, config.slotDuration);
105
+ if (finalStart > endTime) {
106
+ finalStart = endTime;
107
+ finalEnd = addMinutes(state.startSlot, config.slotDuration);
108
+ }
109
+ // Clear selection state
110
+ state.isSelecting = false;
111
+ state.startSlot = null;
112
+ state.currentSlot = null;
113
+ document.body.style.userSelect = '';
114
+ // Clear highlights
115
+ clearHighlights(slotsContainer);
116
+ // Determine if this was a click or drag
117
+ const wasClick = finalStart.getTime() === endTime.getTime();
118
+ if (wasClick && onSlotClick) {
119
+ onSlotClick(finalStart, finalEnd, resourceId);
120
+ }
121
+ else if (!wasClick && onSlotSelect) {
122
+ onSlotSelect(finalStart, finalEnd, resourceId);
123
+ }
124
+ else if (onSlotClick) {
125
+ // Fallback: treat drag as click for the range
126
+ onSlotClick(finalStart, finalEnd, resourceId);
127
+ }
128
+ };
129
+ const handlePointerCancel = () => {
130
+ state.isSelecting = false;
131
+ state.startSlot = null;
132
+ state.currentSlot = null;
133
+ document.body.style.userSelect = '';
134
+ clearHighlights(slotsContainer);
135
+ };
136
+ // Attach listeners
137
+ slotsContainer.addEventListener('pointerdown', handlePointerDown);
138
+ slotsContainer.addEventListener('pointermove', handlePointerMove);
139
+ slotsContainer.addEventListener('pointerup', handlePointerUp);
140
+ slotsContainer.addEventListener('pointercancel', handlePointerCancel);
141
+ // Store cleanup function
142
+ state.cleanup = () => {
143
+ slotsContainer.removeEventListener('pointerdown', handlePointerDown);
144
+ slotsContainer.removeEventListener('pointermove', handlePointerMove);
145
+ slotsContainer.removeEventListener('pointerup', handlePointerUp);
146
+ slotsContainer.removeEventListener('pointercancel', handlePointerCancel);
147
+ clearHighlights(slotsContainer);
148
+ };
149
+ return state;
150
+ }
151
+ //# sourceMappingURL=slot-selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slot-selection.js","sourceRoot":"","sources":["../../src/interactions/slot-selection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @liteforge/calendar - Recurring Event Expansion
3
+ *
4
+ * Expands recurring events into individual occurrences within a date range.
5
+ */
6
+ import type { CalendarEvent } from './types.js';
7
+ /**
8
+ * Expand a recurring event into individual occurrences within a date range.
9
+ */
10
+ export declare function expandRecurring<T extends CalendarEvent>(event: T, rangeStart: Date, rangeEnd: Date): T[];
11
+ /**
12
+ * Expand all recurring events in an array
13
+ */
14
+ export declare function expandAllRecurring<T extends CalendarEvent>(events: T[], rangeStart: Date, rangeEnd: Date): T[];
15
+ //# sourceMappingURL=recurring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recurring.d.ts","sourceRoot":"","sources":["../src/recurring.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAA;AA+E9D;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,aAAa,EACrD,KAAK,EAAE,CAAC,EACR,UAAU,EAAE,IAAI,EAChB,QAAQ,EAAE,IAAI,GACb,CAAC,EAAE,CA4FL;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,aAAa,EACxD,MAAM,EAAE,CAAC,EAAE,EACX,UAAU,EAAE,IAAI,EAChB,QAAQ,EAAE,IAAI,GACb,CAAC,EAAE,CAeL"}
@@ -0,0 +1,158 @@
1
+ /**
2
+ * @liteforge/calendar - Recurring Event Expansion
3
+ *
4
+ * Expands recurring events into individual occurrences within a date range.
5
+ */
6
+ import { addDays, addWeeks, addMonths, isSameDay, isBefore, isAfter, startOfDay, } from './date-utils.js';
7
+ /**
8
+ * Check if a date is in the exceptions list
9
+ */
10
+ function isException(date, exceptions) {
11
+ if (!exceptions || exceptions.length === 0)
12
+ return false;
13
+ return exceptions.some((ex) => isSameDay(date, ex));
14
+ }
15
+ /**
16
+ * Get the next occurrence date based on frequency
17
+ */
18
+ function getNextOccurrence(current, rule, eventStart) {
19
+ const interval = rule.interval ?? 1;
20
+ switch (rule.frequency) {
21
+ case 'daily':
22
+ return addDays(current, interval);
23
+ case 'weekly':
24
+ return addWeeks(current, interval);
25
+ case 'biweekly':
26
+ return addWeeks(current, 2 * interval);
27
+ case 'monthly': {
28
+ // Keep the same day of month
29
+ const next = addMonths(current, interval);
30
+ const targetDay = eventStart.getDate();
31
+ const maxDay = new Date(next.getFullYear(), next.getMonth() + 1, 0).getDate();
32
+ next.setDate(Math.min(targetDay, maxDay));
33
+ return next;
34
+ }
35
+ default:
36
+ return addDays(current, interval);
37
+ }
38
+ }
39
+ /**
40
+ * Get all days of week for a weekly recurring event
41
+ */
42
+ function getWeeklyOccurrences(weekStart, daysOfWeek, eventStart) {
43
+ const occurrences = [];
44
+ const startHour = eventStart.getHours();
45
+ const startMinute = eventStart.getMinutes();
46
+ for (let i = 0; i < 7; i++) {
47
+ const day = addDays(weekStart, i);
48
+ const dayOfWeek = day.getDay();
49
+ if (daysOfWeek.includes(dayOfWeek)) {
50
+ const occurrence = new Date(day);
51
+ occurrence.setHours(startHour, startMinute, 0, 0);
52
+ occurrences.push(occurrence);
53
+ }
54
+ }
55
+ return occurrences;
56
+ }
57
+ /**
58
+ * Expand a recurring event into individual occurrences within a date range.
59
+ */
60
+ export function expandRecurring(event, rangeStart, rangeEnd) {
61
+ const rule = event.recurring;
62
+ if (!rule)
63
+ return [event];
64
+ const occurrences = [];
65
+ const eventDuration = event.end.getTime() - event.start.getTime();
66
+ let count = 0;
67
+ const maxCount = rule.count ?? 1000; // Safety limit
68
+ const endDate = rule.endDate ? startOfDay(rule.endDate) : null;
69
+ // For weekly events with specific days
70
+ if (rule.frequency === 'weekly' && rule.daysOfWeek && rule.daysOfWeek.length > 0) {
71
+ // Start from the week containing event.start
72
+ let currentWeekStart = startOfDay(event.start);
73
+ currentWeekStart.setDate(currentWeekStart.getDate() - currentWeekStart.getDay());
74
+ while (count < maxCount) {
75
+ // Check if we've passed the end date
76
+ if (endDate && isAfter(currentWeekStart, endDate))
77
+ break;
78
+ // Get all occurrences for this week
79
+ const weekOccurrences = getWeeklyOccurrences(currentWeekStart, rule.daysOfWeek, event.start);
80
+ for (const occurrenceStart of weekOccurrences) {
81
+ // Skip if before event start
82
+ if (isBefore(occurrenceStart, event.start))
83
+ continue;
84
+ // Check end conditions
85
+ if (endDate && isAfter(occurrenceStart, endDate))
86
+ break;
87
+ if (count >= maxCount)
88
+ break;
89
+ // Skip exceptions
90
+ if (isException(occurrenceStart, rule.exceptions))
91
+ continue;
92
+ // Check if within view range
93
+ const occurrenceEnd = new Date(occurrenceStart.getTime() + eventDuration);
94
+ if (occurrenceEnd > rangeStart && occurrenceStart < rangeEnd) {
95
+ occurrences.push({
96
+ ...event,
97
+ id: `${event.id}_${occurrenceStart.toISOString().split('T')[0]}`,
98
+ start: occurrenceStart,
99
+ end: occurrenceEnd,
100
+ });
101
+ }
102
+ count++;
103
+ }
104
+ // Move to next week (accounting for interval)
105
+ const interval = rule.interval ?? 1;
106
+ currentWeekStart = addWeeks(currentWeekStart, interval);
107
+ // Safety: stop if we're way past the range
108
+ if (isAfter(currentWeekStart, addWeeks(rangeEnd, 52)))
109
+ break;
110
+ }
111
+ return occurrences;
112
+ }
113
+ // For other frequencies (daily, biweekly, monthly)
114
+ let currentStart = new Date(event.start);
115
+ while (count < maxCount) {
116
+ // Check end conditions
117
+ if (endDate && isAfter(currentStart, endDate))
118
+ break;
119
+ // Skip exceptions
120
+ if (!isException(currentStart, rule.exceptions)) {
121
+ // Check if within view range
122
+ const currentEnd = new Date(currentStart.getTime() + eventDuration);
123
+ if (currentEnd > rangeStart && currentStart < rangeEnd) {
124
+ occurrences.push({
125
+ ...event,
126
+ id: `${event.id}_${currentStart.toISOString().split('T')[0]}`,
127
+ start: new Date(currentStart),
128
+ end: currentEnd,
129
+ });
130
+ }
131
+ }
132
+ count++;
133
+ currentStart = getNextOccurrence(currentStart, rule, event.start);
134
+ // Safety: stop if we're way past the range
135
+ if (isAfter(currentStart, addMonths(rangeEnd, 12)))
136
+ break;
137
+ }
138
+ return occurrences;
139
+ }
140
+ /**
141
+ * Expand all recurring events in an array
142
+ */
143
+ export function expandAllRecurring(events, rangeStart, rangeEnd) {
144
+ const result = [];
145
+ for (const event of events) {
146
+ if (event.recurring) {
147
+ result.push(...expandRecurring(event, rangeStart, rangeEnd));
148
+ }
149
+ else {
150
+ // Non-recurring: just check if in range
151
+ if (event.end > rangeStart && event.start < rangeEnd) {
152
+ result.push(event);
153
+ }
154
+ }
155
+ }
156
+ return result;
157
+ }
158
+ //# sourceMappingURL=recurring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recurring.js","sourceRoot":"","sources":["../src/recurring.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @liteforge/calendar - Default CSS Theme
3
+ */
4
+ export declare function injectCalendarStyles(): void;
5
+ export declare function resetCalendarStylesInjection(): void;
6
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;GAEG;AAsmBH,wBAAgB,oBAAoB,IAAI,IAAI,CAU3C;AAED,wBAAgB,4BAA4B,IAAI,IAAI,CAMnD"}