@forcecalendar/interface 1.0.30 → 1.0.32

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forcecalendar/interface",
3
- "version": "1.0.30",
3
+ "version": "1.0.32",
4
4
  "type": "module",
5
5
  "description": "Official interface layer for forceCalendar Core - Enterprise calendar components",
6
6
  "main": "dist/force-calendar-interface.umd.js",
@@ -347,7 +347,10 @@ export class EventForm extends BaseComponent {
347
347
  this.endInput.value = this.formatDateForInput(this._formData.end);
348
348
  this.updateColorSelection();
349
349
 
350
- // Focus trapping
350
+ // Clean up previous focus trap before creating a new one
351
+ if (this._cleanupFocusTrap) {
352
+ this._cleanupFocusTrap();
353
+ }
351
354
  this._cleanupFocusTrap = DOMUtils.trapFocus(this.modalContent);
352
355
  }
353
356
  }
@@ -46,12 +46,21 @@ class StateManager {
46
46
 
47
47
  /**
48
48
  * Sync state.events from Core calendar (single source of truth)
49
- * This ensures state.events always matches Core's event store
49
+ * This ensures state.events always matches Core's event store.
50
+ *
51
+ * @param {object} options
52
+ * @param {boolean} options.silent - suppress subscriber notifications
53
+ * @param {boolean} options.force - always update even when IDs match
54
+ * (required after updateEvent where IDs
55
+ * are unchanged but content has changed)
50
56
  */
51
57
  _syncEventsFromCore(options = {}) {
58
+ const { force = false } = options;
52
59
  const coreEvents = this.calendar.getEvents() || [];
53
- // Only update if different to avoid unnecessary re-renders
60
+ // Skip the update when nothing changed, unless the caller forces a sync
61
+ // (e.g. after updateEvent where IDs are the same but content differs)
54
62
  if (
63
+ force ||
55
64
  this.state.events.length !== coreEvents.length ||
56
65
  !this._eventsMatch(this.state.events, coreEvents)
57
66
  ) {
@@ -61,7 +70,9 @@ class StateManager {
61
70
  }
62
71
 
63
72
  /**
64
- * Check if two event arrays have the same events (by id)
73
+ * Check if two event arrays have the same events by id.
74
+ * Only used for add/delete guards — updateEvent must pass force:true
75
+ * to bypass this check because IDs are unchanged after an update.
65
76
  */
66
77
  _eventsMatch(arr1, arr2) {
67
78
  if (arr1.length !== arr2.length) return false;
@@ -71,7 +82,11 @@ class StateManager {
71
82
 
72
83
  // State management
73
84
  getState() {
74
- return { ...this.state };
85
+ return {
86
+ ...this.state,
87
+ config: { ...this.state.config },
88
+ events: [...this.state.events]
89
+ };
75
90
  }
76
91
 
77
92
  setState(updates, options = {}) {
@@ -236,8 +251,9 @@ class StateManager {
236
251
  return null;
237
252
  }
238
253
 
239
- // Sync from Core to ensure consistency (single source of truth)
240
- this._syncEventsFromCore();
254
+ // Force sync from Core IDs are unchanged after an update so the
255
+ // ID-only guard in _eventsMatch would otherwise skip the state update
256
+ this._syncEventsFromCore({ force: true });
241
257
  eventBus.emit('event:update', { event });
242
258
  eventBus.emit('event:updated', { event });
243
259
  return event;