@nomideusz/svelte-calendar 0.3.1 → 0.5.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 +193 -406
- package/dist/adapters/recurring.d.ts +45 -8
- package/dist/adapters/recurring.js +227 -49
- package/dist/adapters/rest.js +6 -1
- package/dist/calendar/Calendar.svelte +137 -29
- package/dist/calendar/Calendar.svelte.d.ts +10 -8
- package/dist/calendar/index.d.ts +0 -1
- package/dist/calendar/index.js +0 -1
- package/dist/core/index.d.ts +5 -4
- package/dist/core/index.js +2 -3
- package/dist/core/locale.d.ts +74 -0
- package/dist/core/locale.js +96 -4
- package/dist/core/time.d.ts +30 -0
- package/dist/core/time.js +42 -0
- package/dist/core/types.d.ts +1 -72
- package/dist/core/types.js +1 -8
- package/dist/engine/event-store.svelte.js +2 -2
- package/dist/engine/index.d.ts +1 -1
- package/dist/engine/view-state.svelte.d.ts +12 -6
- package/dist/engine/view-state.svelte.js +8 -4
- package/dist/index.d.ts +6 -7
- package/dist/index.js +4 -4
- package/dist/primitives/EmptySlot.svelte +6 -16
- package/dist/primitives/EventBlock.svelte +6 -19
- package/dist/primitives/NowIndicator.svelte +7 -4
- package/dist/primitives/TimeGutter.svelte +1 -1
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/index.js +1 -1
- package/dist/theme/presets.d.ts +11 -19
- package/dist/theme/presets.js +25 -111
- package/dist/views/agenda/Agenda.svelte +17 -1514
- package/dist/views/agenda/Agenda.svelte.d.ts +0 -13
- package/dist/views/agenda/AgendaDay.svelte +976 -0
- package/dist/views/agenda/AgendaDay.svelte.d.ts +14 -0
- package/dist/views/agenda/AgendaWeek.svelte +775 -0
- package/dist/views/agenda/AgendaWeek.svelte.d.ts +15 -0
- package/dist/views/agenda/index.d.ts +2 -0
- package/dist/views/agenda/index.js +2 -0
- package/dist/views/index.d.ts +2 -6
- package/dist/views/index.js +2 -6
- package/dist/views/planner/Planner.svelte +17 -0
- package/dist/views/planner/Planner.svelte.d.ts +7 -0
- package/dist/views/planner/PlannerDay.svelte +957 -0
- package/dist/views/{day/DayGrid.svelte.d.ts → planner/PlannerDay.svelte.d.ts} +8 -16
- package/dist/views/planner/PlannerWeek.svelte +826 -0
- package/dist/views/{week/WeekGrid.svelte.d.ts → planner/PlannerWeek.svelte.d.ts} +5 -9
- package/dist/views/planner/index.d.ts +1 -0
- package/dist/views/planner/index.js +1 -0
- package/dist/widget/CalendarWidget.svelte +54 -35
- package/dist/widget/CalendarWidget.svelte.d.ts +1 -1
- package/dist/widget/widget.d.ts +1 -7
- package/dist/widget/widget.js +44 -3
- package/package.json +5 -2
- package/widget/svelte-calendar.css +1 -0
- package/widget/widget.js +6 -144
- package/dist/calendar/Toolbar.svelte +0 -241
- package/dist/calendar/Toolbar.svelte.d.ts +0 -19
- package/dist/views/day/DayGrid.svelte +0 -1101
- package/dist/views/day/DayTimeline.svelte +0 -532
- package/dist/views/day/DayTimeline.svelte.d.ts +0 -4
- package/dist/views/day/index.d.ts +0 -2
- package/dist/views/day/index.js +0 -2
- package/dist/views/schedule/WeekSchedule.svelte +0 -133
- package/dist/views/schedule/WeekSchedule.svelte.d.ts +0 -38
- package/dist/views/schedule/index.d.ts +0 -1
- package/dist/views/schedule/index.js +0 -2
- package/dist/views/settings/Settings.svelte +0 -307
- package/dist/views/settings/Settings.svelte.d.ts +0 -23
- package/dist/views/settings/index.d.ts +0 -2
- package/dist/views/settings/index.js +0 -1
- package/dist/views/week/WeekGrid.svelte +0 -631
- package/dist/views/week/WeekHeatmap.svelte +0 -460
- package/dist/views/week/WeekHeatmap.svelte.d.ts +0 -8
- package/dist/views/week/index.d.ts +0 -2
- package/dist/views/week/index.js +0 -2
|
@@ -1,16 +1,54 @@
|
|
|
1
1
|
import type { CalendarAdapter } from './types.js';
|
|
2
2
|
/**
|
|
3
|
-
* A
|
|
3
|
+
* A recurring event definition.
|
|
4
|
+
*
|
|
5
|
+
* Backward-compatible: the only required fields are `id`, `title`,
|
|
6
|
+
* `startTime`, `endTime`, and `dayOfWeek` (for weekly, the default).
|
|
4
7
|
*/
|
|
5
8
|
export interface RecurringEvent {
|
|
6
9
|
id: string;
|
|
7
10
|
title: string;
|
|
8
|
-
/** ISO weekday: 1 = Monday … 7 = Sunday */
|
|
9
|
-
dayOfWeek: number;
|
|
10
11
|
/** Start time in "HH:MM" 24-hour format */
|
|
11
12
|
startTime: string;
|
|
12
13
|
/** End time in "HH:MM" 24-hour format */
|
|
13
14
|
endTime: string;
|
|
15
|
+
/** Recurrence frequency (default: `'weekly'`) */
|
|
16
|
+
frequency?: 'daily' | 'weekly' | 'monthly';
|
|
17
|
+
/**
|
|
18
|
+
* Repeat every N periods — e.g. `2` with `weekly` = biweekly.
|
|
19
|
+
* Requires `startDate` when > 1 so the adapter knows the anchor.
|
|
20
|
+
* @default 1
|
|
21
|
+
*/
|
|
22
|
+
interval?: number;
|
|
23
|
+
/**
|
|
24
|
+
* ISO weekday(s): 1 = Monday … 7 = Sunday.
|
|
25
|
+
* Required for `weekly` frequency. Pass an array for multiple days
|
|
26
|
+
* (e.g. `[1, 3, 5]` for Mon/Wed/Fri).
|
|
27
|
+
*/
|
|
28
|
+
dayOfWeek?: number | number[];
|
|
29
|
+
/**
|
|
30
|
+
* Day of month (1–31). Required for `monthly` frequency.
|
|
31
|
+
* Clamped to the last day of shorter months (e.g. 31 → 28 in Feb).
|
|
32
|
+
* @default 1
|
|
33
|
+
*/
|
|
34
|
+
dayOfMonth?: number;
|
|
35
|
+
/**
|
|
36
|
+
* First possible occurrence in `"YYYY-MM-DD"` format.
|
|
37
|
+
* Events before this date are excluded. Required when using
|
|
38
|
+
* `interval > 1` or `count`.
|
|
39
|
+
*/
|
|
40
|
+
startDate?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Last possible occurrence in `"YYYY-MM-DD"` format.
|
|
43
|
+
* No events are generated after this date.
|
|
44
|
+
*/
|
|
45
|
+
until?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Maximum number of occurrences, counted from `startDate`.
|
|
48
|
+
* Alternative to `until` — if both are set the stricter bound wins.
|
|
49
|
+
* Requires `startDate`.
|
|
50
|
+
*/
|
|
51
|
+
count?: number;
|
|
14
52
|
/** Accent color */
|
|
15
53
|
color?: string;
|
|
16
54
|
/** Optional subtitle displayed below the title */
|
|
@@ -30,15 +68,14 @@ export interface RecurringAdapterOptions {
|
|
|
30
68
|
/**
|
|
31
69
|
* Auto-assign colors to events by category or title.
|
|
32
70
|
* true → use the default vivid palette
|
|
33
|
-
* string → hex accent color
|
|
34
|
-
* theme-harmonious palette via golden-angle hue rotation
|
|
71
|
+
* string → hex accent color to generate a harmonious palette
|
|
35
72
|
*/
|
|
36
73
|
autoColor?: boolean | string;
|
|
37
74
|
}
|
|
38
75
|
/**
|
|
39
|
-
* Create a CalendarAdapter that projects recurring
|
|
40
|
-
*
|
|
76
|
+
* Create a CalendarAdapter that projects recurring events onto concrete
|
|
77
|
+
* dates for whatever range the calendar requests.
|
|
41
78
|
*
|
|
42
|
-
* Read-only by default — create/update/delete throw
|
|
79
|
+
* Read-only by default — create/update/delete throw.
|
|
43
80
|
*/
|
|
44
81
|
export declare function createRecurringAdapter(schedule: RecurringEvent[], options?: RecurringAdapterOptions): CalendarAdapter;
|
|
@@ -1,70 +1,231 @@
|
|
|
1
|
-
import { startOfWeek } from '../core/time.js';
|
|
2
|
-
import { DAY_MS } from '../core/time.js';
|
|
1
|
+
import { startOfWeek, DAY_MS } from '../core/time.js';
|
|
3
2
|
import { generatePalette, VIVID_PALETTE } from '../core/palette.js';
|
|
3
|
+
// ── Helpers ─────────────────────────────────────────────
|
|
4
4
|
/** Parse "HH:MM" into [hours, minutes] */
|
|
5
5
|
function parseTime(time) {
|
|
6
|
-
const
|
|
7
|
-
|
|
6
|
+
const parts = time.split(':');
|
|
7
|
+
if (parts.length < 2) {
|
|
8
|
+
throw new Error(`Invalid time format "${time}": expected "HH:MM"`);
|
|
9
|
+
}
|
|
10
|
+
const h = Number(parts[0]);
|
|
11
|
+
const m = Number(parts[1]);
|
|
12
|
+
if (!Number.isInteger(h) || h < 0 || h > 23) {
|
|
13
|
+
throw new Error(`Invalid hours in time "${time}": expected 0–23`);
|
|
14
|
+
}
|
|
15
|
+
if (!Number.isInteger(m) || m < 0 || m > 59) {
|
|
16
|
+
throw new Error(`Invalid minutes in time "${time}": expected 0–59`);
|
|
17
|
+
}
|
|
18
|
+
return [h, m];
|
|
8
19
|
}
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
/** Parse "YYYY-MM-DD" to a midnight Date */
|
|
21
|
+
function parseDate(str) {
|
|
22
|
+
const parts = str.split('-');
|
|
23
|
+
if (parts.length !== 3)
|
|
24
|
+
throw new Error(`Invalid date "${str}": expected "YYYY-MM-DD"`);
|
|
25
|
+
const y = Number(parts[0]);
|
|
26
|
+
const m = Number(parts[1]) - 1;
|
|
27
|
+
const d = Number(parts[2]);
|
|
28
|
+
const date = new Date(y, m, d);
|
|
29
|
+
if (Number.isNaN(date.getTime()))
|
|
30
|
+
throw new Error(`Invalid date "${str}"`);
|
|
31
|
+
return date;
|
|
32
|
+
}
|
|
33
|
+
/** ISO weekday offset from Monday: 1→0, 2→1, … 7→6 */
|
|
13
34
|
function isoWeekdayToOffset(dayOfWeek) {
|
|
14
|
-
return dayOfWeek - 1;
|
|
35
|
+
return dayOfWeek - 1;
|
|
15
36
|
}
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
37
|
+
/** Normalize `dayOfWeek` to a sorted array */
|
|
38
|
+
function normalizeDays(value) {
|
|
39
|
+
if (value == null)
|
|
40
|
+
return [];
|
|
41
|
+
const arr = Array.isArray(value) ? [...value] : [value];
|
|
42
|
+
return arr.sort((a, b) => a - b);
|
|
43
|
+
}
|
|
44
|
+
/** Clamp day to last day of month (e.g. 31 → 28 in Feb) */
|
|
45
|
+
function clampDayOfMonth(year, month, day) {
|
|
46
|
+
const lastDay = new Date(year, month + 1, 0).getDate();
|
|
47
|
+
return new Date(year, month, Math.min(day, lastDay));
|
|
48
|
+
}
|
|
49
|
+
/** Create a concrete TimelineEvent from a recurring def + occurrence date */
|
|
50
|
+
function createConcreteEvent(rec, date, instanceKey) {
|
|
23
51
|
const [sh, sm] = parseTime(rec.startTime);
|
|
24
52
|
const [eh, em] = parseTime(rec.endTime);
|
|
25
|
-
const start = new Date(
|
|
26
|
-
const end = new Date(
|
|
53
|
+
const start = new Date(date.getFullYear(), date.getMonth(), date.getDate(), sh, sm);
|
|
54
|
+
const end = new Date(date.getFullYear(), date.getMonth(), date.getDate(), eh, em);
|
|
27
55
|
return {
|
|
28
|
-
id: `${rec.id}
|
|
56
|
+
id: `${rec.id}--${instanceKey}`,
|
|
29
57
|
title: rec.title,
|
|
30
58
|
start,
|
|
31
59
|
end,
|
|
32
60
|
color: rec.color,
|
|
33
61
|
category: rec.category,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
...(rec.subtitle ? { subtitle: rec.subtitle } : {}),
|
|
38
|
-
...(rec.tags ? { tags: rec.tags } : {}),
|
|
39
|
-
},
|
|
62
|
+
subtitle: rec.subtitle,
|
|
63
|
+
tags: rec.tags,
|
|
64
|
+
data: { ...rec.data, recurringId: rec.id },
|
|
40
65
|
};
|
|
41
66
|
}
|
|
67
|
+
// ── Count → until resolution ────────────────────────────
|
|
42
68
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
69
|
+
* When `count` is set, compute the date of the last occurrence so the
|
|
70
|
+
* projection loop only needs to check date bounds.
|
|
45
71
|
*/
|
|
46
|
-
function
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
72
|
+
function computeUntilFromCount(rec, startDateObj, mondayStart) {
|
|
73
|
+
const count = rec.count;
|
|
74
|
+
if (count == null || count <= 0)
|
|
75
|
+
return undefined;
|
|
76
|
+
const freq = rec.frequency ?? 'weekly';
|
|
77
|
+
const interval = rec.interval ?? 1;
|
|
78
|
+
switch (freq) {
|
|
79
|
+
case 'daily': {
|
|
80
|
+
const d = new Date(startDateObj);
|
|
81
|
+
d.setDate(d.getDate() + (count - 1) * interval);
|
|
82
|
+
return d;
|
|
83
|
+
}
|
|
84
|
+
case 'weekly': {
|
|
85
|
+
const days = normalizeDays(rec.dayOfWeek);
|
|
86
|
+
if (days.length === 0)
|
|
87
|
+
return undefined;
|
|
88
|
+
const startMs = startDateObj.getTime();
|
|
89
|
+
let weekMs = startOfWeek(startMs, mondayStart);
|
|
90
|
+
let remaining = count;
|
|
91
|
+
const maxCycles = Math.ceil(count / days.length) + 2;
|
|
92
|
+
for (let c = 0; c < maxCycles; c++) {
|
|
93
|
+
for (const day of days) {
|
|
94
|
+
const dayMs = weekMs + isoWeekdayToOffset(day) * DAY_MS;
|
|
95
|
+
if (dayMs >= startMs) {
|
|
96
|
+
remaining--;
|
|
97
|
+
if (remaining === 0)
|
|
98
|
+
return new Date(dayMs);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
weekMs += interval * 7 * DAY_MS;
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
case 'monthly': {
|
|
106
|
+
const totalMonths = startDateObj.getFullYear() * 12 +
|
|
107
|
+
startDateObj.getMonth() +
|
|
108
|
+
(count - 1) * interval;
|
|
109
|
+
const y = Math.floor(totalMonths / 12);
|
|
110
|
+
const m = totalMonths % 12;
|
|
111
|
+
return clampDayOfMonth(y, m, rec.dayOfMonth ?? 1);
|
|
112
|
+
}
|
|
54
113
|
}
|
|
55
|
-
return weeks;
|
|
56
114
|
}
|
|
115
|
+
// ── Projection functions ────────────────────────────────
|
|
116
|
+
function projectDaily(rec, range, startDate, effectiveUntil, out) {
|
|
117
|
+
const interval = rec.interval ?? 1;
|
|
118
|
+
// Anchor at startDate or range.start (midnight)
|
|
119
|
+
const anchor = new Date(startDate ?? range.start);
|
|
120
|
+
anchor.setHours(0, 0, 0, 0);
|
|
121
|
+
const rangeStart = new Date(range.start);
|
|
122
|
+
rangeStart.setHours(0, 0, 0, 0);
|
|
123
|
+
// Skip ahead to first occurrence at or after range.start
|
|
124
|
+
const cursor = new Date(anchor);
|
|
125
|
+
if (cursor < rangeStart) {
|
|
126
|
+
const daysBetween = Math.round((rangeStart.getTime() - cursor.getTime()) / DAY_MS);
|
|
127
|
+
const skip = Math.floor(daysBetween / interval);
|
|
128
|
+
cursor.setDate(cursor.getDate() + skip * interval);
|
|
129
|
+
if (cursor < rangeStart)
|
|
130
|
+
cursor.setDate(cursor.getDate() + interval);
|
|
131
|
+
}
|
|
132
|
+
let idx = 0;
|
|
133
|
+
while (cursor < range.end) {
|
|
134
|
+
if (effectiveUntil && cursor > effectiveUntil)
|
|
135
|
+
break;
|
|
136
|
+
const ev = createConcreteEvent(rec, cursor, `d${idx}`);
|
|
137
|
+
if (ev.start < range.end && ev.end > range.start) {
|
|
138
|
+
out.push(ev);
|
|
139
|
+
}
|
|
140
|
+
cursor.setDate(cursor.getDate() + interval);
|
|
141
|
+
idx++;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function projectWeekly(rec, range, startDate, effectiveUntil, mondayStart, out) {
|
|
145
|
+
const interval = rec.interval ?? 1;
|
|
146
|
+
const days = normalizeDays(rec.dayOfWeek);
|
|
147
|
+
if (days.length === 0)
|
|
148
|
+
return;
|
|
149
|
+
const startDateMs = startDate?.getTime();
|
|
150
|
+
const rangeStartMs = range.start.getTime();
|
|
151
|
+
const rangeEndMs = range.end.getTime();
|
|
152
|
+
// Anchor week: week containing startDate, or range.start if none
|
|
153
|
+
const anchorMs = startDateMs ?? rangeStartMs;
|
|
154
|
+
const anchorWeekMs = startOfWeek(anchorMs, mondayStart);
|
|
155
|
+
// Jump to first interval-aligned week that could overlap the range
|
|
156
|
+
let weekMs;
|
|
157
|
+
if (anchorWeekMs >= rangeStartMs) {
|
|
158
|
+
weekMs = anchorWeekMs;
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
const weeksBetween = Math.floor((rangeStartMs - anchorWeekMs) / (7 * DAY_MS));
|
|
162
|
+
const skip = Math.floor(weeksBetween / interval);
|
|
163
|
+
weekMs = anchorWeekMs + skip * interval * 7 * DAY_MS;
|
|
164
|
+
if (weekMs + 7 * DAY_MS <= rangeStartMs) {
|
|
165
|
+
weekMs += interval * 7 * DAY_MS;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
let weekIndex = Math.round((weekMs - anchorWeekMs) / (7 * DAY_MS));
|
|
169
|
+
while (weekMs < rangeEndMs) {
|
|
170
|
+
for (const day of days) {
|
|
171
|
+
const dayMs = weekMs + isoWeekdayToOffset(day) * DAY_MS;
|
|
172
|
+
if (startDateMs != null && dayMs < startDateMs)
|
|
173
|
+
continue;
|
|
174
|
+
if (dayMs >= rangeEndMs)
|
|
175
|
+
break;
|
|
176
|
+
if (effectiveUntil && dayMs > effectiveUntil.getTime())
|
|
177
|
+
return;
|
|
178
|
+
const dayDate = new Date(dayMs);
|
|
179
|
+
const ev = createConcreteEvent(rec, dayDate, `w${weekIndex}--d${day}`);
|
|
180
|
+
if (ev.start < range.end && ev.end > range.start) {
|
|
181
|
+
out.push(ev);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
weekMs += interval * 7 * DAY_MS;
|
|
185
|
+
weekIndex += interval;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function projectMonthly(rec, range, startDate, effectiveUntil, out) {
|
|
189
|
+
const interval = rec.interval ?? 1;
|
|
190
|
+
const dayOfMonth = rec.dayOfMonth ?? 1;
|
|
191
|
+
const anchor = startDate ?? range.start;
|
|
192
|
+
let totalMonths = anchor.getFullYear() * 12 + anchor.getMonth();
|
|
193
|
+
// Skip ahead to first interval-aligned month at or near range.start
|
|
194
|
+
if (startDate && startDate < range.start) {
|
|
195
|
+
const rangeMonths = range.start.getFullYear() * 12 + range.start.getMonth();
|
|
196
|
+
const gap = rangeMonths - totalMonths;
|
|
197
|
+
const skip = Math.floor(gap / interval);
|
|
198
|
+
totalMonths += skip * interval;
|
|
199
|
+
}
|
|
200
|
+
for (let i = 0; i < 120; i++) {
|
|
201
|
+
const y = Math.floor(totalMonths / 12);
|
|
202
|
+
const m = totalMonths % 12;
|
|
203
|
+
const date = clampDayOfMonth(y, m, dayOfMonth);
|
|
204
|
+
if (date >= range.end)
|
|
205
|
+
break;
|
|
206
|
+
if (effectiveUntil && date > effectiveUntil)
|
|
207
|
+
break;
|
|
208
|
+
if (date >= range.start && (!startDate || date >= startDate)) {
|
|
209
|
+
const ev = createConcreteEvent(rec, date, `m${y}-${String(m + 1).padStart(2, '0')}`);
|
|
210
|
+
if (ev.start < range.end && ev.end > range.start) {
|
|
211
|
+
out.push(ev);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
totalMonths += interval;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// ── Adapter factory ─────────────────────────────────────
|
|
57
218
|
/** Default palette for auto-coloring */
|
|
58
219
|
const AUTO_COLORS = VIVID_PALETTE;
|
|
59
220
|
/**
|
|
60
|
-
* Create a CalendarAdapter that projects recurring
|
|
61
|
-
*
|
|
221
|
+
* Create a CalendarAdapter that projects recurring events onto concrete
|
|
222
|
+
* dates for whatever range the calendar requests.
|
|
62
223
|
*
|
|
63
|
-
* Read-only by default — create/update/delete throw
|
|
224
|
+
* Read-only by default — create/update/delete throw.
|
|
64
225
|
*/
|
|
65
226
|
export function createRecurringAdapter(schedule, options = {}) {
|
|
66
227
|
const { mondayStart = true, colorMap, autoColor } = options;
|
|
67
|
-
// Resolve palette
|
|
228
|
+
// Resolve palette
|
|
68
229
|
const palette = autoColor
|
|
69
230
|
? typeof autoColor === 'string'
|
|
70
231
|
? generatePalette(autoColor)
|
|
@@ -93,16 +254,33 @@ export function createRecurringAdapter(schedule, options = {}) {
|
|
|
93
254
|
}
|
|
94
255
|
return {
|
|
95
256
|
async fetchEvents(range) {
|
|
96
|
-
const weeks = getOverlappingWeeks(range, mondayStart);
|
|
97
257
|
const events = [];
|
|
98
|
-
for (const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
258
|
+
for (const rec of schedule) {
|
|
259
|
+
const colored = { ...rec, color: resolveColor(rec) };
|
|
260
|
+
const freq = rec.frequency ?? 'weekly';
|
|
261
|
+
// Parse bounds
|
|
262
|
+
const sd = rec.startDate ? parseDate(rec.startDate) : undefined;
|
|
263
|
+
const untilDate = rec.until ? parseDate(rec.until) : undefined;
|
|
264
|
+
const countUntil = sd
|
|
265
|
+
? computeUntilFromCount(rec, sd, mondayStart)
|
|
266
|
+
: undefined;
|
|
267
|
+
// Effective until = tighter of the two bounds
|
|
268
|
+
let effectiveUntil = untilDate;
|
|
269
|
+
if (countUntil) {
|
|
270
|
+
effectiveUntil = effectiveUntil
|
|
271
|
+
? countUntil < effectiveUntil ? countUntil : effectiveUntil
|
|
272
|
+
: countUntil;
|
|
273
|
+
}
|
|
274
|
+
switch (freq) {
|
|
275
|
+
case 'daily':
|
|
276
|
+
projectDaily(colored, range, sd, effectiveUntil, events);
|
|
277
|
+
break;
|
|
278
|
+
case 'weekly':
|
|
279
|
+
projectWeekly(colored, range, sd, effectiveUntil, mondayStart, events);
|
|
280
|
+
break;
|
|
281
|
+
case 'monthly':
|
|
282
|
+
projectMonthly(colored, range, sd, effectiveUntil, events);
|
|
283
|
+
break;
|
|
106
284
|
}
|
|
107
285
|
}
|
|
108
286
|
return events;
|
package/dist/adapters/rest.js
CHANGED
|
@@ -16,7 +16,12 @@ export function createRestAdapter(options) {
|
|
|
16
16
|
}
|
|
17
17
|
if (res.status === 204)
|
|
18
18
|
return undefined;
|
|
19
|
-
|
|
19
|
+
try {
|
|
20
|
+
return await res.json();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
throw new Error(`Calendar API error: invalid JSON response from ${path}`);
|
|
24
|
+
}
|
|
20
25
|
}
|
|
21
26
|
return {
|
|
22
27
|
async fetchEvents(range) {
|