@forcecalendar/interface 0.10.0 → 1.0.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.
- package/dist/force-calendar-interface.esm.js +112 -95
- package/dist/force-calendar-interface.esm.js.map +1 -1
- package/dist/force-calendar-interface.umd.js +22 -22
- package/dist/force-calendar-interface.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/components/views/MonthView.js +24 -5
- package/src/core/EventBus.js +6 -3
- package/src/core/StateManager.js +34 -14
- package/src/utils/DOMUtils.js +11 -2
package/package.json
CHANGED
|
@@ -108,11 +108,30 @@ export class MonthView extends BaseComponent {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
getContrastingTextColor(bgColor) {
|
|
111
|
-
if (!bgColor) return 'white';
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
111
|
+
if (!bgColor || typeof bgColor !== 'string') return 'white';
|
|
112
|
+
|
|
113
|
+
// Extract hex color, removing # if present
|
|
114
|
+
const color = (bgColor.charAt(0) === '#') ? bgColor.substring(1) : bgColor;
|
|
115
|
+
|
|
116
|
+
// Validate hex format (3 or 6 characters)
|
|
117
|
+
if (!/^[0-9A-Fa-f]{3}$|^[0-9A-Fa-f]{6}$/.test(color)) {
|
|
118
|
+
return 'white'; // Fallback for invalid format
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Expand 3-char hex to 6-char
|
|
122
|
+
const fullColor = color.length === 3
|
|
123
|
+
? color[0] + color[0] + color[1] + color[1] + color[2] + color[2]
|
|
124
|
+
: color;
|
|
125
|
+
|
|
126
|
+
const r = parseInt(fullColor.substring(0, 2), 16);
|
|
127
|
+
const g = parseInt(fullColor.substring(2, 4), 16);
|
|
128
|
+
const b = parseInt(fullColor.substring(4, 6), 16);
|
|
129
|
+
|
|
130
|
+
// Check for NaN (shouldn't happen with validation, but just in case)
|
|
131
|
+
if (isNaN(r) || isNaN(g) || isNaN(b)) {
|
|
132
|
+
return 'white';
|
|
133
|
+
}
|
|
134
|
+
|
|
116
135
|
const uicolors = [r / 255, g / 255, b / 255];
|
|
117
136
|
const c = uicolors.map((col) => {
|
|
118
137
|
if (col <= 0.03928) {
|
package/src/core/EventBus.js
CHANGED
|
@@ -102,13 +102,14 @@ class EventBus {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
// Handle wildcard subscriptions
|
|
106
|
-
|
|
105
|
+
// Handle wildcard subscriptions (copy Set to avoid mutation during iteration)
|
|
106
|
+
const toRemove = [];
|
|
107
|
+
for (const subscription of [...this.wildcardHandlers]) {
|
|
107
108
|
if (this.matchesPattern(eventName, subscription.pattern)) {
|
|
108
109
|
const { handler, once } = subscription;
|
|
109
110
|
|
|
110
111
|
if (once) {
|
|
111
|
-
|
|
112
|
+
toRemove.push(subscription);
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
try {
|
|
@@ -121,6 +122,8 @@ class EventBus {
|
|
|
121
122
|
}
|
|
122
123
|
}
|
|
123
124
|
}
|
|
125
|
+
// Remove one-time handlers after iteration
|
|
126
|
+
toRemove.forEach(sub => this.wildcardHandlers.delete(sub));
|
|
124
127
|
|
|
125
128
|
return Promise.all(promises);
|
|
126
129
|
}
|
package/src/core/StateManager.js
CHANGED
|
@@ -145,33 +145,53 @@ class StateManager {
|
|
|
145
145
|
// Event management
|
|
146
146
|
addEvent(event) {
|
|
147
147
|
const addedEvent = this.calendar.addEvent(event);
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
if (!addedEvent) {
|
|
149
|
+
console.error('Failed to add event to calendar');
|
|
150
|
+
eventBus.emit('event:error', { action: 'add', event, error: 'Failed to add event' });
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
// Create new array to avoid mutation before setState
|
|
154
|
+
const newEvents = [...this.state.events, addedEvent];
|
|
155
|
+
this.setState({ events: newEvents });
|
|
150
156
|
eventBus.emit('event:added', { event: addedEvent });
|
|
151
157
|
return addedEvent;
|
|
152
158
|
}
|
|
153
159
|
|
|
154
160
|
updateEvent(eventId, updates) {
|
|
155
161
|
const event = this.calendar.updateEvent(eventId, updates);
|
|
156
|
-
if (event) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
this.setState({ events: [...this.state.events] });
|
|
161
|
-
eventBus.emit('event:updated', { event });
|
|
162
|
-
}
|
|
162
|
+
if (!event) {
|
|
163
|
+
console.error(`Failed to update event: ${eventId}`);
|
|
164
|
+
eventBus.emit('event:error', { action: 'update', eventId, updates, error: 'Event not found in calendar' });
|
|
165
|
+
return null;
|
|
163
166
|
}
|
|
167
|
+
|
|
168
|
+
const index = this.state.events.findIndex(e => e.id === eventId);
|
|
169
|
+
if (index === -1) {
|
|
170
|
+
console.error(`Event ${eventId} not found in state`);
|
|
171
|
+
eventBus.emit('event:error', { action: 'update', eventId, error: 'Event not found in state' });
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Create new array to avoid mutation before setState
|
|
176
|
+
const newEvents = [...this.state.events];
|
|
177
|
+
newEvents[index] = event;
|
|
178
|
+
this.setState({ events: newEvents });
|
|
179
|
+
eventBus.emit('event:updated', { event });
|
|
164
180
|
return event;
|
|
165
181
|
}
|
|
166
182
|
|
|
167
183
|
deleteEvent(eventId) {
|
|
168
184
|
const deleted = this.calendar.removeEvent(eventId);
|
|
169
|
-
if (deleted) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
185
|
+
if (!deleted) {
|
|
186
|
+
console.error(`Failed to delete event: ${eventId}`);
|
|
187
|
+
eventBus.emit('event:error', { action: 'delete', eventId, error: 'Event not found' });
|
|
188
|
+
return false;
|
|
173
189
|
}
|
|
174
|
-
|
|
190
|
+
// Create new array to avoid mutation before setState
|
|
191
|
+
const newEvents = this.state.events.filter(e => e.id !== eventId);
|
|
192
|
+
this.setState({ events: newEvents });
|
|
193
|
+
eventBus.emit('event:deleted', { eventId });
|
|
194
|
+
return true;
|
|
175
195
|
}
|
|
176
196
|
|
|
177
197
|
getEvents() {
|
package/src/utils/DOMUtils.js
CHANGED
|
@@ -255,6 +255,15 @@ export class DOMUtils {
|
|
|
255
255
|
const focusableElements = container.querySelectorAll(
|
|
256
256
|
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
257
257
|
);
|
|
258
|
+
|
|
259
|
+
// Handle case where there are no focusable elements
|
|
260
|
+
if (focusableElements.length === 0) {
|
|
261
|
+
// Make container focusable as fallback
|
|
262
|
+
container.setAttribute('tabindex', '-1');
|
|
263
|
+
container.focus();
|
|
264
|
+
return () => container.removeAttribute('tabindex');
|
|
265
|
+
}
|
|
266
|
+
|
|
258
267
|
const firstFocusable = focusableElements[0];
|
|
259
268
|
const lastFocusable = focusableElements[focusableElements.length - 1];
|
|
260
269
|
|
|
@@ -263,12 +272,12 @@ export class DOMUtils {
|
|
|
263
272
|
|
|
264
273
|
if (e.shiftKey) {
|
|
265
274
|
if (document.activeElement === firstFocusable) {
|
|
266
|
-
lastFocusable
|
|
275
|
+
lastFocusable?.focus();
|
|
267
276
|
e.preventDefault();
|
|
268
277
|
}
|
|
269
278
|
} else {
|
|
270
279
|
if (document.activeElement === lastFocusable) {
|
|
271
|
-
firstFocusable
|
|
280
|
+
firstFocusable?.focus();
|
|
272
281
|
e.preventDefault();
|
|
273
282
|
}
|
|
274
283
|
}
|