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.
- package/README.md +318 -680
- package/dist/core/index.js +3 -3
- package/dist/index.d.mts +53 -601
- package/dist/index.d.ts +53 -601
- package/dist/index.js +760 -2350
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +734 -2339
- package/dist/index.mjs.map +1 -1
- package/dist/{vanilla/index.umd.js → index.umd.js} +323 -307
- package/dist/index.umd.js.map +1 -0
- package/package.json +41 -91
- package/dist/react/index.d.mts +0 -251
- package/dist/react/index.d.ts +0 -251
- package/dist/react/index.js +0 -969
- package/dist/react/index.js.map +0 -1
- package/dist/react/index.mjs +0 -924
- package/dist/react/index.mjs.map +0 -1
- package/dist/react-native/index.d.mts +0 -642
- package/dist/react-native/index.d.ts +0 -642
- package/dist/react-native/index.js +0 -1649
- package/dist/react-native/index.js.map +0 -1
- package/dist/react-native/index.mjs +0 -1615
- package/dist/react-native/index.mjs.map +0 -1
- package/dist/vanilla/index.d.mts +0 -265
- package/dist/vanilla/index.d.ts +0 -265
- package/dist/vanilla/index.js +0 -1048
- package/dist/vanilla/index.js.map +0 -1
- package/dist/vanilla/index.mjs +0 -997
- package/dist/vanilla/index.mjs.map +0 -1
- package/dist/vanilla/index.umd.js.map +0 -1
- package/dist/vue/components/Calendar.vue.d.ts +0 -43
- package/dist/vue/components/Calendar.vue.d.ts.map +0 -1
- package/dist/vue/index.d.ts +0 -134
- package/dist/vue/index.d.ts.map +0 -1
- package/dist/vue/index.js +0 -1
- package/dist/vue/index.mjs +0 -717
- package/dist/vue/types.d.ts +0 -21
- package/dist/vue/types.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
# kalendly
|
|
1
|
+
# kalendly
|
|
2
2
|
|
|
3
|
-
A universal calendar component
|
|
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
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
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
|
-
##
|
|
15
|
+
## Live Demo
|
|
17
16
|
|
|
18
17
|
<div align="center">
|
|
19
18
|
|
|
20
|
-
[](https://kalendly-example.netlify.app/)
|
|
21
20
|
|
|
22
|
-
**[
|
|
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
|
-
###
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
107
|
-
import
|
|
91
|
+
```js
|
|
92
|
+
import 'kalendly';
|
|
108
93
|
import 'kalendly/styles';
|
|
109
94
|
|
|
110
|
-
|
|
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
|
|
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
|
|
133
|
-
import
|
|
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
|
-
<
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
-
|
|
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
|
-
|
|
354
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
178
|
+
### Angular
|
|
438
179
|
|
|
439
|
-
|
|
180
|
+
```ts
|
|
181
|
+
// app.config.ts
|
|
182
|
+
import { ApplicationConfig } from '@angular/core';
|
|
183
|
+
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
440
184
|
|
|
441
|
-
|
|
442
|
-
import
|
|
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
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
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
|
-
|
|
470
|
-
import { Calendar } from 'kalendly/vue';
|
|
471
|
-
import 'kalendly/styles';
|
|
200
|
+
### Svelte 5
|
|
472
201
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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
|
-
|
|
209
|
+
<kal-calendar {events} oncal-date-select={e => console.log(e.detail.date)} />
|
|
210
|
+
```
|
|
485
211
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
|
-
|
|
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
|
-
|
|
228
|
+
### Solid.js
|
|
505
229
|
|
|
506
|
-
```
|
|
507
|
-
import
|
|
230
|
+
```jsx
|
|
231
|
+
import 'kalendly';
|
|
232
|
+
import 'kalendly/styles';
|
|
508
233
|
|
|
509
234
|
function App() {
|
|
510
235
|
return (
|
|
511
|
-
<
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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
|
-
|
|
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
|
-
|
|
544
|
-
```
|
|
247
|
+
v0.2.0 replaces the four separate framework packages with a single web component.
|
|
545
248
|
|
|
546
|
-
|
|
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
|
-
|
|
257
|
+
React Native is out of scope and not replaced.
|
|
549
258
|
|
|
550
|
-
|
|
551
|
-
import { useState } from 'react';
|
|
552
|
-
import { Calendar } from 'kalendly/react';
|
|
259
|
+
## Styling
|
|
553
260
|
|
|
554
|
-
|
|
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
|
-
|
|
561
|
-
|
|
263
|
+
```js
|
|
264
|
+
// Bundler (Vite, webpack) — add once in your app entry (e.g. main.tsx)
|
|
265
|
+
import 'kalendly/styles';
|
|
562
266
|
|
|
563
|
-
|
|
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
|
-
|
|
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
|
-
|
|
274
|
+
### Overriding styles
|
|
576
275
|
|
|
577
|
-
|
|
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
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
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
|
-
|
|
599
|
-
|
|
286
|
+
/* 2. Direct class overrides */
|
|
287
|
+
.kalendly-calendar .calendar--card {
|
|
288
|
+
border-radius: 12px;
|
|
289
|
+
}
|
|
600
290
|
```
|
|
601
291
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
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
|
-
|
|
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
|
-
|
|
302
|
+
Primitives are set as HTML attributes:
|
|
639
303
|
|
|
640
|
-
|
|
|
641
|
-
|
|
|
642
|
-
| `title`
|
|
643
|
-
| `
|
|
644
|
-
| `
|
|
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
|
-
|
|
313
|
+
## Properties
|
|
647
314
|
|
|
648
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
334
|
+
## JavaScript API
|
|
678
335
|
|
|
679
|
-
```
|
|
680
|
-
const
|
|
336
|
+
```js
|
|
337
|
+
const cal = document.querySelector('kal-calendar');
|
|
681
338
|
|
|
682
|
-
//
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
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
|
-
##
|
|
691
|
-
|
|
692
|
-
### Vanilla JavaScript
|
|
693
|
-
|
|
694
|
-
**Issue: "Function is not defined" errors with onclick handlers**
|
|
695
|
-
|
|
696
|
-
❌ **Wrong:**
|
|
346
|
+
## CalendarEvent Interface
|
|
697
347
|
|
|
698
|
-
```
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
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
|
-
|
|
354
|
+
startTime?: string; // e.g. "09:00"
|
|
355
|
+
endTime?: string; // e.g. "10:00"
|
|
356
|
+
allDay?: boolean;
|
|
718
357
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
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
|
-
|
|
370
|
+
status?: 'scheduled' | 'completed' | 'cancelled' | 'tentative';
|
|
371
|
+
priority?: 'low' | 'medium' | 'high';
|
|
724
372
|
|
|
725
|
-
|
|
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
|
-
|
|
728
|
-
|
|
383
|
+
notes?: string;
|
|
384
|
+
tags?: string[];
|
|
385
|
+
[key: string]: unknown;
|
|
386
|
+
}
|
|
729
387
|
```
|
|
730
388
|
|
|
731
|
-
##
|
|
389
|
+
## Theming
|
|
732
390
|
|
|
733
|
-
### CSS
|
|
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
|
-
|
|
409
|
+
### JS theme property (full reference)
|
|
751
410
|
|
|
752
|
-
|
|
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
|
-
|
|
755
|
-
import { calendarStyles } from 'kalendly/react-native';
|
|
427
|
+
### Dark theme example
|
|
756
428
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
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
|
|
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
|
-
|
|
452
|
+
const viewModel = engine.getViewModel();
|
|
453
|
+
// re-render
|
|
782
454
|
});
|
|
783
455
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
487
|
+
See [CHANGELOG.md](CHANGELOG.md).
|
|
848
488
|
|
|
849
489
|
### Recent Updates
|
|
850
490
|
|
|
851
|
-
- **v0.
|
|
852
|
-
- **v0.1.
|
|
853
|
-
- **v0.1.
|
|
854
|
-
- **v0.1.
|
|
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
|