kalendly 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +192 -55
- package/dist/core/index.d.mts +2 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/index.d.mts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +229 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +229 -33
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +228 -32
- package/dist/index.umd.js.map +1 -1
- package/dist/styles/calendar.css +135 -27
- package/package.json +20 -34
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@ A universal calendar web component — works in React, Vue, Svelte, Angular, Sol
|
|
|
9
9
|
- **Themeable**: CSS variables + JS property API
|
|
10
10
|
- **Type Safe**: Full TypeScript support
|
|
11
11
|
- **Event-rich**: Categories, priorities, time ranges, attendees, and more
|
|
12
|
+
- **Availability mode**: Day and time views for booking flows — hides event details, shows booked/free cells
|
|
13
|
+
- **Lazy loading**: Per-month on-demand fetch with skeleton shimmer state
|
|
12
14
|
- **Accessible**: Built with accessibility in mind
|
|
13
15
|
- **Tree-shakeable**: Import only what you need
|
|
14
16
|
|
|
@@ -60,11 +62,7 @@ npm install kalendly
|
|
|
60
62
|
/>
|
|
61
63
|
<script src="https://unpkg.com/kalendly/dist/index.umd.js"></script>
|
|
62
64
|
|
|
63
|
-
<kal-calendar
|
|
64
|
-
id="cal"
|
|
65
|
-
title="My Calendar"
|
|
66
|
-
initial-date="2025-01-15"
|
|
67
|
-
></kal-calendar>
|
|
65
|
+
<kal-calendar id="cal" title="My Calendar"></kal-calendar>
|
|
68
66
|
|
|
69
67
|
<script>
|
|
70
68
|
const cal = document.getElementById('cal');
|
|
@@ -147,7 +145,55 @@ function App() {
|
|
|
147
145
|
|
|
148
146
|
### Vue 3
|
|
149
147
|
|
|
150
|
-
Vue 3 supports custom elements natively — bind props with `:` and listen to events with
|
|
148
|
+
Vue 3 supports custom elements natively — bind props with `:` and listen to events with `@`.
|
|
149
|
+
|
|
150
|
+
> **Vue-specific:** Vue's template compiler warns on unknown tags. React, Solid.js, and Svelte treat hyphenated tags as DOM elements natively — no config needed. Angular uses `CUSTOM_ELEMENTS_SCHEMA` (see the Angular section below).
|
|
151
|
+
|
|
152
|
+
Tell Vue's compiler that `<kal-*>` tags are native custom elements. The config location depends on your build tool:
|
|
153
|
+
|
|
154
|
+
**Vite** (`vite.config.ts`):
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
vue({
|
|
158
|
+
template: {
|
|
159
|
+
compilerOptions: {
|
|
160
|
+
isCustomElement: tag => tag.startsWith('kal-'),
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Nuxt 3** (`nuxt.config.ts`):
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
export default defineNuxtConfig({
|
|
170
|
+
vue: {
|
|
171
|
+
compilerOptions: {
|
|
172
|
+
isCustomElement: tag => tag.startsWith('kal-'),
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**webpack / Vue CLI** (`vue.config.js`):
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
module.exports = {
|
|
182
|
+
chainWebpack: config => {
|
|
183
|
+
config.module
|
|
184
|
+
.rule('vue')
|
|
185
|
+
.use('vue-loader')
|
|
186
|
+
.tap(options => ({
|
|
187
|
+
...options,
|
|
188
|
+
compilerOptions: {
|
|
189
|
+
isCustomElement: tag => tag.startsWith('kal-'),
|
|
190
|
+
},
|
|
191
|
+
}));
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Without this the component still renders correctly — Vue falls back to a native DOM element. This only suppresses the console warning.
|
|
151
197
|
|
|
152
198
|
```vue
|
|
153
199
|
<template>
|
|
@@ -242,20 +288,6 @@ function App() {
|
|
|
242
288
|
}
|
|
243
289
|
```
|
|
244
290
|
|
|
245
|
-
## Migration from v0.1.x
|
|
246
|
-
|
|
247
|
-
v0.2.0 replaces the four separate framework packages with a single web component.
|
|
248
|
-
|
|
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} />` |
|
|
256
|
-
|
|
257
|
-
React Native is out of scope and not replaced.
|
|
258
|
-
|
|
259
291
|
## Styling
|
|
260
292
|
|
|
261
293
|
### Loading styles
|
|
@@ -301,46 +333,158 @@ document.querySelector('kal-calendar').theme = {
|
|
|
301
333
|
|
|
302
334
|
Primitives are set as HTML attributes:
|
|
303
335
|
|
|
304
|
-
| Attribute | Type
|
|
305
|
-
| ----------------------- |
|
|
306
|
-
| `title` | `string`
|
|
307
|
-
| `initial-date` | `string`
|
|
308
|
-
| `min-year` | `string`
|
|
309
|
-
| `max-year` | `string`
|
|
310
|
-
| `week-starts-on` | `"0"\|"1"`
|
|
311
|
-
| `use-short-month-names` | `string`
|
|
336
|
+
| Attribute | Type | Default | Description |
|
|
337
|
+
| ----------------------- | ---------------- | ---------------- | ------------------------------------------------ |
|
|
338
|
+
| `title` | `string` | — | Calendar title |
|
|
339
|
+
| `initial-date` | `string` | today | ISO date string for initial view |
|
|
340
|
+
| `min-year` | `string` | currentYear - 30 | Minimum year in picker |
|
|
341
|
+
| `max-year` | `string` | currentYear + 10 | Maximum year in picker |
|
|
342
|
+
| `week-starts-on` | `"0"\|"1"` | `"0"` | Week start: 0 = Sunday, 1 = Monday |
|
|
343
|
+
| `use-short-month-names` | `string` | — | Present = use abbreviated month names |
|
|
344
|
+
| `availability-mode` | `"day"\|"time"` | — | Hides event details; shows booked/free cells |
|
|
345
|
+
| `selectable` | `"range"` | — | Enables day/slot selection (requires avail mode) |
|
|
346
|
+
| `loading` | `boolean` (flag) | — | Present = render skeleton shimmer cells |
|
|
312
347
|
|
|
313
348
|
## Properties
|
|
314
349
|
|
|
315
350
|
Rich objects are set as JS properties (not attributes):
|
|
316
351
|
|
|
317
|
-
| Property | Type | Description
|
|
318
|
-
| ---------------- | ---------------------------------- |
|
|
319
|
-
| `events` | `CalendarEvent[]` | Events to display
|
|
320
|
-
| `
|
|
321
|
-
| `
|
|
322
|
-
| `
|
|
323
|
-
| `
|
|
352
|
+
| Property | Type | Description |
|
|
353
|
+
| ---------------- | ---------------------------------- | ------------------------------------------------- |
|
|
354
|
+
| `events` | `CalendarEvent[]` | Events to display |
|
|
355
|
+
| `loading` | `boolean` | `true` = render skeleton cells; `false` = restore |
|
|
356
|
+
| `theme` | `CalendarTheme` | Custom theme colors |
|
|
357
|
+
| `categoryColors` | `CategoryColorMap` | Per-category color overrides |
|
|
358
|
+
| `renderEvent` | `(event: CalendarEvent) => string` | Custom event HTML renderer |
|
|
359
|
+
| `renderNoEvents` | `() => string` | Custom empty-state HTML renderer |
|
|
360
|
+
|
|
361
|
+
> `renderEvent` and `renderNoEvents` are ignored when `availability-mode` is set.
|
|
324
362
|
|
|
325
363
|
## Custom Events
|
|
326
364
|
|
|
327
|
-
| Event
|
|
328
|
-
|
|
|
329
|
-
| `cal-date-select`
|
|
330
|
-
| `cal-month-change`
|
|
365
|
+
| Event | `detail` shape | Description |
|
|
366
|
+
| ------------------------- | ---------------------------------------------------------------------- | -------------------------------------- |
|
|
367
|
+
| `cal-date-select` | `{ date: Date, events: CalendarEvent[] }` | User clicked a date (normal mode) |
|
|
368
|
+
| `cal-month-change` | `{ year: number, month: number }` | Fires **before** the new month renders |
|
|
369
|
+
| `cal-availability-select` | `{ startDate: Date, endDate: Date }` or `{ date, startTime, endTime }` | Day/slot selected in availability mode |
|
|
370
|
+
|
|
371
|
+
All events bubble and are composed (cross Shadow DOM boundaries).
|
|
372
|
+
|
|
373
|
+
`cal-availability-select` detail shape depends on mode:
|
|
374
|
+
|
|
375
|
+
- **Day mode** (`availability-mode="day"`): `{ startDate: Date, endDate: Date }` — first click gives `startDate === endDate`; second click extends the range; third click resets
|
|
376
|
+
- **Time mode** (`availability-mode="time"`): `{ date: Date, startTime: string, endTime: string }` — first click selects a single slot; second click extends; third click resets
|
|
377
|
+
|
|
378
|
+
## Availability Mode
|
|
379
|
+
|
|
380
|
+
Hides all event details from the end user — only booked/free state is shown. Designed for scheduling and booking flows where the server's event data must not be exposed to the viewer.
|
|
381
|
+
|
|
382
|
+
### Day view
|
|
383
|
+
|
|
384
|
+
```html
|
|
385
|
+
<kal-calendar availability-mode="day"></kal-calendar>
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Days with events are tinted red (booked); days without events are tinted green (free). Only cells in the current month are colour-coded — other-month cells remain grayed out. Clicking a day fires no popup and reveals no event details.
|
|
389
|
+
|
|
390
|
+
<div align="center">
|
|
391
|
+
<img src="./docs/images/day.png" alt="Availability day view — month grid with red booked cells and green free cells"/>
|
|
392
|
+
<p><em>Day view: the month grid shows only booked/free state per day — event names, times, and attendees are never revealed</em></p>
|
|
393
|
+
</div>
|
|
394
|
+
|
|
395
|
+
Pass the minimal event shape — only `id` and `date` are required; `startTime`/`endTime` are optional and mark the whole day as booked regardless:
|
|
331
396
|
|
|
332
|
-
|
|
397
|
+
```js
|
|
398
|
+
cal.events = [
|
|
399
|
+
{ id: 1, date: new Date(2025, 4, 8) },
|
|
400
|
+
{ id: 2, date: new Date(2025, 4, 8), startTime: '14:00', endTime: '16:00' },
|
|
401
|
+
{ id: 3, date: new Date(2025, 4, 20), startTime: '10:00', endTime: '12:00' },
|
|
402
|
+
];
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Time view
|
|
406
|
+
|
|
407
|
+
```html
|
|
408
|
+
<kal-calendar availability-mode="time"></kal-calendar>
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Clicking a day opens a popup with a 24-slot hourly grid (00:00 – 23:00). Each slot shows only "Booked" or "Available" — no event name or organiser is ever rendered. A slot is booked if any event's time window overlaps that hour; the rest are free.
|
|
412
|
+
|
|
413
|
+
<div align="center">
|
|
414
|
+
<img src="./docs/images/time.png" alt="Availability time view — day popup showing 24 hourly slots coloured red (booked) or green (available)"/>
|
|
415
|
+
<p><em>Time view: clicking a day opens an hourly grid — booked slots in red, available slots in green; no event details exposed</em></p>
|
|
416
|
+
</div>
|
|
417
|
+
|
|
418
|
+
### Selectable range
|
|
419
|
+
|
|
420
|
+
Add `selectable="range"` to let the user pick a free day or time slot:
|
|
421
|
+
|
|
422
|
+
```html
|
|
423
|
+
<kal-calendar availability-mode="day" selectable="range"></kal-calendar>
|
|
424
|
+
<kal-calendar availability-mode="time" selectable="range"></kal-calendar>
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
```js
|
|
428
|
+
// Day mode — fires on every click
|
|
429
|
+
cal.addEventListener('cal-availability-select', e => {
|
|
430
|
+
const { startDate, endDate } = e.detail;
|
|
431
|
+
console.log('Selected:', startDate, '→', endDate);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// Time mode — fires on every slot click
|
|
435
|
+
cal.addEventListener('cal-availability-select', e => {
|
|
436
|
+
const { date, startTime, endTime } = e.detail;
|
|
437
|
+
console.log('Slot:', date, startTime, '–', endTime);
|
|
438
|
+
});
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
Booked days/slots cannot be selected. The 3-click state machine: first click selects, second extends, third resets.
|
|
442
|
+
|
|
443
|
+
## Lazy Event Fetching
|
|
444
|
+
|
|
445
|
+
`cal-month-change` fires **before** the new month renders, so you can set `loading = true` synchronously — the calendar shows skeleton shimmer cells from the first frame with no empty-calendar flash.
|
|
446
|
+
|
|
447
|
+
```js
|
|
448
|
+
cal.addEventListener('cal-month-change', async ({ detail }) => {
|
|
449
|
+
const { year, month } = detail;
|
|
450
|
+
cal.loading = true;
|
|
451
|
+
cal.events = await fetchEvents(year, month); // your API call
|
|
452
|
+
cal.loading = false;
|
|
453
|
+
});
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
The "dump all events upfront" pattern still works unchanged — `cal-month-change` is optional:
|
|
457
|
+
|
|
458
|
+
```js
|
|
459
|
+
// Load once, component handles all months
|
|
460
|
+
cal.events = allEvents;
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## Core API
|
|
464
|
+
|
|
465
|
+
`querySelector('kal-calendar')` returns `CalendarElement | null` automatically — no cast needed:
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
import type { CalendarElement } from 'kalendly';
|
|
469
|
+
|
|
470
|
+
const cal = document.querySelector('kal-calendar'); // CalendarElement | null
|
|
471
|
+
cal?.goToDate(new Date());
|
|
472
|
+
cal?.updateEvents(events);
|
|
473
|
+
cal?.updateTheme(theme);
|
|
474
|
+
cal?.getCurrentDate(); // Date | null
|
|
475
|
+
cal?.getEngine(); // CalendarEngine
|
|
476
|
+
```
|
|
333
477
|
|
|
334
|
-
|
|
478
|
+
**JavaScript** works the same way without the import:
|
|
335
479
|
|
|
336
480
|
```js
|
|
337
481
|
const cal = document.querySelector('kal-calendar');
|
|
338
482
|
|
|
339
|
-
cal.updateEvents(newEvents);
|
|
340
|
-
cal.updateTheme(newTheme);
|
|
341
|
-
cal.goToDate(new Date(
|
|
342
|
-
cal.getCurrentDate();
|
|
343
|
-
cal.getEngine();
|
|
483
|
+
cal.updateEvents(newEvents);
|
|
484
|
+
cal.updateTheme(newTheme);
|
|
485
|
+
cal.goToDate(new Date());
|
|
486
|
+
cal.getCurrentDate();
|
|
487
|
+
cal.getEngine();
|
|
344
488
|
```
|
|
345
489
|
|
|
346
490
|
## CalendarEvent Interface
|
|
@@ -470,14 +614,6 @@ Custom Elements v1 — Chrome 67+, Firefox 63+, Safari 12.1+, Edge 79+.
|
|
|
470
614
|
|
|
471
615
|
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
472
616
|
|
|
473
|
-
```bash
|
|
474
|
-
git clone https://github.com/callezenwaka/kalendly.git
|
|
475
|
-
cd kalendly
|
|
476
|
-
npm install
|
|
477
|
-
npm test
|
|
478
|
-
npm run dev:examples
|
|
479
|
-
```
|
|
480
|
-
|
|
481
617
|
## License
|
|
482
618
|
|
|
483
619
|
MIT © Callis Ezenwaka
|
|
@@ -488,7 +624,8 @@ See [CHANGELOG.md](CHANGELOG.md).
|
|
|
488
624
|
|
|
489
625
|
### Recent Updates
|
|
490
626
|
|
|
491
|
-
- **v0.2.
|
|
627
|
+
- **v0.2.1**: Add availability mode (day/time views), selectable range, lazy event fetching with skeleton loading
|
|
628
|
+
- **v0.2.0**: Migrated to a single `<kal-calendar>` web component — works natively in React, Vue, Angular, Svelte, Solid.js, and plain HTML with no framework dependency
|
|
492
629
|
- **v0.1.7**: Vanilla calendar performance optimization with event delegation
|
|
493
630
|
- **v0.1.6**: Navigation enhancements — Today button, month/year picker, optional `title` prop
|
|
494
631
|
- **v0.1.5**: Universal theming system, TypeScript improvements
|
package/dist/core/index.d.mts
CHANGED
package/dist/core/index.d.ts
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -98,6 +98,8 @@ interface CalendarTheme {
|
|
|
98
98
|
borderColor?: string;
|
|
99
99
|
todayOutline?: string;
|
|
100
100
|
selectedBg?: string;
|
|
101
|
+
headerBg?: string;
|
|
102
|
+
popupBg?: string;
|
|
101
103
|
eventIndicator?: string;
|
|
102
104
|
}
|
|
103
105
|
|
|
@@ -239,15 +241,23 @@ declare class CalendarElement extends HTMLElement {
|
|
|
239
241
|
private _categoryColors;
|
|
240
242
|
private _renderEvent;
|
|
241
243
|
private _renderNoEvents;
|
|
244
|
+
private _rangeStart;
|
|
245
|
+
private _rangeEnd;
|
|
246
|
+
private _timeRangeDate;
|
|
247
|
+
private _timeRangeStart;
|
|
248
|
+
private _timeRangeEnd;
|
|
249
|
+
private _timeRangeComplete;
|
|
242
250
|
get events(): CalendarEvent[];
|
|
243
251
|
set events(val: CalendarEvent[]);
|
|
244
252
|
set theme(val: CalendarTheme);
|
|
245
253
|
set categoryColors(val: CategoryColorMap);
|
|
246
254
|
set renderEvent(val: (e: CalendarEvent) => string);
|
|
247
255
|
set renderNoEvents(val: () => string);
|
|
256
|
+
get loading(): boolean;
|
|
257
|
+
set loading(val: boolean);
|
|
248
258
|
connectedCallback(): void;
|
|
249
259
|
disconnectedCallback(): void;
|
|
250
|
-
attributeChangedCallback(
|
|
260
|
+
attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
|
|
251
261
|
private get minYear();
|
|
252
262
|
private get maxYear();
|
|
253
263
|
private initEngine;
|
|
@@ -265,6 +275,11 @@ declare class CalendarElement extends HTMLElement {
|
|
|
265
275
|
getEngine(): CalendarEngine;
|
|
266
276
|
}
|
|
267
277
|
declare function defineCalendarElement(tagName?: string): void;
|
|
278
|
+
declare global {
|
|
279
|
+
interface HTMLElementTagNameMap {
|
|
280
|
+
'kal-calendar': CalendarElement;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
268
283
|
|
|
269
284
|
interface CalendarElementProps {
|
|
270
285
|
events?: CalendarEvent[];
|
package/dist/index.d.ts
CHANGED
|
@@ -98,6 +98,8 @@ interface CalendarTheme {
|
|
|
98
98
|
borderColor?: string;
|
|
99
99
|
todayOutline?: string;
|
|
100
100
|
selectedBg?: string;
|
|
101
|
+
headerBg?: string;
|
|
102
|
+
popupBg?: string;
|
|
101
103
|
eventIndicator?: string;
|
|
102
104
|
}
|
|
103
105
|
|
|
@@ -239,15 +241,23 @@ declare class CalendarElement extends HTMLElement {
|
|
|
239
241
|
private _categoryColors;
|
|
240
242
|
private _renderEvent;
|
|
241
243
|
private _renderNoEvents;
|
|
244
|
+
private _rangeStart;
|
|
245
|
+
private _rangeEnd;
|
|
246
|
+
private _timeRangeDate;
|
|
247
|
+
private _timeRangeStart;
|
|
248
|
+
private _timeRangeEnd;
|
|
249
|
+
private _timeRangeComplete;
|
|
242
250
|
get events(): CalendarEvent[];
|
|
243
251
|
set events(val: CalendarEvent[]);
|
|
244
252
|
set theme(val: CalendarTheme);
|
|
245
253
|
set categoryColors(val: CategoryColorMap);
|
|
246
254
|
set renderEvent(val: (e: CalendarEvent) => string);
|
|
247
255
|
set renderNoEvents(val: () => string);
|
|
256
|
+
get loading(): boolean;
|
|
257
|
+
set loading(val: boolean);
|
|
248
258
|
connectedCallback(): void;
|
|
249
259
|
disconnectedCallback(): void;
|
|
250
|
-
attributeChangedCallback(
|
|
260
|
+
attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
|
|
251
261
|
private get minYear();
|
|
252
262
|
private get maxYear();
|
|
253
263
|
private initEngine;
|
|
@@ -265,6 +275,11 @@ declare class CalendarElement extends HTMLElement {
|
|
|
265
275
|
getEngine(): CalendarEngine;
|
|
266
276
|
}
|
|
267
277
|
declare function defineCalendarElement(tagName?: string): void;
|
|
278
|
+
declare global {
|
|
279
|
+
interface HTMLElementTagNameMap {
|
|
280
|
+
'kal-calendar': CalendarElement;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
268
283
|
|
|
269
284
|
interface CalendarElementProps {
|
|
270
285
|
events?: CalendarEvent[];
|