kalendly 0.2.1 → 0.3.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 CHANGED
@@ -62,7 +62,7 @@ npm install kalendly
62
62
  />
63
63
  <script src="https://unpkg.com/kalendly/dist/index.umd.js"></script>
64
64
 
65
- <kal-calendar id="cal" title="My Calendar"></kal-calendar>
65
+ <kal-calendar id="cal" heading="My Calendar"></kal-calendar>
66
66
 
67
67
  <script>
68
68
  const cal = document.getElementById('cal');
@@ -104,7 +104,7 @@ import 'kalendly/styles';
104
104
  function App() {
105
105
  return (
106
106
  <kal-calendar
107
- title="My Calendar"
107
+ heading="My Calendar"
108
108
  events={events}
109
109
  oncal-date-select={e => console.log(e.detail.date)}
110
110
  oncal-month-change={e => console.log(e.detail.year, e.detail.month)}
@@ -145,12 +145,60 @@ function App() {
145
145
 
146
146
  ### Vue 3
147
147
 
148
- 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.
149
197
 
150
198
  ```vue
151
199
  <template>
152
200
  <kal-calendar
153
- title="My Calendar"
201
+ heading="My Calendar"
154
202
  :events="events"
155
203
  @cal-date-select="onDateSelect"
156
204
  @cal-month-change="onMonthChange"
@@ -188,7 +236,7 @@ import 'kalendly/styles';
188
236
  ```html
189
237
  <!-- app.component.html -->
190
238
  <kal-calendar
191
- title="My Calendar"
239
+ heading="My Calendar"
192
240
  [events]="events"
193
241
  (cal-date-select)="onDateSelect($event)"
194
242
  (cal-month-change)="onMonthChange($event)"
@@ -232,7 +280,7 @@ import 'kalendly/styles';
232
280
  function App() {
233
281
  return (
234
282
  <kal-calendar
235
- title="My Calendar"
283
+ heading="My Calendar"
236
284
  prop:events={events}
237
285
  on:cal-date-select={e => console.log(e.detail.date)}
238
286
  />
@@ -268,7 +316,7 @@ kalendly uses Light DOM — all standard CSS techniques work:
268
316
  }
269
317
 
270
318
  /* 2. Direct class overrides */
271
- .kalendly-calendar .calendar--card {
319
+ .kalendly-calendar .calendar-card {
272
320
  border-radius: 12px;
273
321
  }
274
322
  ```
@@ -287,8 +335,10 @@ Primitives are set as HTML attributes:
287
335
 
288
336
  | Attribute | Type | Default | Description |
289
337
  | ----------------------- | ---------------- | ---------------- | ------------------------------------------------ |
290
- | `title` | `string` | — | Calendar title |
338
+ | `heading` | `string` | — | Calendar heading |
339
+ | `title` | `string` | — | **Deprecated** — use `heading` |
291
340
  | `initial-date` | `string` | today | ISO date string for initial view |
341
+ | `months` | `"1"\|"2"` | `"1"` | Render two months side by side |
292
342
  | `min-year` | `string` | currentYear - 30 | Minimum year in picker |
293
343
  | `max-year` | `string` | currentYear + 10 | Maximum year in picker |
294
344
  | `week-starts-on` | `"0"\|"1"` | `"0"` | Week start: 0 = Sunday, 1 = Monday |
@@ -301,17 +351,23 @@ Primitives are set as HTML attributes:
301
351
 
302
352
  Rich objects are set as JS properties (not attributes):
303
353
 
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 |
354
+ | Property | Type | Description |
355
+ | -------------------- | ---------------------------------- | ------------------------------------------------- |
356
+ | `events` | `CalendarEvent[]` | Events to display |
357
+ | `loading` | `boolean` | `true` = render skeleton cells; `false` = restore |
358
+ | `theme` | `CalendarTheme` | Custom theme colors |
359
+ | `categoryColors` | `CategoryColorMap` | Per-category color overrides |
360
+ | `renderEvent` | `(event: CalendarEvent) => string` | Custom event HTML renderer |
361
+ | `renderNoEvents` | `() => string` | Custom empty-state HTML renderer |
362
+ | `availabilityColors` | `Record<string, string>` | Colour per availability bucket |
363
+ | `selectableStatuses` | `string[]` | Buckets a range may start, end or span |
312
364
 
313
365
  > `renderEvent` and `renderNoEvents` are ignored when `availability-mode` is set.
314
366
 
367
+ ### Why `heading` and not `title`
368
+
369
+ `title` is a global HTML attribute, so the browser renders it as a tooltip floating over the whole calendar as well as using it as the heading. `heading` does the same job without the tooltip. `title` still works and warns once per page; it will be removed in a future release.
370
+
315
371
  ## Custom Events
316
372
 
317
373
  | Event | `detail` shape | Description |
@@ -337,9 +393,77 @@ Hides all event details from the end user — only booked/free state is shown. D
337
393
  <kal-calendar availability-mode="day"></kal-calendar>
338
394
  ```
339
395
 
340
- Days with events get a red tint (booked); days without get a green tint (free). Clicking a day opens no popup.
396
+ Every event must declare `availabilityStatus`. Three buckets ship built in, coloured as a traffic light:
397
+
398
+ | Bucket | Colour | Meaning |
399
+ | ------------- | ------ | ----------------------------------------- |
400
+ | `open` | green | nothing claims this day |
401
+ | `conditional` | amber | claimed, but not necessarily hard-blocked |
402
+ | `blocked` | red | not available |
403
+
404
+ ```js
405
+ cal.events = [
406
+ { id: 1, date: '2026-03-02', availabilityStatus: 'blocked' },
407
+ { id: 2, date: '2026-03-05', availabilityStatus: 'conditional' },
408
+ ];
409
+ ```
410
+
411
+ 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.
412
+
413
+ A day holding several events resolves by severity: `blocked` beats `conditional` beats `open`, so a day with both a conditional and a blocked booking reads blocked. Precedence never depends on the order events arrive in.
414
+
415
+ #### Your own buckets
416
+
417
+ `availabilityColors` merges over the built-in three — override one, or add your own:
418
+
419
+ ```js
420
+ cal.availabilityColors = {
421
+ conditional: '#7c3aed', // recolour a built-in
422
+ maintenance: '#0891b2', // add a bucket
423
+ };
424
+ cal.events = [{ id: 3, date: '2026-03-09', availabilityStatus: 'maintenance' }];
425
+ ```
426
+
427
+ A bucket named in `availabilityColors` paints from an inline colour, which takes precedence over the same colour set through `theme`. The built-in three paint from CSS variables and are themeable the usual way.
428
+
429
+ Caller-defined buckets resolve by the order their keys appear in `availabilityColors`.
430
+
431
+ #### Which days are selectable
432
+
433
+ By default only `open` days can start, end or span a range. `selectableStatuses` widens that:
434
+
435
+ ```js
436
+ cal.selectableStatuses = ['open', 'conditional'];
437
+ ```
438
+
439
+ #### Misconfiguration throws
440
+
441
+ An event with no `availabilityStatus`, or one naming a bucket that is neither built in nor declared in `availabilityColors`, throws and names the offending events:
442
+
443
+ ```
444
+ <kal-calendar> availability-mode requires availabilityStatus on every event. Missing on: 7, 9.
445
+ ```
446
+
447
+ Setting `events` throws where you set it. One case cannot: markup parsed before the module defines the element configures the calendar inside a custom element upgrade, and the browser reports exceptions there as uncaught rather than passing them to your code. The failure is kept, so the next call to `getEngine()`, `getCurrentDate()` or `goToDate()` throws it, and correcting `events` or `availabilityColors` clears it.
341
448
 
342
- Pass the minimal event shape — no names, no descriptions:
449
+ #### Two months side by side
450
+
451
+ ```html
452
+ <kal-calendar
453
+ availability-mode="day"
454
+ months="2"
455
+ selectable="range"
456
+ ></kal-calendar>
457
+ ```
458
+
459
+ Navigation advances one month at a time, so a range spanning a month boundary stays visible. Ranges cross panes freely, and it works in standard and time modes too. Panes stack vertically on narrow screens.
460
+
461
+ <div align="center">
462
+ <img src="./docs/images/day.png" alt="Availability day view — month grid with red booked cells and green free cells"/>
463
+ <p><em>Day view: the month grid shows only booked/free state per day — event names, times, and attendees are never revealed</em></p>
464
+ </div>
465
+
466
+ Pass the minimal event shape — only `id` and `date` are required; `startTime`/`endTime` are optional and mark the whole day as booked regardless:
343
467
 
344
468
  ```js
345
469
  cal.events = [
@@ -355,7 +479,12 @@ cal.events = [
355
479
  <kal-calendar availability-mode="time"></kal-calendar>
356
480
  ```
357
481
 
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.
482
+ 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.
483
+
484
+ <div align="center">
485
+ <img src="./docs/images/time.png" alt="Availability time view — day popup showing 24 hourly slots coloured red (booked) or green (available)"/>
486
+ <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>
487
+ </div>
359
488
 
360
489
  ### Selectable range
361
490
 
@@ -431,6 +560,18 @@ cal.getEngine();
431
560
 
432
561
  ## CalendarEvent Interface
433
562
 
563
+ > **Event text renders as text.** `name`, `description`, `location`,
564
+ > `organizer`, `notes`, `tags` and `attendees` are HTML-escaped, so markup in
565
+ > those fields shows as characters rather than being parsed. Use `renderEvent`
566
+ > if you need to emit your own markup. `url` accepts `http:`, `https:`,
567
+ > `mailto:` and relative URLs; anything else becomes `#`. `color` accepts hex
568
+ > values and CSS colour keywords.
569
+
570
+ > **Custom values.** `status`, `category` and `priority` accept any string. An
571
+ > unrecognised value renders as an uppercased badge with a neutral fill, which
572
+ > you can style via `.badge.status-<your-value>` or recolour through
573
+ > `--calendar-badge-bg` / `--calendar-badge-text`.
574
+
434
575
  ```typescript
435
576
  interface CalendarEvent {
436
577
  id: string | number;
@@ -443,18 +584,18 @@ interface CalendarEvent {
443
584
 
444
585
  description?: string;
445
586
  color?: string;
446
- category?:
447
- | 'work'
448
- | 'personal'
449
- | 'meeting'
450
- | 'deadline'
451
- | 'appointment'
452
- | 'other';
587
+ // Known values keep autocomplete; any other string is accepted
588
+ category?: Open<
589
+ 'work' | 'personal' | 'meeting' | 'deadline' | 'appointment' | 'other'
590
+ >;
453
591
  location?: string;
454
592
  url?: string;
455
593
 
456
- status?: 'scheduled' | 'completed' | 'cancelled' | 'tentative';
457
- priority?: 'low' | 'medium' | 'high';
594
+ status?: Open<'scheduled' | 'completed' | 'cancelled' | 'tentative'>;
595
+ priority?: Open<'low' | 'medium' | 'high'>;
596
+
597
+ // Required under availability-mode; see Availability Mode below
598
+ availabilityStatus?: Open<'open' | 'conditional' | 'blocked'>;
458
599
 
459
600
  attendees?: string[];
460
601
  organizer?: string;
@@ -474,26 +615,77 @@ interface CalendarEvent {
474
615
 
475
616
  ## Theming
476
617
 
477
- ### All CSS variables
618
+ ### Design tokens
619
+
620
+ Every colour, size, radius, shadow and spacing step is a custom property. Two
621
+ tiers: `--kal-*` holds the raw palette, `--calendar-*` names what each value is
622
+ for. Override the `--calendar-*` layer — the primitives are internal.
478
623
 
479
624
  ```css
480
625
  :root {
626
+ /* Brand */
481
627
  --calendar-primary-color: #fc8917;
482
628
  --calendar-secondary-color: #fca045;
483
629
  --calendar-tertiary-color: #fdb873;
630
+
631
+ /* Surfaces and text */
484
632
  --calendar-text-color: #2c3e50;
485
633
  --calendar-text-light: #6b7280;
486
- --calendar-border-color: #dee2e6;
487
- --calendar-today-outline: #f7db04;
488
- --calendar-event-indicator: #1890ff;
634
+ --calendar-on-accent: #fff;
489
635
  --calendar-background: #fff;
636
+ --calendar-border-color: #dee2e6;
490
637
  --calendar-cell-hover: #f3f4f6;
638
+ --calendar-header-bg: #f8f9fa;
491
639
  --calendar-selected-bg: #eff6ff;
640
+ --calendar-popup-bg: #fff;
641
+ --calendar-picker-bg: #fff;
642
+ --calendar-today-outline: #f7db04;
643
+ --calendar-event-indicator: #1890ff;
644
+ --calendar-input-invalid: #ef4444;
645
+ --calendar-link: #2563eb;
646
+ --calendar-skeleton-base: #f0f0f0;
647
+ --calendar-skeleton-highlight: #e8e8e8;
648
+
649
+ /* Availability */
650
+ --calendar-open-bg: #dcfce7;
651
+ --calendar-open-fg: #16a34a;
652
+ --calendar-conditional-bg: #fef3c7;
653
+ --calendar-conditional-fg: #d97706;
654
+ --calendar-blocked-bg: #fee2e2;
655
+ --calendar-blocked-fg: #dc2626;
656
+ --calendar-range-bg: #16a34a;
657
+ --calendar-range-outline: #15803d;
658
+ --calendar-in-range-bg: #bbf7d0;
659
+ --calendar-in-range-outline: #86efac;
660
+
661
+ /* Badges — bg/text is the fallback for caller-defined values */
662
+ --calendar-badge-bg: #f3f4f6;
663
+ --calendar-badge-text: #4b5563;
664
+ --calendar-badge-success-bg: #d1fae5;
665
+ --calendar-badge-success-text: #059669;
666
+ --calendar-badge-info-bg: #dbeafe;
667
+ --calendar-badge-info-text: #2563eb;
668
+ --calendar-badge-warning-bg: #fef3c7;
669
+ --calendar-badge-warning-text: #d97706;
670
+ --calendar-badge-danger-bg: #fee2e2;
671
+ --calendar-badge-danger-text: #dc2626;
672
+ --calendar-badge-neutral-bg: #f3f4f6;
673
+ --calendar-badge-neutral-text: #6b7280;
674
+ --calendar-badge-positive-bg: #dcfce7;
675
+ --calendar-badge-positive-text: #16a34a;
676
+ --calendar-badge-tentative-bg: #e0e7ff;
677
+ --calendar-badge-tentative-text: #4f46e5;
492
678
  }
493
679
  ```
494
680
 
681
+ Type, radius, elevation and spacing scales are exposed the same way —
682
+ `--calendar-font-*`, `--calendar-radius-*`, `--calendar-shadow-*` and
683
+ `--calendar-space-*`. See `dist/styles/calendar.css` for the full set.
684
+
495
685
  ### JS theme property (full reference)
496
686
 
687
+ Every `--calendar-*` colour token has a matching camelCase theme key.
688
+
497
689
  ```js
498
690
  cal.theme = {
499
691
  primary: '#3b82f6',
@@ -501,15 +693,54 @@ cal.theme = {
501
693
  tertiary: '#93c5fd',
502
694
  textColor: '#111827',
503
695
  textLight: '#6b7280',
696
+ onAccent: '#ffffff',
504
697
  background: '#ffffff',
505
698
  cellHover: '#f3f4f6',
506
699
  borderColor: '#e5e7eb',
507
700
  todayOutline: '#fbbf24',
508
701
  selectedBg: '#eff6ff',
702
+ headerBg: '#f8f9fa',
703
+ popupBg: '#ffffff',
704
+ pickerBg: '#ffffff',
705
+ pickerShadow: '0 4px 20px rgba(0, 0, 0, 0.15)',
509
706
  eventIndicator: '#10b981',
707
+ link: '#2563eb',
708
+
709
+ // Availability
710
+ openBg: '#dcfce7',
711
+ openFg: '#16a34a',
712
+ conditionalBg: '#fef3c7',
713
+ conditionalFg: '#d97706',
714
+ blockedBg: '#fee2e2',
715
+ blockedFg: '#dc2626',
716
+ rangeBg: '#16a34a',
717
+ rangeOutline: '#15803d',
718
+ inRangeBg: '#bbf7d0',
719
+ inRangeOutline: '#86efac',
720
+
721
+ // Badges
722
+ badgeBg: '#f3f4f6',
723
+ badgeText: '#4b5563',
724
+ badgeSuccessBg: '#d1fae5',
725
+ badgeSuccessText: '#059669',
726
+ badgeInfoBg: '#dbeafe',
727
+ badgeInfoText: '#2563eb',
728
+ badgeWarningBg: '#fef3c7',
729
+ badgeWarningText: '#d97706',
730
+ badgeDangerBg: '#fee2e2',
731
+ badgeDangerText: '#dc2626',
732
+ badgeNeutralBg: '#f3f4f6',
733
+ badgeNeutralText: '#6b7280',
734
+ badgePositiveBg: '#dcfce7',
735
+ badgePositiveText: '#16a34a',
736
+ badgeTentativeBg: '#e0e7ff',
737
+ badgeTentativeText: '#4f46e5',
510
738
  };
511
739
  ```
512
740
 
741
+ > `availabilityColors` writes an inline colour on the cell, so for any bucket it
742
+ > names it takes precedence over the matching `theme` key.
743
+
513
744
  ### Dark theme example
514
745
 
515
746
  ```js
@@ -1,3 +1,4 @@
1
+ type Open<T extends string> = T | (string & {});
1
2
  interface CalendarEvent {
2
3
  id: string | number;
3
4
  name: string;
@@ -7,11 +8,12 @@ interface CalendarEvent {
7
8
  allDay?: boolean;
8
9
  description?: string;
9
10
  color?: string;
10
- category?: 'work' | 'personal' | 'meeting' | 'deadline' | 'appointment' | 'other';
11
+ category?: Open<'work' | 'personal' | 'meeting' | 'deadline' | 'appointment' | 'other'>;
11
12
  location?: string;
12
13
  url?: string;
13
- status?: 'scheduled' | 'completed' | 'cancelled' | 'tentative';
14
- priority?: 'low' | 'medium' | 'high';
14
+ status?: Open<'scheduled' | 'completed' | 'cancelled' | 'tentative'>;
15
+ priority?: Open<'low' | 'medium' | 'high'>;
16
+ availabilityStatus?: Open<'open' | 'conditional' | 'blocked'>;
15
17
  attendees?: string[];
16
18
  organizer?: string;
17
19
  reminders?: number[];
@@ -52,6 +54,13 @@ interface CalendarConfig {
52
54
  maxYear?: number;
53
55
  weekStartsOn?: 0 | 1;
54
56
  categoryColors?: CategoryColorMap;
57
+ monthCount?: number;
58
+ }
59
+ interface CalendarPane {
60
+ year: number;
61
+ month: number;
62
+ monthAndYearText: string;
63
+ calendarDates: CalendarDate[][];
55
64
  }
56
65
  interface CalendarActions {
57
66
  next: () => void;
@@ -62,18 +71,14 @@ interface CalendarActions {
62
71
  selectDate: (date: Date) => void;
63
72
  updateTasks: () => void;
64
73
  }
65
- interface PopupPosition {
66
- class: 'popup-left' | 'popup-right' | 'popup-center-top' | 'popup-center-bottom';
67
- style?: Record<string, string | number>;
68
- }
69
74
  interface CalendarViewModel extends CalendarState {
70
75
  months: string[];
71
76
  days: string[];
72
77
  years: number[];
73
78
  monthAndYearText: string;
74
79
  scheduleDay: string;
80
+ panes: CalendarPane[];
75
81
  calendarDates: CalendarDate[][];
76
- popupPositionClass: string;
77
82
  }
78
83
  type CalendarEventHandler = (event: CalendarEvent) => void;
79
84
  interface CalendarProps {
@@ -98,7 +103,39 @@ interface CalendarTheme {
98
103
  borderColor?: string;
99
104
  todayOutline?: string;
100
105
  selectedBg?: string;
106
+ headerBg?: string;
107
+ popupBg?: string;
108
+ pickerBg?: string;
109
+ pickerShadow?: string;
101
110
  eventIndicator?: string;
111
+ onAccent?: string;
112
+ link?: string;
113
+ openBg?: string;
114
+ openFg?: string;
115
+ conditionalBg?: string;
116
+ conditionalFg?: string;
117
+ blockedBg?: string;
118
+ blockedFg?: string;
119
+ rangeBg?: string;
120
+ rangeOutline?: string;
121
+ inRangeBg?: string;
122
+ inRangeOutline?: string;
123
+ badgeBg?: string;
124
+ badgeText?: string;
125
+ badgeSuccessBg?: string;
126
+ badgeSuccessText?: string;
127
+ badgeInfoBg?: string;
128
+ badgeInfoText?: string;
129
+ badgeWarningBg?: string;
130
+ badgeWarningText?: string;
131
+ badgeDangerBg?: string;
132
+ badgeDangerText?: string;
133
+ badgeNeutralBg?: string;
134
+ badgeNeutralText?: string;
135
+ badgePositiveBg?: string;
136
+ badgePositiveText?: string;
137
+ badgeTentativeBg?: string;
138
+ badgeTentativeText?: string;
102
139
  }
103
140
 
104
141
  declare const MONTHS: string[];
@@ -111,7 +148,6 @@ declare function generateYears(minYear?: number, maxYear?: number): number[];
111
148
  declare function getEventsForDate(events: CalendarEvent[], date: Date): CalendarEvent[];
112
149
  declare function hasEvents(events: CalendarEvent[], date: Date): boolean;
113
150
  declare function generateCalendarDates(year: number, month: number, events?: CalendarEvent[], weekStartsOn?: 0 | 1): CalendarDate[][];
114
- declare function getPopupPositionClass(selectedDayIndex: number | null): string;
115
151
  declare function getCellClasses(calendarDate: CalendarDate): string[];
116
152
  declare function formatDateForDisplay(date: Date): string;
117
153
  declare function getMonthYearText(year: number, month: number): string;
@@ -123,6 +159,10 @@ declare function getDefaultEventColor(category?: string, customColors?: Category
123
159
  declare function mergeCategoryColors(customColors?: CategoryColorMap): CategoryColorMap;
124
160
  declare function isValidHexColor(color: string): boolean;
125
161
  declare function getCategoryColor(category: string, customColors?: CategoryColorMap): string;
162
+ declare function escapeHtml(value: unknown): string;
163
+ declare function slugifyToken(value: string): string;
164
+ declare function safeUrl(value: string): string;
165
+ declare function safeColor(value: string): string;
126
166
 
127
167
  declare class CalendarEngine {
128
168
  private state;
@@ -220,4 +260,4 @@ declare class CalendarEngine {
220
260
  destroy(): void;
221
261
  }
222
262
 
223
- export { type CalendarActions, type CalendarConfig, type CalendarDate, CalendarEngine, type CalendarEvent, type CalendarEventHandler, type CalendarProps, type CalendarState, type CalendarTheme, type CalendarViewModel, type CategoryColorMap, DAYS, DEFAULT_CATEGORY_COLORS, MONTHS, MONTHS_FULL, type PopupPosition, formatAttendees, formatDateForDisplay, formatTimeRange, generateCalendarDates, generateYears, getCategoryColor, getCellClasses, getDefaultEventColor, getEventsForDate, getMonthYearText, getPopupPositionClass, hasEvents, isSameDay, isToday, isValidHexColor, mergeCategoryColors, normalizeDate, sortEventsByTime };
263
+ export { type CalendarActions, type CalendarConfig, type CalendarDate, CalendarEngine, type CalendarEvent, type CalendarEventHandler, type CalendarPane, type CalendarProps, type CalendarState, type CalendarTheme, type CalendarViewModel, type CategoryColorMap, DAYS, DEFAULT_CATEGORY_COLORS, MONTHS, MONTHS_FULL, escapeHtml, formatAttendees, formatDateForDisplay, formatTimeRange, generateCalendarDates, generateYears, getCategoryColor, getCellClasses, getDefaultEventColor, getEventsForDate, getMonthYearText, hasEvents, isSameDay, isToday, isValidHexColor, mergeCategoryColors, normalizeDate, safeColor, safeUrl, slugifyToken, sortEventsByTime };
@@ -1,3 +1,4 @@
1
+ type Open<T extends string> = T | (string & {});
1
2
  interface CalendarEvent {
2
3
  id: string | number;
3
4
  name: string;
@@ -7,11 +8,12 @@ interface CalendarEvent {
7
8
  allDay?: boolean;
8
9
  description?: string;
9
10
  color?: string;
10
- category?: 'work' | 'personal' | 'meeting' | 'deadline' | 'appointment' | 'other';
11
+ category?: Open<'work' | 'personal' | 'meeting' | 'deadline' | 'appointment' | 'other'>;
11
12
  location?: string;
12
13
  url?: string;
13
- status?: 'scheduled' | 'completed' | 'cancelled' | 'tentative';
14
- priority?: 'low' | 'medium' | 'high';
14
+ status?: Open<'scheduled' | 'completed' | 'cancelled' | 'tentative'>;
15
+ priority?: Open<'low' | 'medium' | 'high'>;
16
+ availabilityStatus?: Open<'open' | 'conditional' | 'blocked'>;
15
17
  attendees?: string[];
16
18
  organizer?: string;
17
19
  reminders?: number[];
@@ -52,6 +54,13 @@ interface CalendarConfig {
52
54
  maxYear?: number;
53
55
  weekStartsOn?: 0 | 1;
54
56
  categoryColors?: CategoryColorMap;
57
+ monthCount?: number;
58
+ }
59
+ interface CalendarPane {
60
+ year: number;
61
+ month: number;
62
+ monthAndYearText: string;
63
+ calendarDates: CalendarDate[][];
55
64
  }
56
65
  interface CalendarActions {
57
66
  next: () => void;
@@ -62,18 +71,14 @@ interface CalendarActions {
62
71
  selectDate: (date: Date) => void;
63
72
  updateTasks: () => void;
64
73
  }
65
- interface PopupPosition {
66
- class: 'popup-left' | 'popup-right' | 'popup-center-top' | 'popup-center-bottom';
67
- style?: Record<string, string | number>;
68
- }
69
74
  interface CalendarViewModel extends CalendarState {
70
75
  months: string[];
71
76
  days: string[];
72
77
  years: number[];
73
78
  monthAndYearText: string;
74
79
  scheduleDay: string;
80
+ panes: CalendarPane[];
75
81
  calendarDates: CalendarDate[][];
76
- popupPositionClass: string;
77
82
  }
78
83
  type CalendarEventHandler = (event: CalendarEvent) => void;
79
84
  interface CalendarProps {
@@ -98,7 +103,39 @@ interface CalendarTheme {
98
103
  borderColor?: string;
99
104
  todayOutline?: string;
100
105
  selectedBg?: string;
106
+ headerBg?: string;
107
+ popupBg?: string;
108
+ pickerBg?: string;
109
+ pickerShadow?: string;
101
110
  eventIndicator?: string;
111
+ onAccent?: string;
112
+ link?: string;
113
+ openBg?: string;
114
+ openFg?: string;
115
+ conditionalBg?: string;
116
+ conditionalFg?: string;
117
+ blockedBg?: string;
118
+ blockedFg?: string;
119
+ rangeBg?: string;
120
+ rangeOutline?: string;
121
+ inRangeBg?: string;
122
+ inRangeOutline?: string;
123
+ badgeBg?: string;
124
+ badgeText?: string;
125
+ badgeSuccessBg?: string;
126
+ badgeSuccessText?: string;
127
+ badgeInfoBg?: string;
128
+ badgeInfoText?: string;
129
+ badgeWarningBg?: string;
130
+ badgeWarningText?: string;
131
+ badgeDangerBg?: string;
132
+ badgeDangerText?: string;
133
+ badgeNeutralBg?: string;
134
+ badgeNeutralText?: string;
135
+ badgePositiveBg?: string;
136
+ badgePositiveText?: string;
137
+ badgeTentativeBg?: string;
138
+ badgeTentativeText?: string;
102
139
  }
103
140
 
104
141
  declare const MONTHS: string[];
@@ -111,7 +148,6 @@ declare function generateYears(minYear?: number, maxYear?: number): number[];
111
148
  declare function getEventsForDate(events: CalendarEvent[], date: Date): CalendarEvent[];
112
149
  declare function hasEvents(events: CalendarEvent[], date: Date): boolean;
113
150
  declare function generateCalendarDates(year: number, month: number, events?: CalendarEvent[], weekStartsOn?: 0 | 1): CalendarDate[][];
114
- declare function getPopupPositionClass(selectedDayIndex: number | null): string;
115
151
  declare function getCellClasses(calendarDate: CalendarDate): string[];
116
152
  declare function formatDateForDisplay(date: Date): string;
117
153
  declare function getMonthYearText(year: number, month: number): string;
@@ -123,6 +159,10 @@ declare function getDefaultEventColor(category?: string, customColors?: Category
123
159
  declare function mergeCategoryColors(customColors?: CategoryColorMap): CategoryColorMap;
124
160
  declare function isValidHexColor(color: string): boolean;
125
161
  declare function getCategoryColor(category: string, customColors?: CategoryColorMap): string;
162
+ declare function escapeHtml(value: unknown): string;
163
+ declare function slugifyToken(value: string): string;
164
+ declare function safeUrl(value: string): string;
165
+ declare function safeColor(value: string): string;
126
166
 
127
167
  declare class CalendarEngine {
128
168
  private state;
@@ -220,4 +260,4 @@ declare class CalendarEngine {
220
260
  destroy(): void;
221
261
  }
222
262
 
223
- export { type CalendarActions, type CalendarConfig, type CalendarDate, CalendarEngine, type CalendarEvent, type CalendarEventHandler, type CalendarProps, type CalendarState, type CalendarTheme, type CalendarViewModel, type CategoryColorMap, DAYS, DEFAULT_CATEGORY_COLORS, MONTHS, MONTHS_FULL, type PopupPosition, formatAttendees, formatDateForDisplay, formatTimeRange, generateCalendarDates, generateYears, getCategoryColor, getCellClasses, getDefaultEventColor, getEventsForDate, getMonthYearText, getPopupPositionClass, hasEvents, isSameDay, isToday, isValidHexColor, mergeCategoryColors, normalizeDate, sortEventsByTime };
263
+ export { type CalendarActions, type CalendarConfig, type CalendarDate, CalendarEngine, type CalendarEvent, type CalendarEventHandler, type CalendarPane, type CalendarProps, type CalendarState, type CalendarTheme, type CalendarViewModel, type CategoryColorMap, DAYS, DEFAULT_CATEGORY_COLORS, MONTHS, MONTHS_FULL, escapeHtml, formatAttendees, formatDateForDisplay, formatTimeRange, generateCalendarDates, generateYears, getCategoryColor, getCellClasses, getDefaultEventColor, getEventsForDate, getMonthYearText, hasEvents, isSameDay, isToday, isValidHexColor, mergeCategoryColors, normalizeDate, safeColor, safeUrl, slugifyToken, sortEventsByTime };