kalendly 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Barestripe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,492 @@
1
+ # kalendly Universal Calendar Scheduler
2
+
3
+ A universal calendar scheduler component that works seamlessly across React, Vue, and React Native with full TypeScript support.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **Universal**: Works with React, Vue, React Native, and Vanilla JavaScript
8
+ - 📱 **Responsive**: Mobile-friendly design that matches your existing Vue 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
12
+ - 🌐 **Accessible**: Built with accessibility in mind
13
+ - 📦 **Tree Shakeable**: Import only what you need
14
+ - 🎯 **Design Consistent**: Matches the original Vue calendar design and feel
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install kalendly
20
+ # or
21
+ yarn add kalendly
22
+ # or
23
+ pnpm add kalendly
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ### React
29
+
30
+ ```jsx
31
+ import React from 'react';
32
+ import { Calendar } from 'kalendly/react';
33
+ import 'kalendly/styles';
34
+
35
+ const events = [
36
+ {
37
+ id: 1,
38
+ name: 'Team Meeting',
39
+ date: '2025-01-15',
40
+ },
41
+ {
42
+ id: 2,
43
+ name: 'Project Deadline',
44
+ date: '2025-01-20',
45
+ },
46
+ ];
47
+
48
+ function App() {
49
+ const handleDateSelect = date => {
50
+ console.log('Selected date:', date);
51
+ };
52
+
53
+ return (
54
+ <div>
55
+ <Calendar
56
+ events={events}
57
+ onDateSelect={handleDateSelect}
58
+ title="My Calendar"
59
+ />
60
+ </div>
61
+ );
62
+ }
63
+
64
+ export default App;
65
+ ```
66
+
67
+ ### Vue
68
+
69
+ ```vue
70
+ <template>
71
+ <div>
72
+ <Calendar
73
+ :events="events"
74
+ @date-select="handleDateSelect"
75
+ title="My Calendar"
76
+ >
77
+ <template #event="{ event }">
78
+ <div class="custom-event">{{ event.name }}</div>
79
+ </template>
80
+ </Calendar>
81
+ </div>
82
+ </template>
83
+
84
+ <script setup>
85
+ import { Calendar } from 'kalendly/vue';
86
+ import 'kalendly/styles';
87
+
88
+ const events = [
89
+ {
90
+ id: 1,
91
+ name: 'Team Meeting',
92
+ date: '2025-01-15',
93
+ },
94
+ {
95
+ id: 2,
96
+ name: 'Project Deadline',
97
+ date: '2025-01-20',
98
+ },
99
+ ];
100
+
101
+ const handleDateSelect = date => {
102
+ console.log('Selected date:', date);
103
+ };
104
+ </script>
105
+ ```
106
+
107
+ ### React Native
108
+
109
+ ```jsx
110
+ import React from 'react';
111
+ import { View } from 'react-native';
112
+ import { Calendar } from 'kalendly/react-native';
113
+
114
+ const events = [
115
+ {
116
+ id: 1,
117
+ name: 'Team Meeting',
118
+ date: '2025-01-15',
119
+ },
120
+ {
121
+ id: 2,
122
+ name: 'Project Deadline',
123
+ date: '2025-01-20',
124
+ },
125
+ ];
126
+
127
+ function App() {
128
+ const handleDateSelect = date => {
129
+ console.log('Selected date:', date);
130
+ };
131
+
132
+ return (
133
+ <View style={{ flex: 1 }}>
134
+ <Calendar
135
+ events={events}
136
+ onDateSelect={handleDateSelect}
137
+ title="My Calendar"
138
+ />
139
+ </View>
140
+ );
141
+ }
142
+
143
+ export default App;
144
+ ```
145
+
146
+ ### Vanilla JavaScript
147
+
148
+ #### Modern ES Modules (Recommended)
149
+
150
+ ```html
151
+ <!DOCTYPE html>
152
+ <html>
153
+ <head>
154
+ <link
155
+ rel="stylesheet"
156
+ href="node_modules/kalendly/dist/styles/calendar.css"
157
+ />
158
+ <title>Vanilla JS Calendar</title>
159
+ </head>
160
+ <body>
161
+ <div id="calendar"></div>
162
+
163
+ <script type="module">
164
+ import { createCalendar } from 'kalendly/vanilla';
165
+
166
+ const events = [
167
+ { id: 1, name: 'Team Meeting', date: '2025-01-15' },
168
+ { id: 2, name: 'Project Deadline', date: '2025-01-20' },
169
+ ];
170
+
171
+ const calendar = createCalendar({
172
+ container: '#calendar',
173
+ events: events,
174
+ title: 'My Calendar',
175
+ });
176
+
177
+ // Listen to events (note: use addEventListener, not onclick)
178
+ document.getElementById('calendar').addEventListener('dateSelect', e => {
179
+ console.log('Selected date:', e.detail.date);
180
+ });
181
+
182
+ document.getElementById('calendar').addEventListener('monthChange', e => {
183
+ console.log('Month changed:', e.detail.year, e.detail.month);
184
+ });
185
+
186
+ // Update events dynamically
187
+ // calendar.updateEvents(newEvents);
188
+
189
+ // Navigate to specific date
190
+ // calendar.goToDate(new Date(2025, 5, 15));
191
+
192
+ // Cleanup when done
193
+ // calendar.destroy();
194
+ </script>
195
+ </body>
196
+ </html>
197
+ ```
198
+
199
+ #### CDN Usage (Browser)
200
+
201
+ ```html
202
+ <!DOCTYPE html>
203
+ <html>
204
+ <head>
205
+ <link
206
+ rel="stylesheet"
207
+ href="https://unpkg.com/kalendly/dist/styles/calendar.css"
208
+ />
209
+ </head>
210
+ <body>
211
+ <div id="calendar"></div>
212
+
213
+ <script type="module">
214
+ import { createCalendar } from 'https://unpkg.com/kalendly/dist/vanilla/index.mjs';
215
+
216
+ const calendar = createCalendar({
217
+ container: '#calendar',
218
+ events: [{ id: 1, name: 'Meeting', date: '2025-01-15' }],
219
+ title: 'My Calendar',
220
+ });
221
+ </script>
222
+ </body>
223
+ </html>
224
+ ```
225
+
226
+ #### Legacy/Global Usage (Without Modules)
227
+
228
+ ```html
229
+ <!DOCTYPE html>
230
+ <html>
231
+ <head>
232
+ <link
233
+ rel="stylesheet"
234
+ href="https://unpkg.com/kalendly/dist/styles/calendar.css"
235
+ />
236
+ </head>
237
+ <body>
238
+ <div id="calendar"></div>
239
+
240
+ <script src="https://unpkg.com/kalendly/dist/vanilla/index.umd.js"></script>
241
+ <script>
242
+ const { createCalendar } = Kalendly;
243
+
244
+ const calendar = createCalendar({
245
+ container: '#calendar',
246
+ events: [{ id: 1, name: 'Meeting', date: '2025-01-15' }],
247
+ title: 'My Calendar',
248
+ });
249
+
250
+ // Now you can use global functions with onclick
251
+ function handleDateSelect() {
252
+ console.log('Date selected!');
253
+ }
254
+
255
+ document
256
+ .getElementById('calendar')
257
+ .addEventListener('dateSelect', handleDateSelect);
258
+ </script>
259
+ </body>
260
+ </html>
261
+ ```
262
+
263
+ ## API Reference
264
+
265
+ ### Props
266
+
267
+ | Prop | Type | Default | Description |
268
+ | --------------- | --------------------------------------- | ------------------ | --------------------------------------- |
269
+ | `events` | `CalendarEvent[]` | `[]` | Array of events to display |
270
+ | `initialDate` | `Date` | `new Date()` | Initial date to display |
271
+ | `minYear` | `number` | `currentYear - 30` | Minimum selectable year |
272
+ | `maxYear` | `number` | `currentYear + 10` | Maximum selectable year |
273
+ | `weekStartsOn` | `0 \| 1` | `0` | Week start day (0 = Sunday, 1 = Monday) |
274
+ | `onDateSelect` | `(date: Date) => void` | - | Callback when date is selected |
275
+ | `onEventClick` | `(event: CalendarEvent) => void` | - | Callback when event is clicked |
276
+ | `onMonthChange` | `(year: number, month: number) => void` | - | Callback when month changes |
277
+
278
+ ### CalendarEvent Interface
279
+
280
+ ```typescript
281
+ interface CalendarEvent {
282
+ id: string | number;
283
+ name: string;
284
+ date: string | Date;
285
+ description?: string;
286
+ color?: string;
287
+ [key: string]: any;
288
+ }
289
+ ```
290
+
291
+ ## Framework-Specific Features
292
+
293
+ ### React Props
294
+
295
+ | Prop | Type | Description |
296
+ | ---------------- | ------------------------------------- | ------------------------ |
297
+ | `className` | `string` | CSS class name |
298
+ | `style` | `React.CSSProperties` | Inline styles |
299
+ | `renderEvent` | `(event: CalendarEvent) => ReactNode` | Custom event renderer |
300
+ | `renderNoEvents` | `() => ReactNode` | Custom no events message |
301
+
302
+ ### Vue Props & Slots
303
+
304
+ | Slot | Props | Description |
305
+ | ----------- | -------------------------- | ------------------------ |
306
+ | `title` | - | Custom title content |
307
+ | `event` | `{ event: CalendarEvent }` | Custom event display |
308
+ | `no-events` | - | Custom no events message |
309
+
310
+ ### React Native Props
311
+
312
+ | Prop | Type | Description |
313
+ | ----------------- | ----------- | -------------------------- |
314
+ | `style` | `ViewStyle` | Container style |
315
+ | `headerStyle` | `ViewStyle` | Header style |
316
+ | `cellStyle` | `ViewStyle` | Calendar cell style |
317
+ | `showCloseButton` | `boolean` | Show close button in popup |
318
+
319
+ ### Vanilla JavaScript Options
320
+
321
+ | Option | Type | Description |
322
+ | ---------------- | ---------------------------------- | ----------------------------- |
323
+ | `container` | `HTMLElement \| string` | Container element or selector |
324
+ | `className` | `string` | Additional CSS class |
325
+ | `renderEvent` | `(event: CalendarEvent) => string` | Custom event HTML renderer |
326
+ | `renderNoEvents` | `() => string` | Custom no events HTML |
327
+
328
+ ### Vanilla JavaScript Events
329
+
330
+ ```javascript
331
+ // Listen to calendar events
332
+ calendar.container.addEventListener('dateSelect', e => {
333
+ console.log('Date selected:', e.detail.date);
334
+ });
335
+
336
+ calendar.container.addEventListener('monthChange', e => {
337
+ console.log('Month changed:', e.detail.year, e.detail.month);
338
+ });
339
+ ```
340
+
341
+ ### Vanilla JavaScript API
342
+
343
+ ```javascript
344
+ const calendar = createCalendar(options);
345
+
346
+ // Methods
347
+ calendar.updateEvents(newEvents); // Update events
348
+ calendar.getCurrentDate(); // Get selected date
349
+ calendar.goToDate(new Date()); // Navigate to date
350
+ calendar.getEngine(); // Access core engine
351
+ calendar.destroy(); // Cleanup
352
+ ```
353
+
354
+ ## Common Issues & Solutions
355
+
356
+ ### Vanilla JavaScript
357
+
358
+ **Issue: "Function is not defined" errors with onclick handlers**
359
+
360
+ ❌ **Wrong:**
361
+
362
+ ```html
363
+ <button onclick="myFunction()">Click me</button>
364
+ <script type="module">
365
+ function myFunction() {} // Not accessible globally in modules
366
+ </script>
367
+ ```
368
+
369
+ ✅ **Correct:**
370
+
371
+ ```html
372
+ <button id="myButton">Click me</button>
373
+ <script type="module">
374
+ function myFunction() {}
375
+ document.getElementById('myButton').addEventListener('click', myFunction);
376
+ </script>
377
+ ```
378
+
379
+ **Issue: Module imports not working**
380
+
381
+ Use the correct import paths:
382
+
383
+ - Local: `'./dist/vanilla/index.mjs'`
384
+ - NPM: `'kalendly/vanilla'`
385
+ - CDN: `'https://unpkg.com/kalendly/dist/vanilla/index.mjs'`
386
+
387
+ **Issue: Styles not loading**
388
+
389
+ Always include the CSS file:
390
+
391
+ ```html
392
+ <link rel="stylesheet" href="path/to/kalendly/dist/styles/calendar.css" />
393
+ ```
394
+
395
+ ## Customization
396
+
397
+ ### CSS Variables (React/Vue)
398
+
399
+ ```css
400
+ :root {
401
+ --calendar-primary-color: #fc8917;
402
+ --calendar-secondary-color: #fca045;
403
+ --calendar-tertiary-color: #fdb873;
404
+ --calendar-text-color: #2c3e50;
405
+ --calendar-border-color: #dee2e6;
406
+ --calendar-today-outline: #f7db04;
407
+ --calendar-event-indicator: #1890ff;
408
+ --calendar-background: #fff;
409
+ }
410
+ ```
411
+
412
+ ### React Native Theming
413
+
414
+ ```javascript
415
+ import { calendarStyles } from 'kalendly/react-native';
416
+
417
+ // Customize styles
418
+ const customStyles = {
419
+ ...calendarStyles,
420
+ container: {
421
+ ...calendarStyles.container,
422
+ backgroundColor: '#f5f5f5',
423
+ },
424
+ };
425
+ ```
426
+
427
+ ## Core API
428
+
429
+ You can also use the core calendar engine directly:
430
+
431
+ ```typescript
432
+ import { CalendarEngine } from 'kalendly/core';
433
+
434
+ const engine = new CalendarEngine({
435
+ events: myEvents,
436
+ initialDate: new Date(),
437
+ });
438
+
439
+ // Subscribe to changes
440
+ const unsubscribe = engine.subscribe(() => {
441
+ console.log('Calendar state changed');
442
+ });
443
+
444
+ // Get current state
445
+ const viewModel = engine.getViewModel();
446
+ const actions = engine.getActions();
447
+
448
+ // Navigate months
449
+ actions.next();
450
+ actions.previous();
451
+ actions.jump(2025, 5); // June 2025
452
+
453
+ // Clean up
454
+ unsubscribe();
455
+ engine.destroy();
456
+ ```
457
+
458
+ ## TypeScript Support
459
+
460
+ The package is written in TypeScript and provides full type definitions:
461
+
462
+ ```typescript
463
+ import type { CalendarEvent, CalendarProps, CalendarState } from 'kalendly';
464
+ ```
465
+
466
+ ## Browser Support
467
+
468
+ - **React/Vue**: Modern browsers (Chrome 90+, Firefox 88+, Safari 14+)
469
+ - **React Native**: iOS 12+, Android API 21+
470
+
471
+ ## Contributing
472
+
473
+ 1. Fork the repository
474
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
475
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
476
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
477
+ 5. Open a Pull Request
478
+
479
+ ## License
480
+
481
+ MIT © Callis Ezenwaka
482
+
483
+ ## Changelog
484
+
485
+ ### 1.0.0
486
+
487
+ - Initial release
488
+ - React, Vue, and React Native support
489
+ - TypeScript definitions
490
+ - Core calendar engine
491
+ - Event management
492
+ - Responsive design
@@ -0,0 +1,181 @@
1
+ interface CalendarEvent {
2
+ id: string | number;
3
+ name: string;
4
+ date: string | Date;
5
+ description?: string;
6
+ color?: string;
7
+ [key: string]: any;
8
+ }
9
+ interface CalendarDate {
10
+ date: Date;
11
+ isCurrentMonth: boolean;
12
+ isToday: boolean;
13
+ hasEvents: boolean;
14
+ events: CalendarEvent[];
15
+ }
16
+ interface CalendarState {
17
+ currentYear: number;
18
+ currentMonth: number;
19
+ currentDate: number;
20
+ selectedDate: Date | null;
21
+ selectedDayIndex: number | null;
22
+ tasks: CalendarEvent[];
23
+ }
24
+ interface CalendarConfig {
25
+ events: CalendarEvent[];
26
+ initialDate?: Date;
27
+ minYear?: number;
28
+ maxYear?: number;
29
+ weekStartsOn?: 0 | 1;
30
+ }
31
+ interface CalendarActions {
32
+ next: () => void;
33
+ previous: () => void;
34
+ jump: (year: number, month: number) => void;
35
+ selectDate: (date: Date) => void;
36
+ updateTasks: () => void;
37
+ }
38
+ interface PopupPosition {
39
+ class: 'popup-left' | 'popup-right' | 'popup-center-top' | 'popup-center-bottom';
40
+ style?: Record<string, string | number>;
41
+ }
42
+ interface CalendarViewModel extends CalendarState {
43
+ months: string[];
44
+ days: string[];
45
+ years: number[];
46
+ monthAndYearText: string;
47
+ scheduleDay: string;
48
+ calendarDates: (CalendarDate | null)[][];
49
+ popupPositionClass: string;
50
+ }
51
+ type CalendarEventHandler = (event: CalendarEvent) => void;
52
+ interface CalendarProps {
53
+ events: CalendarEvent[];
54
+ initialDate?: Date;
55
+ minYear?: number;
56
+ maxYear?: number;
57
+ weekStartsOn?: 0 | 1;
58
+ onDateSelect?: (date: Date) => void;
59
+ onEventClick?: CalendarEventHandler;
60
+ onMonthChange?: (year: number, month: number) => void;
61
+ }
62
+
63
+ declare const MONTHS: string[];
64
+ declare const DAYS: string[];
65
+ /**
66
+ * Normalizes a date to midnight (00:00:00) for comparison purposes
67
+ */
68
+ declare function normalizeDate(date: Date): Date;
69
+ /**
70
+ * Checks if two dates are the same day
71
+ */
72
+ declare function isSameDay(date1: Date, date2: Date): boolean;
73
+ /**
74
+ * Checks if a date is today
75
+ */
76
+ declare function isToday(date: Date): boolean;
77
+ /**
78
+ * Generates an array of years for the year selector
79
+ */
80
+ declare function generateYears(minYear?: number, maxYear?: number): number[];
81
+ /**
82
+ * Gets events for a specific date
83
+ */
84
+ declare function getEventsForDate(events: CalendarEvent[], date: Date): CalendarEvent[];
85
+ /**
86
+ * Checks if a date has any events
87
+ */
88
+ declare function hasEvents(events: CalendarEvent[], date: Date): boolean;
89
+ /**
90
+ * Generates calendar dates for a given month and year
91
+ */
92
+ declare function generateCalendarDates(year: number, month: number, events?: CalendarEvent[], weekStartsOn?: 0 | 1): (CalendarDate | null)[][];
93
+ /**
94
+ * Gets the popup position class based on the selected day index
95
+ */
96
+ declare function getPopupPositionClass(selectedDayIndex: number | null): string;
97
+ /**
98
+ * Gets CSS classes for a calendar cell
99
+ */
100
+ declare function getCellClasses(calendarDate: CalendarDate | null): string[];
101
+ /**
102
+ * Formats a date for display
103
+ */
104
+ declare function formatDateForDisplay(date: Date): string;
105
+ /**
106
+ * Gets month and year text for display
107
+ */
108
+ declare function getMonthYearText(year: number, month: number): string;
109
+
110
+ declare class CalendarEngine {
111
+ private state;
112
+ private config;
113
+ private listeners;
114
+ constructor(config: CalendarConfig);
115
+ /**
116
+ * Subscribe to state changes
117
+ */
118
+ subscribe(listener: () => void): () => void;
119
+ /**
120
+ * Notify all listeners of state changes
121
+ */
122
+ private notify;
123
+ /**
124
+ * Get current state
125
+ */
126
+ getState(): CalendarState;
127
+ /**
128
+ * Get view model with computed properties
129
+ */
130
+ getViewModel(): CalendarViewModel;
131
+ /**
132
+ * Get actions object
133
+ */
134
+ getActions(): CalendarActions;
135
+ /**
136
+ * Navigate to next month
137
+ */
138
+ private next;
139
+ /**
140
+ * Navigate to previous month
141
+ */
142
+ private previous;
143
+ /**
144
+ * Jump to specific month and year
145
+ */
146
+ private jump;
147
+ /**
148
+ * Select a specific date
149
+ */
150
+ private selectDate;
151
+ /**
152
+ * Update tasks for the currently selected date
153
+ */
154
+ private updateTasks;
155
+ /**
156
+ * Update events configuration
157
+ */
158
+ updateEvents(events: CalendarEvent[]): void;
159
+ /**
160
+ * Handle date cell click
161
+ */
162
+ handleDateClick(date: Date, dayIndex?: number): void;
163
+ /**
164
+ * Check if date has events
165
+ */
166
+ hasEventsForDate(date: Date): boolean;
167
+ /**
168
+ * Get events for a specific date
169
+ */
170
+ getEventsForDate(date: Date): CalendarEvent[];
171
+ /**
172
+ * Clear selected date
173
+ */
174
+ clearSelection(): void;
175
+ /**
176
+ * Destroy the engine and cleanup listeners
177
+ */
178
+ destroy(): void;
179
+ }
180
+
181
+ export { type CalendarActions, type CalendarConfig, type CalendarDate, CalendarEngine, type CalendarEvent, type CalendarEventHandler, type CalendarProps, type CalendarState, type CalendarViewModel, DAYS, MONTHS, type PopupPosition, formatDateForDisplay, generateCalendarDates, generateYears, getCellClasses, getEventsForDate, getMonthYearText, getPopupPositionClass, hasEvents, isSameDay, isToday, normalizeDate };