@forcecalendar/core 2.1.0 → 2.1.2

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.
@@ -8,456 +8,421 @@ import { SearchWorkerManager } from '../search/SearchWorkerManager.js';
8
8
  import { RecurrenceEngineV2 } from '../events/RecurrenceEngineV2.js';
9
9
 
10
10
  export class EnhancedCalendar extends Calendar {
11
- constructor(config) {
12
- super(config);
13
-
14
- // Initialize enhanced components
15
- this.searchManager = new SearchWorkerManager(this.eventStore);
16
- this.recurrenceEngine = new RecurrenceEngineV2();
17
-
18
- // Performance monitoring
19
- this.performanceMetrics = {
20
- searchTime: [],
21
- expansionTime: [],
22
- renderTime: []
23
- };
24
-
25
- // Setup event listeners for real-time indexing
26
- this.setupRealtimeIndexing();
11
+ constructor(config) {
12
+ super(config);
13
+
14
+ // Initialize enhanced components
15
+ this.searchManager = new SearchWorkerManager(this.eventStore);
16
+ this.recurrenceEngine = new RecurrenceEngineV2();
17
+
18
+ // Performance monitoring
19
+ this.performanceMetrics = {
20
+ searchTime: [],
21
+ expansionTime: [],
22
+ renderTime: []
23
+ };
24
+
25
+ // Setup event listeners for real-time indexing
26
+ this.setupRealtimeIndexing();
27
+ }
28
+
29
+ /**
30
+ * Enhanced search with worker support
31
+ */
32
+ async search(query, options = {}) {
33
+ const startTime = performance.now();
34
+
35
+ try {
36
+ // Use enhanced search manager
37
+ const results = await this.searchManager.search(query, {
38
+ fields: options.fields || ['title', 'description', 'location', 'category'],
39
+ fuzzy: options.fuzzy !== false,
40
+ limit: options.limit || 50,
41
+ prefixMatch: options.autocomplete || false,
42
+ ...options
43
+ });
44
+
45
+ const endTime = performance.now();
46
+ this.recordMetric('searchTime', endTime - startTime);
47
+
48
+ // Transform results to match expected format
49
+ return results.map(r => r.event);
50
+ } catch (error) {
51
+ console.error('Search error:', error);
52
+ // Fallback to basic search
53
+ return super.search ? super.search(query, options) : [];
27
54
  }
28
-
29
- /**
30
- * Enhanced search with worker support
31
- */
32
- async search(query, options = {}) {
33
- const startTime = performance.now();
34
-
35
- try {
36
- // Use enhanced search manager
37
- const results = await this.searchManager.search(query, {
38
- fields: options.fields || ['title', 'description', 'location', 'category'],
39
- fuzzy: options.fuzzy !== false,
40
- limit: options.limit || 50,
41
- prefixMatch: options.autocomplete || false,
42
- ...options
43
- });
44
-
45
- const endTime = performance.now();
46
- this.recordMetric('searchTime', endTime - startTime);
47
-
48
- // Transform results to match expected format
49
- return results.map(r => r.event);
50
- } catch (error) {
51
- console.error('Search error:', error);
52
- // Fallback to basic search
53
- return super.search ? super.search(query, options) : [];
54
- }
55
+ }
56
+
57
+ /**
58
+ * Get events with enhanced recurrence expansion
59
+ */
60
+ async getEventsInRange(startDate, endDate, options = {}) {
61
+ const startTime = performance.now();
62
+
63
+ const regularEvents = [];
64
+ const recurringEvents = [];
65
+
66
+ // Separate regular and recurring events
67
+ const allEvents = this.eventStore.getEventsInDateRange(startDate, endDate);
68
+
69
+ for (const event of allEvents) {
70
+ if (event.recurring) {
71
+ recurringEvents.push(event);
72
+ } else {
73
+ regularEvents.push(event);
74
+ }
55
75
  }
56
76
 
57
- /**
58
- * Get events with enhanced recurrence expansion
59
- */
60
- async getEventsInRange(startDate, endDate, options = {}) {
61
- const startTime = performance.now();
77
+ // Expand recurring events with enhanced engine
78
+ const expandedOccurrences = [];
62
79
 
63
- const regularEvents = [];
64
- const recurringEvents = [];
80
+ for (const event of recurringEvents) {
81
+ const occurrences = this.recurrenceEngine.expandEvent(event, startDate, endDate, {
82
+ maxOccurrences: options.maxOccurrences || 365,
83
+ includeModified: options.includeModified !== false,
84
+ includeCancelled: options.includeCancelled || false,
85
+ timezone: options.timezone || event.timeZone,
86
+ handleDST: options.handleDST !== false
87
+ });
65
88
 
66
- // Separate regular and recurring events
67
- const allEvents = this.eventStore.getEventsInDateRange(startDate, endDate);
89
+ expandedOccurrences.push(...occurrences);
90
+ }
68
91
 
69
- for (const event of allEvents) {
70
- if (event.recurring) {
71
- recurringEvents.push(event);
72
- } else {
73
- regularEvents.push(event);
74
- }
75
- }
92
+ const endTime = performance.now();
93
+ this.recordMetric('expansionTime', endTime - startTime);
76
94
 
77
- // Expand recurring events with enhanced engine
78
- const expandedOccurrences = [];
79
-
80
- for (const event of recurringEvents) {
81
- const occurrences = this.recurrenceEngine.expandEvent(
82
- event,
83
- startDate,
84
- endDate,
85
- {
86
- maxOccurrences: options.maxOccurrences || 365,
87
- includeModified: options.includeModified !== false,
88
- includeCancelled: options.includeCancelled || false,
89
- timezone: options.timezone || event.timeZone,
90
- handleDST: options.handleDST !== false
91
- }
92
- );
93
-
94
- expandedOccurrences.push(...occurrences);
95
- }
95
+ // Combine and sort
96
+ const allEventsInRange = [...regularEvents, ...expandedOccurrences];
97
+ allEventsInRange.sort((a, b) => a.start - b.start);
96
98
 
97
- const endTime = performance.now();
98
- this.recordMetric('expansionTime', endTime - startTime);
99
+ return allEventsInRange;
100
+ }
99
101
 
100
- // Combine and sort
101
- const allEventsInRange = [...regularEvents, ...expandedOccurrences];
102
- allEventsInRange.sort((a, b) => a.start - b.start);
102
+ /**
103
+ * Modify a single occurrence of a recurring event
104
+ */
105
+ modifyOccurrence(eventId, occurrenceDate, modifications) {
106
+ // Add to modified instances
107
+ this.recurrenceEngine.addModifiedInstance(eventId, occurrenceDate, modifications);
103
108
 
104
- return allEventsInRange;
105
- }
109
+ // Emit change event
110
+ this.emit('occurrence:modified', {
111
+ eventId,
112
+ occurrenceDate,
113
+ modifications
114
+ });
115
+
116
+ // Trigger re-render if in view
117
+ this.refreshView();
118
+ }
119
+
120
+ /**
121
+ * Cancel a single occurrence of a recurring event
122
+ */
123
+ cancelOccurrence(eventId, occurrenceDate, reason = 'Cancelled') {
124
+ // Add exception
125
+ this.recurrenceEngine.addException(eventId, occurrenceDate, reason);
126
+
127
+ // Emit change event
128
+ this.emit('occurrence:cancelled', {
129
+ eventId,
130
+ occurrenceDate,
131
+ reason
132
+ });
106
133
 
107
- /**
108
- * Modify a single occurrence of a recurring event
109
- */
110
- modifyOccurrence(eventId, occurrenceDate, modifications) {
111
- // Add to modified instances
112
- this.recurrenceEngine.addModifiedInstance(
113
- eventId,
114
- occurrenceDate,
115
- modifications
116
- );
117
-
118
- // Emit change event
119
- this.emit('occurrence:modified', {
120
- eventId,
121
- occurrenceDate,
122
- modifications
123
- });
124
-
125
- // Trigger re-render if in view
126
- this.refreshView();
134
+ // Trigger re-render
135
+ this.refreshView();
136
+ }
137
+
138
+ /**
139
+ * Bulk operations for recurring events
140
+ */
141
+ async bulkModifyOccurrences(eventId, dateRange, modifications) {
142
+ const event = this.eventStore.getEvent(eventId);
143
+ if (!event || !event.recurring) {
144
+ throw new Error('Event not found or not recurring');
127
145
  }
128
146
 
129
- /**
130
- * Cancel a single occurrence of a recurring event
131
- */
132
- cancelOccurrence(eventId, occurrenceDate, reason = 'Cancelled') {
133
- // Add exception
134
- this.recurrenceEngine.addException(eventId, occurrenceDate, reason);
135
-
136
- // Emit change event
137
- this.emit('occurrence:cancelled', {
138
- eventId,
139
- occurrenceDate,
140
- reason
141
- });
142
-
143
- // Trigger re-render
144
- this.refreshView();
147
+ // Get all occurrences in range
148
+ const occurrences = this.recurrenceEngine.expandEvent(event, dateRange.start, dateRange.end);
149
+
150
+ // Apply modifications to each
151
+ for (const occurrence of occurrences) {
152
+ this.recurrenceEngine.addModifiedInstance(eventId, occurrence.start, modifications);
145
153
  }
146
154
 
147
- /**
148
- * Bulk operations for recurring events
149
- */
150
- async bulkModifyOccurrences(eventId, dateRange, modifications) {
151
- const event = this.eventStore.getEvent(eventId);
152
- if (!event || !event.recurring) {
153
- throw new Error('Event not found or not recurring');
154
- }
155
+ // Emit bulk change event
156
+ this.emit('occurrences:bulk-modified', {
157
+ eventId,
158
+ count: occurrences.length,
159
+ modifications
160
+ });
155
161
 
156
- // Get all occurrences in range
157
- const occurrences = this.recurrenceEngine.expandEvent(
158
- event,
159
- dateRange.start,
160
- dateRange.end
161
- );
162
-
163
- // Apply modifications to each
164
- for (const occurrence of occurrences) {
165
- this.recurrenceEngine.addModifiedInstance(
166
- eventId,
167
- occurrence.start,
168
- modifications
169
- );
170
- }
162
+ this.refreshView();
163
+ }
171
164
 
172
- // Emit bulk change event
173
- this.emit('occurrences:bulk-modified', {
174
- eventId,
175
- count: occurrences.length,
176
- modifications
177
- });
165
+ /**
166
+ * Advanced search with filters and recurrence awareness
167
+ */
168
+ async advancedSearch(query, filters = {}, options = {}) {
169
+ // First get search results
170
+ const searchResults = await this.search(query, options);
178
171
 
179
- this.refreshView();
180
- }
172
+ // Apply additional filters
173
+ let filtered = searchResults;
181
174
 
182
- /**
183
- * Advanced search with filters and recurrence awareness
184
- */
185
- async advancedSearch(query, filters = {}, options = {}) {
186
- // First get search results
187
- const searchResults = await this.search(query, options);
188
-
189
- // Apply additional filters
190
- let filtered = searchResults;
191
-
192
- // Date range filter with recurrence expansion
193
- if (filters.dateRange) {
194
- const expandedEvents = await this.getEventsInRange(
195
- filters.dateRange.start,
196
- filters.dateRange.end,
197
- { includeModified: true }
198
- );
199
-
200
- const expandedIds = new Set(expandedEvents.map(e =>
201
- e.recurringEventId || e.id
202
- ));
203
-
204
- filtered = filtered.filter(e => expandedIds.has(e.id));
205
- }
175
+ // Date range filter with recurrence expansion
176
+ if (filters.dateRange) {
177
+ const expandedEvents = await this.getEventsInRange(
178
+ filters.dateRange.start,
179
+ filters.dateRange.end,
180
+ { includeModified: true }
181
+ );
206
182
 
207
- // Category filter
208
- if (filters.categories && filters.categories.length > 0) {
209
- const categorySet = new Set(filters.categories);
210
- filtered = filtered.filter(e =>
211
- e.categories && e.categories.some(c => categorySet.has(c))
212
- );
213
- }
183
+ const expandedIds = new Set(expandedEvents.map(e => e.recurringEventId || e.id));
214
184
 
215
- // Status filter
216
- if (filters.status) {
217
- filtered = filtered.filter(e => e.status === filters.status);
218
- }
185
+ filtered = filtered.filter(e => expandedIds.has(e.id));
186
+ }
219
187
 
220
- // Modified only filter
221
- if (filters.modifiedOnly) {
222
- filtered = filtered.filter(e => {
223
- const modifications = this.recurrenceEngine.modifiedInstances.get(e.id);
224
- return modifications && modifications.size > 0;
225
- });
226
- }
188
+ // Category filter
189
+ if (filters.categories && filters.categories.length > 0) {
190
+ const categorySet = new Set(filters.categories);
191
+ filtered = filtered.filter(e => e.categories && e.categories.some(c => categorySet.has(c)));
192
+ }
227
193
 
228
- return filtered;
194
+ // Status filter
195
+ if (filters.status) {
196
+ filtered = filtered.filter(e => e.status === filters.status);
229
197
  }
230
198
 
231
- /**
232
- * Setup real-time indexing for search
233
- */
234
- setupRealtimeIndexing() {
235
- // Re-index when events are added
236
- this.on('event:added', (event) => {
237
- this.searchManager.indexEvents();
238
- });
239
-
240
- // Re-index when events are modified
241
- this.on('event:updated', (event) => {
242
- this.searchManager.indexEvents();
243
- });
244
-
245
- // Re-index when events are removed
246
- this.on('event:removed', (eventId) => {
247
- this.searchManager.indexEvents();
248
- });
249
-
250
- // Batch re-indexing for bulk operations
251
- let reindexTimeout;
252
- this.on('events:bulk-operation', () => {
253
- clearTimeout(reindexTimeout);
254
- reindexTimeout = setTimeout(() => {
255
- this.searchManager.indexEvents();
256
- }, 100);
257
- });
199
+ // Modified only filter
200
+ if (filters.modifiedOnly) {
201
+ filtered = filtered.filter(e => {
202
+ const modifications = this.recurrenceEngine.modifiedInstances.get(e.id);
203
+ return modifications && modifications.size > 0;
204
+ });
258
205
  }
259
206
 
260
- /**
261
- * Get search suggestions (autocomplete)
262
- */
263
- async getSuggestions(partial, field = 'title') {
264
- if (partial.length < 2) {
265
- return [];
266
- }
207
+ return filtered;
208
+ }
267
209
 
268
- // Use search with prefix matching
269
- const results = await this.searchManager.search(partial, {
270
- fields: [field],
271
- prefixMatch: true,
272
- limit: 10
273
- });
274
-
275
- // Extract unique values
276
- const suggestions = new Set();
277
- for (const result of results) {
278
- const value = result.event[field];
279
- if (value) {
280
- suggestions.add(value);
281
- }
282
- }
210
+ /**
211
+ * Setup real-time indexing for search
212
+ */
213
+ setupRealtimeIndexing() {
214
+ // Re-index when events are added
215
+ this.on('event:added', event => {
216
+ this.searchManager.indexEvents();
217
+ });
283
218
 
284
- return Array.from(suggestions);
285
- }
219
+ // Re-index when events are modified
220
+ this.on('event:updated', event => {
221
+ this.searchManager.indexEvents();
222
+ });
286
223
 
287
- /**
288
- * Performance monitoring
289
- */
290
- recordMetric(type, value) {
291
- this.performanceMetrics[type].push(value);
224
+ // Re-index when events are removed
225
+ this.on('event:removed', eventId => {
226
+ this.searchManager.indexEvents();
227
+ });
292
228
 
293
- // Keep only last 100 measurements
294
- if (this.performanceMetrics[type].length > 100) {
295
- this.performanceMetrics[type].shift();
296
- }
229
+ // Batch re-indexing for bulk operations
230
+ let reindexTimeout;
231
+ this.on('events:bulk-operation', () => {
232
+ clearTimeout(reindexTimeout);
233
+ reindexTimeout = setTimeout(() => {
234
+ this.searchManager.indexEvents();
235
+ }, 100);
236
+ });
237
+ }
238
+
239
+ /**
240
+ * Get search suggestions (autocomplete)
241
+ */
242
+ async getSuggestions(partial, field = 'title') {
243
+ if (partial.length < 2) {
244
+ return [];
297
245
  }
298
246
 
299
- /**
300
- * Get performance statistics
301
- */
302
- getPerformanceStats() {
303
- const stats = {};
304
-
305
- for (const [metric, values] of Object.entries(this.performanceMetrics)) {
306
- if (values.length === 0) {
307
- stats[metric] = { avg: 0, min: 0, max: 0, p95: 0 };
308
- continue;
309
- }
310
-
311
- const sorted = [...values].sort((a, b) => a - b);
312
- const sum = sorted.reduce((a, b) => a + b, 0);
313
-
314
- stats[metric] = {
315
- avg: sum / sorted.length,
316
- min: sorted[0],
317
- max: sorted[sorted.length - 1],
318
- p95: sorted[Math.floor(sorted.length * 0.95)]
319
- };
320
- }
247
+ // Use search with prefix matching
248
+ const results = await this.searchManager.search(partial, {
249
+ fields: [field],
250
+ prefixMatch: true,
251
+ limit: 10
252
+ });
321
253
 
322
- return stats;
254
+ // Extract unique values
255
+ const suggestions = new Set();
256
+ for (const result of results) {
257
+ const value = result.event[field];
258
+ if (value) {
259
+ suggestions.add(value);
260
+ }
323
261
  }
324
262
 
325
- /**
326
- * Export calendar with recurrence data
327
- */
328
- exportWithRecurrence(format = 'json') {
329
- const data = {
330
- events: this.eventStore.getAllEvents(),
331
- modifiedInstances: {},
332
- exceptions: {}
333
- };
334
-
335
- // Include modified instances
336
- for (const [eventId, modifications] of this.recurrenceEngine.modifiedInstances) {
337
- data.modifiedInstances[eventId] = Array.from(modifications.entries());
338
- }
263
+ return Array.from(suggestions);
264
+ }
339
265
 
340
- // Include exceptions
341
- for (const [eventId, exceptions] of this.recurrenceEngine.exceptionStore) {
342
- data.exceptions[eventId] = Array.from(exceptions.entries());
343
- }
266
+ /**
267
+ * Performance monitoring
268
+ */
269
+ recordMetric(type, value) {
270
+ this.performanceMetrics[type].push(value);
344
271
 
345
- if (format === 'json') {
346
- return JSON.stringify(data, null, 2);
347
- }
272
+ // Keep only last 100 measurements
273
+ if (this.performanceMetrics[type].length > 100) {
274
+ this.performanceMetrics[type].shift();
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Get performance statistics
280
+ */
281
+ getPerformanceStats() {
282
+ const stats = {};
283
+
284
+ for (const [metric, values] of Object.entries(this.performanceMetrics)) {
285
+ if (values.length === 0) {
286
+ stats[metric] = { avg: 0, min: 0, max: 0, p95: 0 };
287
+ continue;
288
+ }
289
+
290
+ const sorted = [...values].sort((a, b) => a - b);
291
+ const sum = sorted.reduce((a, b) => a + b, 0);
292
+
293
+ stats[metric] = {
294
+ avg: sum / sorted.length,
295
+ min: sorted[0],
296
+ max: sorted[sorted.length - 1],
297
+ p95: sorted[Math.floor(sorted.length * 0.95)]
298
+ };
299
+ }
348
300
 
349
- // Could add ICS export here
350
- return data;
301
+ return stats;
302
+ }
303
+
304
+ /**
305
+ * Export calendar with recurrence data
306
+ */
307
+ exportWithRecurrence(format = 'json') {
308
+ const data = {
309
+ events: this.eventStore.getAllEvents(),
310
+ modifiedInstances: {},
311
+ exceptions: {}
312
+ };
313
+
314
+ // Include modified instances
315
+ for (const [eventId, modifications] of this.recurrenceEngine.modifiedInstances) {
316
+ data.modifiedInstances[eventId] = Array.from(modifications.entries());
351
317
  }
352
318
 
353
- /**
354
- * Import calendar with recurrence data
355
- */
356
- importWithRecurrence(data, format = 'json') {
357
- if (format === 'json') {
358
- const parsed = typeof data === 'string' ? JSON.parse(data) : data;
359
-
360
- // Import events
361
- for (const event of parsed.events) {
362
- this.addEvent(event);
363
- }
364
-
365
- // Import modified instances
366
- if (parsed.modifiedInstances) {
367
- for (const [eventId, modifications] of Object.entries(parsed.modifiedInstances)) {
368
- for (const [dateKey, mods] of modifications) {
369
- this.recurrenceEngine.addModifiedInstance(
370
- eventId,
371
- new Date(dateKey),
372
- mods
373
- );
374
- }
375
- }
376
- }
377
-
378
- // Import exceptions
379
- if (parsed.exceptions) {
380
- for (const [eventId, exceptions] of Object.entries(parsed.exceptions)) {
381
- for (const [dateKey, reason] of exceptions) {
382
- this.recurrenceEngine.addException(
383
- eventId,
384
- new Date(dateKey),
385
- reason
386
- );
387
- }
388
- }
389
- }
390
- }
319
+ // Include exceptions
320
+ for (const [eventId, exceptions] of this.recurrenceEngine.exceptionStore) {
321
+ data.exceptions[eventId] = Array.from(exceptions.entries());
391
322
  }
392
323
 
393
- /**
394
- * Clean up resources
395
- */
396
- destroy() {
397
- // Clean up worker
398
- if (this.searchManager) {
399
- this.searchManager.destroy();
400
- }
324
+ if (format === 'json') {
325
+ return JSON.stringify(data, null, 2);
326
+ }
401
327
 
402
- // Clear caches
403
- if (this.recurrenceEngine) {
404
- this.recurrenceEngine.occurrenceCache.clear();
328
+ // Could add ICS export here
329
+ return data;
330
+ }
331
+
332
+ /**
333
+ * Import calendar with recurrence data
334
+ */
335
+ importWithRecurrence(data, format = 'json') {
336
+ if (format === 'json') {
337
+ const parsed = typeof data === 'string' ? JSON.parse(data) : data;
338
+
339
+ // Import events
340
+ for (const event of parsed.events) {
341
+ this.addEvent(event);
342
+ }
343
+
344
+ // Import modified instances
345
+ if (parsed.modifiedInstances) {
346
+ for (const [eventId, modifications] of Object.entries(parsed.modifiedInstances)) {
347
+ for (const [dateKey, mods] of modifications) {
348
+ this.recurrenceEngine.addModifiedInstance(eventId, new Date(dateKey), mods);
349
+ }
405
350
  }
406
-
407
- // Call parent destroy if exists
408
- if (super.destroy) {
409
- super.destroy();
351
+ }
352
+
353
+ // Import exceptions
354
+ if (parsed.exceptions) {
355
+ for (const [eventId, exceptions] of Object.entries(parsed.exceptions)) {
356
+ for (const [dateKey, reason] of exceptions) {
357
+ this.recurrenceEngine.addException(eventId, new Date(dateKey), reason);
358
+ }
410
359
  }
360
+ }
361
+ }
362
+ }
363
+
364
+ /**
365
+ * Clean up resources
366
+ */
367
+ destroy() {
368
+ // Clean up worker
369
+ if (this.searchManager) {
370
+ this.searchManager.destroy();
371
+ }
372
+
373
+ // Clear caches
374
+ if (this.recurrenceEngine) {
375
+ this.recurrenceEngine.occurrenceCache.clear();
411
376
  }
377
+
378
+ // Call parent destroy if exists
379
+ if (super.destroy) {
380
+ super.destroy();
381
+ }
382
+ }
412
383
  }
413
384
 
414
385
  // Usage Example
415
386
  export function createEnhancedCalendar(config) {
416
- const calendar = new EnhancedCalendar(config);
417
-
418
- // Example: Add a complex recurring event
419
- calendar.addEvent({
420
- id: 'meeting-1',
421
- title: 'Weekly Team Standup',
422
- start: new Date('2024-01-01T10:00:00'),
423
- end: new Date('2024-01-01T10:30:00'),
424
- recurring: true,
425
- recurrenceRule: 'FREQ=WEEKLY;BYDAY=MO,WE,FR;UNTIL=20241231T235959Z',
426
- timeZone: 'America/New_York',
427
- categories: ['meetings', 'team']
428
- });
429
-
430
- // Example: Modify a single occurrence
431
- calendar.modifyOccurrence(
432
- 'meeting-1',
433
- new Date('2024-01-08T10:00:00'),
434
- {
435
- title: 'Extended Team Standup - Sprint Planning',
436
- end: new Date('2024-01-08T11:30:00'),
437
- location: 'Conference Room A'
438
- }
439
- );
440
-
441
- // Example: Cancel an occurrence
442
- calendar.cancelOccurrence(
443
- 'meeting-1',
444
- new Date('2024-01-15T10:00:00'),
445
- 'Public Holiday'
446
- );
447
-
448
- // Example: Advanced search
449
- calendar.advancedSearch('standup', {
450
- dateRange: {
451
- start: new Date('2024-01-01'),
452
- end: new Date('2024-01-31')
453
- },
454
- categories: ['meetings'],
455
- modifiedOnly: false
456
- }).then(results => {
457
- console.log('Search results:', results);
387
+ const calendar = new EnhancedCalendar(config);
388
+
389
+ // Example: Add a complex recurring event
390
+ calendar.addEvent({
391
+ id: 'meeting-1',
392
+ title: 'Weekly Team Standup',
393
+ start: new Date('2024-01-01T10:00:00'),
394
+ end: new Date('2024-01-01T10:30:00'),
395
+ recurring: true,
396
+ recurrenceRule: 'FREQ=WEEKLY;BYDAY=MO,WE,FR;UNTIL=20241231T235959Z',
397
+ timeZone: 'America/New_York',
398
+ categories: ['meetings', 'team']
399
+ });
400
+
401
+ // Example: Modify a single occurrence
402
+ calendar.modifyOccurrence('meeting-1', new Date('2024-01-08T10:00:00'), {
403
+ title: 'Extended Team Standup - Sprint Planning',
404
+ end: new Date('2024-01-08T11:30:00'),
405
+ location: 'Conference Room A'
406
+ });
407
+
408
+ // Example: Cancel an occurrence
409
+ calendar.cancelOccurrence('meeting-1', new Date('2024-01-15T10:00:00'), 'Public Holiday');
410
+
411
+ // Example: Advanced search
412
+ calendar
413
+ .advancedSearch('standup', {
414
+ dateRange: {
415
+ start: new Date('2024-01-01'),
416
+ end: new Date('2024-01-31')
417
+ },
418
+ categories: ['meetings'],
419
+ modifiedOnly: false
420
+ })
421
+ .then(results => {
422
+ console.log('Search results:', results);
458
423
  });
459
424
 
460
- return calendar;
425
+ return calendar;
461
426
  }
462
427
 
463
- export default EnhancedCalendar;
428
+ export default EnhancedCalendar;