kalendly 0.1.6 → 0.2.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 (38) hide show
  1. package/README.md +318 -680
  2. package/dist/core/index.js +3 -3
  3. package/dist/index.d.mts +53 -601
  4. package/dist/index.d.ts +53 -601
  5. package/dist/index.js +760 -2350
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +734 -2339
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/{vanilla/index.umd.js → index.umd.js} +323 -307
  10. package/dist/index.umd.js.map +1 -0
  11. package/package.json +41 -91
  12. package/dist/react/index.d.mts +0 -251
  13. package/dist/react/index.d.ts +0 -251
  14. package/dist/react/index.js +0 -969
  15. package/dist/react/index.js.map +0 -1
  16. package/dist/react/index.mjs +0 -924
  17. package/dist/react/index.mjs.map +0 -1
  18. package/dist/react-native/index.d.mts +0 -642
  19. package/dist/react-native/index.d.ts +0 -642
  20. package/dist/react-native/index.js +0 -1649
  21. package/dist/react-native/index.js.map +0 -1
  22. package/dist/react-native/index.mjs +0 -1615
  23. package/dist/react-native/index.mjs.map +0 -1
  24. package/dist/vanilla/index.d.mts +0 -265
  25. package/dist/vanilla/index.d.ts +0 -265
  26. package/dist/vanilla/index.js +0 -1048
  27. package/dist/vanilla/index.js.map +0 -1
  28. package/dist/vanilla/index.mjs +0 -997
  29. package/dist/vanilla/index.mjs.map +0 -1
  30. package/dist/vanilla/index.umd.js.map +0 -1
  31. package/dist/vue/components/Calendar.vue.d.ts +0 -43
  32. package/dist/vue/components/Calendar.vue.d.ts.map +0 -1
  33. package/dist/vue/index.d.ts +0 -134
  34. package/dist/vue/index.d.ts.map +0 -1
  35. package/dist/vue/index.js +0 -1
  36. package/dist/vue/index.mjs +0 -717
  37. package/dist/vue/types.d.ts +0 -21
  38. package/dist/vue/types.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,25 +1,24 @@
1
- # kalendly Universal Calendar
1
+ # kalendly
2
2
 
3
- A universal calendar component that works seamlessly across React, Vue, and React Native with full TypeScript support.
3
+ A universal calendar web component works in React, Vue, Svelte, Angular, Solid.js, and plain HTML with no framework dependency.
4
4
 
5
5
  ## Features
6
6
 
7
- - 🚀 **Universal**: Works with React, Vue, React Native, and Vanilla JavaScript
8
- - 📱 **Responsive**: Mobile-friendly design that matches your existing UI implementation
9
- - 🎨 **Customizable**: Easy to theme and customize with CSS variables
10
- - 🔒 **Type Safe**: Full TypeScript support
11
- - 📅 **Event Management**: Add, display, and manage events with rich metadata
12
- - 🔔 **Advanced Features**: Recurring events, reminders, categories, priorities, and collaboration
13
- - 🌐 **Accessible**: Built with accessibility in mind
14
- - 📦 **Tree Shakeable**: Import only what you need
7
+ - **Framework-agnostic**: Single `<kal-calendar>` custom element, no framework required
8
+ - **Responsive**: Mobile-friendly, matches your existing UI
9
+ - **Themeable**: CSS variables + JS property API
10
+ - **Type Safe**: Full TypeScript support
11
+ - **Event-rich**: Categories, priorities, time ranges, attendees, and more
12
+ - **Accessible**: Built with accessibility in mind
13
+ - **Tree-shakeable**: Import only what you need
15
14
 
16
- ## 🎯 Live Demo
15
+ ## Live Demo
17
16
 
18
17
  <div align="center">
19
18
 
20
- [![Kalendly Calendar Demo](./docs/images/calendar-demo.png)](https://kalendly-example.netlify.app/)
19
+ [![Kalendly Calendar Demo](./docs/images/calendar-demo.gif)](https://kalendly-example.netlify.app/)
21
20
 
22
- **[🚀 Try the Interactive Demo →](https://kalendly-example.netlify.app/)**
21
+ **[Try the Interactive Demo →](https://kalendly-example.netlify.app/)**
23
22
 
24
23
  </div>
25
24
 
@@ -33,808 +32,449 @@ A universal calendar component that works seamlessly across React, Vue, and Reac
33
32
  <a href="https://kalendly-example.netlify.app/vue">
34
33
  <img src="https://img.shields.io/badge/Vue-4FC08D?style=for-the-badge&logo=vue.js&logoColor=white" alt="Vue Demo"/>
35
34
  </a>
35
+ <a href="https://kalendly-example.netlify.app/angular">
36
+ <img src="https://img.shields.io/badge/Angular-DD0031?style=for-the-badge&logo=angular&logoColor=white" alt="Angular Demo"/>
37
+ </a>
38
+ <a href="https://kalendly-example.netlify.app/svelte">
39
+ <img src="https://img.shields.io/badge/Svelte-FF3E00?style=for-the-badge&logo=svelte&logoColor=white" alt="Svelte Demo"/>
40
+ </a>
41
+ <a href="https://kalendly-example.netlify.app/solid">
42
+ <img src="https://img.shields.io/badge/Solid.js-2C4F7C?style=for-the-badge&logo=solid&logoColor=white" alt="Solid.js Demo"/>
43
+ </a>
36
44
  </p>
37
45
 
38
46
  ## Installation
39
47
 
40
48
  ```bash
41
49
  npm install kalendly
42
- # or
43
- yarn add kalendly
44
- # or
45
- pnpm add kalendly
46
50
  ```
47
51
 
48
52
  ## Usage
49
53
 
50
- ### React
51
-
52
- ```jsx
53
- import React from 'react';
54
- import { Calendar } from 'kalendly/react';
55
- import 'kalendly/styles';
54
+ ### Vanilla HTML / CDN
56
55
 
57
- const events = [
58
- {
59
- id: 1,
60
- name: 'Team Meeting',
61
- date: '2025-01-15',
62
- },
63
- {
64
- id: 2,
65
- name: 'Project Deadline',
66
- date: '2025-01-20',
67
- },
68
- ];
69
-
70
- function App() {
71
- const handleDateSelect = date => {
72
- console.log('Selected date:', date);
73
- };
74
-
75
- return (
76
- <div>
77
- <Calendar
78
- events={events}
79
- onDateSelect={handleDateSelect}
80
- title="My Calendar"
81
- />
82
- </div>
83
- );
84
- }
85
-
86
- export default App;
56
+ ```html
57
+ <link
58
+ rel="stylesheet"
59
+ href="https://unpkg.com/kalendly/dist/styles/calendar.css"
60
+ />
61
+ <script src="https://unpkg.com/kalendly/dist/index.umd.js"></script>
62
+
63
+ <kal-calendar
64
+ id="cal"
65
+ title="My Calendar"
66
+ initial-date="2025-01-15"
67
+ ></kal-calendar>
68
+
69
+ <script>
70
+ const cal = document.getElementById('cal');
71
+
72
+ // Set events (JS property — not an attribute)
73
+ cal.events = [
74
+ { id: 1, name: 'Team Meeting', date: new Date(2025, 0, 15) },
75
+ { id: 2, name: 'Project Deadline', date: new Date(2025, 0, 20) },
76
+ ];
77
+
78
+ // Listen to custom events
79
+ cal.addEventListener('cal-date-select', e => {
80
+ console.log('Selected:', e.detail.date, e.detail.events);
81
+ });
82
+
83
+ cal.addEventListener('cal-month-change', e => {
84
+ console.log('Month:', e.detail.year, e.detail.month);
85
+ });
86
+ </script>
87
87
  ```
88
88
 
89
- ### Vue
90
-
91
- ```vue
92
- <template>
93
- <div>
94
- <Calendar
95
- :events="events"
96
- @date-select="handleDateSelect"
97
- title="My Calendar"
98
- >
99
- <template #event="{ event }">
100
- <div class="custom-event">{{ event.name }}</div>
101
- </template>
102
- </Calendar>
103
- </div>
104
- </template>
89
+ ### ES Modules
105
90
 
106
- <script setup>
107
- import { Calendar } from 'kalendly/vue';
91
+ ```js
92
+ import 'kalendly';
108
93
  import 'kalendly/styles';
109
94
 
110
- const events = [
111
- {
112
- id: 1,
113
- name: 'Team Meeting',
114
- date: '2025-01-15',
115
- },
116
- {
117
- id: 2,
118
- name: 'Project Deadline',
119
- date: '2025-01-20',
120
- },
121
- ];
122
-
123
- const handleDateSelect = date => {
124
- console.log('Selected date:', date);
125
- };
126
- </script>
95
+ // <kal-calendar> is now registered and ready
127
96
  ```
128
97
 
129
- ### React Native
98
+ ### React 19
99
+
100
+ React 19 has full custom element support — pass objects/arrays as props and listen to custom events directly.
130
101
 
131
102
  ```jsx
132
- import React from 'react';
133
- import { View } from 'react-native';
134
- import { Calendar } from 'kalendly/react-native';
135
-
136
- const events = [
137
- {
138
- id: 1,
139
- name: 'Team Meeting',
140
- date: '2025-01-15',
141
- },
142
- {
143
- id: 2,
144
- name: 'Project Deadline',
145
- date: '2025-01-20',
146
- },
147
- ];
103
+ import 'kalendly';
104
+ import 'kalendly/styles';
148
105
 
149
106
  function App() {
150
- const handleDateSelect = date => {
151
- console.log('Selected date:', date);
152
- };
153
-
154
107
  return (
155
- <View style={{ flex: 1 }}>
156
- <Calendar
157
- events={events}
158
- onDateSelect={handleDateSelect}
159
- title="My Calendar"
160
- />
161
- </View>
108
+ <kal-calendar
109
+ title="My Calendar"
110
+ events={events}
111
+ oncal-date-select={e => console.log(e.detail.date)}
112
+ oncal-month-change={e => console.log(e.detail.year, e.detail.month)}
113
+ />
162
114
  );
163
115
  }
164
-
165
- export default App;
166
- ```
167
-
168
- ### Vanilla JavaScript
169
-
170
- #### Modern ES Modules (Recommended)
171
-
172
- ```html
173
- <!DOCTYPE html>
174
- <html>
175
- <head>
176
- <link
177
- rel="stylesheet"
178
- href="node_modules/kalendly/dist/styles/calendar.css"
179
- />
180
- <title>Vanilla JS Calendar</title>
181
- </head>
182
- <body>
183
- <div id="calendar"></div>
184
-
185
- <script type="module">
186
- import { createCalendar } from 'kalendly/vanilla';
187
-
188
- const events = [
189
- { id: 1, name: 'Team Meeting', date: '2025-01-15' },
190
- { id: 2, name: 'Project Deadline', date: '2025-01-20' },
191
- ];
192
-
193
- const calendar = createCalendar({
194
- container: '#calendar',
195
- events: events,
196
- title: 'My Calendar',
197
- });
198
-
199
- // Listen to events (note: use addEventListener, not onclick)
200
- document.getElementById('calendar').addEventListener('dateSelect', e => {
201
- console.log('Selected date:', e.detail.date);
202
- });
203
-
204
- document.getElementById('calendar').addEventListener('monthChange', e => {
205
- console.log('Month changed:', e.detail.year, e.detail.month);
206
- });
207
-
208
- // Update events dynamically
209
- // calendar.updateEvents(newEvents);
210
-
211
- // Navigate to specific date
212
- // calendar.goToDate(new Date(2025, 5, 15));
213
-
214
- // Cleanup when done
215
- // calendar.destroy();
216
- </script>
217
- </body>
218
- </html>
219
116
  ```
220
117
 
221
- #### CDN Usage (Browser)
222
-
223
- ```html
224
- <!DOCTYPE html>
225
- <html>
226
- <head>
227
- <link
228
- rel="stylesheet"
229
- href="https://unpkg.com/kalendly/dist/styles/calendar.css"
230
- />
231
- </head>
232
- <body>
233
- <div id="calendar"></div>
234
-
235
- <script type="module">
236
- import { createCalendar } from 'https://unpkg.com/kalendly/dist/vanilla/index.mjs';
237
-
238
- const calendar = createCalendar({
239
- container: '#calendar',
240
- events: [{ id: 1, name: 'Meeting', date: '2025-01-15' }],
241
- title: 'My Calendar',
242
- });
243
- </script>
244
- </body>
245
- </html>
246
- ```
247
-
248
- #### Legacy/Global Usage (Without Modules)
249
-
250
- ```html
251
- <!DOCTYPE html>
252
- <html>
253
- <head>
254
- <link
255
- rel="stylesheet"
256
- href="https://unpkg.com/kalendly/dist/styles/calendar.css"
257
- />
258
- </head>
259
- <body>
260
- <div id="calendar"></div>
261
-
262
- <script src="https://unpkg.com/kalendly/dist/vanilla/index.umd.js"></script>
263
- <script>
264
- const { createCalendar } = Kalendly;
265
-
266
- const calendar = createCalendar({
267
- container: '#calendar',
268
- events: [{ id: 1, name: 'Meeting', date: '2025-01-15' }],
269
- title: 'My Calendar',
270
- });
271
-
272
- // Now you can use global functions with onclick
273
- function handleDateSelect() {
274
- console.log('Date selected!');
275
- }
276
-
277
- document
278
- .getElementById('calendar')
279
- .addEventListener('dateSelect', handleDateSelect);
280
- </script>
281
- </body>
282
- </html>
283
- ```
284
-
285
- ## API Reference
286
-
287
- ### Props
288
-
289
- | Prop | Type | Default | Description |
290
- | -------------------- | --------------------------------------- | ------------------ | --------------------------------------- |
291
- | `events` | `CalendarEvent[]` | `[]` | Array of events to display |
292
- | `initialDate` | `Date` | `new Date()` | Initial date to display |
293
- | `minYear` | `number` | `currentYear - 30` | Minimum selectable year |
294
- | `maxYear` | `number` | `currentYear + 10` | Maximum selectable year |
295
- | `weekStartsOn` | `0 \| 1` | `0` | Week start day (0 = Sunday, 1 = Monday) |
296
- | `useShortMonthNames` | `boolean` | `false` | Use abbreviated month names (Jan, Feb) |
297
- | `categoryColors` | `CategoryColorMap` | `{}` | Custom colors for event categories |
298
- | `theme` | `CalendarTheme` | `undefined` | Custom theme colors for the calendar |
299
- | `onDateSelect` | `(date: Date) => void` | - | Callback when date is selected |
300
- | `onEventClick` | `(event: CalendarEvent) => void` | - | Callback when event is clicked |
301
- | `onMonthChange` | `(year: number, month: number) => void` | - | Callback when month changes |
302
-
303
- ### CalendarEvent Interface
304
-
305
- ```typescript
306
- interface CalendarEvent {
307
- // Required fields
308
- id: string | number;
309
- name: string;
310
- date: string | Date;
311
-
312
- // Time fields
313
- startTime?: string; // e.g., "09:00", "14:30"
314
- endTime?: string; // e.g., "10:00", "16:00"
315
- allDay?: boolean; // True for all-day events
316
-
317
- // Display & categorization
318
- description?: string;
319
- color?: string; // Custom event color
320
- category?:
321
- | 'work'
322
- | 'personal'
323
- | 'meeting'
324
- | 'deadline'
325
- | 'appointment'
326
- | 'other';
327
- location?: string; // Event location
328
- url?: string; // Related URL or meeting link
118
+ > **React 18 users:** React 18 does not forward object/array props or custom events to custom elements. You need to wire these up via a `ref`:
119
+ >
120
+ > ```jsx
121
+ > import { useRef, useEffect } from 'react';
122
+ > import 'kalendly';
123
+ >
124
+ > function Calendar({ events, onDateSelect, onMonthChange, ...attrs }) {
125
+ > const ref = useRef(null);
126
+ >
127
+ > useEffect(() => {
128
+ > if (ref.current) ref.current.events = events;
129
+ > }, [events]);
130
+ >
131
+ > useEffect(() => {
132
+ > const el = ref.current;
133
+ > if (!el) return;
134
+ > const onSelect = e => onDateSelect?.(e.detail.date, e.detail.events);
135
+ > const onChange = e => onMonthChange?.(e.detail.year, e.detail.month);
136
+ > el.addEventListener('cal-date-select', onSelect);
137
+ > el.addEventListener('cal-month-change', onChange);
138
+ > return () => {
139
+ > el.removeEventListener('cal-date-select', onSelect);
140
+ > el.removeEventListener('cal-month-change', onChange);
141
+ > };
142
+ > }, [onDateSelect, onMonthChange]);
143
+ >
144
+ > return <kal-calendar ref={ref} {...attrs} />;
145
+ > }
146
+ > ```
147
+
148
+ ### Vue 3
149
+
150
+ Vue 3 supports custom elements natively — bind props with `:` and listen to events with `@`:
329
151
 
330
- // Status & priority
331
- status?: 'scheduled' | 'completed' | 'cancelled' | 'tentative';
332
- priority?: 'low' | 'medium' | 'high';
333
-
334
- // Collaboration
335
- attendees?: string[]; // List of attendee names/emails
336
- organizer?: string; // Event organizer
152
+ ```vue
153
+ <template>
154
+ <kal-calendar
155
+ title="My Calendar"
156
+ :events="events"
157
+ @cal-date-select="onDateSelect"
158
+ @cal-month-change="onMonthChange"
159
+ />
160
+ </template>
337
161
 
338
- // Reminders & recurrence
339
- reminders?: number[]; // Minutes before event to remind (e.g., [15, 60])
340
- recurring?: {
341
- frequency: 'daily' | 'weekly' | 'monthly' | 'yearly';
342
- interval?: number; // Every X days/weeks/months/years
343
- endDate?: string | Date; // When recurrence ends
344
- daysOfWeek?: number[]; // For weekly recurrence (0 = Sunday)
345
- };
162
+ <script setup>
163
+ import 'kalendly';
164
+ import 'kalendly/styles';
346
165
 
347
- // Metadata
348
- notes?: string; // Additional notes
349
- tags?: string[]; // Event tags
350
- createdAt?: string | Date; // Creation timestamp
351
- updatedAt?: string | Date; // Last update timestamp
166
+ const events = [{ id: 1, name: 'Team Meeting', date: new Date(2025, 0, 15) }];
352
167
 
353
- // Flexibility for custom fields
354
- [key: string]: unknown;
168
+ function onDateSelect(e) {
169
+ console.log('Selected:', e.detail.date);
355
170
  }
356
- ```
357
-
358
- #### Example with Enhanced Fields
359
-
360
- ```typescript
361
- const events = [
362
- {
363
- id: 1,
364
- name: 'Team Standup',
365
- date: '2025-01-15',
366
- startTime: '09:00',
367
- endTime: '09:30',
368
- category: 'meeting',
369
- location: 'Conference Room A',
370
- url: 'https://meet.google.com/abc-defg-hij',
371
- attendees: ['john@example.com', 'jane@example.com'],
372
- organizer: 'team-lead@example.com',
373
- priority: 'high',
374
- status: 'scheduled',
375
- reminders: [15, 60],
376
- recurring: {
377
- frequency: 'daily',
378
- interval: 1,
379
- daysOfWeek: [1, 2, 3, 4, 5], // Mon-Fri
380
- endDate: '2025-12-31',
381
- },
382
- tags: ['team', 'sync'],
383
- },
384
- {
385
- id: 2,
386
- name: 'Project Deadline',
387
- date: '2025-01-20',
388
- allDay: true,
389
- category: 'deadline',
390
- priority: 'high',
391
- status: 'scheduled',
392
- description: 'Final submission for Q1 project',
393
- notes: 'Make sure all tests pass before submission',
394
- },
395
- ];
396
- ```
397
-
398
- #### Category Colors
399
-
400
- You can customize colors for different event categories:
401
-
402
- ```typescript
403
- const categoryColors = {
404
- work: '#4CAF50',
405
- personal: '#2196F3',
406
- meeting: '#FF9800',
407
- deadline: '#F44336',
408
- appointment: '#9C27B0',
409
- other: '#607D8B'
410
- };
411
171
 
412
- <Calendar events={events} categoryColors={categoryColors} />
413
- ```
414
-
415
- ## Theming
416
-
417
- Kalendly supports custom theming across all frameworks with a **consistent API**. Customize calendar colors to match your brand or create light/dark themes.
418
-
419
- ### CalendarTheme Interface
420
-
421
- ```typescript
422
- interface CalendarTheme {
423
- primary?: string; // Primary brand color
424
- secondary?: string; // Secondary brand color
425
- tertiary?: string; // Tertiary/accent color
426
- textColor?: string; // Main text color
427
- textLight?: string; // Light/secondary text color
428
- background?: string; // Background color
429
- cellHover?: string; // Cell hover state color
430
- borderColor?: string; // Border color
431
- todayOutline?: string; // Today indicator color
432
- selectedBg?: string; // Selected date background
433
- eventIndicator?: string; // Event indicator dot color
172
+ function onMonthChange(e) {
173
+ console.log('Month:', e.detail.year, e.detail.month);
434
174
  }
175
+ </script>
435
176
  ```
436
177
 
437
- ### Basic Theme Example
178
+ ### Angular
438
179
 
439
- **React:**
180
+ ```ts
181
+ // app.config.ts
182
+ import { ApplicationConfig } from '@angular/core';
183
+ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
440
184
 
441
- ```tsx
442
- import { Calendar } from 'kalendly/react';
185
+ // app.component.ts
186
+ import 'kalendly';
443
187
  import 'kalendly/styles';
444
-
445
- function App() {
446
- return (
447
- <Calendar
448
- events={events}
449
- theme={{
450
- primary: '#3b82f6',
451
- secondary: '#60a5fa',
452
- tertiary: '#93c5fd',
453
- borderColor: '#e5e7eb',
454
- todayOutline: '#fbbf24',
455
- eventIndicator: '#10b981',
456
- }}
457
- />
458
- );
459
- }
460
188
  ```
461
189
 
462
- **Vue:**
463
-
464
- ```vue
465
- <template>
466
- <Calendar :events="events" :theme="calendarTheme" />
467
- </template>
190
+ ```html
191
+ <!-- app.component.html -->
192
+ <kal-calendar
193
+ title="My Calendar"
194
+ [events]="events"
195
+ (cal-date-select)="onDateSelect($event)"
196
+ (cal-month-change)="onMonthChange($event)"
197
+ ></kal-calendar>
198
+ ```
468
199
 
469
- <script setup lang="ts">
470
- import { Calendar } from 'kalendly/vue';
471
- import 'kalendly/styles';
200
+ ### Svelte 5
472
201
 
473
- const calendarTheme = {
474
- primary: '#3b82f6',
475
- secondary: '#60a5fa',
476
- tertiary: '#93c5fd',
477
- borderColor: '#e5e7eb',
478
- todayOutline: '#fbbf24',
479
- eventIndicator: '#10b981',
480
- };
202
+ ```svelte
203
+ <script>
204
+ import 'kalendly';
205
+ import 'kalendly/styles';
206
+ let { events = [] } = $props();
481
207
  </script>
482
- ```
483
208
 
484
- **Vanilla JavaScript:**
209
+ <kal-calendar {events} oncal-date-select={e => console.log(e.detail.date)} />
210
+ ```
485
211
 
486
- ```javascript
487
- import { createCalendar } from 'kalendly/vanilla';
488
- import 'kalendly/styles';
212
+ ### Svelte 4
213
+
214
+ ```svelte
215
+ <script>
216
+ import { onMount } from 'svelte';
217
+ import 'kalendly';
218
+ import 'kalendly/styles';
219
+ export let events = [];
220
+ let calEl;
221
+ onMount(() => { calEl.events = events; });
222
+ $: if (calEl) calEl.events = events;
223
+ </script>
489
224
 
490
- const calendar = createCalendar({
491
- container: '#calendar',
492
- events: events,
493
- theme: {
494
- primary: '#3b82f6',
495
- secondary: '#60a5fa',
496
- tertiary: '#93c5fd',
497
- borderColor: '#e5e7eb',
498
- todayOutline: '#fbbf24',
499
- eventIndicator: '#10b981',
500
- },
501
- });
225
+ <kal-calendar bind:this={calEl} on:cal-date-select on:cal-month-change />
502
226
  ```
503
227
 
504
- **React Native:**
228
+ ### Solid.js
505
229
 
506
- ```tsx
507
- import { Calendar } from 'kalendly/react-native';
230
+ ```jsx
231
+ import 'kalendly';
232
+ import 'kalendly/styles';
508
233
 
509
234
  function App() {
510
235
  return (
511
- <Calendar
512
- events={events}
513
- theme={{
514
- primary: '#3b82f6',
515
- secondary: '#60a5fa',
516
- tertiary: '#93c5fd',
517
- borderColor: '#e5e7eb',
518
- todayOutline: '#fbbf24',
519
- eventIndicator: '#10b981',
520
- }}
236
+ <kal-calendar
237
+ title="My Calendar"
238
+ prop:events={events}
239
+ on:cal-date-select={e => console.log(e.detail.date)}
521
240
  />
522
241
  );
523
242
  }
524
243
  ```
525
244
 
526
- ### Dark Theme Example
527
-
528
- ```typescript
529
- const darkTheme = {
530
- primary: '#6366f1',
531
- secondary: '#818cf8',
532
- tertiary: '#a5b4fc',
533
- textColor: '#f9fafb',
534
- textLight: '#d1d5db',
535
- background: '#1f2937',
536
- cellHover: '#374151',
537
- borderColor: '#4b5563',
538
- todayOutline: '#fbbf24',
539
- selectedBg: '#312e81',
540
- eventIndicator: '#34d399'
541
- };
245
+ ## Migration from v0.1.x
542
246
 
543
- <Calendar events={events} theme={darkTheme} />
544
- ```
247
+ v0.2.0 replaces the four separate framework packages with a single web component.
545
248
 
546
- ### Dynamic Theme Switching
249
+ | Before (v0.1.x) | After (v0.2.0+) |
250
+ | --------------------------------------------------- | ---------------------------------- |
251
+ | `import { Calendar } from 'kalendly/react'` | `import 'kalendly'` |
252
+ | `import { Calendar } from 'kalendly/vue'` | `import 'kalendly'` |
253
+ | `import { Calendar } from 'kalendly/react-native'` | Not supported |
254
+ | `import { createCalendar } from 'kalendly/vanilla'` | `import 'kalendly'` |
255
+ | `<Calendar events={events} />` | `<kal-calendar events={events} />` |
547
256
 
548
- **React:**
257
+ React Native is out of scope and not replaced.
549
258
 
550
- ```tsx
551
- import { useState } from 'react';
552
- import { Calendar } from 'kalendly/react';
259
+ ## Styling
553
260
 
554
- const themes = {
555
- blue: { primary: '#3b82f6', secondary: '#60a5fa' },
556
- purple: { primary: '#8b5cf6', secondary: '#a78bfa' },
557
- green: { primary: '#10b981', secondary: '#34d399' },
558
- };
261
+ ### Loading styles
559
262
 
560
- function App() {
561
- const [currentTheme, setCurrentTheme] = useState('blue');
263
+ ```js
264
+ // Bundler (Vite, webpack) add once in your app entry (e.g. main.tsx)
265
+ import 'kalendly/styles';
562
266
 
563
- return (
564
- <>
565
- <button onClick={() => setCurrentTheme('blue')}>Blue</button>
566
- <button onClick={() => setCurrentTheme('purple')}>Purple</button>
567
- <button onClick={() => setCurrentTheme('green')}>Green</button>
267
+ // Plain HTML
268
+ // <link rel="stylesheet" href="/node_modules/kalendly/dist/styles/calendar.css">
568
269
 
569
- <Calendar events={events} theme={themes[currentTheme]} />
570
- </>
571
- );
572
- }
270
+ // Angular add to angular.json → projects → architect → build → styles
271
+ // "node_modules/kalendly/dist/styles/calendar.css"
573
272
  ```
574
273
 
575
- **Vue:**
274
+ ### Overriding styles
576
275
 
577
- ```vue
578
- <template>
579
- <div>
580
- <button @click="currentTheme = 'blue'">Blue</button>
581
- <button @click="currentTheme = 'purple'">Purple</button>
582
- <button @click="currentTheme = 'green'">Green</button>
276
+ kalendly uses Light DOM — all standard CSS techniques work:
583
277
 
584
- <Calendar :events="events" :theme="themes[currentTheme]" />
585
- </div>
586
- </template>
587
-
588
- <script setup lang="ts">
589
- import { ref } from 'vue';
590
- import { Calendar } from 'kalendly/vue';
591
-
592
- const themes = {
593
- blue: { primary: '#3b82f6', secondary: '#60a5fa' },
594
- purple: { primary: '#8b5cf6', secondary: '#a78bfa' },
595
- green: { primary: '#10b981', secondary: '#34d399' },
596
- };
278
+ ```css
279
+ /* 1. CSS custom properties (recommended) */
280
+ :root {
281
+ --calendar-primary-color: #6366f1;
282
+ --calendar-background: #1e1e2e;
283
+ --calendar-border-color: #334155;
284
+ }
597
285
 
598
- const currentTheme = ref('blue');
599
- </script>
286
+ /* 2. Direct class overrides */
287
+ .kalendly-calendar .calendar--card {
288
+ border-radius: 12px;
289
+ }
600
290
  ```
601
291
 
602
- **Vanilla JavaScript:**
603
-
604
- ```javascript
605
- import { createCalendar } from 'kalendly/vanilla';
606
-
607
- const themes = {
608
- blue: { primary: '#3b82f6', secondary: '#60a5fa' },
609
- purple: { primary: '#8b5cf6', secondary: '#a78bfa' },
610
- green: { primary: '#10b981', secondary: '#34d399' },
292
+ ```js
293
+ // 3. JS theme property
294
+ document.querySelector('kal-calendar').theme = {
295
+ primary: '#6366f1',
296
+ background: '#1e1e2e',
611
297
  };
612
-
613
- const calendar = createCalendar({
614
- container: '#calendar',
615
- events: events,
616
- theme: themes.blue,
617
- });
618
-
619
- // Update theme dynamically without recreation
620
- document.getElementById('purple-btn').addEventListener('click', () => {
621
- calendar.updateTheme(themes.purple);
622
- });
623
298
  ```
624
299
 
625
- > **Note:** The `updateTheme()` method is available in Vanilla JavaScript for efficient theme updates without recreating the calendar instance.
626
-
627
- ## Framework-Specific Features
628
-
629
- ### React Props
630
-
631
- | Prop | Type | Description |
632
- | ---------------- | ------------------------------------- | ------------------------ |
633
- | `className` | `string` | CSS class name |
634
- | `style` | `React.CSSProperties` | Inline styles |
635
- | `renderEvent` | `(event: CalendarEvent) => ReactNode` | Custom event renderer |
636
- | `renderNoEvents` | `() => ReactNode` | Custom no events message |
300
+ ## Attributes
637
301
 
638
- ### Vue Props & Slots
302
+ Primitives are set as HTML attributes:
639
303
 
640
- | Slot | Props | Description |
641
- | ----------- | -------------------------- | ------------------------ |
642
- | `title` | - | Custom title content |
643
- | `event` | `{ event: CalendarEvent }` | Custom event display |
644
- | `no-events` | - | Custom no events message |
304
+ | Attribute | Type | Default | Description |
305
+ | ----------------------- | ---------- | ---------------- | ------------------------------------- |
306
+ | `title` | `string` | — | Calendar title |
307
+ | `initial-date` | `string` | today | ISO date string for initial view |
308
+ | `min-year` | `string` | currentYear - 30 | Minimum year in picker |
309
+ | `max-year` | `string` | currentYear + 10 | Maximum year in picker |
310
+ | `week-starts-on` | `"0"\|"1"` | `"0"` | Week start: 0 = Sunday, 1 = Monday |
311
+ | `use-short-month-names` | `string` | — | Present = use abbreviated month names |
645
312
 
646
- ### React Native Props
313
+ ## Properties
647
314
 
648
- | Prop | Type | Description |
649
- | ----------------- | ----------- | -------------------------- |
650
- | `style` | `ViewStyle` | Container style |
651
- | `headerStyle` | `ViewStyle` | Header style |
652
- | `cellStyle` | `ViewStyle` | Calendar cell style |
653
- | `showCloseButton` | `boolean` | Show close button in popup |
315
+ Rich objects are set as JS properties (not attributes):
654
316
 
655
- ### Vanilla JavaScript Options
317
+ | Property | Type | Description |
318
+ | ---------------- | ---------------------------------- | -------------------------------- |
319
+ | `events` | `CalendarEvent[]` | Events to display |
320
+ | `theme` | `CalendarTheme` | Custom theme colors |
321
+ | `categoryColors` | `CategoryColorMap` | Per-category color overrides |
322
+ | `renderEvent` | `(event: CalendarEvent) => string` | Custom event HTML renderer |
323
+ | `renderNoEvents` | `() => string` | Custom empty-state HTML renderer |
656
324
 
657
- | Option | Type | Description |
658
- | ---------------- | ---------------------------------- | ----------------------------- |
659
- | `container` | `HTMLElement \| string` | Container element or selector |
660
- | `className` | `string` | Additional CSS class |
661
- | `renderEvent` | `(event: CalendarEvent) => string` | Custom event HTML renderer |
662
- | `renderNoEvents` | `() => string` | Custom no events HTML |
325
+ ## Custom Events
663
326
 
664
- ### Vanilla JavaScript Events
327
+ | Event | `detail` shape | Description |
328
+ | ------------------ | ----------------------------------------- | ------------------------- |
329
+ | `cal-date-select` | `{ date: Date, events: CalendarEvent[] }` | User clicked a date |
330
+ | `cal-month-change` | `{ year: number, month: number }` | Month navigation occurred |
665
331
 
666
- ```javascript
667
- // Listen to calendar events
668
- calendar.container.addEventListener('dateSelect', e => {
669
- console.log('Date selected:', e.detail.date);
670
- });
671
-
672
- calendar.container.addEventListener('monthChange', e => {
673
- console.log('Month changed:', e.detail.year, e.detail.month);
674
- });
675
- ```
332
+ Both events bubble and are composed (cross Shadow DOM boundaries).
676
333
 
677
- ### Vanilla JavaScript API
334
+ ## JavaScript API
678
335
 
679
- ```javascript
680
- const calendar = createCalendar(options);
336
+ ```js
337
+ const cal = document.querySelector('kal-calendar');
681
338
 
682
- // Methods
683
- calendar.updateEvents(newEvents); // Update events
684
- calendar.getCurrentDate(); // Get selected date
685
- calendar.goToDate(new Date()); // Navigate to date
686
- calendar.getEngine(); // Access core engine
687
- calendar.destroy(); // Cleanup
339
+ cal.updateEvents(newEvents); // Re-render with new events
340
+ cal.updateTheme(newTheme); // Apply new theme
341
+ cal.goToDate(new Date(2025, 5, 1)); // Navigate to date
342
+ cal.getCurrentDate(); // Returns currently selected Date
343
+ cal.getEngine(); // Access CalendarEngine directly
688
344
  ```
689
345
 
690
- ## Common Issues & Solutions
691
-
692
- ### Vanilla JavaScript
693
-
694
- **Issue: "Function is not defined" errors with onclick handlers**
695
-
696
- ❌ **Wrong:**
346
+ ## CalendarEvent Interface
697
347
 
698
- ```html
699
- <button onclick="myFunction()">Click me</button>
700
- <script type="module">
701
- function myFunction() {} // Not accessible globally in modules
702
- </script>
703
- ```
704
-
705
- ✅ **Correct:**
706
-
707
- ```html
708
- <button id="myButton">Click me</button>
709
- <script type="module">
710
- function myFunction() {}
711
- document.getElementById('myButton').addEventListener('click', myFunction);
712
- </script>
713
- ```
714
-
715
- **Issue: Module imports not working**
348
+ ```typescript
349
+ interface CalendarEvent {
350
+ id: string | number;
351
+ name: string;
352
+ date: string | Date;
716
353
 
717
- Use the correct import paths:
354
+ startTime?: string; // e.g. "09:00"
355
+ endTime?: string; // e.g. "10:00"
356
+ allDay?: boolean;
718
357
 
719
- - Local: `'./dist/vanilla/index.mjs'`
720
- - NPM: `'kalendly/vanilla'`
721
- - CDN: `'https://unpkg.com/kalendly/dist/vanilla/index.mjs'`
358
+ description?: string;
359
+ color?: string;
360
+ category?:
361
+ | 'work'
362
+ | 'personal'
363
+ | 'meeting'
364
+ | 'deadline'
365
+ | 'appointment'
366
+ | 'other';
367
+ location?: string;
368
+ url?: string;
722
369
 
723
- **Issue: Styles not loading**
370
+ status?: 'scheduled' | 'completed' | 'cancelled' | 'tentative';
371
+ priority?: 'low' | 'medium' | 'high';
724
372
 
725
- Always include the CSS file:
373
+ attendees?: string[];
374
+ organizer?: string;
375
+ reminders?: number[]; // minutes before event
376
+ recurring?: {
377
+ frequency: 'daily' | 'weekly' | 'monthly' | 'yearly';
378
+ interval?: number;
379
+ endDate?: string | Date;
380
+ daysOfWeek?: number[];
381
+ };
726
382
 
727
- ```html
728
- <link rel="stylesheet" href="path/to/kalendly/dist/styles/calendar.css" />
383
+ notes?: string;
384
+ tags?: string[];
385
+ [key: string]: unknown;
386
+ }
729
387
  ```
730
388
 
731
- ## Customization
389
+ ## Theming
732
390
 
733
- ### CSS Variables (React/Vue)
391
+ ### All CSS variables
734
392
 
735
393
  ```css
736
394
  :root {
737
395
  --calendar-primary-color: #fc8917;
738
- --calendar-primary-color-rgb: 252, 137, 23;
739
396
  --calendar-secondary-color: #fca045;
740
- --calendar-secondary-color-rgb: 252, 160, 69;
741
397
  --calendar-tertiary-color: #fdb873;
742
398
  --calendar-text-color: #2c3e50;
399
+ --calendar-text-light: #6b7280;
743
400
  --calendar-border-color: #dee2e6;
744
401
  --calendar-today-outline: #f7db04;
745
402
  --calendar-event-indicator: #1890ff;
746
403
  --calendar-background: #fff;
404
+ --calendar-cell-hover: #f3f4f6;
405
+ --calendar-selected-bg: #eff6ff;
747
406
  }
748
407
  ```
749
408
 
750
- > **Note:** The `-rgb` variables are used for semi-transparent backgrounds (e.g., hover states) and provide compatibility with older browsers (Chrome < 111, Safari iOS < 16.2).
409
+ ### JS theme property (full reference)
751
410
 
752
- ### React Native Theming
411
+ ```js
412
+ cal.theme = {
413
+ primary: '#3b82f6',
414
+ secondary: '#60a5fa',
415
+ tertiary: '#93c5fd',
416
+ textColor: '#111827',
417
+ textLight: '#6b7280',
418
+ background: '#ffffff',
419
+ cellHover: '#f3f4f6',
420
+ borderColor: '#e5e7eb',
421
+ todayOutline: '#fbbf24',
422
+ selectedBg: '#eff6ff',
423
+ eventIndicator: '#10b981',
424
+ };
425
+ ```
753
426
 
754
- ```javascript
755
- import { calendarStyles } from 'kalendly/react-native';
427
+ ### Dark theme example
756
428
 
757
- // Customize styles
758
- const customStyles = {
759
- ...calendarStyles,
760
- container: {
761
- ...calendarStyles.container,
762
- backgroundColor: '#f5f5f5',
763
- },
429
+ ```js
430
+ cal.theme = {
431
+ primary: '#6366f1',
432
+ secondary: '#818cf8',
433
+ textColor: '#f9fafb',
434
+ textLight: '#d1d5db',
435
+ background: '#1f2937',
436
+ cellHover: '#374151',
437
+ borderColor: '#4b5563',
438
+ todayOutline: '#fbbf24',
439
+ selectedBg: '#312e81',
440
+ eventIndicator: '#34d399',
764
441
  };
765
442
  ```
766
443
 
767
- ## Core API
768
-
769
- You can also use the core calendar engine directly:
444
+ ## Core Engine (advanced)
770
445
 
771
446
  ```typescript
772
447
  import { CalendarEngine } from 'kalendly/core';
773
448
 
774
- const engine = new CalendarEngine({
775
- events: myEvents,
776
- initialDate: new Date(),
777
- });
449
+ const engine = new CalendarEngine({ events, initialDate: new Date() });
778
450
 
779
- // Subscribe to changes
780
451
  const unsubscribe = engine.subscribe(() => {
781
- console.log('Calendar state changed');
452
+ const viewModel = engine.getViewModel();
453
+ // re-render
782
454
  });
783
455
 
784
- // Get current state
785
- const viewModel = engine.getViewModel();
786
- const actions = engine.getActions();
787
-
788
- // Navigate months
789
- actions.next();
790
- actions.previous();
791
- actions.jump(2025, 5); // June 2025
792
- actions.goToToday(); // Navigate to current month
456
+ engine.getActions().next();
457
+ engine.getActions().previous();
458
+ engine.getActions().jump(2025, 5);
459
+ engine.getActions().goToToday();
793
460
 
794
- // Check current view
795
- const isCurrentMonth = actions.isCurrentMonth(); // true if viewing today's month
796
-
797
- // Clean up
798
461
  unsubscribe();
799
462
  engine.destroy();
800
463
  ```
801
464
 
802
- ## TypeScript Support
803
-
804
- The package is written in TypeScript and provides full type definitions:
805
-
806
- ```typescript
807
- import type { CalendarEvent, CalendarProps, CalendarState } from 'kalendly';
808
- ```
809
-
810
465
  ## Browser Support
811
466
 
812
- - **React/Vue**: Modern browsers (Chrome 90+, Firefox 88+, Safari 14+)
813
- - **React Native**: iOS 12+, Android API 21+
467
+ Custom Elements v1 Chrome 67+, Firefox 63+, Safari 12.1+, Edge 79+.
814
468
 
815
469
  ## Contributing
816
470
 
817
- We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for detailed information on:
818
-
819
- - Development setup
820
- - Project structure
821
- - Building and testing
822
- - Running examples
823
- - Submitting pull requests
824
- - Release process
825
-
826
- Quick start:
471
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
827
472
 
828
473
  ```bash
829
- # Clone and install
830
474
  git clone https://github.com/callezenwaka/kalendly.git
831
475
  cd kalendly
832
476
  npm install
833
-
834
- # Run tests
835
477
  npm test
836
-
837
- # Run examples locally
838
478
  npm run dev:examples
839
479
  ```
840
480
 
@@ -844,14 +484,12 @@ MIT © Callis Ezenwaka
844
484
 
845
485
  ## Changelog
846
486
 
847
- See [CHANGELOG.md](CHANGELOG.md) for detailed release notes and version history.
487
+ See [CHANGELOG.md](CHANGELOG.md).
848
488
 
849
489
  ### Recent Updates
850
490
 
851
- - **v0.1.6** (Upcoming): Navigation enhancements with Today button, month/year picker dropdown, calendar grid improvements showing previous/next month days, and browser compatibility fixes
852
- - **v0.1.5**: Universal theming system, TypeScript support improvements, integration test enhancements
853
- - **v0.1.4**: Netlify configuration updates
854
- - **v0.1.3**: Vue types generation improvements
855
- - **v0.1.2**: Enhanced event parameters with structured metadata, categories, recurrence, collaboration features
856
- - **v0.1.1**: Pre-commit hooks and trusted publishing with OIDC
491
+ - **v0.2.0**: Migrated to a single `<kal-calendar>` web component works natively in React, Vue, Angular, and plain HTML with no framework dependency
492
+ - **v0.1.7**: Vanilla calendar performance optimization with event delegation
493
+ - **v0.1.6**: Navigation enhancements — Today button, month/year picker, optional `title` prop
494
+ - **v0.1.5**: Universal theming system, TypeScript improvements
857
495
  - **v0.1.0**: Initial release with React, Vue, React Native, and Vanilla JavaScript support