kalendly 0.1.7 → 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 +317 -681
  2. package/dist/core/index.js +3 -3
  3. package/dist/index.d.mts +48 -602
  4. package/dist/index.d.ts +48 -602
  5. package/dist/index.js +753 -2359
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +727 -2348
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/{vanilla/index.umd.js → index.umd.js} +218 -218
  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 -271
  25. package/dist/vanilla/index.d.ts +0 -271
  26. package/dist/vanilla/index.js +0 -1064
  27. package/dist/vanilla/index.js.map +0 -1
  28. package/dist/vanilla/index.mjs +0 -1013
  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
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,809 +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
- | `title` | `string` | `undefined` | Optional calendar title displayed above |
300
- | `onDateSelect` | `(date: Date) => void` | - | Callback when date is selected |
301
- | `onEventClick` | `(event: CalendarEvent) => void` | - | Callback when event is clicked |
302
- | `onMonthChange` | `(year: number, month: number) => void` | - | Callback when month changes |
303
-
304
- ### CalendarEvent Interface
305
-
306
- ```typescript
307
- interface CalendarEvent {
308
- // Required fields
309
- id: string | number;
310
- name: string;
311
- date: string | Date;
312
-
313
- // Time fields
314
- startTime?: string; // e.g., "09:00", "14:30"
315
- endTime?: string; // e.g., "10:00", "16:00"
316
- allDay?: boolean; // True for all-day events
317
-
318
- // Display & categorization
319
- description?: string;
320
- color?: string; // Custom event color
321
- category?:
322
- | 'work'
323
- | 'personal'
324
- | 'meeting'
325
- | 'deadline'
326
- | 'appointment'
327
- | 'other';
328
- location?: string; // Event location
329
- 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 `@`:
330
151
 
331
- // Status & priority
332
- status?: 'scheduled' | 'completed' | 'cancelled' | 'tentative';
333
- priority?: 'low' | 'medium' | 'high';
334
-
335
- // Collaboration
336
- attendees?: string[]; // List of attendee names/emails
337
- 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>
338
161
 
339
- // Reminders & recurrence
340
- reminders?: number[]; // Minutes before event to remind (e.g., [15, 60])
341
- recurring?: {
342
- frequency: 'daily' | 'weekly' | 'monthly' | 'yearly';
343
- interval?: number; // Every X days/weeks/months/years
344
- endDate?: string | Date; // When recurrence ends
345
- daysOfWeek?: number[]; // For weekly recurrence (0 = Sunday)
346
- };
162
+ <script setup>
163
+ import 'kalendly';
164
+ import 'kalendly/styles';
347
165
 
348
- // Metadata
349
- notes?: string; // Additional notes
350
- tags?: string[]; // Event tags
351
- createdAt?: string | Date; // Creation timestamp
352
- updatedAt?: string | Date; // Last update timestamp
166
+ const events = [{ id: 1, name: 'Team Meeting', date: new Date(2025, 0, 15) }];
353
167
 
354
- // Flexibility for custom fields
355
- [key: string]: unknown;
168
+ function onDateSelect(e) {
169
+ console.log('Selected:', e.detail.date);
356
170
  }
357
- ```
358
-
359
- #### Example with Enhanced Fields
360
-
361
- ```typescript
362
- const events = [
363
- {
364
- id: 1,
365
- name: 'Team Standup',
366
- date: '2025-01-15',
367
- startTime: '09:00',
368
- endTime: '09:30',
369
- category: 'meeting',
370
- location: 'Conference Room A',
371
- url: 'https://meet.google.com/abc-defg-hij',
372
- attendees: ['john@example.com', 'jane@example.com'],
373
- organizer: 'team-lead@example.com',
374
- priority: 'high',
375
- status: 'scheduled',
376
- reminders: [15, 60],
377
- recurring: {
378
- frequency: 'daily',
379
- interval: 1,
380
- daysOfWeek: [1, 2, 3, 4, 5], // Mon-Fri
381
- endDate: '2025-12-31',
382
- },
383
- tags: ['team', 'sync'],
384
- },
385
- {
386
- id: 2,
387
- name: 'Project Deadline',
388
- date: '2025-01-20',
389
- allDay: true,
390
- category: 'deadline',
391
- priority: 'high',
392
- status: 'scheduled',
393
- description: 'Final submission for Q1 project',
394
- notes: 'Make sure all tests pass before submission',
395
- },
396
- ];
397
- ```
398
-
399
- #### Category Colors
400
-
401
- You can customize colors for different event categories:
402
-
403
- ```typescript
404
- const categoryColors = {
405
- work: '#4CAF50',
406
- personal: '#2196F3',
407
- meeting: '#FF9800',
408
- deadline: '#F44336',
409
- appointment: '#9C27B0',
410
- other: '#607D8B'
411
- };
412
171
 
413
- <Calendar events={events} categoryColors={categoryColors} />
414
- ```
415
-
416
- ## Theming
417
-
418
- Kalendly supports custom theming across all frameworks with a **consistent API**. Customize calendar colors to match your brand or create light/dark themes.
419
-
420
- ### CalendarTheme Interface
421
-
422
- ```typescript
423
- interface CalendarTheme {
424
- primary?: string; // Primary brand color
425
- secondary?: string; // Secondary brand color
426
- tertiary?: string; // Tertiary/accent color
427
- textColor?: string; // Main text color
428
- textLight?: string; // Light/secondary text color
429
- background?: string; // Background color
430
- cellHover?: string; // Cell hover state color
431
- borderColor?: string; // Border color
432
- todayOutline?: string; // Today indicator color
433
- selectedBg?: string; // Selected date background
434
- eventIndicator?: string; // Event indicator dot color
172
+ function onMonthChange(e) {
173
+ console.log('Month:', e.detail.year, e.detail.month);
435
174
  }
175
+ </script>
436
176
  ```
437
177
 
438
- ### Basic Theme Example
178
+ ### Angular
439
179
 
440
- **React:**
180
+ ```ts
181
+ // app.config.ts
182
+ import { ApplicationConfig } from '@angular/core';
183
+ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
441
184
 
442
- ```tsx
443
- import { Calendar } from 'kalendly/react';
185
+ // app.component.ts
186
+ import 'kalendly';
444
187
  import 'kalendly/styles';
445
-
446
- function App() {
447
- return (
448
- <Calendar
449
- events={events}
450
- theme={{
451
- primary: '#3b82f6',
452
- secondary: '#60a5fa',
453
- tertiary: '#93c5fd',
454
- borderColor: '#e5e7eb',
455
- todayOutline: '#fbbf24',
456
- eventIndicator: '#10b981',
457
- }}
458
- />
459
- );
460
- }
461
188
  ```
462
189
 
463
- **Vue:**
464
-
465
- ```vue
466
- <template>
467
- <Calendar :events="events" :theme="calendarTheme" />
468
- </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
+ ```
469
199
 
470
- <script setup lang="ts">
471
- import { Calendar } from 'kalendly/vue';
472
- import 'kalendly/styles';
200
+ ### Svelte 5
473
201
 
474
- const calendarTheme = {
475
- primary: '#3b82f6',
476
- secondary: '#60a5fa',
477
- tertiary: '#93c5fd',
478
- borderColor: '#e5e7eb',
479
- todayOutline: '#fbbf24',
480
- eventIndicator: '#10b981',
481
- };
202
+ ```svelte
203
+ <script>
204
+ import 'kalendly';
205
+ import 'kalendly/styles';
206
+ let { events = [] } = $props();
482
207
  </script>
483
- ```
484
208
 
485
- **Vanilla JavaScript:**
209
+ <kal-calendar {events} oncal-date-select={e => console.log(e.detail.date)} />
210
+ ```
486
211
 
487
- ```javascript
488
- import { createCalendar } from 'kalendly/vanilla';
489
- 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>
490
224
 
491
- const calendar = createCalendar({
492
- container: '#calendar',
493
- events: events,
494
- theme: {
495
- primary: '#3b82f6',
496
- secondary: '#60a5fa',
497
- tertiary: '#93c5fd',
498
- borderColor: '#e5e7eb',
499
- todayOutline: '#fbbf24',
500
- eventIndicator: '#10b981',
501
- },
502
- });
225
+ <kal-calendar bind:this={calEl} on:cal-date-select on:cal-month-change />
503
226
  ```
504
227
 
505
- **React Native:**
228
+ ### Solid.js
506
229
 
507
- ```tsx
508
- import { Calendar } from 'kalendly/react-native';
230
+ ```jsx
231
+ import 'kalendly';
232
+ import 'kalendly/styles';
509
233
 
510
234
  function App() {
511
235
  return (
512
- <Calendar
513
- events={events}
514
- theme={{
515
- primary: '#3b82f6',
516
- secondary: '#60a5fa',
517
- tertiary: '#93c5fd',
518
- borderColor: '#e5e7eb',
519
- todayOutline: '#fbbf24',
520
- eventIndicator: '#10b981',
521
- }}
236
+ <kal-calendar
237
+ title="My Calendar"
238
+ prop:events={events}
239
+ on:cal-date-select={e => console.log(e.detail.date)}
522
240
  />
523
241
  );
524
242
  }
525
243
  ```
526
244
 
527
- ### Dark Theme Example
528
-
529
- ```typescript
530
- const darkTheme = {
531
- primary: '#6366f1',
532
- secondary: '#818cf8',
533
- tertiary: '#a5b4fc',
534
- textColor: '#f9fafb',
535
- textLight: '#d1d5db',
536
- background: '#1f2937',
537
- cellHover: '#374151',
538
- borderColor: '#4b5563',
539
- todayOutline: '#fbbf24',
540
- selectedBg: '#312e81',
541
- eventIndicator: '#34d399'
542
- };
245
+ ## Migration from v0.1.x
543
246
 
544
- <Calendar events={events} theme={darkTheme} />
545
- ```
247
+ v0.2.0 replaces the four separate framework packages with a single web component.
546
248
 
547
- ### 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} />` |
548
256
 
549
- **React:**
257
+ React Native is out of scope and not replaced.
550
258
 
551
- ```tsx
552
- import { useState } from 'react';
553
- import { Calendar } from 'kalendly/react';
259
+ ## Styling
554
260
 
555
- const themes = {
556
- blue: { primary: '#3b82f6', secondary: '#60a5fa' },
557
- purple: { primary: '#8b5cf6', secondary: '#a78bfa' },
558
- green: { primary: '#10b981', secondary: '#34d399' },
559
- };
261
+ ### Loading styles
560
262
 
561
- function App() {
562
- 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';
563
266
 
564
- return (
565
- <>
566
- <button onClick={() => setCurrentTheme('blue')}>Blue</button>
567
- <button onClick={() => setCurrentTheme('purple')}>Purple</button>
568
- <button onClick={() => setCurrentTheme('green')}>Green</button>
267
+ // Plain HTML
268
+ // <link rel="stylesheet" href="/node_modules/kalendly/dist/styles/calendar.css">
569
269
 
570
- <Calendar events={events} theme={themes[currentTheme]} />
571
- </>
572
- );
573
- }
270
+ // Angular add to angular.json → projects → architect → build → styles
271
+ // "node_modules/kalendly/dist/styles/calendar.css"
574
272
  ```
575
273
 
576
- **Vue:**
274
+ ### Overriding styles
577
275
 
578
- ```vue
579
- <template>
580
- <div>
581
- <button @click="currentTheme = 'blue'">Blue</button>
582
- <button @click="currentTheme = 'purple'">Purple</button>
583
- <button @click="currentTheme = 'green'">Green</button>
276
+ kalendly uses Light DOM — all standard CSS techniques work:
584
277
 
585
- <Calendar :events="events" :theme="themes[currentTheme]" />
586
- </div>
587
- </template>
588
-
589
- <script setup lang="ts">
590
- import { ref } from 'vue';
591
- import { Calendar } from 'kalendly/vue';
592
-
593
- const themes = {
594
- blue: { primary: '#3b82f6', secondary: '#60a5fa' },
595
- purple: { primary: '#8b5cf6', secondary: '#a78bfa' },
596
- green: { primary: '#10b981', secondary: '#34d399' },
597
- };
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
+ }
598
285
 
599
- const currentTheme = ref('blue');
600
- </script>
286
+ /* 2. Direct class overrides */
287
+ .kalendly-calendar .calendar--card {
288
+ border-radius: 12px;
289
+ }
601
290
  ```
602
291
 
603
- **Vanilla JavaScript:**
604
-
605
- ```javascript
606
- import { createCalendar } from 'kalendly/vanilla';
607
-
608
- const themes = {
609
- blue: { primary: '#3b82f6', secondary: '#60a5fa' },
610
- purple: { primary: '#8b5cf6', secondary: '#a78bfa' },
611
- green: { primary: '#10b981', secondary: '#34d399' },
292
+ ```js
293
+ // 3. JS theme property
294
+ document.querySelector('kal-calendar').theme = {
295
+ primary: '#6366f1',
296
+ background: '#1e1e2e',
612
297
  };
613
-
614
- const calendar = createCalendar({
615
- container: '#calendar',
616
- events: events,
617
- theme: themes.blue,
618
- });
619
-
620
- // Update theme dynamically without recreation
621
- document.getElementById('purple-btn').addEventListener('click', () => {
622
- calendar.updateTheme(themes.purple);
623
- });
624
298
  ```
625
299
 
626
- > **Note:** The `updateTheme()` method is available in Vanilla JavaScript for efficient theme updates without recreating the calendar instance.
627
-
628
- ## Framework-Specific Features
629
-
630
- ### React Props
631
-
632
- | Prop | Type | Description |
633
- | ---------------- | ------------------------------------- | ------------------------ |
634
- | `className` | `string` | CSS class name |
635
- | `style` | `React.CSSProperties` | Inline styles |
636
- | `renderEvent` | `(event: CalendarEvent) => ReactNode` | Custom event renderer |
637
- | `renderNoEvents` | `() => ReactNode` | Custom no events message |
300
+ ## Attributes
638
301
 
639
- ### Vue Props & Slots
302
+ Primitives are set as HTML attributes:
640
303
 
641
- | Slot | Props | Description |
642
- | ----------- | -------------------------- | ------------------------ |
643
- | `title` | - | Custom title content |
644
- | `event` | `{ event: CalendarEvent }` | Custom event display |
645
- | `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 |
646
312
 
647
- ### React Native Props
313
+ ## Properties
648
314
 
649
- | Prop | Type | Description |
650
- | ----------------- | ----------- | -------------------------- |
651
- | `style` | `ViewStyle` | Container style |
652
- | `headerStyle` | `ViewStyle` | Header style |
653
- | `cellStyle` | `ViewStyle` | Calendar cell style |
654
- | `showCloseButton` | `boolean` | Show close button in popup |
315
+ Rich objects are set as JS properties (not attributes):
655
316
 
656
- ### 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 |
657
324
 
658
- | Option | Type | Description |
659
- | ---------------- | ---------------------------------- | ----------------------------- |
660
- | `container` | `HTMLElement \| string` | Container element or selector |
661
- | `className` | `string` | Additional CSS class |
662
- | `renderEvent` | `(event: CalendarEvent) => string` | Custom event HTML renderer |
663
- | `renderNoEvents` | `() => string` | Custom no events HTML |
325
+ ## Custom Events
664
326
 
665
- ### 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 |
666
331
 
667
- ```javascript
668
- // Listen to calendar events
669
- calendar.container.addEventListener('dateSelect', e => {
670
- console.log('Date selected:', e.detail.date);
671
- });
672
-
673
- calendar.container.addEventListener('monthChange', e => {
674
- console.log('Month changed:', e.detail.year, e.detail.month);
675
- });
676
- ```
332
+ Both events bubble and are composed (cross Shadow DOM boundaries).
677
333
 
678
- ### Vanilla JavaScript API
334
+ ## JavaScript API
679
335
 
680
- ```javascript
681
- const calendar = createCalendar(options);
336
+ ```js
337
+ const cal = document.querySelector('kal-calendar');
682
338
 
683
- // Methods
684
- calendar.updateEvents(newEvents); // Update events
685
- calendar.getCurrentDate(); // Get selected date
686
- calendar.goToDate(new Date()); // Navigate to date
687
- calendar.getEngine(); // Access core engine
688
- 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
689
344
  ```
690
345
 
691
- ## Common Issues & Solutions
692
-
693
- ### Vanilla JavaScript
694
-
695
- **Issue: "Function is not defined" errors with onclick handlers**
696
-
697
- ❌ **Wrong:**
346
+ ## CalendarEvent Interface
698
347
 
699
- ```html
700
- <button onclick="myFunction()">Click me</button>
701
- <script type="module">
702
- function myFunction() {} // Not accessible globally in modules
703
- </script>
704
- ```
705
-
706
- ✅ **Correct:**
707
-
708
- ```html
709
- <button id="myButton">Click me</button>
710
- <script type="module">
711
- function myFunction() {}
712
- document.getElementById('myButton').addEventListener('click', myFunction);
713
- </script>
714
- ```
715
-
716
- **Issue: Module imports not working**
348
+ ```typescript
349
+ interface CalendarEvent {
350
+ id: string | number;
351
+ name: string;
352
+ date: string | Date;
717
353
 
718
- Use the correct import paths:
354
+ startTime?: string; // e.g. "09:00"
355
+ endTime?: string; // e.g. "10:00"
356
+ allDay?: boolean;
719
357
 
720
- - Local: `'./dist/vanilla/index.mjs'`
721
- - NPM: `'kalendly/vanilla'`
722
- - 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;
723
369
 
724
- **Issue: Styles not loading**
370
+ status?: 'scheduled' | 'completed' | 'cancelled' | 'tentative';
371
+ priority?: 'low' | 'medium' | 'high';
725
372
 
726
- 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
+ };
727
382
 
728
- ```html
729
- <link rel="stylesheet" href="path/to/kalendly/dist/styles/calendar.css" />
383
+ notes?: string;
384
+ tags?: string[];
385
+ [key: string]: unknown;
386
+ }
730
387
  ```
731
388
 
732
- ## Customization
389
+ ## Theming
733
390
 
734
- ### CSS Variables (React/Vue)
391
+ ### All CSS variables
735
392
 
736
393
  ```css
737
394
  :root {
738
395
  --calendar-primary-color: #fc8917;
739
- --calendar-primary-color-rgb: 252, 137, 23;
740
396
  --calendar-secondary-color: #fca045;
741
- --calendar-secondary-color-rgb: 252, 160, 69;
742
397
  --calendar-tertiary-color: #fdb873;
743
398
  --calendar-text-color: #2c3e50;
399
+ --calendar-text-light: #6b7280;
744
400
  --calendar-border-color: #dee2e6;
745
401
  --calendar-today-outline: #f7db04;
746
402
  --calendar-event-indicator: #1890ff;
747
403
  --calendar-background: #fff;
404
+ --calendar-cell-hover: #f3f4f6;
405
+ --calendar-selected-bg: #eff6ff;
748
406
  }
749
407
  ```
750
408
 
751
- > **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)
752
410
 
753
- ### 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
+ ```
754
426
 
755
- ```javascript
756
- import { calendarStyles } from 'kalendly/react-native';
427
+ ### Dark theme example
757
428
 
758
- // Customize styles
759
- const customStyles = {
760
- ...calendarStyles,
761
- container: {
762
- ...calendarStyles.container,
763
- backgroundColor: '#f5f5f5',
764
- },
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',
765
441
  };
766
442
  ```
767
443
 
768
- ## Core API
769
-
770
- You can also use the core calendar engine directly:
444
+ ## Core Engine (advanced)
771
445
 
772
446
  ```typescript
773
447
  import { CalendarEngine } from 'kalendly/core';
774
448
 
775
- const engine = new CalendarEngine({
776
- events: myEvents,
777
- initialDate: new Date(),
778
- });
449
+ const engine = new CalendarEngine({ events, initialDate: new Date() });
779
450
 
780
- // Subscribe to changes
781
451
  const unsubscribe = engine.subscribe(() => {
782
- console.log('Calendar state changed');
452
+ const viewModel = engine.getViewModel();
453
+ // re-render
783
454
  });
784
455
 
785
- // Get current state
786
- const viewModel = engine.getViewModel();
787
- const actions = engine.getActions();
788
-
789
- // Navigate months
790
- actions.next();
791
- actions.previous();
792
- actions.jump(2025, 5); // June 2025
793
- actions.goToToday(); // Navigate to current month
456
+ engine.getActions().next();
457
+ engine.getActions().previous();
458
+ engine.getActions().jump(2025, 5);
459
+ engine.getActions().goToToday();
794
460
 
795
- // Check current view
796
- const isCurrentMonth = actions.isCurrentMonth(); // true if viewing today's month
797
-
798
- // Clean up
799
461
  unsubscribe();
800
462
  engine.destroy();
801
463
  ```
802
464
 
803
- ## TypeScript Support
804
-
805
- The package is written in TypeScript and provides full type definitions:
806
-
807
- ```typescript
808
- import type { CalendarEvent, CalendarProps, CalendarState } from 'kalendly';
809
- ```
810
-
811
465
  ## Browser Support
812
466
 
813
- - **React/Vue**: Modern browsers (Chrome 90+, Firefox 88+, Safari 14+)
814
- - **React Native**: iOS 12+, Android API 21+
467
+ Custom Elements v1 Chrome 67+, Firefox 63+, Safari 12.1+, Edge 79+.
815
468
 
816
469
  ## Contributing
817
470
 
818
- We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for detailed information on:
819
-
820
- - Development setup
821
- - Project structure
822
- - Building and testing
823
- - Running examples
824
- - Submitting pull requests
825
- - Release process
826
-
827
- Quick start:
471
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
828
472
 
829
473
  ```bash
830
- # Clone and install
831
474
  git clone https://github.com/callezenwaka/kalendly.git
832
475
  cd kalendly
833
476
  npm install
834
-
835
- # Run tests
836
477
  npm test
837
-
838
- # Run examples locally
839
478
  npm run dev:examples
840
479
  ```
841
480
 
@@ -845,15 +484,12 @@ MIT © Callis Ezenwaka
845
484
 
846
485
  ## Changelog
847
486
 
848
- See [CHANGELOG.md](CHANGELOG.md) for detailed release notes and version history.
487
+ See [CHANGELOG.md](CHANGELOG.md).
849
488
 
850
489
  ### Recent Updates
851
490
 
852
- - **v0.1.7**: Vanilla calendar performance optimization with event delegation, targeted DOM updates for picker navigation
853
- - **v0.1.6**: Navigation enhancements with Today button, month/year picker dropdown, optional `title` prop, calendar grid improvements showing previous/next month days, and browser compatibility fixes
854
- - **v0.1.5**: Universal theming system, TypeScript support improvements, integration test enhancements
855
- - **v0.1.4**: Netlify configuration updates
856
- - **v0.1.3**: Vue types generation improvements
857
- - **v0.1.2**: Enhanced event parameters with structured metadata, categories, recurrence, collaboration features
858
- - **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
859
495
  - **v0.1.0**: Initial release with React, Vue, React Native, and Vanilla JavaScript support