kalendly 0.1.1 → 0.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.
package/README.md CHANGED
@@ -8,11 +8,20 @@ A universal calendar scheduler component that works seamlessly across React, Vue
8
8
  - 📱 **Responsive**: Mobile-friendly design that matches your existing Vue implementation
9
9
  - 🎨 **Customizable**: Easy to theme and customize with CSS variables
10
10
  - 🔒 **Type Safe**: Full TypeScript support
11
- - 📅 **Event Management**: Add, display, and manage events
11
+ - 📅 **Event Management**: Add, display, and manage events with rich metadata
12
+ - 🔔 **Advanced Features**: Recurring events, reminders, categories, priorities, and collaboration
12
13
  - 🌐 **Accessible**: Built with accessibility in mind
13
14
  - 📦 **Tree Shakeable**: Import only what you need
14
15
  - 🎯 **Design Consistent**: Matches the original Vue calendar design and feel
15
16
 
17
+ ## Live Examples
18
+
19
+ Check out the interactive examples: [https://kalendly.netlify.app](https://kalendly.netlify.app)
20
+
21
+ - [Vanilla JavaScript](https://kalendly.netlify.app/vanilla)
22
+ - [React](https://kalendly.netlify.app/react)
23
+ - [Vue](https://kalendly.netlify.app/vue)
24
+
16
25
  ## Installation
17
26
 
18
27
  ```bash
@@ -264,30 +273,130 @@ export default App;
264
273
 
265
274
  ### Props
266
275
 
267
- | Prop | Type | Default | Description |
268
- | --------------- | --------------------------------------- | ------------------ | --------------------------------------- |
269
- | `events` | `CalendarEvent[]` | `[]` | Array of events to display |
270
- | `initialDate` | `Date` | `new Date()` | Initial date to display |
271
- | `minYear` | `number` | `currentYear - 30` | Minimum selectable year |
272
- | `maxYear` | `number` | `currentYear + 10` | Maximum selectable year |
273
- | `weekStartsOn` | `0 \| 1` | `0` | Week start day (0 = Sunday, 1 = Monday) |
274
- | `onDateSelect` | `(date: Date) => void` | - | Callback when date is selected |
275
- | `onEventClick` | `(event: CalendarEvent) => void` | - | Callback when event is clicked |
276
- | `onMonthChange` | `(year: number, month: number) => void` | - | Callback when month changes |
276
+ | Prop | Type | Default | Description |
277
+ | ---------------- | --------------------------------------- | ------------------ | --------------------------------------- |
278
+ | `events` | `CalendarEvent[]` | `[]` | Array of events to display |
279
+ | `initialDate` | `Date` | `new Date()` | Initial date to display |
280
+ | `minYear` | `number` | `currentYear - 30` | Minimum selectable year |
281
+ | `maxYear` | `number` | `currentYear + 10` | Maximum selectable year |
282
+ | `weekStartsOn` | `0 \| 1` | `0` | Week start day (0 = Sunday, 1 = Monday) |
283
+ | `categoryColors` | `CategoryColorMap` | `{}` | Custom colors for event categories |
284
+ | `onDateSelect` | `(date: Date) => void` | - | Callback when date is selected |
285
+ | `onEventClick` | `(event: CalendarEvent) => void` | - | Callback when event is clicked |
286
+ | `onMonthChange` | `(year: number, month: number) => void` | - | Callback when month changes |
277
287
 
278
288
  ### CalendarEvent Interface
279
289
 
280
290
  ```typescript
281
291
  interface CalendarEvent {
292
+ // Required fields
282
293
  id: string | number;
283
294
  name: string;
284
295
  date: string | Date;
296
+
297
+ // Time fields
298
+ startTime?: string; // e.g., "09:00", "14:30"
299
+ endTime?: string; // e.g., "10:00", "16:00"
300
+ allDay?: boolean; // True for all-day events
301
+
302
+ // Display & categorization
285
303
  description?: string;
286
- color?: string;
287
- [key: string]: any;
304
+ color?: string; // Custom event color
305
+ category?:
306
+ | 'work'
307
+ | 'personal'
308
+ | 'meeting'
309
+ | 'deadline'
310
+ | 'appointment'
311
+ | 'other';
312
+ location?: string; // Event location
313
+ url?: string; // Related URL or meeting link
314
+
315
+ // Status & priority
316
+ status?: 'scheduled' | 'completed' | 'cancelled' | 'tentative';
317
+ priority?: 'low' | 'medium' | 'high';
318
+
319
+ // Collaboration
320
+ attendees?: string[]; // List of attendee names/emails
321
+ organizer?: string; // Event organizer
322
+
323
+ // Reminders & recurrence
324
+ reminders?: number[]; // Minutes before event to remind (e.g., [15, 60])
325
+ recurring?: {
326
+ frequency: 'daily' | 'weekly' | 'monthly' | 'yearly';
327
+ interval?: number; // Every X days/weeks/months/years
328
+ endDate?: string | Date; // When recurrence ends
329
+ daysOfWeek?: number[]; // For weekly recurrence (0 = Sunday)
330
+ };
331
+
332
+ // Metadata
333
+ notes?: string; // Additional notes
334
+ tags?: string[]; // Event tags
335
+ createdAt?: string | Date; // Creation timestamp
336
+ updatedAt?: string | Date; // Last update timestamp
337
+
338
+ // Flexibility for custom fields
339
+ [key: string]: unknown;
288
340
  }
289
341
  ```
290
342
 
343
+ #### Example with Enhanced Fields
344
+
345
+ ```typescript
346
+ const events = [
347
+ {
348
+ id: 1,
349
+ name: 'Team Standup',
350
+ date: '2025-01-15',
351
+ startTime: '09:00',
352
+ endTime: '09:30',
353
+ category: 'meeting',
354
+ location: 'Conference Room A',
355
+ url: 'https://meet.google.com/abc-defg-hij',
356
+ attendees: ['john@example.com', 'jane@example.com'],
357
+ organizer: 'team-lead@example.com',
358
+ priority: 'high',
359
+ status: 'scheduled',
360
+ reminders: [15, 60],
361
+ recurring: {
362
+ frequency: 'daily',
363
+ interval: 1,
364
+ daysOfWeek: [1, 2, 3, 4, 5], // Mon-Fri
365
+ endDate: '2025-12-31',
366
+ },
367
+ tags: ['team', 'sync'],
368
+ },
369
+ {
370
+ id: 2,
371
+ name: 'Project Deadline',
372
+ date: '2025-01-20',
373
+ allDay: true,
374
+ category: 'deadline',
375
+ priority: 'high',
376
+ status: 'scheduled',
377
+ description: 'Final submission for Q1 project',
378
+ notes: 'Make sure all tests pass before submission',
379
+ },
380
+ ];
381
+ ```
382
+
383
+ #### Category Colors
384
+
385
+ You can customize colors for different event categories:
386
+
387
+ ```typescript
388
+ const categoryColors = {
389
+ work: '#4CAF50',
390
+ personal: '#2196F3',
391
+ meeting: '#FF9800',
392
+ deadline: '#F44336',
393
+ appointment: '#9C27B0',
394
+ other: '#607D8B'
395
+ };
396
+
397
+ <Calendar events={events} categoryColors={categoryColors} />
398
+ ```
399
+
291
400
  ## Framework-Specific Features
292
401
 
293
402
  ### React Props
@@ -470,11 +579,29 @@ import type { CalendarEvent, CalendarProps, CalendarState } from 'kalendly';
470
579
 
471
580
  ## Contributing
472
581
 
473
- 1. Fork the repository
474
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
475
- 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
476
- 4. Push to the branch (`git push origin feature/amazing-feature`)
477
- 5. Open a Pull Request
582
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for detailed information on:
583
+
584
+ - Development setup
585
+ - Project structure
586
+ - Building and testing
587
+ - Running examples
588
+ - Submitting pull requests
589
+ - Release process
590
+
591
+ Quick start:
592
+
593
+ ```bash
594
+ # Clone and install
595
+ git clone https://github.com/callezenwaka/kalendly.git
596
+ cd kalendly
597
+ npm install
598
+
599
+ # Run tests
600
+ npm test
601
+
602
+ # Run examples locally
603
+ npm run dev:examples
604
+ ```
478
605
 
479
606
  ## License
480
607
 
@@ -482,11 +609,10 @@ MIT © Callis Ezenwaka
482
609
 
483
610
  ## Changelog
484
611
 
485
- ### 1.0.0
612
+ See [CHANGELOG.md](CHANGELOG.md) for detailed release notes and version history.
613
+
614
+ ### Recent Updates
486
615
 
487
- - Initial release
488
- - React, Vue, and React Native support
489
- - TypeScript definitions
490
- - Core calendar engine
491
- - Event management
492
- - Responsive design
616
+ - **v0.1.2** (Upcoming): Enhanced event parameters with structured metadata, categories, recurrence, collaboration features, and Netlify deployment
617
+ - **v0.1.1**: Pre-commit hooks and trusted publishing with OIDC
618
+ - **v0.1.0**: Initial release with React, Vue, React Native, and Vanilla JavaScript support
@@ -2,9 +2,30 @@ interface CalendarEvent {
2
2
  id: string | number;
3
3
  name: string;
4
4
  date: string | Date;
5
+ startTime?: string;
6
+ endTime?: string;
7
+ allDay?: boolean;
5
8
  description?: string;
6
9
  color?: string;
7
- [key: string]: any;
10
+ category?: 'work' | 'personal' | 'meeting' | 'deadline' | 'appointment' | 'other';
11
+ location?: string;
12
+ url?: string;
13
+ status?: 'scheduled' | 'completed' | 'cancelled' | 'tentative';
14
+ priority?: 'low' | 'medium' | 'high';
15
+ attendees?: string[];
16
+ organizer?: string;
17
+ reminders?: number[];
18
+ recurring?: {
19
+ frequency: 'daily' | 'weekly' | 'monthly' | 'yearly';
20
+ interval?: number;
21
+ endDate?: string | Date;
22
+ daysOfWeek?: number[];
23
+ };
24
+ notes?: string;
25
+ tags?: string[];
26
+ createdAt?: string | Date;
27
+ updatedAt?: string | Date;
28
+ [key: string]: unknown;
8
29
  }
9
30
  interface CalendarDate {
10
31
  date: Date;
@@ -21,12 +42,16 @@ interface CalendarState {
21
42
  selectedDayIndex: number | null;
22
43
  tasks: CalendarEvent[];
23
44
  }
45
+ interface CategoryColorMap {
46
+ [category: string]: string;
47
+ }
24
48
  interface CalendarConfig {
25
49
  events: CalendarEvent[];
26
50
  initialDate?: Date;
27
51
  minYear?: number;
28
52
  maxYear?: number;
29
53
  weekStartsOn?: 0 | 1;
54
+ categoryColors?: CategoryColorMap;
30
55
  }
31
56
  interface CalendarActions {
32
57
  next: () => void;
@@ -62,55 +87,31 @@ interface CalendarProps {
62
87
 
63
88
  declare const MONTHS: string[];
64
89
  declare const DAYS: string[];
65
- /**
66
- * Normalizes a date to midnight (00:00:00) for comparison purposes
67
- */
68
90
  declare function normalizeDate(date: Date): Date;
69
- /**
70
- * Checks if two dates are the same day
71
- */
72
91
  declare function isSameDay(date1: Date, date2: Date): boolean;
73
- /**
74
- * Checks if a date is today
75
- */
76
92
  declare function isToday(date: Date): boolean;
77
- /**
78
- * Generates an array of years for the year selector
79
- */
80
93
  declare function generateYears(minYear?: number, maxYear?: number): number[];
81
- /**
82
- * Gets events for a specific date
83
- */
84
94
  declare function getEventsForDate(events: CalendarEvent[], date: Date): CalendarEvent[];
85
- /**
86
- * Checks if a date has any events
87
- */
88
95
  declare function hasEvents(events: CalendarEvent[], date: Date): boolean;
89
- /**
90
- * Generates calendar dates for a given month and year
91
- */
92
96
  declare function generateCalendarDates(year: number, month: number, events?: CalendarEvent[], weekStartsOn?: 0 | 1): (CalendarDate | null)[][];
93
- /**
94
- * Gets the popup position class based on the selected day index
95
- */
96
97
  declare function getPopupPositionClass(selectedDayIndex: number | null): string;
97
- /**
98
- * Gets CSS classes for a calendar cell
99
- */
100
98
  declare function getCellClasses(calendarDate: CalendarDate | null): string[];
101
- /**
102
- * Formats a date for display
103
- */
104
99
  declare function formatDateForDisplay(date: Date): string;
105
- /**
106
- * Gets month and year text for display
107
- */
108
100
  declare function getMonthYearText(year: number, month: number): string;
101
+ declare function formatTimeRange(event: CalendarEvent): string;
102
+ declare function formatAttendees(attendees?: string[]): string;
103
+ declare function sortEventsByTime(events: CalendarEvent[]): CalendarEvent[];
104
+ declare const DEFAULT_CATEGORY_COLORS: CategoryColorMap;
105
+ declare function getDefaultEventColor(category?: string, customColors?: CategoryColorMap): string;
106
+ declare function mergeCategoryColors(customColors?: CategoryColorMap): CategoryColorMap;
107
+ declare function isValidHexColor(color: string): boolean;
108
+ declare function getCategoryColor(category: string, customColors?: CategoryColorMap): string;
109
109
 
110
110
  declare class CalendarEngine {
111
111
  private state;
112
112
  private config;
113
113
  private listeners;
114
+ private categoryColors;
114
115
  constructor(config: CalendarConfig);
115
116
  /**
116
117
  * Subscribe to state changes
@@ -172,10 +173,26 @@ declare class CalendarEngine {
172
173
  * Clear selected date
173
174
  */
174
175
  clearSelection(): void;
176
+ /**
177
+ * Get category color (with custom colors support)
178
+ */
179
+ getCategoryColor(category?: string): string;
180
+ /**
181
+ * Update category colors dynamically
182
+ */
183
+ updateCategoryColors(colors: CategoryColorMap): void;
184
+ /**
185
+ * Get all category colors
186
+ */
187
+ getCategoryColors(): CategoryColorMap;
188
+ /**
189
+ * Register a new category with color
190
+ */
191
+ registerCategory(name: string, color: string): void;
175
192
  /**
176
193
  * Destroy the engine and cleanup listeners
177
194
  */
178
195
  destroy(): void;
179
196
  }
180
197
 
181
- export { type CalendarActions, type CalendarConfig, type CalendarDate, CalendarEngine, type CalendarEvent, type CalendarEventHandler, type CalendarProps, type CalendarState, type CalendarViewModel, DAYS, MONTHS, type PopupPosition, formatDateForDisplay, generateCalendarDates, generateYears, getCellClasses, getEventsForDate, getMonthYearText, getPopupPositionClass, hasEvents, isSameDay, isToday, normalizeDate };
198
+ export { type CalendarActions, type CalendarConfig, type CalendarDate, CalendarEngine, type CalendarEvent, type CalendarEventHandler, type CalendarProps, type CalendarState, type CalendarViewModel, type CategoryColorMap, DAYS, DEFAULT_CATEGORY_COLORS, MONTHS, type PopupPosition, formatAttendees, formatDateForDisplay, formatTimeRange, generateCalendarDates, generateYears, getCategoryColor, getCellClasses, getDefaultEventColor, getEventsForDate, getMonthYearText, getPopupPositionClass, hasEvents, isSameDay, isToday, isValidHexColor, mergeCategoryColors, normalizeDate, sortEventsByTime };
@@ -2,9 +2,30 @@ interface CalendarEvent {
2
2
  id: string | number;
3
3
  name: string;
4
4
  date: string | Date;
5
+ startTime?: string;
6
+ endTime?: string;
7
+ allDay?: boolean;
5
8
  description?: string;
6
9
  color?: string;
7
- [key: string]: any;
10
+ category?: 'work' | 'personal' | 'meeting' | 'deadline' | 'appointment' | 'other';
11
+ location?: string;
12
+ url?: string;
13
+ status?: 'scheduled' | 'completed' | 'cancelled' | 'tentative';
14
+ priority?: 'low' | 'medium' | 'high';
15
+ attendees?: string[];
16
+ organizer?: string;
17
+ reminders?: number[];
18
+ recurring?: {
19
+ frequency: 'daily' | 'weekly' | 'monthly' | 'yearly';
20
+ interval?: number;
21
+ endDate?: string | Date;
22
+ daysOfWeek?: number[];
23
+ };
24
+ notes?: string;
25
+ tags?: string[];
26
+ createdAt?: string | Date;
27
+ updatedAt?: string | Date;
28
+ [key: string]: unknown;
8
29
  }
9
30
  interface CalendarDate {
10
31
  date: Date;
@@ -21,12 +42,16 @@ interface CalendarState {
21
42
  selectedDayIndex: number | null;
22
43
  tasks: CalendarEvent[];
23
44
  }
45
+ interface CategoryColorMap {
46
+ [category: string]: string;
47
+ }
24
48
  interface CalendarConfig {
25
49
  events: CalendarEvent[];
26
50
  initialDate?: Date;
27
51
  minYear?: number;
28
52
  maxYear?: number;
29
53
  weekStartsOn?: 0 | 1;
54
+ categoryColors?: CategoryColorMap;
30
55
  }
31
56
  interface CalendarActions {
32
57
  next: () => void;
@@ -62,55 +87,31 @@ interface CalendarProps {
62
87
 
63
88
  declare const MONTHS: string[];
64
89
  declare const DAYS: string[];
65
- /**
66
- * Normalizes a date to midnight (00:00:00) for comparison purposes
67
- */
68
90
  declare function normalizeDate(date: Date): Date;
69
- /**
70
- * Checks if two dates are the same day
71
- */
72
91
  declare function isSameDay(date1: Date, date2: Date): boolean;
73
- /**
74
- * Checks if a date is today
75
- */
76
92
  declare function isToday(date: Date): boolean;
77
- /**
78
- * Generates an array of years for the year selector
79
- */
80
93
  declare function generateYears(minYear?: number, maxYear?: number): number[];
81
- /**
82
- * Gets events for a specific date
83
- */
84
94
  declare function getEventsForDate(events: CalendarEvent[], date: Date): CalendarEvent[];
85
- /**
86
- * Checks if a date has any events
87
- */
88
95
  declare function hasEvents(events: CalendarEvent[], date: Date): boolean;
89
- /**
90
- * Generates calendar dates for a given month and year
91
- */
92
96
  declare function generateCalendarDates(year: number, month: number, events?: CalendarEvent[], weekStartsOn?: 0 | 1): (CalendarDate | null)[][];
93
- /**
94
- * Gets the popup position class based on the selected day index
95
- */
96
97
  declare function getPopupPositionClass(selectedDayIndex: number | null): string;
97
- /**
98
- * Gets CSS classes for a calendar cell
99
- */
100
98
  declare function getCellClasses(calendarDate: CalendarDate | null): string[];
101
- /**
102
- * Formats a date for display
103
- */
104
99
  declare function formatDateForDisplay(date: Date): string;
105
- /**
106
- * Gets month and year text for display
107
- */
108
100
  declare function getMonthYearText(year: number, month: number): string;
101
+ declare function formatTimeRange(event: CalendarEvent): string;
102
+ declare function formatAttendees(attendees?: string[]): string;
103
+ declare function sortEventsByTime(events: CalendarEvent[]): CalendarEvent[];
104
+ declare const DEFAULT_CATEGORY_COLORS: CategoryColorMap;
105
+ declare function getDefaultEventColor(category?: string, customColors?: CategoryColorMap): string;
106
+ declare function mergeCategoryColors(customColors?: CategoryColorMap): CategoryColorMap;
107
+ declare function isValidHexColor(color: string): boolean;
108
+ declare function getCategoryColor(category: string, customColors?: CategoryColorMap): string;
109
109
 
110
110
  declare class CalendarEngine {
111
111
  private state;
112
112
  private config;
113
113
  private listeners;
114
+ private categoryColors;
114
115
  constructor(config: CalendarConfig);
115
116
  /**
116
117
  * Subscribe to state changes
@@ -172,10 +173,26 @@ declare class CalendarEngine {
172
173
  * Clear selected date
173
174
  */
174
175
  clearSelection(): void;
176
+ /**
177
+ * Get category color (with custom colors support)
178
+ */
179
+ getCategoryColor(category?: string): string;
180
+ /**
181
+ * Update category colors dynamically
182
+ */
183
+ updateCategoryColors(colors: CategoryColorMap): void;
184
+ /**
185
+ * Get all category colors
186
+ */
187
+ getCategoryColors(): CategoryColorMap;
188
+ /**
189
+ * Register a new category with color
190
+ */
191
+ registerCategory(name: string, color: string): void;
175
192
  /**
176
193
  * Destroy the engine and cleanup listeners
177
194
  */
178
195
  destroy(): void;
179
196
  }
180
197
 
181
- export { type CalendarActions, type CalendarConfig, type CalendarDate, CalendarEngine, type CalendarEvent, type CalendarEventHandler, type CalendarProps, type CalendarState, type CalendarViewModel, DAYS, MONTHS, type PopupPosition, formatDateForDisplay, generateCalendarDates, generateYears, getCellClasses, getEventsForDate, getMonthYearText, getPopupPositionClass, hasEvents, isSameDay, isToday, normalizeDate };
198
+ export { type CalendarActions, type CalendarConfig, type CalendarDate, CalendarEngine, type CalendarEvent, type CalendarEventHandler, type CalendarProps, type CalendarState, type CalendarViewModel, type CategoryColorMap, DAYS, DEFAULT_CATEGORY_COLORS, MONTHS, type PopupPosition, formatAttendees, formatDateForDisplay, formatTimeRange, generateCalendarDates, generateYears, getCategoryColor, getCellClasses, getDefaultEventColor, getEventsForDate, getMonthYearText, getPopupPositionClass, hasEvents, isSameDay, isToday, isValidHexColor, mergeCategoryColors, normalizeDate, sortEventsByTime };
@@ -22,18 +22,26 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  CalendarEngine: () => CalendarEngine,
24
24
  DAYS: () => DAYS,
25
+ DEFAULT_CATEGORY_COLORS: () => DEFAULT_CATEGORY_COLORS,
25
26
  MONTHS: () => MONTHS,
27
+ formatAttendees: () => formatAttendees,
26
28
  formatDateForDisplay: () => formatDateForDisplay,
29
+ formatTimeRange: () => formatTimeRange,
27
30
  generateCalendarDates: () => generateCalendarDates,
28
31
  generateYears: () => generateYears,
32
+ getCategoryColor: () => getCategoryColor,
29
33
  getCellClasses: () => getCellClasses,
34
+ getDefaultEventColor: () => getDefaultEventColor,
30
35
  getEventsForDate: () => getEventsForDate,
31
36
  getMonthYearText: () => getMonthYearText,
32
37
  getPopupPositionClass: () => getPopupPositionClass,
33
38
  hasEvents: () => hasEvents,
34
39
  isSameDay: () => isSameDay,
35
40
  isToday: () => isToday,
36
- normalizeDate: () => normalizeDate
41
+ isValidHexColor: () => isValidHexColor,
42
+ mergeCategoryColors: () => mergeCategoryColors,
43
+ normalizeDate: () => normalizeDate,
44
+ sortEventsByTime: () => sortEventsByTime
37
45
  });
38
46
  module.exports = __toCommonJS(index_exports);
39
47
 
@@ -150,12 +158,70 @@ function formatDateForDisplay(date) {
150
158
  function getMonthYearText(year, month) {
151
159
  return `${MONTHS[month]} ${year}`;
152
160
  }
161
+ function formatTimeRange(event) {
162
+ if (event.allDay || !event.startTime && !event.endTime) {
163
+ return "All day";
164
+ }
165
+ if (event.startTime && event.endTime) {
166
+ return `${event.startTime} - ${event.endTime}`;
167
+ }
168
+ if (event.startTime) {
169
+ return `${event.startTime}`;
170
+ }
171
+ return "";
172
+ }
173
+ function formatAttendees(attendees) {
174
+ if (!attendees || attendees.length === 0) return "";
175
+ if (attendees.length === 1) return attendees[0];
176
+ if (attendees.length === 2) return attendees.join(" and ");
177
+ return `${attendees.slice(0, -1).join(", ")}, and ${attendees[attendees.length - 1]}`;
178
+ }
179
+ function sortEventsByTime(events) {
180
+ return [...events].sort((a, b) => {
181
+ const aAllDay = a.allDay || !a.startTime && !a.endTime;
182
+ const bAllDay = b.allDay || !b.startTime && !b.endTime;
183
+ if (aAllDay && !bAllDay) return -1;
184
+ if (!aAllDay && bAllDay) return 1;
185
+ if (!a.startTime || !b.startTime) return 0;
186
+ return a.startTime.localeCompare(b.startTime);
187
+ });
188
+ }
189
+ var DEFAULT_CATEGORY_COLORS = {
190
+ work: "#3b82f6",
191
+ personal: "#8b5cf6",
192
+ meeting: "#10b981",
193
+ deadline: "#ef4444",
194
+ appointment: "#f59e0b",
195
+ other: "#6b7280"
196
+ };
197
+ function getDefaultEventColor(category, customColors) {
198
+ const colorMap = customColors || DEFAULT_CATEGORY_COLORS;
199
+ if (category && colorMap[category]) {
200
+ return colorMap[category];
201
+ }
202
+ return "#fc8917";
203
+ }
204
+ function mergeCategoryColors(customColors) {
205
+ return {
206
+ ...DEFAULT_CATEGORY_COLORS,
207
+ ...customColors
208
+ };
209
+ }
210
+ function isValidHexColor(color) {
211
+ return /^#([0-9A-F]{3}){1,2}$/i.test(color);
212
+ }
213
+ function getCategoryColor(category, customColors) {
214
+ const colorMap = mergeCategoryColors(customColors);
215
+ const color = colorMap[category] || colorMap.other || "#fc8917";
216
+ return isValidHexColor(color) ? color : "#fc8917";
217
+ }
153
218
 
154
219
  // src/core/calendar-engine.ts
155
220
  var CalendarEngine = class {
156
221
  constructor(config) {
157
222
  this.listeners = /* @__PURE__ */ new Set();
158
223
  this.config = config;
224
+ this.categoryColors = mergeCategoryColors(config.categoryColors);
159
225
  const initialDate = config.initialDate || /* @__PURE__ */ new Date();
160
226
  this.state = {
161
227
  currentYear: initialDate.getFullYear(),
@@ -322,6 +388,33 @@ var CalendarEngine = class {
322
388
  this.state.tasks = [];
323
389
  this.notify();
324
390
  }
391
+ /**
392
+ * Get category color (with custom colors support)
393
+ */
394
+ getCategoryColor(category) {
395
+ if (!category) return "#fc8917";
396
+ return getCategoryColor(category, this.categoryColors);
397
+ }
398
+ /**
399
+ * Update category colors dynamically
400
+ */
401
+ updateCategoryColors(colors) {
402
+ this.categoryColors = mergeCategoryColors(colors);
403
+ this.notify();
404
+ }
405
+ /**
406
+ * Get all category colors
407
+ */
408
+ getCategoryColors() {
409
+ return { ...this.categoryColors };
410
+ }
411
+ /**
412
+ * Register a new category with color
413
+ */
414
+ registerCategory(name, color) {
415
+ this.categoryColors[name] = color;
416
+ this.notify();
417
+ }
325
418
  /**
326
419
  * Destroy the engine and cleanup listeners
327
420
  */
@@ -333,17 +426,25 @@ var CalendarEngine = class {
333
426
  0 && (module.exports = {
334
427
  CalendarEngine,
335
428
  DAYS,
429
+ DEFAULT_CATEGORY_COLORS,
336
430
  MONTHS,
431
+ formatAttendees,
337
432
  formatDateForDisplay,
433
+ formatTimeRange,
338
434
  generateCalendarDates,
339
435
  generateYears,
436
+ getCategoryColor,
340
437
  getCellClasses,
438
+ getDefaultEventColor,
341
439
  getEventsForDate,
342
440
  getMonthYearText,
343
441
  getPopupPositionClass,
344
442
  hasEvents,
345
443
  isSameDay,
346
444
  isToday,
347
- normalizeDate
445
+ isValidHexColor,
446
+ mergeCategoryColors,
447
+ normalizeDate,
448
+ sortEventsByTime
348
449
  });
349
450
  //# sourceMappingURL=index.js.map