calkit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +734 -0
- package/dist/booking.es.js +1845 -0
- package/dist/booking.es.js.map +1 -0
- package/dist/booking.umd.js +888 -0
- package/dist/booking.umd.js.map +1 -0
- package/dist/calkit.es.js +5230 -0
- package/dist/calkit.es.js.map +1 -0
- package/dist/calkit.umd.js +2164 -0
- package/dist/calkit.umd.js.map +1 -0
- package/dist/datepicker.es.js +1619 -0
- package/dist/datepicker.es.js.map +1 -0
- package/dist/datepicker.umd.js +818 -0
- package/dist/datepicker.umd.js.map +1 -0
- package/dist/scheduler.es.js +3235 -0
- package/dist/scheduler.es.js.map +1 -0
- package/dist/scheduler.umd.js +1547 -0
- package/dist/scheduler.umd.js.map +1 -0
- package/dist/timepicker.es.js +962 -0
- package/dist/timepicker.es.js.map +1 -0
- package/dist/timepicker.umd.js +517 -0
- package/dist/timepicker.umd.js.map +1 -0
- package/llms.txt +594 -0
- package/package.json +58 -0
package/llms.txt
ADDED
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
# CalKit
|
|
2
|
+
|
|
3
|
+
> Vanilla JS web component library for date pickers, time pickers, booking calendars, and resource schedulers.
|
|
4
|
+
|
|
5
|
+
Package: calkit
|
|
6
|
+
Version: 0.1.0
|
|
7
|
+
Tags: <cal-datepicker>, <cal-timepicker>, <cal-booking>, <cal-scheduler>
|
|
8
|
+
Install (CDN script tag): <script src="https://cdn.jsdelivr.net/gh/SimonKefas/calkit/dist/calkit.umd.js"></script>
|
|
9
|
+
Install (Bundler ESM): import { CalDatepicker, CalBooking, CalTimepicker, CalScheduler } from 'calkit';
|
|
10
|
+
|
|
11
|
+
All date strings use "YYYY-MM-DD" format.
|
|
12
|
+
All time strings use "HH:MM" 24h format.
|
|
13
|
+
All color tokens use raw HSL channels consumed as hsl(var(--cal-...)).
|
|
14
|
+
All events are CustomEvents with { bubbles: true, composed: true }.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Component: cal-datepicker
|
|
19
|
+
|
|
20
|
+
Tag: <cal-datepicker>
|
|
21
|
+
Class: CalDatepicker
|
|
22
|
+
Extends: CalendarBase → HTMLElement
|
|
23
|
+
|
|
24
|
+
### Attributes (observedAttributes)
|
|
25
|
+
|
|
26
|
+
- mode: "single" | "multi" | "range" (default: "single")
|
|
27
|
+
- display: "inline" | "popover" (default: "inline")
|
|
28
|
+
- theme: "light" | "dark" | "auto" (default: "light")
|
|
29
|
+
- value: string (single: "2026-03-15", range: "2026-03-10/2026-03-15", multi: "2026-03-10,2026-03-12")
|
|
30
|
+
- min-date: string | null (default: null) — "YYYY-MM-DD" earliest selectable date
|
|
31
|
+
- max-date: string | null (default: null) — "YYYY-MM-DD" latest selectable date
|
|
32
|
+
- disabled-dates: string | null (default: null) — comma-separated "YYYY-MM-DD" list
|
|
33
|
+
- first-day: number (default: 0) — 0=Sunday, 1=Monday, ..., 6=Saturday
|
|
34
|
+
- locale: string | null (default: null)
|
|
35
|
+
- presets: string | null (default: null) — comma-separated keys: "today", "this-week", "next-7", "next-30"
|
|
36
|
+
- placeholder: string (default: "Select date") — popover trigger text
|
|
37
|
+
- dual: boolean attribute (default: absent) — show two months side-by-side in range mode
|
|
38
|
+
- loading: boolean attribute (default: absent) — show skeleton loading state
|
|
39
|
+
|
|
40
|
+
### JS Properties
|
|
41
|
+
|
|
42
|
+
- value: string | null (mode="single"), string[] (mode="multi"), {start: string, end: string} | null (mode="range") — read/write
|
|
43
|
+
- loading: boolean — read/write, reflects attribute
|
|
44
|
+
|
|
45
|
+
### Methods
|
|
46
|
+
|
|
47
|
+
- open(): void — open popover (popover display only)
|
|
48
|
+
- close(): void — close popover
|
|
49
|
+
- goToMonth(month: number, year: number): void — month is 0-indexed (0=Jan, 11=Dec)
|
|
50
|
+
- showStatus(type: "error"|"warning"|"info"|"success", message: string, opts?: {autoDismiss?: number, dismissible?: boolean}): void
|
|
51
|
+
- clearStatus(): void
|
|
52
|
+
|
|
53
|
+
### Events
|
|
54
|
+
|
|
55
|
+
- cal:change
|
|
56
|
+
- mode="single": detail = {value: string} — "YYYY-MM-DD"
|
|
57
|
+
- mode="range": detail = {value: {start: string, end: string}}
|
|
58
|
+
- mode="multi": detail = {value: string[]} — sorted array of "YYYY-MM-DD"
|
|
59
|
+
- cal:month-change — detail = {year: number, month: number}
|
|
60
|
+
- cal:open — detail = {}
|
|
61
|
+
- cal:close — detail = {}
|
|
62
|
+
- cal:status — detail = {type: string|null, message: string|null}
|
|
63
|
+
|
|
64
|
+
### Preset Keys
|
|
65
|
+
|
|
66
|
+
- "today" → {start: today, end: today}
|
|
67
|
+
- "this-week" → {start: Sunday, end: Saturday} of current week
|
|
68
|
+
- "next-7" → {start: today, end: today+6}
|
|
69
|
+
- "next-30" → {start: today, end: today+29}
|
|
70
|
+
|
|
71
|
+
### Example
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<cal-datepicker mode="range" dual presets="today,next-7,next-30" theme="light"></cal-datepicker>
|
|
75
|
+
<script>
|
|
76
|
+
const el = document.querySelector('cal-datepicker');
|
|
77
|
+
el.addEventListener('cal:change', (e) => {
|
|
78
|
+
const { start, end } = e.detail.value;
|
|
79
|
+
console.log(start, end);
|
|
80
|
+
});
|
|
81
|
+
</script>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Component: cal-timepicker
|
|
87
|
+
|
|
88
|
+
Tag: <cal-timepicker>
|
|
89
|
+
Class: CalTimepicker
|
|
90
|
+
Extends: CalendarBase → HTMLElement
|
|
91
|
+
|
|
92
|
+
### Attributes (observedAttributes)
|
|
93
|
+
|
|
94
|
+
- mode: "single" | "multi" | "range" (default: "single")
|
|
95
|
+
- display: "inline" | "popover" (default: "inline")
|
|
96
|
+
- theme: "light" | "dark" | "auto" (default: "light")
|
|
97
|
+
- start-time: string (default: "09:00") — "HH:MM"
|
|
98
|
+
- end-time: string (default: "17:00") — "HH:MM"
|
|
99
|
+
- interval: number (default: 30) — minutes between slots
|
|
100
|
+
- format: "12h" | "24h" (default: "24h")
|
|
101
|
+
- placeholder: string (default: "Select time") — popover trigger text
|
|
102
|
+
- value: string (single: "14:30", range: "09:00/12:00", multi: "09:00,10:30")
|
|
103
|
+
- duration-labels: boolean attribute (default: absent) — show duration labels
|
|
104
|
+
- loading: boolean attribute (default: absent)
|
|
105
|
+
|
|
106
|
+
### JS Properties
|
|
107
|
+
|
|
108
|
+
- value: string | null (mode="single"), string[] (mode="multi"), {start: string, end: string} | null (mode="range") — read/write
|
|
109
|
+
- slots: Array<{time: string, label?: string, available?: boolean}> | null — custom slot definitions, overrides attribute-based generation
|
|
110
|
+
- unavailableTimes: string[] — array of "HH:MM" strings to mark unavailable (default: [])
|
|
111
|
+
- loading: boolean — read/write
|
|
112
|
+
|
|
113
|
+
### Methods
|
|
114
|
+
|
|
115
|
+
- open(): void — open popover
|
|
116
|
+
- close(): void — close popover
|
|
117
|
+
- showStatus(type: "error"|"warning"|"info"|"success", message: string, opts?: {autoDismiss?: number, dismissible?: boolean}): void
|
|
118
|
+
- clearStatus(): void
|
|
119
|
+
|
|
120
|
+
### Events
|
|
121
|
+
|
|
122
|
+
- cal:time-change
|
|
123
|
+
- mode="single": detail = {value: string} — "HH:MM"
|
|
124
|
+
- mode="range": detail = {value: {start: string, end: string}}
|
|
125
|
+
- mode="multi": detail = {value: string[]} — sorted array of "HH:MM"
|
|
126
|
+
- cal:open — detail = {}
|
|
127
|
+
- cal:close — detail = {}
|
|
128
|
+
- cal:status — detail = {type: string|null, message: string|null}
|
|
129
|
+
|
|
130
|
+
### Example
|
|
131
|
+
|
|
132
|
+
```html
|
|
133
|
+
<cal-timepicker start-time="08:00" end-time="20:00" interval="60" format="12h" duration-labels></cal-timepicker>
|
|
134
|
+
<script>
|
|
135
|
+
const el = document.querySelector('cal-timepicker');
|
|
136
|
+
el.addEventListener('cal:time-change', (e) => {
|
|
137
|
+
console.log(e.detail.value); // "14:00"
|
|
138
|
+
});
|
|
139
|
+
</script>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Component: cal-booking
|
|
145
|
+
|
|
146
|
+
Tag: <cal-booking>
|
|
147
|
+
Class: CalBooking
|
|
148
|
+
Extends: CalendarBase → HTMLElement
|
|
149
|
+
|
|
150
|
+
### Attributes (observedAttributes)
|
|
151
|
+
|
|
152
|
+
- theme: "light" | "dark" | "auto" (default: "light")
|
|
153
|
+
- display: "inline" | "popover" (default: "inline")
|
|
154
|
+
- min-date: string | null (default: null)
|
|
155
|
+
- max-date: string | null (default: null)
|
|
156
|
+
- first-day: number (default: 0)
|
|
157
|
+
- placeholder: string (default: "Select dates")
|
|
158
|
+
- dual: boolean attribute (default: absent) — two month panels
|
|
159
|
+
- show-labels-on-hover: boolean attribute (default: absent)
|
|
160
|
+
- time-slots: boolean attribute (default: absent) — enable date→time two-step selection
|
|
161
|
+
- time-start: string (default: "09:00") — time grid start when time-slots enabled
|
|
162
|
+
- time-end: string (default: "17:00") — time grid end
|
|
163
|
+
- time-interval: number (default: 60) — time slot interval in minutes
|
|
164
|
+
- time-format: "12h" | "24h" (default: "24h")
|
|
165
|
+
- duration-labels: boolean attribute (default: absent)
|
|
166
|
+
- loading: boolean attribute (default: absent)
|
|
167
|
+
|
|
168
|
+
### JS Properties
|
|
169
|
+
|
|
170
|
+
- value: {start: string, end: string, startTime?: string, endTime?: string} | null — read/write
|
|
171
|
+
- bookings: Booking[] — array of existing bookings (default: [])
|
|
172
|
+
- dayData: Record<string, {label?: string, status?: string}> — static per-date metadata (default: {})
|
|
173
|
+
- labelFormula: ((dateStr: string) => {label?: string, status?: string} | null) | null — dynamic labels (highest priority, default: null)
|
|
174
|
+
- timeSlots: Array<{time: string, label?: string, available?: boolean}> | null — custom time slots (default: null)
|
|
175
|
+
- loading: boolean — read/write
|
|
176
|
+
|
|
177
|
+
### Booking Object Shape
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
{
|
|
181
|
+
id: string,
|
|
182
|
+
start: string, // "YYYY-MM-DD" — first day of booking
|
|
183
|
+
end: string, // "YYYY-MM-DD" — last day (checkout day)
|
|
184
|
+
label?: string, // displayed on hover / in cell
|
|
185
|
+
color?: "blue" | "green" | "red" | "orange" | "gray" // default: "blue"
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Cell Status Resolution (priority order)
|
|
190
|
+
|
|
191
|
+
1. labelFormula(dateStr) — if returns {status, label}, overrides everything
|
|
192
|
+
2. dayData[dateStr] — static {status, label}
|
|
193
|
+
3. Derived from bookings — "available", "booked", "half-day", "checkin-only", "checkout-only"
|
|
194
|
+
|
|
195
|
+
### Methods
|
|
196
|
+
|
|
197
|
+
- open(): void
|
|
198
|
+
- close(): void
|
|
199
|
+
- goToMonth(month: number, year: number): void
|
|
200
|
+
- showStatus(type: "error"|"warning"|"info"|"success", message: string, opts?: {autoDismiss?: number, dismissible?: boolean}): void
|
|
201
|
+
- clearStatus(): void
|
|
202
|
+
|
|
203
|
+
### Events
|
|
204
|
+
|
|
205
|
+
- cal:change — detail = {value: {start: string, end: string, startTime?: string, endTime?: string}}
|
|
206
|
+
- cal:selection-invalid — detail = {start: string, end: string} — fires when selection overlaps booking
|
|
207
|
+
- cal:month-change — detail = {year: number, month: number}
|
|
208
|
+
- cal:open — detail = {}
|
|
209
|
+
- cal:close — detail = {}
|
|
210
|
+
- cal:status — detail = {type: string|null, message: string|null}
|
|
211
|
+
|
|
212
|
+
### Overlap Validation
|
|
213
|
+
|
|
214
|
+
Selecting a range that overlaps any booking (where selStart < booking.end && selEnd > booking.start) will:
|
|
215
|
+
1. Emit cal:selection-invalid
|
|
216
|
+
2. Clear the selection
|
|
217
|
+
3. Show an error status banner "Selection overlaps an existing booking" (auto-dismiss 4s)
|
|
218
|
+
|
|
219
|
+
Same-day boundaries are disallowed: a selection ending on a booking start date, or starting on a booking end date, triggers overlap.
|
|
220
|
+
|
|
221
|
+
### Example
|
|
222
|
+
|
|
223
|
+
```html
|
|
224
|
+
<cal-booking theme="light" dual time-slots time-format="12h"></cal-booking>
|
|
225
|
+
<script>
|
|
226
|
+
const el = document.querySelector('cal-booking');
|
|
227
|
+
el.bookings = [
|
|
228
|
+
{ id: '1', start: '2026-03-05', end: '2026-03-10', label: 'Alice', color: 'blue' },
|
|
229
|
+
{ id: '2', start: '2026-03-10', end: '2026-03-15', label: 'Bob', color: 'green' },
|
|
230
|
+
];
|
|
231
|
+
el.labelFormula = (date) => {
|
|
232
|
+
const d = new Date(date);
|
|
233
|
+
return { label: (d.getDay() === 0 || d.getDay() === 6) ? '$150' : '$100' };
|
|
234
|
+
};
|
|
235
|
+
el.addEventListener('cal:change', (e) => {
|
|
236
|
+
console.log(e.detail.value);
|
|
237
|
+
// { start: "2026-03-16", end: "2026-03-20", startTime: "14:00", endTime: "11:00" }
|
|
238
|
+
});
|
|
239
|
+
</script>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Component: cal-scheduler
|
|
245
|
+
|
|
246
|
+
Tag: <cal-scheduler>
|
|
247
|
+
Class: CalScheduler
|
|
248
|
+
Extends: CalendarBase → HTMLElement
|
|
249
|
+
|
|
250
|
+
### Attributes (observedAttributes)
|
|
251
|
+
|
|
252
|
+
- theme: "light" | "dark" | "auto" (default: "light")
|
|
253
|
+
- view: "day" | "week" | "month" (default: "week")
|
|
254
|
+
- layout: "vertical" (default: "vertical")
|
|
255
|
+
- date: string (default: today()) — anchor date "YYYY-MM-DD"
|
|
256
|
+
- start-time: string (default: "08:00") — day grid start
|
|
257
|
+
- end-time: string (default: "18:00") — day grid end
|
|
258
|
+
- interval: number (default: 30) — slot interval in minutes
|
|
259
|
+
- format: "12h" | "24h" (default: "24h")
|
|
260
|
+
- first-day: number (default: 0)
|
|
261
|
+
- slot-height: number (default: 48) — pixels per time slot
|
|
262
|
+
- resource-mode: "tabs" | "columns" (default: "tabs")
|
|
263
|
+
- show-event-time: "true" | "false" (default: "true") — show time row in default event rendering
|
|
264
|
+
- show-fab: boolean attribute (default: absent) — floating action button
|
|
265
|
+
- draggable-events: boolean attribute (default: absent) — enable drag move/resize/create
|
|
266
|
+
- snap-interval: number | null (default: null, falls back to interval) — drag snap granularity in minutes
|
|
267
|
+
- min-duration: number | null (default: null, falls back to snap interval) — minimum event duration in minutes
|
|
268
|
+
- max-duration: number | null (default: null, no limit) — maximum event duration in minutes
|
|
269
|
+
- loading: boolean attribute (default: absent)
|
|
270
|
+
|
|
271
|
+
### JS Properties
|
|
272
|
+
|
|
273
|
+
- resources: Resource[] — default: []
|
|
274
|
+
- events: Event[] — default: []
|
|
275
|
+
- eventActions: EventAction[] — default: []
|
|
276
|
+
- eventContent: ((event: Event, resource: Resource) => HTMLElement | string) | null — custom renderer (default: null)
|
|
277
|
+
- value: {date: string, startTime: string, endTime: string, resourceId: string, resource: Resource} | null — read-only, last selected slot
|
|
278
|
+
- loading: boolean — read/write
|
|
279
|
+
|
|
280
|
+
### Event Object Shape
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
{
|
|
284
|
+
id: string, // unique identifier
|
|
285
|
+
title: string, // display title
|
|
286
|
+
start: string, // "YYYY-MM-DD"
|
|
287
|
+
end?: string, // "YYYY-MM-DD" for multi-day events
|
|
288
|
+
startTime?: string, // "HH:MM" — omit for all-day events
|
|
289
|
+
endTime?: string, // "HH:MM" — omit for all-day events
|
|
290
|
+
resourceId?: string, // links to Resource.id
|
|
291
|
+
color?: "blue" | "green" | "red" | "orange" | "gray", // default: "blue"
|
|
292
|
+
locked?: boolean, // prevents drag move/resize (default: false)
|
|
293
|
+
metadata?: Record<string, string | number> // shown in detail popover
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
All-day events: omit startTime and endTime. Rendered in collapsible all-day row (week view) or as chips (month view).
|
|
298
|
+
|
|
299
|
+
### Resource Object Shape
|
|
300
|
+
|
|
301
|
+
```
|
|
302
|
+
{
|
|
303
|
+
id: string,
|
|
304
|
+
name: string,
|
|
305
|
+
capacity?: number,
|
|
306
|
+
color?: "blue" | "green" | "red" | "orange" | "gray"
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### EventAction Object Shape
|
|
311
|
+
|
|
312
|
+
```
|
|
313
|
+
{
|
|
314
|
+
label: string, // button text, also used as action identifier in cal:event-action
|
|
315
|
+
type?: "danger" // applies red/destructive styling
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Methods
|
|
320
|
+
|
|
321
|
+
- goToDate(dateStr: string): void — navigate to date, emits cal:date-change
|
|
322
|
+
- setView(view: "day"|"week"|"month"): void — switch view, emits cal:view-change
|
|
323
|
+
- today(): void — navigate to today
|
|
324
|
+
- next(): void — advance by 1 day/week/month depending on current view
|
|
325
|
+
- prev(): void — go back by 1 day/week/month
|
|
326
|
+
- findAvailableSlot(opts: {date?: string, duration: number, resourceId?: string, minCapacity?: number}): {resourceId: string, date: string, startTime: string, endTime: string} | null — searches up to 14 days
|
|
327
|
+
- isSlotAvailable(date: string, startTime: string, endTime: string, resourceId: string): boolean — checks for time overlap with existing events
|
|
328
|
+
- showStatus(type: "error"|"warning"|"info"|"success", message: string, opts?: {autoDismiss?: number, dismissible?: boolean}): void
|
|
329
|
+
- clearStatus(): void
|
|
330
|
+
|
|
331
|
+
### Events
|
|
332
|
+
|
|
333
|
+
- cal:slot-select — detail = {date: string, startTime: string|null, endTime: string|null, resourceId: string|null, resource: Resource|null}
|
|
334
|
+
Fires when an empty time slot is clicked. In month view, startTime/endTime are null.
|
|
335
|
+
|
|
336
|
+
- cal:slot-create — detail = {date: string, startTime: string, endTime: string, resourceId: string|null, resource: Resource|null}
|
|
337
|
+
Fires when drag-to-create completes (requires draggable-events).
|
|
338
|
+
|
|
339
|
+
- cal:event-click — detail = {event: Event, resourceId: string|null, resource: Resource|null}
|
|
340
|
+
Fires when an event block is clicked. Opens detail popover.
|
|
341
|
+
|
|
342
|
+
- cal:event-move — detail = {event: Event, from: {date, startTime, endTime, resourceId}, to: {date, startTime, endTime, resourceId}}
|
|
343
|
+
Fires when drag-to-move completes. The consumer must update events array.
|
|
344
|
+
|
|
345
|
+
- cal:event-resize — detail = {event: Event, from: {endTime: string}, to: {endTime: string}}
|
|
346
|
+
Fires when drag-to-resize completes. Only bottom-edge resize is supported.
|
|
347
|
+
|
|
348
|
+
- cal:event-action — detail = {action: string, event: Event, resourceId: string|null, resource: Resource|null}
|
|
349
|
+
Fires when an action button in the event detail popover is clicked.
|
|
350
|
+
|
|
351
|
+
- cal:fab-create — detail = {date: string, view: string}
|
|
352
|
+
Fires when the floating action button is clicked (requires show-fab).
|
|
353
|
+
|
|
354
|
+
- cal:date-change — detail = {date: string, view: string}
|
|
355
|
+
Fires on navigation (prev/next/today/goToDate).
|
|
356
|
+
|
|
357
|
+
- cal:view-change — detail = {view: string, date: string}
|
|
358
|
+
Fires when view changes (setView or nav button).
|
|
359
|
+
|
|
360
|
+
- cal:status — detail = {type: string|null, message: string|null}
|
|
361
|
+
|
|
362
|
+
### Drag System Details
|
|
363
|
+
|
|
364
|
+
Enabled by `draggable-events` attribute. Uses pointer events with 4px threshold.
|
|
365
|
+
|
|
366
|
+
Move: creates a ghost clone at cursor position. Drop target resolved via data-date/data-time/data-resource-id attributes. Duration preserved. Emits cal:event-move.
|
|
367
|
+
|
|
368
|
+
Resize: bottom handle only. Floating time label follows cursor. Snaps to snap-interval (or interval). Enforces min-duration and max-duration. Emits cal:event-resize.
|
|
369
|
+
|
|
370
|
+
Create: drag on empty slot creates dashed preview block. Snaps to grid. Emits cal:slot-create on release.
|
|
371
|
+
|
|
372
|
+
Locked events (event.locked = true) cannot be moved or resized. They display a lock icon.
|
|
373
|
+
|
|
374
|
+
### Resource Modes
|
|
375
|
+
|
|
376
|
+
- tabs: Resource tabs appear above grid. One resource visible at a time. "All" tab shows all.
|
|
377
|
+
- columns: All resources shown as side-by-side columns in day view.
|
|
378
|
+
|
|
379
|
+
### Example
|
|
380
|
+
|
|
381
|
+
```html
|
|
382
|
+
<cal-scheduler
|
|
383
|
+
view="week"
|
|
384
|
+
draggable-events
|
|
385
|
+
snap-interval="15"
|
|
386
|
+
show-fab
|
|
387
|
+
resource-mode="tabs"
|
|
388
|
+
format="12h"
|
|
389
|
+
theme="light"
|
|
390
|
+
></cal-scheduler>
|
|
391
|
+
<script>
|
|
392
|
+
const sched = document.querySelector('cal-scheduler');
|
|
393
|
+
sched.resources = [
|
|
394
|
+
{ id: 'r1', name: 'Room A', capacity: 10 },
|
|
395
|
+
{ id: 'r2', name: 'Room B', capacity: 20 },
|
|
396
|
+
];
|
|
397
|
+
sched.events = [
|
|
398
|
+
{ id: '1', title: 'Standup', start: '2026-03-02', startTime: '09:00', endTime: '09:30', resourceId: 'r1', color: 'blue' },
|
|
399
|
+
{ id: '2', title: 'Workshop', start: '2026-03-03', startTime: '13:00', endTime: '16:00', resourceId: 'r2', color: 'green', locked: true },
|
|
400
|
+
];
|
|
401
|
+
sched.eventActions = [{ label: 'Edit' }, { label: 'Delete', type: 'danger' }];
|
|
402
|
+
sched.eventContent = (event, resource) => {
|
|
403
|
+
const el = document.createElement('div');
|
|
404
|
+
el.innerHTML = `<strong>${event.title}</strong><br><small>${resource?.name || ''}</small>`;
|
|
405
|
+
return el;
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
sched.addEventListener('cal:event-move', (e) => {
|
|
409
|
+
const { event, to } = e.detail;
|
|
410
|
+
const updated = sched.events.map(ev =>
|
|
411
|
+
ev.id === event.id ? { ...ev, start: to.date, startTime: to.startTime, endTime: to.endTime, resourceId: to.resourceId } : ev
|
|
412
|
+
);
|
|
413
|
+
sched.events = updated;
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
sched.addEventListener('cal:event-resize', (e) => {
|
|
417
|
+
const { event, to } = e.detail;
|
|
418
|
+
sched.events = sched.events.map(ev =>
|
|
419
|
+
ev.id === event.id ? { ...ev, endTime: to.endTime } : ev
|
|
420
|
+
);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
sched.addEventListener('cal:slot-create', (e) => {
|
|
424
|
+
const { date, startTime, endTime, resourceId } = e.detail;
|
|
425
|
+
sched.events = [...sched.events, {
|
|
426
|
+
id: String(Date.now()), title: 'New Event',
|
|
427
|
+
start: date, startTime, endTime, resourceId, color: 'orange',
|
|
428
|
+
}];
|
|
429
|
+
});
|
|
430
|
+
</script>
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## Shared Base Class: CalendarBase
|
|
436
|
+
|
|
437
|
+
All components extend CalendarBase → HTMLElement.
|
|
438
|
+
|
|
439
|
+
### Inherited Methods
|
|
440
|
+
|
|
441
|
+
- showStatus(type: "error"|"warning"|"info"|"success", message: string, opts?: {autoDismiss?: number, dismissible?: boolean}): void
|
|
442
|
+
Shows an inline status banner. autoDismiss is milliseconds.
|
|
443
|
+
- clearStatus(): void — clears the banner, emits cal:status with null values
|
|
444
|
+
- emit(name: string, detail?: object): void — dispatches CustomEvent with bubbles:true, composed:true
|
|
445
|
+
|
|
446
|
+
### Inherited Event
|
|
447
|
+
|
|
448
|
+
- cal:status — detail = {type: string|null, message: string|null} — fires on showStatus and clearStatus
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
## Theming
|
|
453
|
+
|
|
454
|
+
Attribute: theme="light" | "dark" | "auto"
|
|
455
|
+
|
|
456
|
+
theme="auto" uses @media (prefers-color-scheme: dark) to switch.
|
|
457
|
+
|
|
458
|
+
All tokens are CSS custom properties set on :host. Values are raw HSL channels (e.g., "240 6% 10%") consumed as hsl(var(--cal-token)).
|
|
459
|
+
|
|
460
|
+
### Core Tokens
|
|
461
|
+
|
|
462
|
+
- --cal-bg: background (light: 0 0% 100%, dark: 240 6% 10%)
|
|
463
|
+
- --cal-bg-muted: muted background (light: 240 5% 96%, dark: 240 4% 16%)
|
|
464
|
+
- --cal-fg: foreground text (light: 240 6% 10%, dark: 0 0% 98%)
|
|
465
|
+
- --cal-fg-muted: muted text (light: 240 4% 46%, dark: 240 4% 54%)
|
|
466
|
+
- --cal-border: borders (light: 240 6% 90%, dark: 240 4% 20%)
|
|
467
|
+
- --cal-accent: accent / selected state (light: 240 6% 10%, dark: 0 0% 98%)
|
|
468
|
+
- --cal-accent-fg: text on accent (light: 0 0% 100%, dark: 240 6% 10%)
|
|
469
|
+
- --cal-accent-subtle: subtle accent background (light: 240 5% 96%, dark: 240 4% 16%)
|
|
470
|
+
- --cal-hover: hover background (light: 240 5% 93%, dark: 240 4% 20%)
|
|
471
|
+
- --cal-ring: focus ring color (light: 240 6% 10%, dark: 0 0% 98%)
|
|
472
|
+
- --cal-radius: border radius (default: 8px)
|
|
473
|
+
- --cal-radius-sm: small border radius (default: 6px)
|
|
474
|
+
- --cal-cell-size: datepicker cell size (default: 36px)
|
|
475
|
+
- --cal-transition: transition timing (default: 150ms ease)
|
|
476
|
+
|
|
477
|
+
### Booking Color Tokens (5 palettes)
|
|
478
|
+
|
|
479
|
+
Each palette has -bg, -fg, and -hover variants.
|
|
480
|
+
|
|
481
|
+
- --cal-booking-blue-bg / --cal-booking-blue-fg / --cal-booking-blue-hover
|
|
482
|
+
- --cal-booking-green-bg / --cal-booking-green-fg / --cal-booking-green-hover
|
|
483
|
+
- --cal-booking-red-bg / --cal-booking-red-fg / --cal-booking-red-hover
|
|
484
|
+
- --cal-booking-orange-bg / --cal-booking-orange-fg / --cal-booking-orange-hover
|
|
485
|
+
- --cal-booking-gray-bg / --cal-booking-gray-fg / --cal-booking-gray-hover
|
|
486
|
+
|
|
487
|
+
### Scheduler Tokens
|
|
488
|
+
|
|
489
|
+
- --cal-sched-grid-line: grid line color
|
|
490
|
+
- --cal-sched-now-line: current time indicator color
|
|
491
|
+
- --cal-sched-slot-hover: slot hover background
|
|
492
|
+
- --cal-sched-header-bg: header/resource header background
|
|
493
|
+
|
|
494
|
+
### Status Tokens (4 types × 3 variants)
|
|
495
|
+
|
|
496
|
+
- --cal-status-error-bg / --cal-status-error-fg / --cal-status-error-border
|
|
497
|
+
- --cal-status-warning-bg / --cal-status-warning-fg / --cal-status-warning-border
|
|
498
|
+
- --cal-status-info-bg / --cal-status-info-fg / --cal-status-info-border
|
|
499
|
+
- --cal-status-success-bg / --cal-status-success-fg / --cal-status-success-border
|
|
500
|
+
|
|
501
|
+
### Override Example
|
|
502
|
+
|
|
503
|
+
```css
|
|
504
|
+
cal-scheduler {
|
|
505
|
+
--cal-accent: 220 90% 56%;
|
|
506
|
+
--cal-accent-fg: 0 0% 100%;
|
|
507
|
+
--cal-radius: 12px;
|
|
508
|
+
--cal-sched-now-line: 142 70% 45%;
|
|
509
|
+
}
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
## Data Shapes
|
|
515
|
+
|
|
516
|
+
### Event
|
|
517
|
+
|
|
518
|
+
```
|
|
519
|
+
{
|
|
520
|
+
id: string,
|
|
521
|
+
title: string,
|
|
522
|
+
start: string, // "YYYY-MM-DD"
|
|
523
|
+
end?: string, // "YYYY-MM-DD"
|
|
524
|
+
startTime?: string, // "HH:MM" — omit for all-day
|
|
525
|
+
endTime?: string, // "HH:MM" — omit for all-day
|
|
526
|
+
resourceId?: string,
|
|
527
|
+
color?: "blue" | "green" | "red" | "orange" | "gray",
|
|
528
|
+
locked?: boolean,
|
|
529
|
+
metadata?: Record<string, string | number>
|
|
530
|
+
}
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### Resource
|
|
534
|
+
|
|
535
|
+
```
|
|
536
|
+
{
|
|
537
|
+
id: string,
|
|
538
|
+
name: string,
|
|
539
|
+
capacity?: number,
|
|
540
|
+
color?: "blue" | "green" | "red" | "orange" | "gray"
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### Booking
|
|
545
|
+
|
|
546
|
+
```
|
|
547
|
+
{
|
|
548
|
+
id: string,
|
|
549
|
+
start: string,
|
|
550
|
+
end: string,
|
|
551
|
+
label?: string,
|
|
552
|
+
color?: "blue" | "green" | "red" | "orange" | "gray"
|
|
553
|
+
}
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
### TimeSlot
|
|
557
|
+
|
|
558
|
+
```
|
|
559
|
+
{
|
|
560
|
+
time: string, // "HH:MM"
|
|
561
|
+
label?: string,
|
|
562
|
+
available?: boolean // default: true
|
|
563
|
+
}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### DayData
|
|
567
|
+
|
|
568
|
+
```
|
|
569
|
+
{
|
|
570
|
+
[dateStr: string]: {
|
|
571
|
+
label?: string,
|
|
572
|
+
status?: string
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### EventAction
|
|
578
|
+
|
|
579
|
+
```
|
|
580
|
+
{
|
|
581
|
+
label: string,
|
|
582
|
+
type?: "danger"
|
|
583
|
+
}
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
## Docs
|
|
589
|
+
|
|
590
|
+
- [cal-datepicker](docs/cal-datepicker.md)
|
|
591
|
+
- [cal-timepicker](docs/cal-timepicker.md)
|
|
592
|
+
- [cal-booking](docs/cal-booking.md)
|
|
593
|
+
- [cal-scheduler](docs/cal-scheduler.md)
|
|
594
|
+
- [Theming](docs/theming.md)
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "calkit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Vanilla JS web component library for date pickers, time pickers, booking calendars, and resource schedulers.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/calkit.umd.js",
|
|
7
|
+
"module": "dist/calkit.es.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/calkit.es.js",
|
|
11
|
+
"require": "./dist/calkit.umd.js"
|
|
12
|
+
},
|
|
13
|
+
"./datepicker": {
|
|
14
|
+
"import": "./dist/datepicker.es.js",
|
|
15
|
+
"require": "./dist/datepicker.umd.js"
|
|
16
|
+
},
|
|
17
|
+
"./timepicker": {
|
|
18
|
+
"import": "./dist/timepicker.es.js",
|
|
19
|
+
"require": "./dist/timepicker.umd.js"
|
|
20
|
+
},
|
|
21
|
+
"./booking": {
|
|
22
|
+
"import": "./dist/booking.es.js",
|
|
23
|
+
"require": "./dist/booking.umd.js"
|
|
24
|
+
},
|
|
25
|
+
"./scheduler": {
|
|
26
|
+
"import": "./dist/scheduler.es.js",
|
|
27
|
+
"require": "./dist/scheduler.umd.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md",
|
|
33
|
+
"llms.txt"
|
|
34
|
+
],
|
|
35
|
+
"keywords": [
|
|
36
|
+
"calendar",
|
|
37
|
+
"datepicker",
|
|
38
|
+
"timepicker",
|
|
39
|
+
"booking",
|
|
40
|
+
"scheduler",
|
|
41
|
+
"web-components",
|
|
42
|
+
"vanilla-js"
|
|
43
|
+
],
|
|
44
|
+
"homepage": "https://github.com/SimonKefas/calkit",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/SimonKefas/calkit.git"
|
|
48
|
+
},
|
|
49
|
+
"license": "MIT",
|
|
50
|
+
"scripts": {
|
|
51
|
+
"dev": "vite",
|
|
52
|
+
"build": "vite build && vite build --config vite.datepicker.config.js && vite build --config vite.booking.config.js && vite build --config vite.timepicker.config.js && vite build --config vite.scheduler.config.js",
|
|
53
|
+
"preview": "vite preview"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"vite": "^6.1.0"
|
|
57
|
+
}
|
|
58
|
+
}
|