kalendly 0.2.0 → 0.2.1

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 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');
@@ -242,20 +240,6 @@ function App() {
242
240
  }
243
241
  ```
244
242
 
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
243
  ## Styling
260
244
 
261
245
  ### Loading styles
@@ -301,46 +285,148 @@ document.querySelector('kal-calendar').theme = {
301
285
 
302
286
  Primitives are set as HTML attributes:
303
287
 
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 |
288
+ | Attribute | Type | Default | Description |
289
+ | ----------------------- | ---------------- | ---------------- | ------------------------------------------------ |
290
+ | `title` | `string` | — | Calendar title |
291
+ | `initial-date` | `string` | today | ISO date string for initial view |
292
+ | `min-year` | `string` | currentYear - 30 | Minimum year in picker |
293
+ | `max-year` | `string` | currentYear + 10 | Maximum year in picker |
294
+ | `week-starts-on` | `"0"\|"1"` | `"0"` | Week start: 0 = Sunday, 1 = Monday |
295
+ | `use-short-month-names` | `string` | — | Present = use abbreviated month names |
296
+ | `availability-mode` | `"day"\|"time"` | — | Hides event details; shows booked/free cells |
297
+ | `selectable` | `"range"` | — | Enables day/slot selection (requires avail mode) |
298
+ | `loading` | `boolean` (flag) | — | Present = render skeleton shimmer cells |
312
299
 
313
300
  ## Properties
314
301
 
315
302
  Rich objects are set as JS properties (not attributes):
316
303
 
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 |
304
+ | Property | Type | Description |
305
+ | ---------------- | ---------------------------------- | ------------------------------------------------- |
306
+ | `events` | `CalendarEvent[]` | Events to display |
307
+ | `loading` | `boolean` | `true` = render skeleton cells; `false` = restore |
308
+ | `theme` | `CalendarTheme` | Custom theme colors |
309
+ | `categoryColors` | `CategoryColorMap` | Per-category color overrides |
310
+ | `renderEvent` | `(event: CalendarEvent) => string` | Custom event HTML renderer |
311
+ | `renderNoEvents` | `() => string` | Custom empty-state HTML renderer |
312
+
313
+ > `renderEvent` and `renderNoEvents` are ignored when `availability-mode` is set.
324
314
 
325
315
  ## Custom Events
326
316
 
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 |
317
+ | Event | `detail` shape | Description |
318
+ | ------------------------- | ---------------------------------------------------------------------- | -------------------------------------- |
319
+ | `cal-date-select` | `{ date: Date, events: CalendarEvent[] }` | User clicked a date (normal mode) |
320
+ | `cal-month-change` | `{ year: number, month: number }` | Fires **before** the new month renders |
321
+ | `cal-availability-select` | `{ startDate: Date, endDate: Date }` or `{ date, startTime, endTime }` | Day/slot selected in availability mode |
322
+
323
+ All events bubble and are composed (cross Shadow DOM boundaries).
324
+
325
+ `cal-availability-select` detail shape depends on mode:
326
+
327
+ - **Day mode** (`availability-mode="day"`): `{ startDate: Date, endDate: Date }` — first click gives `startDate === endDate`; second click extends the range; third click resets
328
+ - **Time mode** (`availability-mode="time"`): `{ date: Date, startTime: string, endTime: string }` — first click selects a single slot; second click extends; third click resets
329
+
330
+ ## Availability Mode
331
+
332
+ 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.
333
+
334
+ ### Day view
335
+
336
+ ```html
337
+ <kal-calendar availability-mode="day"></kal-calendar>
338
+ ```
339
+
340
+ Days with events get a red tint (booked); days without get a green tint (free). Clicking a day opens no popup.
341
+
342
+ Pass the minimal event shape — no names, no descriptions:
343
+
344
+ ```js
345
+ cal.events = [
346
+ { id: 1, date: new Date(2025, 4, 8) },
347
+ { id: 2, date: new Date(2025, 4, 8), startTime: '14:00', endTime: '16:00' },
348
+ { id: 3, date: new Date(2025, 4, 20), startTime: '10:00', endTime: '12:00' },
349
+ ];
350
+ ```
351
+
352
+ ### Time view
353
+
354
+ ```html
355
+ <kal-calendar availability-mode="time"></kal-calendar>
356
+ ```
357
+
358
+ Clicking a day opens a time-grid popup showing which hours are booked (red) and free (green). No event name or organiser is ever rendered.
359
+
360
+ ### Selectable range
331
361
 
332
- Both events bubble and are composed (cross Shadow DOM boundaries).
362
+ Add `selectable="range"` to let the user pick a free day or time slot:
333
363
 
334
- ## JavaScript API
364
+ ```html
365
+ <kal-calendar availability-mode="day" selectable="range"></kal-calendar>
366
+ <kal-calendar availability-mode="time" selectable="range"></kal-calendar>
367
+ ```
368
+
369
+ ```js
370
+ // Day mode — fires on every click
371
+ cal.addEventListener('cal-availability-select', e => {
372
+ const { startDate, endDate } = e.detail;
373
+ console.log('Selected:', startDate, '→', endDate);
374
+ });
375
+
376
+ // Time mode — fires on every slot click
377
+ cal.addEventListener('cal-availability-select', e => {
378
+ const { date, startTime, endTime } = e.detail;
379
+ console.log('Slot:', date, startTime, '–', endTime);
380
+ });
381
+ ```
382
+
383
+ Booked days/slots cannot be selected. The 3-click state machine: first click selects, second extends, third resets.
384
+
385
+ ## Lazy Event Fetching
386
+
387
+ `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.
388
+
389
+ ```js
390
+ cal.addEventListener('cal-month-change', async ({ detail }) => {
391
+ const { year, month } = detail;
392
+ cal.loading = true;
393
+ cal.events = await fetchEvents(year, month); // your API call
394
+ cal.loading = false;
395
+ });
396
+ ```
397
+
398
+ The "dump all events upfront" pattern still works unchanged — `cal-month-change` is optional:
399
+
400
+ ```js
401
+ // Load once, component handles all months
402
+ cal.events = allEvents;
403
+ ```
404
+
405
+ ## Core API
406
+
407
+ `querySelector('kal-calendar')` returns `CalendarElement | null` automatically — no cast needed:
408
+
409
+ ```ts
410
+ import type { CalendarElement } from 'kalendly';
411
+
412
+ const cal = document.querySelector('kal-calendar'); // CalendarElement | null
413
+ cal?.goToDate(new Date());
414
+ cal?.updateEvents(events);
415
+ cal?.updateTheme(theme);
416
+ cal?.getCurrentDate(); // Date | null
417
+ cal?.getEngine(); // CalendarEngine
418
+ ```
419
+
420
+ **JavaScript** works the same way without the import:
335
421
 
336
422
  ```js
337
423
  const cal = document.querySelector('kal-calendar');
338
424
 
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
425
+ cal.updateEvents(newEvents);
426
+ cal.updateTheme(newTheme);
427
+ cal.goToDate(new Date());
428
+ cal.getCurrentDate();
429
+ cal.getEngine();
344
430
  ```
345
431
 
346
432
  ## CalendarEvent Interface
@@ -470,14 +556,6 @@ Custom Elements v1 — Chrome 67+, Firefox 63+, Safari 12.1+, Edge 79+.
470
556
 
471
557
  See [CONTRIBUTING.md](CONTRIBUTING.md).
472
558
 
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
559
  ## License
482
560
 
483
561
  MIT © Callis Ezenwaka
@@ -488,7 +566,8 @@ See [CHANGELOG.md](CHANGELOG.md).
488
566
 
489
567
  ### Recent Updates
490
568
 
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
569
+ - **v0.2.1**: Add availability mode (day/time views), selectable range, lazy event fetching with skeleton loading
570
+ - **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
571
  - **v0.1.7**: Vanilla calendar performance optimization with event delegation
493
572
  - **v0.1.6**: Navigation enhancements — Today button, month/year picker, optional `title` prop
494
573
  - **v0.1.5**: Universal theming system, TypeScript improvements
package/dist/index.d.mts CHANGED
@@ -239,15 +239,23 @@ declare class CalendarElement extends HTMLElement {
239
239
  private _categoryColors;
240
240
  private _renderEvent;
241
241
  private _renderNoEvents;
242
+ private _rangeStart;
243
+ private _rangeEnd;
244
+ private _timeRangeDate;
245
+ private _timeRangeStart;
246
+ private _timeRangeEnd;
247
+ private _timeRangeComplete;
242
248
  get events(): CalendarEvent[];
243
249
  set events(val: CalendarEvent[]);
244
250
  set theme(val: CalendarTheme);
245
251
  set categoryColors(val: CategoryColorMap);
246
252
  set renderEvent(val: (e: CalendarEvent) => string);
247
253
  set renderNoEvents(val: () => string);
254
+ get loading(): boolean;
255
+ set loading(val: boolean);
248
256
  connectedCallback(): void;
249
257
  disconnectedCallback(): void;
250
- attributeChangedCallback(_name: string, oldVal: string | null, newVal: string | null): void;
258
+ attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
251
259
  private get minYear();
252
260
  private get maxYear();
253
261
  private initEngine;
@@ -265,6 +273,11 @@ declare class CalendarElement extends HTMLElement {
265
273
  getEngine(): CalendarEngine;
266
274
  }
267
275
  declare function defineCalendarElement(tagName?: string): void;
276
+ declare global {
277
+ interface HTMLElementTagNameMap {
278
+ 'kal-calendar': CalendarElement;
279
+ }
280
+ }
268
281
 
269
282
  interface CalendarElementProps {
270
283
  events?: CalendarEvent[];
package/dist/index.d.ts CHANGED
@@ -239,15 +239,23 @@ declare class CalendarElement extends HTMLElement {
239
239
  private _categoryColors;
240
240
  private _renderEvent;
241
241
  private _renderNoEvents;
242
+ private _rangeStart;
243
+ private _rangeEnd;
244
+ private _timeRangeDate;
245
+ private _timeRangeStart;
246
+ private _timeRangeEnd;
247
+ private _timeRangeComplete;
242
248
  get events(): CalendarEvent[];
243
249
  set events(val: CalendarEvent[]);
244
250
  set theme(val: CalendarTheme);
245
251
  set categoryColors(val: CategoryColorMap);
246
252
  set renderEvent(val: (e: CalendarEvent) => string);
247
253
  set renderNoEvents(val: () => string);
254
+ get loading(): boolean;
255
+ set loading(val: boolean);
248
256
  connectedCallback(): void;
249
257
  disconnectedCallback(): void;
250
- attributeChangedCallback(_name: string, oldVal: string | null, newVal: string | null): void;
258
+ attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
251
259
  private get minYear();
252
260
  private get maxYear();
253
261
  private initEngine;
@@ -265,6 +273,11 @@ declare class CalendarElement extends HTMLElement {
265
273
  getEngine(): CalendarEngine;
266
274
  }
267
275
  declare function defineCalendarElement(tagName?: string): void;
276
+ declare global {
277
+ interface HTMLElementTagNameMap {
278
+ 'kal-calendar': CalendarElement;
279
+ }
280
+ }
268
281
 
269
282
  interface CalendarElementProps {
270
283
  events?: CalendarEvent[];