@nomideusz/svelte-calendar 0.6.3 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +192 -10
- package/dist/adapters/composite.d.ts +15 -0
- package/dist/adapters/composite.js +65 -0
- package/dist/adapters/index.d.ts +5 -1
- package/dist/adapters/index.js +2 -0
- package/dist/adapters/mapped.d.ts +170 -0
- package/dist/adapters/mapped.js +343 -0
- package/dist/adapters/recurring.d.ts +4 -0
- package/dist/adapters/recurring.js +5 -10
- package/dist/adapters/types.d.ts +11 -2
- package/dist/assets/favicon.svg +9 -1
- package/dist/calendar/Calendar.svelte +161 -90
- package/dist/calendar/Calendar.svelte.d.ts +12 -0
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.js +2 -0
- package/dist/core/measure.d.ts +104 -0
- package/dist/core/measure.js +195 -0
- package/dist/core/time.js +2 -2
- package/dist/core/types.d.ts +32 -0
- package/dist/engine/event-store.svelte.js +22 -7
- package/dist/headless/create-agenda.svelte.d.ts +67 -0
- package/dist/headless/create-agenda.svelte.js +139 -0
- package/dist/headless/create-calendar.svelte.d.ts +2 -0
- package/dist/headless/create-calendar.svelte.js +380 -0
- package/dist/headless/index.d.ts +4 -0
- package/dist/headless/index.js +2 -0
- package/dist/headless/types.d.ts +219 -0
- package/dist/headless/types.js +1 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +4 -2
- package/dist/primitives/DayHeader.svelte +6 -6
- package/dist/primitives/EmptySlot.svelte +3 -3
- package/dist/primitives/EventBlock.svelte +99 -14
- package/dist/primitives/NowIndicator.svelte +5 -5
- package/dist/views/agenda/AgendaDay.svelte +215 -116
- package/dist/views/agenda/AgendaWeek.svelte +166 -113
- package/dist/views/mobile/MobileDay.svelte +45 -21
- package/dist/views/mobile/MobileWeek.svelte +85 -35
- package/dist/views/planner/PlannerDay.svelte +116 -60
- package/dist/views/planner/PlannerWeek.svelte +228 -166
- package/dist/views/shared/context.svelte.d.ts +47 -0
- package/dist/views/shared/context.svelte.js +47 -0
- package/dist/views/shared/format.d.ts +19 -0
- package/dist/views/shared/format.js +46 -0
- package/dist/views/shared/index.d.ts +4 -0
- package/dist/views/shared/index.js +2 -0
- package/package.json +9 -2
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { VIVID_PALETTE } from '../core/palette.js';
|
|
2
|
+
// ── Internal helpers ────────────────────────────────────
|
|
3
|
+
let counter = 0;
|
|
4
|
+
function uid() {
|
|
5
|
+
return `mapped-${Date.now()}-${++counter}`;
|
|
6
|
+
}
|
|
7
|
+
/** Parse a date/time value into a Date object */
|
|
8
|
+
function toDate(value) {
|
|
9
|
+
if (value instanceof Date)
|
|
10
|
+
return value;
|
|
11
|
+
if (typeof value === 'number')
|
|
12
|
+
return new Date(value);
|
|
13
|
+
if (typeof value === 'string') {
|
|
14
|
+
const d = new Date(value);
|
|
15
|
+
if (isNaN(d.getTime())) {
|
|
16
|
+
throw new Error(`Cannot parse date: "${value}"`);
|
|
17
|
+
}
|
|
18
|
+
return d;
|
|
19
|
+
}
|
|
20
|
+
throw new Error(`Cannot convert to Date: ${typeof value}`);
|
|
21
|
+
}
|
|
22
|
+
/** Combine "YYYY-MM-DD" date and "HH:MM" time into a Date */
|
|
23
|
+
function combineDateAndTime(dateStr, timeStr) {
|
|
24
|
+
// Handle various date formats
|
|
25
|
+
const datePart = dateStr.includes('T') ? dateStr.split('T')[0] : dateStr;
|
|
26
|
+
const d = new Date(`${datePart}T${timeStr}:00`);
|
|
27
|
+
if (isNaN(d.getTime())) {
|
|
28
|
+
throw new Error(`Cannot combine date "${dateStr}" and time "${timeStr}"`);
|
|
29
|
+
}
|
|
30
|
+
return d;
|
|
31
|
+
}
|
|
32
|
+
/** Coerce a source value into an EventStatus */
|
|
33
|
+
function coerceStatus(value, fieldName) {
|
|
34
|
+
if (typeof value === 'string') {
|
|
35
|
+
const lower = value.toLowerCase();
|
|
36
|
+
if (lower === 'cancelled' || lower === 'canceled')
|
|
37
|
+
return 'cancelled';
|
|
38
|
+
if (lower === 'tentative')
|
|
39
|
+
return 'tentative';
|
|
40
|
+
if (lower === 'full')
|
|
41
|
+
return 'full';
|
|
42
|
+
if (lower === 'limited')
|
|
43
|
+
return 'limited';
|
|
44
|
+
return 'confirmed';
|
|
45
|
+
}
|
|
46
|
+
if (typeof value === 'boolean') {
|
|
47
|
+
// E.g. is_cancelled: true → 'cancelled'
|
|
48
|
+
const lowerField = fieldName.toLowerCase();
|
|
49
|
+
if (lowerField.includes('cancel'))
|
|
50
|
+
return value ? 'cancelled' : 'confirmed';
|
|
51
|
+
if (lowerField.includes('tentative'))
|
|
52
|
+
return value ? 'tentative' : 'confirmed';
|
|
53
|
+
return value ? 'confirmed' : 'tentative';
|
|
54
|
+
}
|
|
55
|
+
return 'confirmed';
|
|
56
|
+
}
|
|
57
|
+
/** Parse an RGB string like "rgb(62, 101, 255)" to a hex color */
|
|
58
|
+
function normalizeColor(value) {
|
|
59
|
+
if (!value)
|
|
60
|
+
return undefined;
|
|
61
|
+
if (typeof value !== 'string')
|
|
62
|
+
return undefined;
|
|
63
|
+
const str = value.trim();
|
|
64
|
+
// Already hex
|
|
65
|
+
if (str.startsWith('#'))
|
|
66
|
+
return str;
|
|
67
|
+
// rgb(r, g, b) format
|
|
68
|
+
const rgbMatch = str.match(/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i);
|
|
69
|
+
if (rgbMatch) {
|
|
70
|
+
const r = parseInt(rgbMatch[1], 10);
|
|
71
|
+
const g = parseInt(rgbMatch[2], 10);
|
|
72
|
+
const b = parseInt(rgbMatch[3], 10);
|
|
73
|
+
return `#${((1 << 24) | (r << 16) | (g << 8) | b).toString(16).slice(1)}`;
|
|
74
|
+
}
|
|
75
|
+
// CSS named colors and other formats — pass through
|
|
76
|
+
return str;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Collect unmapped fields into the `data` payload.
|
|
80
|
+
*/
|
|
81
|
+
function collectData(raw, mappedKeys, include) {
|
|
82
|
+
const data = {};
|
|
83
|
+
const keys = include === '*' ? Object.keys(raw) : include;
|
|
84
|
+
for (const key of keys) {
|
|
85
|
+
if (!mappedKeys.has(key) && raw[key] !== undefined) {
|
|
86
|
+
data[key] = raw[key];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return data;
|
|
90
|
+
}
|
|
91
|
+
// ── Factory ─────────────────────────────────────────────
|
|
92
|
+
/**
|
|
93
|
+
* Create a CalendarAdapter from an array of external records + mapping config.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* // Yoga school schedule
|
|
98
|
+
* const adapter = createMappedAdapter(yogaSchedule.events, {
|
|
99
|
+
* fields: {
|
|
100
|
+
* title: 'class_name',
|
|
101
|
+
* start: 'starts_at_iso',
|
|
102
|
+
* end: 'ends_at_iso',
|
|
103
|
+
* subtitle: 'teacher',
|
|
104
|
+
* location: 'room',
|
|
105
|
+
* color: 'color',
|
|
106
|
+
* externalId: 'reference_id',
|
|
107
|
+
* status: 'is_cancelled',
|
|
108
|
+
* tags: ['is_free', 'is_bookable_online'],
|
|
109
|
+
* },
|
|
110
|
+
* });
|
|
111
|
+
*
|
|
112
|
+
* // Medical appointments
|
|
113
|
+
* const adapter = createMappedAdapter(appointments, {
|
|
114
|
+
* fields: {
|
|
115
|
+
* title: 'procedure_name',
|
|
116
|
+
* start: 'scheduled_at',
|
|
117
|
+
* end: 'scheduled_end',
|
|
118
|
+
* subtitle: 'doctor_name',
|
|
119
|
+
* location: 'office',
|
|
120
|
+
* resourceId: 'doctor_id',
|
|
121
|
+
* },
|
|
122
|
+
* });
|
|
123
|
+
*
|
|
124
|
+
* // Full custom mapper
|
|
125
|
+
* const adapter = createMappedAdapter(rawData, {
|
|
126
|
+
* mapEvent: (raw) => ({
|
|
127
|
+
* id: raw.uid,
|
|
128
|
+
* title: `${raw.first_name} ${raw.last_name}`,
|
|
129
|
+
* start: new Date(raw.timestamp),
|
|
130
|
+
* end: new Date(raw.timestamp + raw.duration_ms),
|
|
131
|
+
* location: raw.room,
|
|
132
|
+
* }),
|
|
133
|
+
* });
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export function createMappedAdapter(sourceData, options = {}) {
|
|
137
|
+
const { fields, mapEvent: customMapper, includeData = '*', autoColor = true, palette = VIVID_PALETTE, readOnly = true, } = options;
|
|
138
|
+
// Build the internal event list
|
|
139
|
+
const events = [];
|
|
140
|
+
const colorAssignments = new Map();
|
|
141
|
+
let colorIndex = 0;
|
|
142
|
+
function resolveColor(ev) {
|
|
143
|
+
// When autoColor is on, always assign from palette (ignore source color)
|
|
144
|
+
if (!autoColor && ev.color)
|
|
145
|
+
return ev.color;
|
|
146
|
+
const key = ev.category ?? ev.title;
|
|
147
|
+
if (!colorAssignments.has(key)) {
|
|
148
|
+
colorAssignments.set(key, palette[colorIndex % palette.length]);
|
|
149
|
+
colorIndex++;
|
|
150
|
+
}
|
|
151
|
+
return colorAssignments.get(key);
|
|
152
|
+
}
|
|
153
|
+
function withColor(ev) {
|
|
154
|
+
const color = resolveColor(ev);
|
|
155
|
+
return color ? { ...ev, color } : ev;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Map a single raw record using the declarative field mapping.
|
|
159
|
+
*/
|
|
160
|
+
function mapWithFields(raw, index) {
|
|
161
|
+
const f = fields;
|
|
162
|
+
const r = raw;
|
|
163
|
+
// ── ID ──
|
|
164
|
+
const id = f.id
|
|
165
|
+
? String(r[f.id] ?? '')
|
|
166
|
+
: String(r['id'] ?? r['reference_id'] ?? r['externalId'] ?? r['_id'] ?? uid());
|
|
167
|
+
// ── Title ──
|
|
168
|
+
const titleKey = f.title ?? 'title';
|
|
169
|
+
const title = String(r[titleKey] ?? r['name'] ?? r['class_name'] ?? `Event ${index + 1}`);
|
|
170
|
+
// ── Start / End ──
|
|
171
|
+
let start;
|
|
172
|
+
let end;
|
|
173
|
+
if (f.start && r[f.start] !== undefined) {
|
|
174
|
+
start = toDate(r[f.start]);
|
|
175
|
+
}
|
|
176
|
+
else if (f.date && f.startTime && r[f.date] && r[f.startTime]) {
|
|
177
|
+
start = combineDateAndTime(String(r[f.date]), String(r[f.startTime]));
|
|
178
|
+
}
|
|
179
|
+
else if (r['start'] !== undefined) {
|
|
180
|
+
start = toDate(r['start']);
|
|
181
|
+
}
|
|
182
|
+
else if (r['starts_at_iso'] !== undefined) {
|
|
183
|
+
start = toDate(r['starts_at_iso']);
|
|
184
|
+
}
|
|
185
|
+
else if (r['start_time'] !== undefined && r['date'] !== undefined) {
|
|
186
|
+
start = combineDateAndTime(String(r['date']), String(r['start_time']));
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
throw new Error(`Cannot determine start time for record ${index}: no matching field found`);
|
|
190
|
+
}
|
|
191
|
+
if (f.end && r[f.end] !== undefined) {
|
|
192
|
+
end = toDate(r[f.end]);
|
|
193
|
+
}
|
|
194
|
+
else if (f.date && f.endTime && r[f.date] && r[f.endTime]) {
|
|
195
|
+
end = combineDateAndTime(String(r[f.date]), String(r[f.endTime]));
|
|
196
|
+
}
|
|
197
|
+
else if (r['end'] !== undefined) {
|
|
198
|
+
end = toDate(r['end']);
|
|
199
|
+
}
|
|
200
|
+
else if (r['ends_at_iso'] !== undefined) {
|
|
201
|
+
end = toDate(r['ends_at_iso']);
|
|
202
|
+
}
|
|
203
|
+
else if (r['end_time'] !== undefined && r['date'] !== undefined) {
|
|
204
|
+
end = combineDateAndTime(String(r['date']), String(r['end_time']));
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
// Default: 1 hour after start
|
|
208
|
+
end = new Date(start.getTime() + 60 * 60 * 1000);
|
|
209
|
+
}
|
|
210
|
+
// ── Optional fields ──
|
|
211
|
+
const color = normalizeColor(r[f.color ?? 'color']);
|
|
212
|
+
const subtitle = f.subtitle ? String(r[f.subtitle] ?? '') || undefined : undefined;
|
|
213
|
+
const location = f.location
|
|
214
|
+
? String(r[f.location] ?? '') || undefined
|
|
215
|
+
: r['room'] ? String(r['room']) : r['location'] ? String(r['location']) : undefined;
|
|
216
|
+
const category = f.category ? String(r[f.category] ?? '') || undefined : undefined;
|
|
217
|
+
const resourceId = f.resourceId ? String(r[f.resourceId] ?? '') || undefined : undefined;
|
|
218
|
+
const externalId = f.externalId
|
|
219
|
+
? String(r[f.externalId] ?? '') || undefined
|
|
220
|
+
: r['reference_id'] ? String(r['reference_id']) : undefined;
|
|
221
|
+
// ── Status ──
|
|
222
|
+
let status;
|
|
223
|
+
if (f.status && r[f.status] !== undefined) {
|
|
224
|
+
status = coerceStatus(r[f.status], f.status);
|
|
225
|
+
}
|
|
226
|
+
else if (r['is_cancelled'] !== undefined) {
|
|
227
|
+
status = coerceStatus(r['is_cancelled'], 'is_cancelled');
|
|
228
|
+
}
|
|
229
|
+
// ── Tags ──
|
|
230
|
+
let tags;
|
|
231
|
+
if (f.tags && f.tags.length > 0) {
|
|
232
|
+
tags = [];
|
|
233
|
+
for (const tagKey of f.tags) {
|
|
234
|
+
const val = r[tagKey];
|
|
235
|
+
if (val === true) {
|
|
236
|
+
// Convert key name to human-readable tag
|
|
237
|
+
tags.push(tagKey.replace(/^is_/, '').replace(/_/g, ' '));
|
|
238
|
+
}
|
|
239
|
+
else if (typeof val === 'string' && val) {
|
|
240
|
+
tags.push(val);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (tags.length === 0)
|
|
244
|
+
tags = undefined;
|
|
245
|
+
}
|
|
246
|
+
// ── Data (unmapped fields) ──
|
|
247
|
+
const mappedKeys = new Set();
|
|
248
|
+
for (const v of Object.values(f)) {
|
|
249
|
+
if (typeof v === 'string')
|
|
250
|
+
mappedKeys.add(v);
|
|
251
|
+
if (Array.isArray(v))
|
|
252
|
+
v.forEach((k) => mappedKeys.add(k));
|
|
253
|
+
}
|
|
254
|
+
// Also mark auto-detected keys as mapped
|
|
255
|
+
for (const k of ['id', 'title', 'name', 'class_name', 'start', 'end',
|
|
256
|
+
'starts_at_iso', 'ends_at_iso', 'start_time', 'end_time', 'date',
|
|
257
|
+
'color', 'room', 'location', 'reference_id', 'is_cancelled']) {
|
|
258
|
+
mappedKeys.add(k);
|
|
259
|
+
}
|
|
260
|
+
const data = collectData(r, mappedKeys, includeData);
|
|
261
|
+
const ev = { id, title, start, end };
|
|
262
|
+
if (color)
|
|
263
|
+
ev.color = color;
|
|
264
|
+
if (subtitle)
|
|
265
|
+
ev.subtitle = subtitle;
|
|
266
|
+
if (location)
|
|
267
|
+
ev.location = location;
|
|
268
|
+
if (category)
|
|
269
|
+
ev.category = category;
|
|
270
|
+
if (resourceId)
|
|
271
|
+
ev.resourceId = resourceId;
|
|
272
|
+
if (externalId)
|
|
273
|
+
ev.externalId = externalId;
|
|
274
|
+
if (status && status !== 'confirmed')
|
|
275
|
+
ev.status = status;
|
|
276
|
+
if (tags)
|
|
277
|
+
ev.tags = tags;
|
|
278
|
+
if (Object.keys(data).length > 0)
|
|
279
|
+
ev.data = data;
|
|
280
|
+
return ev;
|
|
281
|
+
}
|
|
282
|
+
// ── Build events array ──
|
|
283
|
+
for (let i = 0; i < sourceData.length; i++) {
|
|
284
|
+
const mapper = customMapper ?? mapWithFields;
|
|
285
|
+
const ev = withColor(mapper(sourceData[i], i));
|
|
286
|
+
events.push(ev);
|
|
287
|
+
}
|
|
288
|
+
// ── Adapter interface ──
|
|
289
|
+
function overlaps(ev, range) {
|
|
290
|
+
return ev.start < range.end && ev.end > range.start;
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
async fetchEvents(range) {
|
|
294
|
+
return events.filter((ev) => overlaps(ev, range));
|
|
295
|
+
},
|
|
296
|
+
async createEvent(data) {
|
|
297
|
+
if (readOnly) {
|
|
298
|
+
throw new Error('Mapped adapter is read-only. Set readOnly: false and provide onMutate to enable writes.');
|
|
299
|
+
}
|
|
300
|
+
const handler = options.onMutate?.onCreate;
|
|
301
|
+
if (handler) {
|
|
302
|
+
const raw = await handler(data);
|
|
303
|
+
const mapper = customMapper ?? mapWithFields;
|
|
304
|
+
const ev = withColor(mapper(raw, events.length));
|
|
305
|
+
events.push(ev);
|
|
306
|
+
return ev;
|
|
307
|
+
}
|
|
308
|
+
// Fallback: create locally
|
|
309
|
+
const ev = { ...data, id: uid() };
|
|
310
|
+
events.push(withColor(ev));
|
|
311
|
+
return ev;
|
|
312
|
+
},
|
|
313
|
+
async updateEvent(id, patch) {
|
|
314
|
+
if (readOnly) {
|
|
315
|
+
throw new Error('Mapped adapter is read-only. Set readOnly: false and provide onMutate to enable writes.');
|
|
316
|
+
}
|
|
317
|
+
const idx = events.findIndex((e) => e.id === id);
|
|
318
|
+
if (idx < 0)
|
|
319
|
+
throw new Error(`Event not found: ${id}`);
|
|
320
|
+
const handler = options.onMutate?.onUpdate;
|
|
321
|
+
if (handler) {
|
|
322
|
+
const raw = await handler(id, patch);
|
|
323
|
+
const mapper = customMapper ?? mapWithFields;
|
|
324
|
+
const ev = withColor(mapper(raw, idx));
|
|
325
|
+
events[idx] = ev;
|
|
326
|
+
return ev;
|
|
327
|
+
}
|
|
328
|
+
events[idx] = { ...events[idx], ...patch, id };
|
|
329
|
+
return events[idx];
|
|
330
|
+
},
|
|
331
|
+
async deleteEvent(id) {
|
|
332
|
+
if (readOnly) {
|
|
333
|
+
throw new Error('Mapped adapter is read-only. Set readOnly: false and provide onMutate to enable writes.');
|
|
334
|
+
}
|
|
335
|
+
const handler = options.onMutate?.onDelete;
|
|
336
|
+
if (handler)
|
|
337
|
+
await handler(id);
|
|
338
|
+
const idx = events.findIndex((e) => e.id === id);
|
|
339
|
+
if (idx >= 0)
|
|
340
|
+
events.splice(idx, 1);
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
}
|
|
@@ -57,6 +57,10 @@ export interface RecurringEvent {
|
|
|
57
57
|
tags?: string[];
|
|
58
58
|
/** Category for grouping */
|
|
59
59
|
category?: string;
|
|
60
|
+
/** Location or room name */
|
|
61
|
+
location?: string;
|
|
62
|
+
/** Resource ID for multi-resource views */
|
|
63
|
+
resourceId?: string;
|
|
60
64
|
/** Arbitrary payload */
|
|
61
65
|
data?: Record<string, unknown>;
|
|
62
66
|
}
|
|
@@ -68,7 +68,9 @@ function createConcreteEvent(rec, date) {
|
|
|
68
68
|
category: rec.category,
|
|
69
69
|
subtitle: rec.subtitle,
|
|
70
70
|
tags: rec.tags,
|
|
71
|
-
|
|
71
|
+
location: rec.location,
|
|
72
|
+
resourceId: rec.resourceId,
|
|
73
|
+
data: { ...rec.data, recurringId: rec.id, readOnly: true },
|
|
72
74
|
};
|
|
73
75
|
}
|
|
74
76
|
// ── Count → until resolution ────────────────────────────
|
|
@@ -283,14 +285,7 @@ export function createRecurringAdapter(schedule, options = {}) {
|
|
|
283
285
|
}
|
|
284
286
|
return events;
|
|
285
287
|
},
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
},
|
|
289
|
-
async updateEvent() {
|
|
290
|
-
throw new Error('createRecurringAdapter is read-only. Use a memory or REST adapter for mutations.');
|
|
291
|
-
},
|
|
292
|
-
async deleteEvent() {
|
|
293
|
-
throw new Error('createRecurringAdapter is read-only. Use a memory or REST adapter for mutations.');
|
|
294
|
-
},
|
|
288
|
+
// Read-only adapter: CRUD methods intentionally omitted.
|
|
289
|
+
// Use createMemoryAdapter or createRestAdapter for mutations.
|
|
295
290
|
};
|
|
296
291
|
}
|
package/dist/adapters/types.d.ts
CHANGED
|
@@ -15,9 +15,18 @@ export interface CalendarAdapter {
|
|
|
15
15
|
/** Fetch events that overlap the given date range */
|
|
16
16
|
fetchEvents(range: DateRange): Promise<TimelineEvent[]>;
|
|
17
17
|
/** Create a new event, return it with a server-assigned ID */
|
|
18
|
-
createEvent(event: Omit<TimelineEvent, 'id'>): Promise<TimelineEvent>;
|
|
18
|
+
createEvent?(event: Omit<TimelineEvent, 'id'>): Promise<TimelineEvent>;
|
|
19
19
|
/** Update an event, return the full updated event */
|
|
20
|
-
updateEvent(id: string, patch: Partial<TimelineEvent>): Promise<TimelineEvent>;
|
|
20
|
+
updateEvent?(id: string, patch: Partial<TimelineEvent>): Promise<TimelineEvent>;
|
|
21
21
|
/** Delete an event by ID */
|
|
22
|
+
deleteEvent?(id: string): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A CalendarAdapter that supports full CRUD operations.
|
|
26
|
+
* Use this type when you need to guarantee write support.
|
|
27
|
+
*/
|
|
28
|
+
export interface WritableCalendarAdapter extends CalendarAdapter {
|
|
29
|
+
createEvent(event: Omit<TimelineEvent, 'id'>): Promise<TimelineEvent>;
|
|
30
|
+
updateEvent(id: string, patch: Partial<TimelineEvent>): Promise<TimelineEvent>;
|
|
22
31
|
deleteEvent(id: string): Promise<void>;
|
|
23
32
|
}
|
package/dist/assets/favicon.svg
CHANGED
|
@@ -1 +1,9 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg"
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
2
|
+
<title>svelte-calendar</title>
|
|
3
|
+
<rect width="64" height="64" rx="16" fill="#2563eb"/>
|
|
4
|
+
<rect x="14" y="18" width="36" height="32" rx="6" fill="#fff"/>
|
|
5
|
+
<path d="M14 27h36" stroke="#2563eb" stroke-width="4"/>
|
|
6
|
+
<path d="M23 13v10M41 13v10" stroke="#fff" stroke-width="5" stroke-linecap="round"/>
|
|
7
|
+
<rect x="21" y="33" width="7" height="7" rx="2" fill="#2563eb" opacity=".9"/>
|
|
8
|
+
<rect x="36" y="33" width="7" height="7" rx="2" fill="#2563eb" opacity=".35"/>
|
|
9
|
+
</svg>
|