@craftguild/jscalendar 0.5.6 → 0.6.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 +18 -2
- package/dist/index.cjs +27 -16
- package/dist/index.d.cts +12 -5
- package/dist/index.d.mts +12 -5
- package/dist/index.mjs +26 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ pnpm add @craftguild/jscalendar
|
|
|
31
31
|
|
|
32
32
|
You can also load the library directly in the browser from `esm.sh`.
|
|
33
33
|
The repository includes a single-file example at
|
|
34
|
-
`examples/
|
|
34
|
+
[`examples/browser/demo.html`](./examples/browser/demo.html) that demonstrates:
|
|
35
35
|
|
|
36
36
|
- toggling between `Event` and `Task`
|
|
37
37
|
- defining recurrence rules and expanding them within a date range
|
|
@@ -39,7 +39,7 @@ The repository includes a single-file example at
|
|
|
39
39
|
|
|
40
40
|
```html
|
|
41
41
|
<script type="module">
|
|
42
|
-
import { JsCal } from "https://esm.sh/@craftguild/jscalendar@0.
|
|
42
|
+
import { JsCal } from "https://esm.sh/@craftguild/jscalendar@0.6.0?bundle";
|
|
43
43
|
|
|
44
44
|
const event = new JsCal.Event({
|
|
45
45
|
title: "Browser demo",
|
|
@@ -76,6 +76,11 @@ const task = new JsCal.Task({
|
|
|
76
76
|
const from = new Date(2026, 0, 1, 0, 0, 0, 0);
|
|
77
77
|
const to = new Date(2026, 0, 31, 0, 0, 0, 0);
|
|
78
78
|
const generator = JsCal.expandRecurrence([event, task], { from, to });
|
|
79
|
+
const withoutAnchor = JsCal.expandRecurrence(
|
|
80
|
+
[event],
|
|
81
|
+
{ from, to },
|
|
82
|
+
{ includeAnchor: false },
|
|
83
|
+
);
|
|
79
84
|
|
|
80
85
|
for (const item of generator) {
|
|
81
86
|
// Expanded JSCalendar objects for events and tasks in the range.
|
|
@@ -378,6 +383,9 @@ The recurrence expansion API is a generator.
|
|
|
378
383
|
Expansion follows RFC 8984 semantics for recurrence rules, including
|
|
379
384
|
overrides and exclusions. The output instances contain `recurrenceId`
|
|
380
385
|
and preserve the base object’s data unless a patch modifies fields.
|
|
386
|
+
By default, the source item’s anchor occurrence is included to preserve
|
|
387
|
+
backward compatibility. Pass `{ includeAnchor: false }` to exclude the
|
|
388
|
+
source anchor and return only rule-expanded occurrences.
|
|
381
389
|
|
|
382
390
|
```ts
|
|
383
391
|
for (const occ of JsCal.expandRecurrence([event], {
|
|
@@ -386,6 +394,14 @@ for (const occ of JsCal.expandRecurrence([event], {
|
|
|
386
394
|
})) {
|
|
387
395
|
console.log(occ);
|
|
388
396
|
}
|
|
397
|
+
|
|
398
|
+
for (const occ of JsCal.expandRecurrence(
|
|
399
|
+
[event],
|
|
400
|
+
{ from: new Date("2026-02-01"), to: new Date("2026-03-01") },
|
|
401
|
+
{ includeAnchor: false },
|
|
402
|
+
)) {
|
|
403
|
+
console.log(occ);
|
|
404
|
+
}
|
|
389
405
|
```
|
|
390
406
|
|
|
391
407
|
### Paged Expansion (for infinite scroll)
|
package/dist/index.cjs
CHANGED
|
@@ -1025,7 +1025,7 @@ function generateDateCandidates(periodStart, rule, firstDay, skip) {
|
|
|
1025
1025
|
return result;
|
|
1026
1026
|
}
|
|
1027
1027
|
if (rule.frequency === FREQ_WEEKLY) {
|
|
1028
|
-
let cursor = periodStart;
|
|
1028
|
+
let cursor = startOfWeek(periodStart, firstDay);
|
|
1029
1029
|
for (let i = 0; i < 7; i += 1) {
|
|
1030
1030
|
result.push({
|
|
1031
1031
|
year: cursor.year,
|
|
@@ -1259,10 +1259,12 @@ function expandRule(anchor, rule, fromLocal, toLocal, includeAnchor, timeZone, f
|
|
|
1259
1259
|
const results = [];
|
|
1260
1260
|
let generated = 0;
|
|
1261
1261
|
let cursor = start;
|
|
1262
|
+
const anchorValue = formatLocalDateTime(start);
|
|
1262
1263
|
while (generated < count) {
|
|
1263
1264
|
let filtered = generateDateTimes(cursor, normalized, firstDay, skip).sort();
|
|
1264
1265
|
if (normalized.bySetPosition && normalized.bySetPosition.length > 0) filtered = applyBySetPos(filtered, normalized.bySetPosition);
|
|
1265
1266
|
for (const dt of filtered) {
|
|
1267
|
+
if (compareDate(cursor, start) === 0 && dt < anchorValue) continue;
|
|
1266
1268
|
generated += 1;
|
|
1267
1269
|
if (generated > count) break;
|
|
1268
1270
|
if (until && dt > until) return results;
|
|
@@ -1282,14 +1284,17 @@ function expandRule(anchor, rule, fromLocal, toLocal, includeAnchor, timeZone, f
|
|
|
1282
1284
|
* Expand recurrence into occurrences sorted by recurrenceId/start.
|
|
1283
1285
|
* @param items JSCalendar objects to expand.
|
|
1284
1286
|
* @param range Date range bounds.
|
|
1287
|
+
* @param options Expansion options.
|
|
1285
1288
|
* @return Generator of expanded occurrences.
|
|
1286
1289
|
*/
|
|
1287
|
-
function* expandRecurrence(items, range) {
|
|
1290
|
+
function* expandRecurrence(items, range, options = {}) {
|
|
1291
|
+
var _options$includeAncho;
|
|
1288
1292
|
const occurrences = [];
|
|
1289
1293
|
let index = 0;
|
|
1294
|
+
const includeAnchor = (_options$includeAncho = options.includeAnchor) !== null && _options$includeAncho !== void 0 ? _options$includeAncho : true;
|
|
1290
1295
|
for (const item of items) {
|
|
1291
1296
|
if (item["@type"] === TYPE_EVENT$3) {
|
|
1292
|
-
for (const occurrence of expandEvent(item, range)) {
|
|
1297
|
+
for (const occurrence of expandEvent(item, range, includeAnchor)) {
|
|
1293
1298
|
occurrences.push({
|
|
1294
1299
|
value: occurrence,
|
|
1295
1300
|
key: occurrenceKey(occurrence),
|
|
@@ -1300,7 +1305,7 @@ function* expandRecurrence(items, range) {
|
|
|
1300
1305
|
continue;
|
|
1301
1306
|
}
|
|
1302
1307
|
if (item["@type"] === TYPE_TASK$3) {
|
|
1303
|
-
for (const occurrence of expandTask(item, range)) {
|
|
1308
|
+
for (const occurrence of expandTask(item, range, includeAnchor)) {
|
|
1304
1309
|
occurrences.push({
|
|
1305
1310
|
value: occurrence,
|
|
1306
1311
|
key: occurrenceKey(occurrence),
|
|
@@ -1335,7 +1340,7 @@ function* expandRecurrence(items, range) {
|
|
|
1335
1340
|
function expandRecurrencePaged(items, range, options) {
|
|
1336
1341
|
const result = [];
|
|
1337
1342
|
let nextCursor;
|
|
1338
|
-
for (const occurrence of expandRecurrence(items, range)) {
|
|
1343
|
+
for (const occurrence of expandRecurrence(items, range, options)) {
|
|
1339
1344
|
const key = occurrenceKey(occurrence);
|
|
1340
1345
|
if (options.cursor && key) {
|
|
1341
1346
|
if (key <= options.cursor) continue;
|
|
@@ -1353,23 +1358,25 @@ function expandRecurrencePaged(items, range, options) {
|
|
|
1353
1358
|
* Expand event into occurrences.
|
|
1354
1359
|
* @param event Event to expand.
|
|
1355
1360
|
* @param range Date range bounds.
|
|
1361
|
+
* @param includeAnchor Whether the source event should be included.
|
|
1356
1362
|
* @return Generator of expanded occurrences.
|
|
1357
1363
|
*/
|
|
1358
|
-
function expandEvent(event, range) {
|
|
1364
|
+
function expandEvent(event, range, includeAnchor) {
|
|
1359
1365
|
var _event$timeZone;
|
|
1360
|
-
return expandObject(event, range, event.start, event.recurrenceRules, event.excludedRecurrenceRules, event.recurrenceOverrides, (_event$timeZone = event.timeZone) !== null && _event$timeZone !== void 0 ? _event$timeZone : null);
|
|
1366
|
+
return expandObject(event, range, event.start, event.recurrenceRules, event.excludedRecurrenceRules, event.recurrenceOverrides, (_event$timeZone = event.timeZone) !== null && _event$timeZone !== void 0 ? _event$timeZone : null, includeAnchor);
|
|
1361
1367
|
}
|
|
1362
1368
|
/**
|
|
1363
1369
|
* Expand task into occurrences.
|
|
1364
1370
|
* @param task Task to expand.
|
|
1365
1371
|
* @param range Date range bounds.
|
|
1372
|
+
* @param includeAnchor Whether the source task should be included.
|
|
1366
1373
|
* @return Generator of expanded occurrences.
|
|
1367
1374
|
*/
|
|
1368
|
-
function expandTask(task, range) {
|
|
1375
|
+
function expandTask(task, range, includeAnchor) {
|
|
1369
1376
|
var _task$start, _task$timeZone;
|
|
1370
1377
|
const anchor = (_task$start = task.start) !== null && _task$start !== void 0 ? _task$start : task.due;
|
|
1371
1378
|
if (!anchor) return (function* empty() {})();
|
|
1372
|
-
return expandObject(task, range, anchor, task.recurrenceRules, task.excludedRecurrenceRules, task.recurrenceOverrides, (_task$timeZone = task.timeZone) !== null && _task$timeZone !== void 0 ? _task$timeZone : null);
|
|
1379
|
+
return expandObject(task, range, anchor, task.recurrenceRules, task.excludedRecurrenceRules, task.recurrenceOverrides, (_task$timeZone = task.timeZone) !== null && _task$timeZone !== void 0 ? _task$timeZone : null, includeAnchor);
|
|
1373
1380
|
}
|
|
1374
1381
|
/**
|
|
1375
1382
|
* Build a stable ordering key for an occurrence.
|
|
@@ -1391,9 +1398,10 @@ function occurrenceKey(value) {
|
|
|
1391
1398
|
* @param excludedRules Exclusion recurrence rules.
|
|
1392
1399
|
* @param overrides Recurrence overrides keyed by LocalDateTime.
|
|
1393
1400
|
* @param recurrenceIdTimeZone Optional time zone for recurrence IDs.
|
|
1401
|
+
* @param includeAnchor Whether the source item should be included.
|
|
1394
1402
|
* @return Generator of expanded occurrences.
|
|
1395
1403
|
*/
|
|
1396
|
-
function* expandObject(base, range, anchor, rules, excludedRules, overrides, recurrenceIdTimeZone) {
|
|
1404
|
+
function* expandObject(base, range, anchor, rules, excludedRules, overrides, recurrenceIdTimeZone, includeAnchor = true) {
|
|
1397
1405
|
var _rules$;
|
|
1398
1406
|
const hasZone = Boolean(recurrenceIdTimeZone);
|
|
1399
1407
|
const fromLocal = hasZone && recurrenceIdTimeZone ? dateTimeInTimeZone(range.from, recurrenceIdTimeZone) : localDateTimeFromDate(range.from);
|
|
@@ -1402,9 +1410,11 @@ function* expandObject(base, range, anchor, rules, excludedRules, overrides, rec
|
|
|
1402
1410
|
const toDate = range.to;
|
|
1403
1411
|
const overrideKeys = overrides ? Object.keys(overrides) : [];
|
|
1404
1412
|
if (!rules || rules.length === 0) {
|
|
1405
|
-
if (
|
|
1406
|
-
if (
|
|
1407
|
-
|
|
1413
|
+
if (includeAnchor) {
|
|
1414
|
+
if (hasZone && recurrenceIdTimeZone) {
|
|
1415
|
+
if (isInRangeWithZone(anchor, fromDate, toDate, recurrenceIdTimeZone)) yield base;
|
|
1416
|
+
} else if (isInRange(anchor, fromLocal, toLocal)) yield base;
|
|
1417
|
+
}
|
|
1408
1418
|
for (const key of overrideKeys) {
|
|
1409
1419
|
const instance = buildInstance(base, key, recurrenceIdTimeZone, overrides ? overrides[key] : void 0);
|
|
1410
1420
|
if (!instance) continue;
|
|
@@ -1424,11 +1434,12 @@ function* expandObject(base, range, anchor, rules, excludedRules, overrides, rec
|
|
|
1424
1434
|
const expanded = expandRule(anchor, rule, fromLocal, toLocal, true, recurrenceIdTimeZone !== null && recurrenceIdTimeZone !== void 0 ? recurrenceIdTimeZone : void 0, fromDate, toDate);
|
|
1425
1435
|
for (const value of expanded) excluded.add(value);
|
|
1426
1436
|
}
|
|
1427
|
-
if (!occurrences.includes(anchor)) occurrences.push(anchor);
|
|
1437
|
+
if (includeAnchor && !occurrences.includes(anchor)) occurrences.push(anchor);
|
|
1428
1438
|
for (const key of overrideKeys) if (!occurrences.includes(key)) occurrences.push(key);
|
|
1429
1439
|
let sorted = Array.from(new Set(occurrences)).sort((a, b) => compareLocal(a, b, recurrenceIdTimeZone !== null && recurrenceIdTimeZone !== void 0 ? recurrenceIdTimeZone : void 0));
|
|
1430
1440
|
if (((_rules$ = rules[0]) === null || _rules$ === void 0 ? void 0 : _rules$.count) && sorted.length > rules[0].count) sorted = sorted.slice(0, rules[0].count);
|
|
1431
1441
|
for (const dt of sorted) {
|
|
1442
|
+
if (!includeAnchor && dt === anchor) continue;
|
|
1432
1443
|
if (excluded.has(dt)) continue;
|
|
1433
1444
|
const instance = buildInstance(base, dt, recurrenceIdTimeZone, overrides ? overrides[dt] : void 0);
|
|
1434
1445
|
if (!instance) continue;
|
|
@@ -3950,8 +3961,8 @@ const JsCal = {
|
|
|
3950
3961
|
filterByDateRange(items, range, options) {
|
|
3951
3962
|
return filterByDateRange(normalizeItems(items), range, options);
|
|
3952
3963
|
},
|
|
3953
|
-
expandRecurrence(items, range) {
|
|
3954
|
-
return expandRecurrence(normalizeItems(items), range);
|
|
3964
|
+
expandRecurrence(items, range, options) {
|
|
3965
|
+
return expandRecurrence(normalizeItems(items), range, options);
|
|
3955
3966
|
},
|
|
3956
3967
|
expandRecurrencePaged(items, range, options) {
|
|
3957
3968
|
return expandRecurrencePaged(normalizeItems(items), range, options);
|
package/dist/index.d.cts
CHANGED
|
@@ -263,6 +263,15 @@ type DateRangeOptions = {
|
|
|
263
263
|
includeIncomparable?: boolean;
|
|
264
264
|
};
|
|
265
265
|
//#endregion
|
|
266
|
+
//#region src/recurrence/types.d.ts
|
|
267
|
+
type RecurrenceExpandOptions = {
|
|
268
|
+
includeAnchor?: boolean;
|
|
269
|
+
};
|
|
270
|
+
type RecurrencePageOptions = RecurrenceExpandOptions & {
|
|
271
|
+
limit: number;
|
|
272
|
+
cursor?: string;
|
|
273
|
+
};
|
|
274
|
+
//#endregion
|
|
266
275
|
//#region src/ical.d.ts
|
|
267
276
|
type ICalOptions = {
|
|
268
277
|
prodId?: string;
|
|
@@ -748,6 +757,7 @@ declare const JsCal: {
|
|
|
748
757
|
* Expand recurrence rules into concrete occurrences.
|
|
749
758
|
* @param items JSCalendar objects or JsCal instances.
|
|
750
759
|
* @param range Date range bounds.
|
|
760
|
+
* @param options Expansion options.
|
|
751
761
|
* @return Generator of expanded JSCalendar objects.
|
|
752
762
|
*/
|
|
753
763
|
expandRecurrence(items: Array<JSCalendarObject | {
|
|
@@ -755,7 +765,7 @@ declare const JsCal: {
|
|
|
755
765
|
}>, range: {
|
|
756
766
|
from: Date;
|
|
757
767
|
to: Date;
|
|
758
|
-
}): Generator<JSCalendarObject>;
|
|
768
|
+
}, options?: RecurrenceExpandOptions): Generator<JSCalendarObject>;
|
|
759
769
|
/**
|
|
760
770
|
* Expand recurrence rules with pagination support.
|
|
761
771
|
* @param items JSCalendar objects or JsCal instances.
|
|
@@ -768,10 +778,7 @@ declare const JsCal: {
|
|
|
768
778
|
}>, range: {
|
|
769
779
|
from: Date;
|
|
770
780
|
to: Date;
|
|
771
|
-
}, options: {
|
|
772
|
-
limit: number;
|
|
773
|
-
cursor?: string | undefined;
|
|
774
|
-
}): {
|
|
781
|
+
}, options: RecurrencePageOptions): {
|
|
775
782
|
items: JSCalendarObject[];
|
|
776
783
|
nextCursor?: string;
|
|
777
784
|
};
|
package/dist/index.d.mts
CHANGED
|
@@ -263,6 +263,15 @@ type DateRangeOptions = {
|
|
|
263
263
|
includeIncomparable?: boolean;
|
|
264
264
|
};
|
|
265
265
|
//#endregion
|
|
266
|
+
//#region src/recurrence/types.d.ts
|
|
267
|
+
type RecurrenceExpandOptions = {
|
|
268
|
+
includeAnchor?: boolean;
|
|
269
|
+
};
|
|
270
|
+
type RecurrencePageOptions = RecurrenceExpandOptions & {
|
|
271
|
+
limit: number;
|
|
272
|
+
cursor?: string;
|
|
273
|
+
};
|
|
274
|
+
//#endregion
|
|
266
275
|
//#region src/ical.d.ts
|
|
267
276
|
type ICalOptions = {
|
|
268
277
|
prodId?: string;
|
|
@@ -748,6 +757,7 @@ declare const JsCal: {
|
|
|
748
757
|
* Expand recurrence rules into concrete occurrences.
|
|
749
758
|
* @param items JSCalendar objects or JsCal instances.
|
|
750
759
|
* @param range Date range bounds.
|
|
760
|
+
* @param options Expansion options.
|
|
751
761
|
* @return Generator of expanded JSCalendar objects.
|
|
752
762
|
*/
|
|
753
763
|
expandRecurrence(items: Array<JSCalendarObject | {
|
|
@@ -755,7 +765,7 @@ declare const JsCal: {
|
|
|
755
765
|
}>, range: {
|
|
756
766
|
from: Date;
|
|
757
767
|
to: Date;
|
|
758
|
-
}): Generator<JSCalendarObject>;
|
|
768
|
+
}, options?: RecurrenceExpandOptions): Generator<JSCalendarObject>;
|
|
759
769
|
/**
|
|
760
770
|
* Expand recurrence rules with pagination support.
|
|
761
771
|
* @param items JSCalendar objects or JsCal instances.
|
|
@@ -768,10 +778,7 @@ declare const JsCal: {
|
|
|
768
778
|
}>, range: {
|
|
769
779
|
from: Date;
|
|
770
780
|
to: Date;
|
|
771
|
-
}, options: {
|
|
772
|
-
limit: number;
|
|
773
|
-
cursor?: string | undefined;
|
|
774
|
-
}): {
|
|
781
|
+
}, options: RecurrencePageOptions): {
|
|
775
782
|
items: JSCalendarObject[];
|
|
776
783
|
nextCursor?: string;
|
|
777
784
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -1019,7 +1019,7 @@ function generateDateCandidates(periodStart, rule, firstDay, skip) {
|
|
|
1019
1019
|
return result;
|
|
1020
1020
|
}
|
|
1021
1021
|
if (rule.frequency === FREQ_WEEKLY) {
|
|
1022
|
-
let cursor = periodStart;
|
|
1022
|
+
let cursor = startOfWeek(periodStart, firstDay);
|
|
1023
1023
|
for (let i = 0; i < 7; i += 1) {
|
|
1024
1024
|
result.push({
|
|
1025
1025
|
year: cursor.year,
|
|
@@ -1252,10 +1252,12 @@ function expandRule(anchor, rule, fromLocal, toLocal, includeAnchor, timeZone, f
|
|
|
1252
1252
|
const results = [];
|
|
1253
1253
|
let generated = 0;
|
|
1254
1254
|
let cursor = start;
|
|
1255
|
+
const anchorValue = formatLocalDateTime(start);
|
|
1255
1256
|
while (generated < count) {
|
|
1256
1257
|
let filtered = generateDateTimes(cursor, normalized, firstDay, skip).sort();
|
|
1257
1258
|
if (normalized.bySetPosition && normalized.bySetPosition.length > 0) filtered = applyBySetPos(filtered, normalized.bySetPosition);
|
|
1258
1259
|
for (const dt of filtered) {
|
|
1260
|
+
if (compareDate(cursor, start) === 0 && dt < anchorValue) continue;
|
|
1259
1261
|
generated += 1;
|
|
1260
1262
|
if (generated > count) break;
|
|
1261
1263
|
if (until && dt > until) return results;
|
|
@@ -1275,14 +1277,16 @@ function expandRule(anchor, rule, fromLocal, toLocal, includeAnchor, timeZone, f
|
|
|
1275
1277
|
* Expand recurrence into occurrences sorted by recurrenceId/start.
|
|
1276
1278
|
* @param items JSCalendar objects to expand.
|
|
1277
1279
|
* @param range Date range bounds.
|
|
1280
|
+
* @param options Expansion options.
|
|
1278
1281
|
* @return Generator of expanded occurrences.
|
|
1279
1282
|
*/
|
|
1280
|
-
function* expandRecurrence(items, range) {
|
|
1283
|
+
function* expandRecurrence(items, range, options = {}) {
|
|
1281
1284
|
const occurrences = [];
|
|
1282
1285
|
let index = 0;
|
|
1286
|
+
const includeAnchor = options.includeAnchor ?? true;
|
|
1283
1287
|
for (const item of items) {
|
|
1284
1288
|
if (item["@type"] === TYPE_EVENT$3) {
|
|
1285
|
-
for (const occurrence of expandEvent(item, range)) {
|
|
1289
|
+
for (const occurrence of expandEvent(item, range, includeAnchor)) {
|
|
1286
1290
|
occurrences.push({
|
|
1287
1291
|
value: occurrence,
|
|
1288
1292
|
key: occurrenceKey(occurrence),
|
|
@@ -1293,7 +1297,7 @@ function* expandRecurrence(items, range) {
|
|
|
1293
1297
|
continue;
|
|
1294
1298
|
}
|
|
1295
1299
|
if (item["@type"] === TYPE_TASK$3) {
|
|
1296
|
-
for (const occurrence of expandTask(item, range)) {
|
|
1300
|
+
for (const occurrence of expandTask(item, range, includeAnchor)) {
|
|
1297
1301
|
occurrences.push({
|
|
1298
1302
|
value: occurrence,
|
|
1299
1303
|
key: occurrenceKey(occurrence),
|
|
@@ -1328,7 +1332,7 @@ function* expandRecurrence(items, range) {
|
|
|
1328
1332
|
function expandRecurrencePaged(items, range, options) {
|
|
1329
1333
|
const result = [];
|
|
1330
1334
|
let nextCursor;
|
|
1331
|
-
for (const occurrence of expandRecurrence(items, range)) {
|
|
1335
|
+
for (const occurrence of expandRecurrence(items, range, options)) {
|
|
1332
1336
|
const key = occurrenceKey(occurrence);
|
|
1333
1337
|
if (options.cursor && key) {
|
|
1334
1338
|
if (key <= options.cursor) continue;
|
|
@@ -1346,21 +1350,23 @@ function expandRecurrencePaged(items, range, options) {
|
|
|
1346
1350
|
* Expand event into occurrences.
|
|
1347
1351
|
* @param event Event to expand.
|
|
1348
1352
|
* @param range Date range bounds.
|
|
1353
|
+
* @param includeAnchor Whether the source event should be included.
|
|
1349
1354
|
* @return Generator of expanded occurrences.
|
|
1350
1355
|
*/
|
|
1351
|
-
function expandEvent(event, range) {
|
|
1352
|
-
return expandObject(event, range, event.start, event.recurrenceRules, event.excludedRecurrenceRules, event.recurrenceOverrides, event.timeZone ?? null);
|
|
1356
|
+
function expandEvent(event, range, includeAnchor) {
|
|
1357
|
+
return expandObject(event, range, event.start, event.recurrenceRules, event.excludedRecurrenceRules, event.recurrenceOverrides, event.timeZone ?? null, includeAnchor);
|
|
1353
1358
|
}
|
|
1354
1359
|
/**
|
|
1355
1360
|
* Expand task into occurrences.
|
|
1356
1361
|
* @param task Task to expand.
|
|
1357
1362
|
* @param range Date range bounds.
|
|
1363
|
+
* @param includeAnchor Whether the source task should be included.
|
|
1358
1364
|
* @return Generator of expanded occurrences.
|
|
1359
1365
|
*/
|
|
1360
|
-
function expandTask(task, range) {
|
|
1366
|
+
function expandTask(task, range, includeAnchor) {
|
|
1361
1367
|
const anchor = task.start ?? task.due;
|
|
1362
1368
|
if (!anchor) return (function* empty() {})();
|
|
1363
|
-
return expandObject(task, range, anchor, task.recurrenceRules, task.excludedRecurrenceRules, task.recurrenceOverrides, task.timeZone ?? null);
|
|
1369
|
+
return expandObject(task, range, anchor, task.recurrenceRules, task.excludedRecurrenceRules, task.recurrenceOverrides, task.timeZone ?? null, includeAnchor);
|
|
1364
1370
|
}
|
|
1365
1371
|
/**
|
|
1366
1372
|
* Build a stable ordering key for an occurrence.
|
|
@@ -1381,9 +1387,10 @@ function occurrenceKey(value) {
|
|
|
1381
1387
|
* @param excludedRules Exclusion recurrence rules.
|
|
1382
1388
|
* @param overrides Recurrence overrides keyed by LocalDateTime.
|
|
1383
1389
|
* @param recurrenceIdTimeZone Optional time zone for recurrence IDs.
|
|
1390
|
+
* @param includeAnchor Whether the source item should be included.
|
|
1384
1391
|
* @return Generator of expanded occurrences.
|
|
1385
1392
|
*/
|
|
1386
|
-
function* expandObject(base, range, anchor, rules, excludedRules, overrides, recurrenceIdTimeZone) {
|
|
1393
|
+
function* expandObject(base, range, anchor, rules, excludedRules, overrides, recurrenceIdTimeZone, includeAnchor = true) {
|
|
1387
1394
|
const hasZone = Boolean(recurrenceIdTimeZone);
|
|
1388
1395
|
const fromLocal = hasZone && recurrenceIdTimeZone ? dateTimeInTimeZone(range.from, recurrenceIdTimeZone) : localDateTimeFromDate(range.from);
|
|
1389
1396
|
const toLocal = hasZone && recurrenceIdTimeZone ? dateTimeInTimeZone(range.to, recurrenceIdTimeZone) : localDateTimeFromDate(range.to);
|
|
@@ -1391,9 +1398,11 @@ function* expandObject(base, range, anchor, rules, excludedRules, overrides, rec
|
|
|
1391
1398
|
const toDate = range.to;
|
|
1392
1399
|
const overrideKeys = overrides ? Object.keys(overrides) : [];
|
|
1393
1400
|
if (!rules || rules.length === 0) {
|
|
1394
|
-
if (
|
|
1395
|
-
if (
|
|
1396
|
-
|
|
1401
|
+
if (includeAnchor) {
|
|
1402
|
+
if (hasZone && recurrenceIdTimeZone) {
|
|
1403
|
+
if (isInRangeWithZone(anchor, fromDate, toDate, recurrenceIdTimeZone)) yield base;
|
|
1404
|
+
} else if (isInRange(anchor, fromLocal, toLocal)) yield base;
|
|
1405
|
+
}
|
|
1397
1406
|
for (const key of overrideKeys) {
|
|
1398
1407
|
const instance = buildInstance(base, key, recurrenceIdTimeZone, overrides ? overrides[key] : void 0);
|
|
1399
1408
|
if (!instance) continue;
|
|
@@ -1413,11 +1422,12 @@ function* expandObject(base, range, anchor, rules, excludedRules, overrides, rec
|
|
|
1413
1422
|
const expanded = expandRule(anchor, rule, fromLocal, toLocal, true, recurrenceIdTimeZone ?? void 0, fromDate, toDate);
|
|
1414
1423
|
for (const value of expanded) excluded.add(value);
|
|
1415
1424
|
}
|
|
1416
|
-
if (!occurrences.includes(anchor)) occurrences.push(anchor);
|
|
1425
|
+
if (includeAnchor && !occurrences.includes(anchor)) occurrences.push(anchor);
|
|
1417
1426
|
for (const key of overrideKeys) if (!occurrences.includes(key)) occurrences.push(key);
|
|
1418
1427
|
let sorted = Array.from(new Set(occurrences)).sort((a, b) => compareLocal(a, b, recurrenceIdTimeZone ?? void 0));
|
|
1419
1428
|
if (rules[0]?.count && sorted.length > rules[0].count) sorted = sorted.slice(0, rules[0].count);
|
|
1420
1429
|
for (const dt of sorted) {
|
|
1430
|
+
if (!includeAnchor && dt === anchor) continue;
|
|
1421
1431
|
if (excluded.has(dt)) continue;
|
|
1422
1432
|
const instance = buildInstance(base, dt, recurrenceIdTimeZone, overrides ? overrides[dt] : void 0);
|
|
1423
1433
|
if (!instance) continue;
|
|
@@ -3955,8 +3965,8 @@ const JsCal = {
|
|
|
3955
3965
|
filterByDateRange(items, range, options) {
|
|
3956
3966
|
return filterByDateRange(normalizeItems(items), range, options);
|
|
3957
3967
|
},
|
|
3958
|
-
expandRecurrence(items, range) {
|
|
3959
|
-
return expandRecurrence(normalizeItems(items), range);
|
|
3968
|
+
expandRecurrence(items, range, options) {
|
|
3969
|
+
return expandRecurrence(normalizeItems(items), range, options);
|
|
3960
3970
|
},
|
|
3961
3971
|
expandRecurrencePaged(items, range, options) {
|
|
3962
3972
|
return expandRecurrencePaged(normalizeItems(items), range, options);
|