@nomideusz/svelte-calendar 1.3.0 → 1.4.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 +164 -91
- package/dist/adapters/mapped.d.ts +7 -7
- package/dist/adapters/mapped.js +5 -2
- package/dist/adapters/memory.d.ts +7 -6
- package/dist/adapters/memory.js +3 -4
- package/dist/adapters/recurring.d.ts +6 -6
- package/dist/adapters/recurring.js +12 -14
- package/dist/calendar/Calendar.svelte +65 -18
- package/dist/calendar/Calendar.svelte.d.ts +1 -1
- package/dist/core/locale.d.ts +5 -0
- package/dist/core/locale.js +68 -17
- package/dist/primitives/DayHeader.svelte +9 -9
- package/dist/primitives/EmptySlot.svelte +6 -6
- package/dist/primitives/EventBlock.svelte +19 -19
- package/dist/primitives/FloatingPanel.svelte +4 -4
- package/dist/primitives/NowIndicator.svelte +5 -5
- package/dist/primitives/TimeGutter.svelte +4 -4
- package/dist/theme/auto.d.ts +5 -0
- package/dist/theme/auto.js +32 -4
- package/dist/views/agenda/AgendaDay.svelte +83 -74
- package/dist/views/agenda/AgendaWeek.svelte +53 -52
- package/dist/views/mobile/MobileDay.svelte +34 -34
- package/dist/views/mobile/MobileWeek.svelte +41 -41
- package/dist/views/month/MonthGrid.svelte +49 -23
- package/dist/views/planner/PlannerScroll.svelte +47 -47
- package/dist/views/planner/PlannerWeek.svelte +101 -73
- package/dist/views/shared/context.svelte.d.ts +2 -0
- package/dist/views/shared/context.svelte.js +1 -0
- package/package.json +1 -1
- package/widget/widget.js +376 -223
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@nomideusz/svelte-calendar) [](https://github.com/nomideusz/svelte-calendar/blob/main/LICENSE)
|
|
4
4
|
|
|
5
|
-
A themeable **Svelte 5** calendar: day and week **Planner** time grids, a multi-week **Scroll** view, **Agenda** lists, a **Month** grid and touch-first **Mobile** layouts
|
|
5
|
+
A themeable **Svelte 5** calendar: day and week **Planner** time grids, a multi-week **Scroll** view, **Agenda** lists, a **Month** grid and touch-first **Mobile** layouts, with drag to move, resize and create events, timezones, a headless API, an embeddable web component, and a smart auto-theme that adapts to any page.
|
|
6
6
|
|
|
7
7
|
**[Live demo → svelte-calendar.xyz](https://svelte-calendar.xyz/)**
|
|
8
8
|
|
|
@@ -14,7 +14,7 @@ pnpm add @nomideusz/svelte-calendar
|
|
|
14
14
|
|
|
15
15
|
Requirements:
|
|
16
16
|
|
|
17
|
-
- **Svelte `^5.29.0`** (peer dependency
|
|
17
|
+
- **Svelte `^5.29.0`** (peer dependency: the package uses attachments, `{@attach}`).
|
|
18
18
|
- Runtime dependencies (installed for you): `date-fns`, `date-fns-tz`, `@chenglou/pretext`.
|
|
19
19
|
- Node ≥ 22 is only needed to develop the package itself, not to use it.
|
|
20
20
|
|
|
@@ -28,30 +28,43 @@ Requirements:
|
|
|
28
28
|
const today = new Date();
|
|
29
29
|
const at = (hour) => new Date(today.getFullYear(), today.getMonth(), today.getDate(), hour);
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
let events = $state([
|
|
32
32
|
{ id: '1', title: 'Team Sync', start: at(9), end: at(10) },
|
|
33
33
|
{ id: '2', title: 'Lunch', start: at(12), end: at(13) },
|
|
34
34
|
]);
|
|
35
|
+
// A new adapter whenever the events change; the calendar reloads without blanking
|
|
36
|
+
const adapter = $derived(createMemoryAdapter(events));
|
|
37
|
+
|
|
38
|
+
// Drag on empty time (or press Enter on a day column) to create
|
|
39
|
+
function create({ start, end }) {
|
|
40
|
+
events = [...events, { id: crypto.randomUUID(), title: 'New event', start, end }];
|
|
41
|
+
}
|
|
42
|
+
// Drag an event to move it, or its edge to resize it
|
|
43
|
+
function move(event, start, end) {
|
|
44
|
+
events = events.map((e) => (e.id === event.id ? { ...e, start, end } : e));
|
|
45
|
+
}
|
|
35
46
|
</script>
|
|
36
47
|
|
|
37
|
-
<Calendar {adapter} view="week-planner" />
|
|
48
|
+
<Calendar {adapter} view="week-planner" oneventcreate={create} oneventmove={move} />
|
|
38
49
|
```
|
|
39
50
|
|
|
40
|
-
That's
|
|
51
|
+
That's a working, editable calendar. The calendar stores nothing on create: without `oneventcreate`, dragging on empty time does nothing. Moves and resizes write to the adapter by themselves, and `oneventmove` keeps your own data in step, so a move survives the next new adapter. The default `auto` theme probes your page's background, accent color and fonts, so the calendar adapts to any design. Events without a `color` draw in ink; to color them automatically, give the adapter a [`palette`](#event-colors).
|
|
41
52
|
|
|
42
53
|
## Views
|
|
43
54
|
|
|
44
|
-
The
|
|
55
|
+
Without `view` the calendar opens on `day-planner`, the first registered view. The Quick Start, the demo, the widget and `createCalendar` open on `week-planner`, so pass `view="week-planner"` for the week grid.
|
|
56
|
+
|
|
57
|
+
The default registry (`defaultViews`) holds eight views. Six are the ones you choose with `view`; the two `*-mobile` views are what the others swap to in a narrow container (see [Mobile](#mobile)), so the demo's view list shows six.
|
|
45
58
|
|
|
46
59
|
| View ID | Mode | What it is |
|
|
47
60
|
|---------|------|------------|
|
|
48
|
-
| `day-planner` | day | Vertical time grid, one column.
|
|
61
|
+
| `day-planner` | day | Vertical time grid, one column. The default when `view` is unset. |
|
|
49
62
|
| `week-planner` | week | The same time grid, one column per day (`days`, default 7). |
|
|
50
63
|
| `week-scroll` | week | Weeks stacked in one infinite vertical scroller (Hey-Calendar style): each chip sits at the height of its start time, month names run down the side. Drag to move, drop onto a day; it scrolls under a drag. |
|
|
51
64
|
| `day-agenda` | day | Live list for one day: now / up next / done. |
|
|
52
65
|
| `week-agenda` | week | List of the week's days (`columns` turns it into a timetable grid). |
|
|
53
|
-
| `day-mobile` | day | Touch-first time grid with swipe navigation. |
|
|
54
|
-
| `week-mobile` | week | Touch-first list of the week's days. |
|
|
66
|
+
| `day-mobile` | day | Touch-first time grid with swipe navigation. Used automatically in a narrow container. |
|
|
67
|
+
| `week-mobile` | week | Touch-first list of the week's days. Used automatically in a narrow container. |
|
|
55
68
|
| `month-grid` | month | Week-aligned month grid, chips per day with a "+N more" overflow. |
|
|
56
69
|
|
|
57
70
|
```svelte
|
|
@@ -68,10 +81,10 @@ Users switch with the built-in pills: **Day / Week / Month** for the mode, plus
|
|
|
68
81
|
|
|
69
82
|
Planner views are designed for direct manipulation:
|
|
70
83
|
|
|
71
|
-
- **Move
|
|
72
|
-
- **Resize
|
|
73
|
-
- **Create
|
|
74
|
-
- **External drop
|
|
84
|
+
- **Move**: drag an event to another day or time; a ghost previews the target before the move commits.
|
|
85
|
+
- **Resize**: drag an event's top/bottom grip to change its duration; `minDuration`/`maxDuration` clamp the result.
|
|
86
|
+
- **Create**: press on empty canvas and sweep to draw a range, or click for a default-length slot. Both go through the same validation (blocked slots, disabled dates, `minDuration`/`maxDuration`) and then fire `oneventcreate`.
|
|
87
|
+
- **External drop**: an HTML5 drag from outside the calendar (a class chip, a template) dropped on the planner or `week-scroll` fires `onexternaldrop({ start, dataTransfer })`.
|
|
75
88
|
|
|
76
89
|
`week-scroll` supports move, click-to-create and external drops. In `week-agenda` with `columns` and an `oneventmove` handler, a card can be dragged to another day (it keeps its time of day); that move goes straight to `oneventmove` without writing the adapter.
|
|
77
90
|
|
|
@@ -81,8 +94,8 @@ The month grid shows up to three chips per day. Clicking a day or its "+N more"
|
|
|
81
94
|
|
|
82
95
|
When focus is inside the calendar:
|
|
83
96
|
|
|
84
|
-
- `t
|
|
85
|
-
- `←` /
|
|
97
|
+
- `t`: go to today.
|
|
98
|
+
- `←` / `→`: previous / next period (mirrored in RTL). Ignored while typing in a field or inside a pill group, where arrows move between pills.
|
|
86
99
|
- In `month-grid`: arrow keys move between days, `Enter` / `Space` opens the focused day (`ondayclick`).
|
|
87
100
|
- In `week-planner` / `day-planner`: the day columns are one tab stop; `←` / `→` move between days, `Enter` / `Space` creates an event (`oneventcreate`) at the next slot today, or 09:00. On a focused event, `Alt+↑` / `Alt+↓` move it one `snapInterval`, `Alt+←` / `Alt+→` move it a day, and `Alt+Shift+↑` / `Alt+Shift+↓` move its end; each goes through the same validation as a drag and fires `oneventmove`.
|
|
88
101
|
|
|
@@ -106,13 +119,13 @@ The `views` prop replaces the registry. Spread `defaultViews` to add your own vi
|
|
|
106
119
|
|
|
107
120
|
A `CalendarView` is `{ id, label, mode: 'day' | 'week' | 'month', component, props? }`. `label` names the view-type pill; views sharing a label across modes are what the Day/Week/Month pills switch between.
|
|
108
121
|
|
|
109
|
-
The component receives `CalendarViewProps` (plus your `props`)
|
|
122
|
+
The component receives `CalendarViewProps` (plus your `props`); declare only the ones it uses:
|
|
110
123
|
|
|
111
124
|
| Prop | Type | Notes |
|
|
112
125
|
|------|------|-------|
|
|
113
126
|
| `events` | `TimelineEvent[]` | Everything loaded for the current range |
|
|
114
127
|
| `style` | `string` | The active `--dt-*` theme string |
|
|
115
|
-
| `height` | `number \| null` |
|
|
128
|
+
| `height` | `number \| null` | Always `null`: the view fills its container, sized by the Calendar's own [`height`](#display) (default `600`) |
|
|
116
129
|
| `mode` | `'day' \| 'week' \| 'month'` | |
|
|
117
130
|
| `mondayStart` | `boolean` | |
|
|
118
131
|
| `locale` | `string \| undefined` | |
|
|
@@ -143,14 +156,14 @@ The component receives `CalendarViewProps` (plus your `props`) — declare only
|
|
|
143
156
|
|
|
144
157
|
`useCalendarContext()` (type `CalendarContext`) gives a view the running Calendar's engines and config: `viewState`, `drag`, `commitDrag`, `labels`, `readOnly`, `snapInterval`, `minColumnWidth`, `blockedSlots`, `disabledDates` / `disabledSet`, `minDuration` / `maxDuration`, `hideDays`, `equalDays`, `showDates`, `compact`, `columns`, `isMobile`, `autoHeight`, `timezone`, `oneventmove`, `oneventhover`, `ondayclick`, `eventSnippet`, `emptySnippet`, `dayHeaderSnippet` and `loadRange` (a view can widen the range the store loads). Call it at component init, inside a `<Calendar>`.
|
|
145
158
|
|
|
146
|
-
The raw view components
|
|
159
|
+
The raw view components (`Planner`, `PlannerScroll`, `Agenda`, `Mobile`, `MonthGrid`) are exported so you can register them under your own ids. Building blocks for custom views are exported as primitives: `EventBlock` (an event card), `TimeGutter` (hour labels), `DayHeader`, `NowIndicator`, `EmptySlot` (click-to-create target) and `FloatingPanel` (see [Event details panel](#event-details-panel)).
|
|
147
160
|
|
|
148
161
|
## Mobile
|
|
149
162
|
|
|
150
163
|
When the calendar's **container** is narrower than 768px, it swaps to touch-first Mobile views with swipe navigation and a compact header:
|
|
151
164
|
|
|
152
165
|
```svelte
|
|
153
|
-
<!-- Auto-detect (default)
|
|
166
|
+
<!-- Auto-detect (default): switches at a 768px container width -->
|
|
154
167
|
<Calendar {adapter} view="week-planner" />
|
|
155
168
|
|
|
156
169
|
<!-- Force mobile layout -->
|
|
@@ -160,10 +173,10 @@ When the calendar's **container** is narrower than 768px, it swaps to touch-firs
|
|
|
160
173
|
<Calendar {adapter} mobile={false} />
|
|
161
174
|
```
|
|
162
175
|
|
|
163
|
-
- **MobileDay
|
|
164
|
-
- **MobileWeek
|
|
176
|
+
- **MobileDay**: vertical time grid with hour labels, swipe left/right to change days, all-day chips at the top, tap-to-create, stable columns for overlapping events.
|
|
177
|
+
- **MobileWeek**: vertical day list with relative labels (Today, Tomorrow, …) and accessible event buttons in each row.
|
|
165
178
|
|
|
166
|
-
In mobile mode every view except `*-agenda` and `*-mobile` is swapped for the `{mode}-mobile` entry of the same mode when one is registered
|
|
179
|
+
In mobile mode every view except `*-agenda` and `*-mobile` is swapped for the `{mode}-mobile` entry of the same mode when one is registered, custom views included. Agenda views keep their list layout; `month-grid` has no mobile entry and stays. The mobile header shows only the Day/Week/Month pills.
|
|
167
180
|
|
|
168
181
|
With `'auto'`, the first render (and the server render) is the desktop layout; the container is measured on mount, so SSR hydrates cleanly.
|
|
169
182
|
|
|
@@ -185,9 +198,9 @@ With `'auto'`, the first render (and the server render) is the desktop layout; t
|
|
|
185
198
|
|
|
186
199
|
| Callback | When it fires |
|
|
187
200
|
|----------|---------------|
|
|
188
|
-
| `oneventclick(event, anchor?)` | An event is clicked. The event is also selected (highlighted across views). `anchor` is the clicked block's viewport rect where the view has one (planner, `week-scroll`, `month-grid`)
|
|
201
|
+
| `oneventclick(event, anchor?)` | An event is clicked. The event is also selected (highlighted across views). `anchor` is the clicked block's viewport rect where the view has one (planner, `week-scroll`, `month-grid`), for positioning a `FloatingPanel`. |
|
|
189
202
|
| `oneventcreate({ start, end })` | A drag-create or click-to-create passed validation. The calendar does **not** store anything: add the event to your data and hand the Calendar the updated adapter. |
|
|
190
|
-
| `oneventmove(event, start, end)` | A drag or resize committed
|
|
203
|
+
| `oneventmove(event, start, end)` | A drag or resize committed: **after** the adapter stored it, or when the adapter refused with a read-only error (the host owns persistence then; the block stays where it was dropped). Other adapter failures revert the block and go to `onerror`. (The `week-agenda` columns day-drag is the exception: it only calls `oneventmove`.) |
|
|
191
204
|
| `onexternaldrop({ start, dataTransfer })` | An outside HTML5 drag was dropped on the grid; `start` is the pointer's time, snapped. |
|
|
192
205
|
| `onviewchange(viewId)` | Once on mount, then on every view change. |
|
|
193
206
|
| `ondatechange(date)` | Once on mount, then whenever the focused date changes (navigation, scrolling, a tapped day). |
|
|
@@ -197,7 +210,7 @@ With `'auto'`, the first render (and the server render) is the desktop layout; t
|
|
|
197
210
|
|
|
198
211
|
`oneventcreate`, `oneventmove` and `onexternaldrop` receive real instants, also with [`timezone`](#timezone-support). None of the write callbacks fire when `readOnly` is set.
|
|
199
212
|
|
|
200
|
-
To show a newly created or moved event, update your data and give the Calendar a new adapter
|
|
213
|
+
To show a newly created or moved event, update your data and give the Calendar a new adapter. The store reloads on a new adapter identity without blanking the grid:
|
|
201
214
|
|
|
202
215
|
```svelte
|
|
203
216
|
<script lang="ts">
|
|
@@ -246,7 +259,7 @@ To lock single events, set `data.readOnly: true` on them: views refuse to move o
|
|
|
246
259
|
| Prop | Type | Default | Description |
|
|
247
260
|
|------|------|---------|-------------|
|
|
248
261
|
| `title` | `string` | `''` | Header text and dialog label |
|
|
249
|
-
| `anchor` | `FloatingPanelAnchor` | `{ x: 24, y: 24 }` | `{ x, y, width?, height? }
|
|
262
|
+
| `anchor` | `FloatingPanelAnchor` | `{ x: 24, y: 24 }` | `{ x, y, width?, height? }`; a `DOMRect` works as is |
|
|
250
263
|
| `width` | `number` | `440` | Panel width in px |
|
|
251
264
|
| `theme` | `string` | `''` | A `--dt-*` theme string, as the Calendar takes |
|
|
252
265
|
| `closeLabel` | `string` | `'Close'` | Close button label |
|
|
@@ -320,7 +333,7 @@ Disable specific dates:
|
|
|
320
333
|
/>
|
|
321
334
|
```
|
|
322
335
|
|
|
323
|
-
Disabled dates refuse creating or moving events into those days.
|
|
336
|
+
Disabled dates refuse creating or moving events into those days, which suits holidays or fully booked days. In the planner, agenda and mobile views their existing events stay visible and clickable; `month-grid` dims a disabled day, and its chips ignore clicks.
|
|
324
337
|
|
|
325
338
|
Custom day headers and hover previews:
|
|
326
339
|
|
|
@@ -358,7 +371,7 @@ Replace the built-in navigation or the entire header chrome with your own contro
|
|
|
358
371
|
</Calendar>
|
|
359
372
|
```
|
|
360
373
|
|
|
361
|
-
Let agenda content determine height instead of forcing a fixed box
|
|
374
|
+
Let agenda content determine height instead of forcing a fixed box, for a calendar inside a scrolling page:
|
|
362
375
|
|
|
363
376
|
```svelte
|
|
364
377
|
<Calendar {adapter} view="week-agenda" height="auto" compact />
|
|
@@ -370,7 +383,7 @@ Three built-in presets:
|
|
|
370
383
|
|
|
371
384
|
| Preset | Description |
|
|
372
385
|
|--------|-------------|
|
|
373
|
-
| `auto` | **Default.** Probes the host page at mount
|
|
386
|
+
| `auto` | **Default.** Probes the host page at mount (background, accent color, fonts, light/dark mode) and writes matching `--dt-*` tokens. Re-probes when the host theme changes. |
|
|
374
387
|
| `neutral` | Explicit light theme. White bg, blue accent. Use when embedding standalone. |
|
|
375
388
|
| `midnight` | Explicit dark theme. Charcoal bg, red accent. |
|
|
376
389
|
|
|
@@ -390,11 +403,11 @@ Three built-in presets:
|
|
|
390
403
|
|
|
391
404
|
The default `auto` preset probes the page around the calendar and generates a theme that blends in. It detects:
|
|
392
405
|
|
|
393
|
-
- **Background color
|
|
394
|
-
- **Light/dark mode
|
|
395
|
-
- **Accent/brand color
|
|
396
|
-
- **Text color
|
|
397
|
-
- **Fonts
|
|
406
|
+
- **Background color**: from common CSS variables on `:root` (`--bg`, `--background`, `--color-bg`, `--bs-body-bg`, …), inline styles, or computed styles
|
|
407
|
+
- **Light/dark mode**: from background luminance
|
|
408
|
+
- **Accent/brand color**: from common CSS variables (`--accent`, `--primary`, `--brand`, `--bs-primary`, …), link colors, or button colors
|
|
409
|
+
- **Text color**: validated for contrast against the background
|
|
410
|
+
- **Fonts**: the host's computed font stack; the mono stack from `--font-mono` and similar variables
|
|
398
411
|
|
|
399
412
|
It watches for changes (a dark-mode toggle on `<html>`/`<body>`, the system color scheme) and updates automatically.
|
|
400
413
|
|
|
@@ -420,7 +433,7 @@ Fine-tune auto-detection with the `autoTheme` prop (`AutoThemeOptions`):
|
|
|
420
433
|
|
|
421
434
|
The calendar writes its theme inline on its own root element, so it **wins over** `--dt-*` values set on an ancestor or `:root`. With the default `auto` theme it writes a full probed set. To use your own values:
|
|
422
435
|
|
|
423
|
-
- **Change a few tokens
|
|
436
|
+
- **Change a few tokens**: append them to a preset (later declarations win):
|
|
424
437
|
|
|
425
438
|
```ts
|
|
426
439
|
import { neutral } from '@nomideusz/svelte-calendar';
|
|
@@ -432,8 +445,8 @@ The calendar writes its theme inline on its own root element, so it **wins over*
|
|
|
432
445
|
<Calendar {adapter} theme={custom} />
|
|
433
446
|
```
|
|
434
447
|
|
|
435
|
-
- **Write your own theme string
|
|
436
|
-
- **Inherit from the page
|
|
448
|
+
- **Write your own theme string**: any `--dt-*` declarations; tokens you leave out are inherited from ancestors or fall back to neutral defaults.
|
|
449
|
+
- **Inherit from the page**: pass `autoTheme={false}` (keeping the default `auto` theme). The calendar then writes no tokens and reads whatever `--dt-*` its ancestors define:
|
|
437
450
|
|
|
438
451
|
```svelte
|
|
439
452
|
<div style="--dt-accent: #e11d48; --dt-bg: #1a1a2e; --dt-text: rgba(255,255,255,0.87);">
|
|
@@ -457,6 +470,7 @@ Component fallbacks use system fonts and a neutral blue accent, so the package s
|
|
|
457
470
|
| `--dt-text-3` | Tertiary text |
|
|
458
471
|
| `--dt-accent` | Accent color |
|
|
459
472
|
| `--dt-accent-dim` | Accent at ~12% opacity (selection, highlights) |
|
|
473
|
+
| `--dt-event` | Bar color of an event without its own `color` (the presets set it to the text color, so such events draw in ink) |
|
|
460
474
|
| `--dt-accent-fg` | Text on the planner's today-number badge (default `#fff`; not set by the presets) |
|
|
461
475
|
| `--dt-btn-text` | Text on accent-filled buttons and badges |
|
|
462
476
|
| `--dt-glow` | Accent glow / focus ring |
|
|
@@ -481,8 +495,8 @@ Component fallbacks use system fonts and a neutral blue accent, so the package s
|
|
|
481
495
|
| `id` | `string` | Unique identifier |
|
|
482
496
|
| `title` | `string` | Event title |
|
|
483
497
|
| `start` / `end` | `Date` | Time range |
|
|
484
|
-
| `color` | `string?` |
|
|
485
|
-
| `category` | `string?` | Grouping key
|
|
498
|
+
| `color` | `string?` | The event's bar color. Without it the event draws in `--dt-event` (ink), or takes a color from the adapter's `palette` |
|
|
499
|
+
| `category` | `string?` | Grouping key: with a `palette`, events of the same category share a color |
|
|
486
500
|
| `subtitle` | `string?` | Secondary text below the title |
|
|
487
501
|
| `tags` | `string[]?` | Small accent-colored pills |
|
|
488
502
|
| `allDay` | `boolean?` | Render as an all-day event |
|
|
@@ -494,19 +508,25 @@ Component fallbacks use system fonts and a neutral blue accent, so the package s
|
|
|
494
508
|
|
|
495
509
|
Cancelled events render with a strikethrough but stay visible, so the slot isn't mistaken for free time.
|
|
496
510
|
|
|
497
|
-
###
|
|
511
|
+
### Event colors
|
|
512
|
+
|
|
513
|
+
An event with a `color` draws its bar in that color. An event without one draws in ink: `--dt-event`, which the presets set to the text color. Nothing is colored for you unless you ask.
|
|
498
514
|
|
|
499
|
-
|
|
515
|
+
To color events automatically, pass a `palette` to the memory, recurring or mapped adapter. Events without a `color` then take a palette color, grouped by `category` (or `title`):
|
|
500
516
|
|
|
501
517
|
```ts
|
|
518
|
+
import { createMemoryAdapter, VIVID_PALETTE } from '@nomideusz/svelte-calendar';
|
|
519
|
+
|
|
502
520
|
const events = [
|
|
503
521
|
{ id: '1', title: 'Yoga', category: 'wellness', start, end },
|
|
504
522
|
{ id: '2', title: 'Pilates', category: 'wellness', start, end }, // same color
|
|
505
523
|
{ id: '3', title: 'Standup', start, end }, // different color
|
|
506
524
|
];
|
|
525
|
+
|
|
526
|
+
const adapter = createMemoryAdapter(events, { palette: VIVID_PALETTE });
|
|
507
527
|
```
|
|
508
528
|
|
|
509
|
-
|
|
529
|
+
Or generate a theme-harmonious palette from any accent color:
|
|
510
530
|
|
|
511
531
|
```ts
|
|
512
532
|
import { createMemoryAdapter, generatePalette } from '@nomideusz/svelte-calendar';
|
|
@@ -515,7 +535,7 @@ const palette = generatePalette('#e11d48'); // (accent?, count = 15)
|
|
|
515
535
|
const adapter = createMemoryAdapter(events, { palette });
|
|
516
536
|
```
|
|
517
537
|
|
|
518
|
-
Related exports: `VIVID_PALETTE` (the default
|
|
538
|
+
Related exports: `VIVID_PALETTE` (the vivid palette, the default before 1.4) and `extractAccent(themeString)` (the accent color of a `--dt-*` theme string).
|
|
519
539
|
|
|
520
540
|
### Multi-day & All-day
|
|
521
541
|
|
|
@@ -530,7 +550,7 @@ const events = [
|
|
|
530
550
|
|
|
531
551
|
### Custom Event Rendering
|
|
532
552
|
|
|
533
|
-
The `event` snippet replaces the event *content* in every view (the interactive shell
|
|
553
|
+
The `event` snippet replaces the event *content* in every view (the interactive shell for click, keyboard, drag and selection stays intact). The `empty` snippet replaces the empty state of the day agenda, the day on mobile, and an empty planner week:
|
|
534
554
|
|
|
535
555
|
```svelte
|
|
536
556
|
<Calendar {adapter}>
|
|
@@ -541,11 +561,25 @@ The `event` snippet replaces the event *content* in every view (the interactive
|
|
|
541
561
|
</div>
|
|
542
562
|
{/snippet}
|
|
543
563
|
{#snippet empty()}
|
|
544
|
-
<p>Nothing booked
|
|
564
|
+
<p>Nothing booked. Drag on the grid to add a class.</p>
|
|
545
565
|
{/snippet}
|
|
546
566
|
</Calendar>
|
|
547
567
|
```
|
|
548
568
|
|
|
569
|
+
## Adapters
|
|
570
|
+
|
|
571
|
+
The calendar reads its events through an adapter. Which one:
|
|
572
|
+
|
|
573
|
+
| Adapter | Use it when |
|
|
574
|
+
|---------|-------------|
|
|
575
|
+
| `createMemoryAdapter(events)` | The events are already in the page (a fixed list, your own state). Writes stay in memory. See [Quick Start](#quick-start). |
|
|
576
|
+
| [`createRecurringAdapter(rules)`](#recurring-schedules) | The schedule repeats by rule: class timetables, office hours. Read-only. |
|
|
577
|
+
| [`createRestAdapter(options)`](#rest-adapter) | The events live behind your own HTTP API, which also stores creates and moves. |
|
|
578
|
+
| [`createJmapAdapter(client, options)`](#jmap-adapter) | The events are in a JMAP Calendars server (Fastmail, Stalwart, Cyrus). Read-only. |
|
|
579
|
+
| [`createMappedAdapter(records, options)`](#mapped-adapter) | You have records from another system with their own field names: map the fields instead of writing an adapter. |
|
|
580
|
+
| [`createCompositeAdapter(adapters)`](#composite-adapter) | One calendar shows several sources, e.g. a recurring timetable plus one-off events. |
|
|
581
|
+
| [Custom `CalendarAdapter`](#custom-adapter) | None of the above fits: implement `fetchEvents`, plus the writes you need. |
|
|
582
|
+
|
|
549
583
|
## Recurring Schedules
|
|
550
584
|
|
|
551
585
|
For fixed repeating events (class timetables, office hours):
|
|
@@ -584,13 +618,13 @@ For fixed repeating events (class timetables, office hours):
|
|
|
584
618
|
| `until` | `string` | — | Last possible occurrence `"YYYY-MM-DD"` |
|
|
585
619
|
| `count` | `number` | — | Max occurrences from `startDate` (requires it). With `until`, the stricter wins. |
|
|
586
620
|
| `excludeDates` | `string[]` | — | `"YYYY-MM-DD"` dates this rule skips |
|
|
587
|
-
| `color` | `string` |
|
|
621
|
+
| `color` | `string` | — | Bar color; else from `palette`, else ink (`--dt-event`) |
|
|
588
622
|
| `subtitle` / `tags` / `category` / `location` / `resourceId` | | — | Copied to every occurrence |
|
|
589
623
|
| `data` | `Record<string, unknown>` | — | Copied to every occurrence, plus `recurringId` |
|
|
590
624
|
|
|
591
625
|
</details>
|
|
592
626
|
|
|
593
|
-
Options (`RecurringAdapterOptions`): `mondayStart` (default `true`), `palette
|
|
627
|
+
Options (`RecurringAdapterOptions`): `mondayStart` (default `true`), `palette` (colors rules without a `color`; off by default), `movable` (default `false`).
|
|
594
628
|
|
|
595
629
|
Occurrences get the id `` `${rule.id}--YYYYMMDD` `` and `data.recurringId`. The adapter projects and stores nothing, so by default every occurrence carries `data.readOnly` and can't be dragged (it can still be clicked). With `movable: true` the views allow the drag and the adapter refuses the write with a read-only error, so `oneventmove` fires and the host stores the move: add the date to the rule's `excludeDates` and keep the moved occurrence with your one-off events (for example the memory side of a [composite adapter](#composite-adapter)).
|
|
596
630
|
|
|
@@ -646,7 +680,7 @@ Read-only. Expands recurrences server-side and handles all-day events, ISO-durat
|
|
|
646
680
|
|
|
647
681
|
## Mapped Adapter
|
|
648
682
|
|
|
649
|
-
Wrap any static array of external records (class schedules, appointments, timetables) without writing a custom adapter. Supply a declarative field mapping
|
|
683
|
+
Wrap any static array of external records (class schedules, appointments, timetables) without writing a custom adapter. Supply a declarative field mapping (or a `mapEvent` function for full control) and the adapter handles parsing, colors, tags and status:
|
|
650
684
|
|
|
651
685
|
```ts
|
|
652
686
|
import { createMappedAdapter } from '@nomideusz/svelte-calendar';
|
|
@@ -667,7 +701,7 @@ const adapter = createMappedAdapter(rawClasses, {
|
|
|
667
701
|
|
|
668
702
|
- Works without options too: it looks for common keys (`id`, `title`/`name`, `start`/`end`, `starts_at_iso`/`ends_at_iso`, `date` + `start_time`/`end_time`, `room`/`location`, `is_cancelled`). A missing end defaults to one hour after the start.
|
|
669
703
|
- `start`/`end` accept ISO strings, `Date`s or millisecond timestamps. When records split date and time (`date: "2026-03-03"`, `startTime: "07:00"`), map `date` + `startTime` / `endTime`; times may be `7:00`, `07:00` or `07:00:00`.
|
|
670
|
-
-
|
|
704
|
+
- Source colors are kept. Records without one draw in ink, unless you pass a `palette` to fill them by category/title. `autoColor: true` ignores the source colors and colors every event from `palette` (default `VIVID_PALETTE`).
|
|
671
705
|
- `includeData` (default `'*'`) copies unmapped source fields into `event.data`; pass a list of keys to limit it.
|
|
672
706
|
|
|
673
707
|
For full control, pass `mapEvent(raw, index)` and skip `fields`:
|
|
@@ -689,7 +723,7 @@ Mapped adapters are read-only by default (writes throw a read-only error, so dra
|
|
|
689
723
|
|
|
690
724
|
## Composite Adapter
|
|
691
725
|
|
|
692
|
-
Merge several adapters into one
|
|
726
|
+
Merge several adapters into one, e.g. a recurring weekly schedule plus one-off events:
|
|
693
727
|
|
|
694
728
|
```ts
|
|
695
729
|
import { createMemoryAdapter, createRecurringAdapter, createCompositeAdapter } from '@nomideusz/svelte-calendar';
|
|
@@ -701,9 +735,9 @@ const adapter = createCompositeAdapter([memory, recurring]);
|
|
|
701
735
|
```
|
|
702
736
|
|
|
703
737
|
- **Reads** query every child in parallel and merge the results (the first event with a given id wins).
|
|
704
|
-
- **Creates** go to the primary child
|
|
738
|
+
- **Creates** go to the primary child: the first by default, or `primaryIndex`.
|
|
705
739
|
- **Updates and deletes** go to the child that returned the event. If that child can't write, the composite refuses with a read-only error (so `oneventmove` reaches the host). An id no fetch returned is offered to each writable child in turn.
|
|
706
|
-
- **`onFetchError(error, adapterIndex)
|
|
740
|
+
- **`onFetchError(error, adapterIndex)`**: with it, a failing child is reported to you and the others still load; without it, one failing child fails the whole load (surfacing through `onerror`).
|
|
707
741
|
|
|
708
742
|
```ts
|
|
709
743
|
const adapter = createCompositeAdapter([recurring, memory], {
|
|
@@ -724,7 +758,7 @@ const adapter: CalendarAdapter = {
|
|
|
724
758
|
fetchEvents: async (range: DateRange) => { /* return TimelineEvent[] */ },
|
|
725
759
|
// Optional: the same answer synchronously, when the data is already in memory
|
|
726
760
|
fetchEventsSync: (range) => { /* return TimelineEvent[], or undefined to go async */ },
|
|
727
|
-
// Optional writes
|
|
761
|
+
// Optional writes; omit them for a read-only source
|
|
728
762
|
createEvent: async (event) => { /* return the created TimelineEvent with its id */ },
|
|
729
763
|
updateEvent: async (id, patch) => { /* return the updated TimelineEvent */ },
|
|
730
764
|
deleteEvent: async (id) => { /* void */ },
|
|
@@ -733,7 +767,7 @@ const adapter: CalendarAdapter = {
|
|
|
733
767
|
|
|
734
768
|
`WritableCalendarAdapter` is the same interface with all three write methods required.
|
|
735
769
|
|
|
736
|
-
**`fetchEventsSync`** lets the first render
|
|
770
|
+
**`fetchEventsSync`** lets the first render (the server render too) hold the events instead of a loading state. The memory, recurring and seeded adapters implement it. To seed an async adapter with events you already loaded on the server, wrap it:
|
|
737
771
|
|
|
738
772
|
```ts
|
|
739
773
|
import { createRestAdapter, withInitialEvents } from '@nomideusz/svelte-calendar';
|
|
@@ -750,7 +784,7 @@ The Calendar tells two refusals apart when `updateEvent` throws:
|
|
|
750
784
|
|
|
751
785
|
| Throw | Meaning | What happens |
|
|
752
786
|
|-------|---------|--------------|
|
|
753
|
-
| `new CalendarReadOnlyError(message?)` | "I hold this event but can't store the change." | The block stays where it was dropped and `oneventmove` fires
|
|
787
|
+
| `new CalendarReadOnlyError(message?)` | "I hold this event but can't store the change." | The block stays where it was dropped and `oneventmove` fires; the host stores the move and refreshes. |
|
|
754
788
|
| `new EventNotFoundError(id)` | "Not my event." | A composite asks its next child; a drag ends quietly. |
|
|
755
789
|
| any other error | A real failure | The block reverts and the error goes to `onerror` (or `console.warn`). |
|
|
756
790
|
|
|
@@ -758,7 +792,7 @@ An adapter without `updateEvent` counts as read-only. Plain `Error`s whose messa
|
|
|
758
792
|
|
|
759
793
|
## Headless API
|
|
760
794
|
|
|
761
|
-
For full control over rendering, skip `<Calendar>` and drive everything from reactive state. `createCalendar()` returns computed layouts, navigation actions, drag helpers and the raw engines
|
|
795
|
+
For full control over rendering, skip `<Calendar>` and drive everything from reactive state. `createCalendar()` returns computed layouts, navigation actions, drag helpers and the raw engines. Zero DOM: bring your own UI. Call it during component initialisation.
|
|
762
796
|
|
|
763
797
|
```svelte
|
|
764
798
|
<script lang="ts">
|
|
@@ -793,7 +827,7 @@ Options (`HeadlessCalendarOptions`) mirror the Calendar props: `adapter`, `view`
|
|
|
793
827
|
|
|
794
828
|
`cal.days` gives flat `HeadlessDay[]` with events attached; `cal.weeks` groups them into periods (`HeadlessWeek`). `cal.todayQueue` returns `{ past, current, upcoming }` for today (`TodayQueue`), updated every second. `cal.hours` yields the visible hour numbers. Drag is fully wired: `beginDragMove`, `beginDragCreate`, `updateDrag`, `commitDrag` (validates like the Calendar), `cancelDrag`, plus `isDragging` / `dragPayload` / `dragMode`. `headerContext` / `navigationContext` match the Calendar's `header` / `navigation` snippet contexts. Raw engines (`store`, `viewState`, `selection`, `dragState`, `clock`) are exposed for advanced cases.
|
|
795
829
|
|
|
796
|
-
The engine factories are exported individually
|
|
830
|
+
The engine factories are exported individually (`createEventStore(adapter)`, `createViewState(options)`, `createSelection()`, `createDragState()`, `createClock(timezone?)`) if you want to compose your own calendar loop (types: `EventStore`, `ViewState`, `ViewStateOptions`, `CalendarSelection`, `DragState`, `DragMode`, `DragPayload`, `CalendarViewId`, `BuiltInViewId`, `ViewMode`).
|
|
797
831
|
|
|
798
832
|
### createAgenda
|
|
799
833
|
|
|
@@ -821,7 +855,7 @@ It returns `dayEvents`, `allDay`, `past`, `current`, `upcoming`, `upcomingSlots`
|
|
|
821
855
|
|
|
822
856
|
### createRangeAgenda
|
|
823
857
|
|
|
824
|
-
A window of days with events grouped per day
|
|
858
|
+
A window of days with events grouped per day; no clock, drag or selection. Built for read-only schedule surfaces (public timetables, embeds):
|
|
825
859
|
|
|
826
860
|
```svelte
|
|
827
861
|
<script lang="ts">
|
|
@@ -845,7 +879,7 @@ Options (`RangeAgendaOptions`): `adapter` (or a getter), `days` (default 7), `in
|
|
|
845
879
|
|
|
846
880
|
## Localization (i18n)
|
|
847
881
|
|
|
848
|
-
The `locale` prop controls date/time formatting (BCP 47). Without it, the global default locale is used
|
|
882
|
+
The `locale` prop controls date/time formatting (BCP 47). Without it, the global default locale is used: `'en-US'` unless you call `setDefaultLocale()`:
|
|
849
883
|
|
|
850
884
|
```svelte
|
|
851
885
|
<Calendar {adapter} locale="de-DE" />
|
|
@@ -853,7 +887,7 @@ The `locale` prop controls date/time formatting (BCP 47). Without it, the global
|
|
|
853
887
|
|
|
854
888
|
`setDefaultLocale('de-DE')` sets it for every calendar and formatter; `getDefaultLocale()` reads it back; `is24HourLocale(tag?)` tells you how times will format. An RTL locale (`ar-SA`, `he-IL`, …) sets `dir="rtl"` where the browser reports the locale's text direction (`Intl.Locale` text info); pass `dir` to be explicit.
|
|
855
889
|
|
|
856
|
-
Override UI labels per calendar with the `labels` prop
|
|
890
|
+
Override UI labels per calendar with the `labels` prop. It's reactive (swap it to switch language live) and instance-scoped (two calendars on one page can carry different languages):
|
|
857
891
|
|
|
858
892
|
```svelte
|
|
859
893
|
<Calendar {adapter} locale="de-DE" labels={{
|
|
@@ -871,7 +905,7 @@ import { setLabels } from '@nomideusz/svelte-calendar';
|
|
|
871
905
|
setLabels({ today: 'Heute', day: 'Tag', week: 'Woche' });
|
|
872
906
|
```
|
|
873
907
|
|
|
874
|
-
Both take a `Partial<CalendarLabels
|
|
908
|
+
Both take a `Partial<CalendarLabels>`, and that is the supported input: pass the keys you translate, and keys added in later minor releases fall back to English. The `labels` prop merges over the global set. `resetLabels()` restores English (`defaultLabels` exports it; `getLabels()` reads the active set). Global `setLabels()` calls after mount don't re-render mounted calendars; use the `labels` prop to switch language live. Standalone primitives (`DayHeader`, `EventBlock`, …) and the format helpers read the global set.
|
|
875
909
|
|
|
876
910
|
<details>
|
|
877
911
|
<summary>All label keys</summary>
|
|
@@ -911,7 +945,7 @@ Both take a `Partial<CalendarLabels>` — that is the supported input: pass the
|
|
|
911
945
|
|
|
912
946
|
## Timezone Support
|
|
913
947
|
|
|
914
|
-
Render the whole calendar in any IANA timezone
|
|
948
|
+
Render the whole calendar in any IANA timezone: events, the now-indicator and day boundaries all shift:
|
|
915
949
|
|
|
916
950
|
```svelte
|
|
917
951
|
<Calendar {adapter} timezone="Europe/Warsaw" />
|
|
@@ -919,7 +953,7 @@ Render the whole calendar in any IANA timezone — events, the now-indicator and
|
|
|
919
953
|
|
|
920
954
|
- `initialDate` and `currentDate` are instants, read in that zone.
|
|
921
955
|
- `oneventcreate`, `oneventmove` and `onexternaldrop` receive real instants.
|
|
922
|
-
- `ondatechange` and `ondayclick` receive instants too
|
|
956
|
+
- `ondatechange` and `ondayclick` receive instants too (the start of that day in the zone), so feeding one back is stable: `currentDate={date} ondatechange={(d) => (date = d)}`. To read the date, format it in the zone:
|
|
923
957
|
|
|
924
958
|
```ts
|
|
925
959
|
import { formatInTimeZone } from '@nomideusz/svelte-calendar';
|
|
@@ -970,20 +1004,20 @@ Small helpers used by the built-in views, exported for custom rendering. Formatt
|
|
|
970
1004
|
| `weekdayShort` / `weekdayLong` / `monthShort` / `monthLong` | Name parts of a timestamp |
|
|
971
1005
|
| `sod(ms)` | Start of that day (local midnight) |
|
|
972
1006
|
| `startOfWeek(ms, mondayStart = true)` | Start-of-week timestamp |
|
|
973
|
-
| `addDaysMs(ms, n)` / `diffDays(a, b)` | Add / count **calendar** days
|
|
1007
|
+
| `addDaysMs(ms, n)` / `diffDays(a, b)` | Add / count **calendar** days, DST-safe (a DST day is 23 or 25 hours, so don't add `n * 86_400_000`) |
|
|
974
1008
|
| `isAllDay(ev)` / `isMultiDay(ev)` | Event classification |
|
|
975
1009
|
| `segmentForDay(ev, dayMs)` | The slice of a multi-day event on one day (`DaySegment`), or `null` |
|
|
976
1010
|
| `createClock(timezone?)` | Reactive clock: `tick` (ms, every second), `today`, `hm`, `s`, `fractionalHour`, `destroy()` |
|
|
977
|
-
| `textWidth(text, font)` / `textHeight(text, font, width, lineHeight)` / `lineCount(text, font, width, options?)` / `fits(text, font, width, lines = 1)` | Text measurement via [pretext](https://github.com/chenglou/pretext)
|
|
1011
|
+
| `textWidth(text, font)` / `textHeight(text, font, width, lineHeight)` / `lineCount(text, font, width, options?)` / `fits(text, font, width, lines = 1)` | Text measurement via [pretext](https://github.com/chenglou/pretext), before render, no layout thrash. `options` is `TextLayoutOptions` (`whiteSpace`, `wordBreak`, `letterSpacing`). Browser-only. |
|
|
978
1012
|
| `pickFit(candidates, font, width, lines = 1)` | The longest candidate that fits |
|
|
979
1013
|
| `fitLabel(candidates, lines = 1)` | Attachment: `<span {@attach fitLabel([ev.title, ev.short])}>` keeps the longest label that fits its box, re-fitting on resize |
|
|
980
1014
|
| `fontOf(el)` | An element's computed font as a canvas font string |
|
|
981
1015
|
| `fitParts(parts, budget, measure?)` | Which parts of a dense chip fit, along either axis → `Record<key, boolean>`. Each `ChipPart` is `{ key, text, font }` to measure or `{ key, size }` to state its cost, plus `priority` (`0` is an anchor and never drops; higher numbers drop first) and `extra` (a gap or icon). The first optional part that doesn't fit ends the list, so a chip never keeps a later detail after dropping an earlier one. A budget of `0` (server-rendered, not yet measured) leaves only the anchors. |
|
|
982
|
-
| `typeset(text)` / `breakLines(text, font, width)` | Knuth-Plass paragraph breaking over the text's own spaces and soft hyphens. `<p {@attach typeset(text)}>` sets justified lines, re-done on resize; if the browser disagrees with the measure, the plain text goes back. Progressive
|
|
1016
|
+
| `typeset(text)` / `breakLines(text, font, width)` | Knuth-Plass paragraph breaking over the text's own spaces and soft hyphens. `<p {@attach typeset(text)}>` sets justified lines, re-done on resize; if the browser disagrees with the measure, the plain text goes back. Progressive: SSR text stays. |
|
|
983
1017
|
|
|
984
1018
|
## Embeddable Widget
|
|
985
1019
|
|
|
986
|
-
Drop into any HTML page
|
|
1020
|
+
Drop into any HTML page, no build tools needed. Registers a `<day-calendar>` custom element:
|
|
987
1021
|
|
|
988
1022
|
```html
|
|
989
1023
|
<script src="https://cdn.jsdelivr.net/npm/@nomideusz/svelte-calendar/widget/widget.js"></script>
|
|
@@ -1024,47 +1058,78 @@ Each event in `api` responses and `events` needs `start` and `end` as date strin
|
|
|
1024
1058
|
|
|
1025
1059
|
## All Props
|
|
1026
1060
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1061
|
+
Every `<Calendar>` prop (`CalendarProps`), grouped by what it controls.
|
|
1062
|
+
|
|
1063
|
+
### Data and editing
|
|
1029
1064
|
|
|
1030
1065
|
| Prop | Type | Default | Description |
|
|
1031
1066
|
|------|------|---------|-------------|
|
|
1032
1067
|
| `adapter` | `CalendarAdapter` | *required* | Data source (memory, recurring, mapped, composite, REST, JMAP, or custom) |
|
|
1033
|
-
| `views` | `readonly CalendarView[]` | `defaultViews` | View registry (the 8 built-ins). Spread `defaultViews` to add your own. |
|
|
1034
|
-
| `view` | `CalendarViewId` | first registered (`'day-planner'`) | Active view ID. Reactive. |
|
|
1035
|
-
| `theme` | `string` | `auto` | CSS theme string of `--dt-*` properties. `auto` probes the host page. |
|
|
1036
|
-
| `autoTheme` | `AutoThemeOptions \| false` | `undefined` (probe) | Fine-tune auto-detection: `{ mode, accent, font }`. `false` disables probing. |
|
|
1037
|
-
| `mobile` | `'auto' \| boolean` | `'auto'` | `'auto'` switches to mobile views below a 768px container width |
|
|
1038
|
-
| `height` | `number \| 'auto'` | `600` | Height in px, or `'auto'` to grow with content (ideal for Agenda views) |
|
|
1039
|
-
| `borderRadius` | `number` | `12` | Border radius in px. `0` for none. |
|
|
1040
|
-
| `locale` | `string` | global default (`'en-US'`) | BCP 47 locale tag; see `setDefaultLocale` |
|
|
1041
|
-
| `labels` | `Partial<CalendarLabels>` | — | Per-instance UI labels, merged over the global set. Reactive. |
|
|
1042
|
-
| `dir` | `'ltr' \| 'rtl' \| 'auto'` | from `locale` | Text direction |
|
|
1043
|
-
| `mondayStart` | `boolean` | `true` | Start weeks on Monday |
|
|
1044
1068
|
| `readOnly` | `boolean` | `false` | Disable drag, resize, click-to-create and external drops |
|
|
1045
|
-
| `
|
|
1069
|
+
| `snapInterval` | `number` | `15` | Drag snap in minutes |
|
|
1070
|
+
| `minDuration` | `number` | — | Minimum duration in minutes; clamps creates and resizes |
|
|
1071
|
+
| `maxDuration` | `number` | — | Maximum duration in minutes; clamps creates and resizes |
|
|
1072
|
+
| `blockedSlots` | `BlockedSlot[]` | — | `{ day?, start, end, label? }` hour ranges that can't be booked (hatched in planner views) |
|
|
1073
|
+
| `disabledDates` | `Date[]` | — | Dates that refuse creates/moves; existing events stay visible and clickable (dimmed and inert in `month-grid`) |
|
|
1074
|
+
|
|
1075
|
+
### View and navigation
|
|
1076
|
+
|
|
1077
|
+
| Prop | Type | Default | Description |
|
|
1078
|
+
|------|------|---------|-------------|
|
|
1079
|
+
| `view` | `CalendarViewId` | first registered (`'day-planner'`) | Active view ID. Reactive. |
|
|
1080
|
+
| `views` | `readonly CalendarView[]` | `defaultViews` | View registry (the eight built-ins). Spread `defaultViews` to add your own. |
|
|
1046
1081
|
| `initialDate` | `Date` | today | Date to focus at mount (an instant; read in `timezone`) |
|
|
1047
1082
|
| `currentDate` | `Date` | — | Controlled focus date (an instant; read in `timezone`) |
|
|
1048
|
-
| `timezone` | `string` | viewer's zone | IANA zone to render in. Read at mount. |
|
|
1049
|
-
| `snapInterval` | `number` | `15` | Drag snap in minutes |
|
|
1050
|
-
| `minColumnWidth` | `number` | `110` | Minimum day-column width (px) in planner views; below the total the grid scrolls horizontally |
|
|
1051
1083
|
| `days` | `number` | `7` | Days shown in week mode (e.g. `3` for a rolling 3-day view) |
|
|
1052
|
-
| `
|
|
1084
|
+
| `mobile` | `'auto' \| boolean` | `'auto'` | `'auto'` switches to mobile views below a 768px container width |
|
|
1053
1085
|
| `showModePills` | `boolean` | `true` | Show the Day/Week/Month pills and the view-type pills |
|
|
1054
1086
|
| `showNavigation` | `boolean` | `true` | Show prev/next/today |
|
|
1087
|
+
|
|
1088
|
+
### Display
|
|
1089
|
+
|
|
1090
|
+
| Prop | Type | Default | Description |
|
|
1091
|
+
|------|------|---------|-------------|
|
|
1092
|
+
| `height` | `number \| 'auto'` | `600` | Height in px, or `'auto'` to grow with content (ideal for Agenda views) |
|
|
1093
|
+
| `visibleHours` | `[number, number]` | — | Crop the grid to `[startHour, endHour)` |
|
|
1094
|
+
| `hideDays` | `number[]` | — | ISO weekdays to hide (1=Mon … 7=Sun), e.g. `[6, 7]` |
|
|
1095
|
+
| `mondayStart` | `boolean` | `true` | Start weeks on Monday |
|
|
1055
1096
|
| `showDates` | `boolean` | `true` | Show date numbers and the date label. `false` = day names only (templates, recurring schedules) |
|
|
1056
1097
|
| `equalDays` | `boolean` | `false` | Treat all days equally (no past-day dimming/collapsing) |
|
|
1057
|
-
| `blockedSlots` | `BlockedSlot[]` | — | `{ day?, start, end, label? }` hour ranges that can't be booked (hatched in planner views) |
|
|
1058
|
-
| `disabledDates` | `Date[]` | — | Dates that refuse creates/moves; existing events stay visible and clickable |
|
|
1059
|
-
| `minDuration` | `number` | — | Minimum duration in minutes; clamps creates and resizes |
|
|
1060
|
-
| `maxDuration` | `number` | — | Maximum duration in minutes; clamps creates and resizes |
|
|
1061
1098
|
| `compact` | `boolean` | `false` | Minimal text rows in Agenda views (dot + time + title) |
|
|
1062
1099
|
| `columns` | `boolean` | `false` | Timetable layout: `week-agenda` days side by side on desktop. Pairs with `equalDays`; overrides `compact` |
|
|
1100
|
+
| `minColumnWidth` | `number` | `110` | Minimum day-column width (px) in planner views; below the total the grid scrolls horizontally |
|
|
1101
|
+
|
|
1102
|
+
### Theming
|
|
1103
|
+
|
|
1104
|
+
| Prop | Type | Default | Description |
|
|
1105
|
+
|------|------|---------|-------------|
|
|
1106
|
+
| `theme` | `string` | `auto` | CSS theme string of `--dt-*` properties. `auto` probes the host page. |
|
|
1107
|
+
| `autoTheme` | `AutoThemeOptions \| false` | `undefined` (probe) | Fine-tune auto-detection: `{ mode, accent, font }`. `false` disables probing. |
|
|
1108
|
+
| `borderRadius` | `number` | `0` | Corner radius in px (square by default) |
|
|
1109
|
+
|
|
1110
|
+
### Locale and timezone
|
|
1111
|
+
|
|
1112
|
+
| Prop | Type | Default | Description |
|
|
1113
|
+
|------|------|---------|-------------|
|
|
1114
|
+
| `locale` | `string` | global default (`'en-US'`) | BCP 47 locale tag; see `setDefaultLocale` |
|
|
1115
|
+
| `labels` | `Partial<CalendarLabels>` | — | Per-instance UI labels, merged over the global set. Reactive. |
|
|
1116
|
+
| `dir` | `'ltr' \| 'rtl' \| 'auto'` | from `locale` | Text direction |
|
|
1117
|
+
| `timezone` | `string` | viewer's zone | IANA zone to render in. Read at mount. |
|
|
1118
|
+
|
|
1119
|
+
### Snippets
|
|
1120
|
+
|
|
1121
|
+
| Prop | Type | Default | Description |
|
|
1122
|
+
|------|------|---------|-------------|
|
|
1063
1123
|
| `event` | `Snippet<[TimelineEvent]>` | — | Custom event content |
|
|
1064
1124
|
| `empty` | `Snippet` | — | Empty state for the day agenda, mobile day and an empty planner week |
|
|
1065
1125
|
| `dayHeader` | `Snippet<[{ date, isToday, dayName }]>` | — | Custom day header |
|
|
1066
1126
|
| `header` | `Snippet<[HeaderContext]>` | — | Replace the whole header (`dateLabel`, `mode`, `modes`, `switchMode`, `prev`, `next`, `goToday`, `isViewOnToday`, `focusDate`) |
|
|
1067
1127
|
| `navigation` | `Snippet<[NavigationContext]>` | — | Replace prev/next/today (`prev`, `next`, `goToday`, `isViewOnToday`, `focusDate`, `mode`) |
|
|
1128
|
+
|
|
1129
|
+
### Callbacks
|
|
1130
|
+
|
|
1131
|
+
| Prop | Type | Default | Description |
|
|
1132
|
+
|------|------|---------|-------------|
|
|
1068
1133
|
| `oneventclick` | `(event, anchor?: DOMRect) => void` | — | Event clicked (and selected) |
|
|
1069
1134
|
| `oneventcreate` | `(range: { start, end }) => void` | — | A validated new range (real instants) |
|
|
1070
1135
|
| `oneventmove` | `(event, start, end) => void` | — | Drag/resize stored, or refused read-only (real instants) |
|
|
@@ -1075,7 +1140,15 @@ Each event in `api` responses and `events` needs `start` and `end` as date strin
|
|
|
1075
1140
|
| `oneventhover` | `(event) => void` | — | Pointer enters an event |
|
|
1076
1141
|
| `onerror` | `(error: Error) => void` | — | Load and commit failures |
|
|
1077
1142
|
|
|
1078
|
-
|
|
1143
|
+
## Troubleshooting
|
|
1144
|
+
|
|
1145
|
+
- **Dragging on empty time does nothing.** Creating needs an `oneventcreate` handler (and no `readOnly`); see the [Quick Start](#quick-start).
|
|
1146
|
+
- **A created event doesn't appear.** The calendar stores nothing on create: add the event to your data and hand the Calendar a new adapter (`$derived(createMemoryAdapter(events))`).
|
|
1147
|
+
- **Moved events jump back after the next change.** A new adapter is built from your array, so write each move into it in `oneventmove`.
|
|
1148
|
+
- **The `auto` theme picked the wrong colours.** It probes the host page around the calendar, and probes again when `<html>`/`<body>` change class, style or `data-theme`, when the system scheme flips and when a background transition ends. Steer it with [`autoTheme`](#smart-auto-theme), or set `theme` to a preset or your own tokens to opt out.
|
|
1149
|
+
- **Events are all one colour.** Since 1.4 events without a `color` draw in ink; give the adapter a [`palette`](#event-colors).
|
|
1150
|
+
- **The calendar stops at 600px.** That's the default `height`; pass a number, or `'auto'` to grow with the content.
|
|
1151
|
+
- **The phone layout shows on a wide page (or the desktop one in a narrow column).** The switch reads the calendar's container, not the window; force it with `mobile={false}` or `mobile={true}`.
|
|
1079
1152
|
|
|
1080
1153
|
## Versioning
|
|
1081
1154
|
|
|
@@ -1084,7 +1157,7 @@ The package follows [semver](https://semver.org/) from 1.0:
|
|
|
1084
1157
|
- The public API is exactly what the `.` and `./widget` entry points export. Deep imports into `dist/` are not supported.
|
|
1085
1158
|
- `TimelineEvent`, `CalendarAdapter` and `DateRange` are stable shapes: fields may be added in minor releases, never renamed, removed or given new meaning outside a major.
|
|
1086
1159
|
- The `--dt-*` theme tokens are part of the contract, as are the `<day-calendar>` attributes.
|
|
1087
|
-
- `CalendarLabels` gains keys in minor releases
|
|
1160
|
+
- `CalendarLabels` gains keys in minor releases, so pass a `Partial` (see [Localization](#localization-i18n)).
|
|
1088
1161
|
|
|
1089
1162
|
## Development
|
|
1090
1163
|
|