react-native-roster 0.2.0 → 0.3.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 +46 -1
- package/dist/src/core/index.d.ts +1 -0
- package/dist/src/core/index.d.ts.map +1 -1
- package/dist/src/core/index.js +5 -1
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/core/spans.d.ts +19 -0
- package/dist/src/core/spans.d.ts.map +1 -1
- package/dist/src/core/spans.js +64 -0
- package/dist/src/core/spans.js.map +1 -1
- package/llms.txt +18 -1
- package/package.json +1 -1
- package/src/core/README.md +14 -1
- package/src/core/index.ts +1 -0
- package/src/core/spans.ts +66 -2
package/README.md
CHANGED
|
@@ -168,7 +168,7 @@ and "Availability may be incomplete"; both are localizable props.
|
|
|
168
168
|
| Import | Shipped surface |
|
|
169
169
|
| --- | --- |
|
|
170
170
|
| `react-native-roster` | `Roster`, `Schedule`, hooks, slot components, `RosterSelectionPopover`, `PortalHost`, `Portal`, and all core exports. |
|
|
171
|
-
| `react-native-roster/core` | Types, `layoutLane`, `coverageFor`, `flagFor`, axis helpers, comparators, and counters. Standard JavaScript and `Intl` only. |
|
|
171
|
+
| `react-native-roster/core` | Types, `layoutLane`, `coverageFor`, `flagFor`, `extentOf`, `intersectionOf`, `unionOf`, axis helpers, comparators, and counters. Standard JavaScript and `Intl` only. |
|
|
172
172
|
| `react-native-roster/rrule` | `expandRuleSet`, `envelopeFor`, types, and expansion counters and caches. Uses pinned `rrule-temporal` and `@js-temporal/polyfill`. |
|
|
173
173
|
| `react-native-roster/nativewind` | Registers `Roster` and `Schedule` with NativeWind so their `className` props resolve; re-exports the registered components. |
|
|
174
174
|
|
|
@@ -180,6 +180,51 @@ importing one function from the root costs the whole root bundle; import from
|
|
|
180
180
|
`/core` for a core-only bundle. Cache keys, counters, and clearing are in
|
|
181
181
|
[caches](./docs/caches.md).
|
|
182
182
|
|
|
183
|
+
## Core
|
|
184
|
+
|
|
185
|
+
### Interval helpers
|
|
186
|
+
|
|
187
|
+
`unionOf(spans: readonly Window[]): Window[]` merges overlapping or touching
|
|
188
|
+
windows and returns them sorted by start, keeping disjoint windows separate.
|
|
189
|
+
Empty input returns `[]`. Every returned window is a new object; the input array
|
|
190
|
+
and its windows remain untouched. Like `extentOf` and `intersectionOf`, it uses
|
|
191
|
+
end-exclusive epoch milliseconds and throws `RangeError` for non-finite bounds
|
|
192
|
+
or `end <= start`.
|
|
193
|
+
|
|
194
|
+
Collapse each person's segments with `extentOf`, then use `intersectionOf` to
|
|
195
|
+
find the shared time across those windows. Each extent bridges gaps between
|
|
196
|
+
segments, so the result describes the collapsed windows.
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
import type { Window } from 'react-native-roster/core';
|
|
200
|
+
import { extentOf, intersectionOf } from 'react-native-roster/core';
|
|
201
|
+
|
|
202
|
+
const people: { name: string; segments: Window[] }[] = [
|
|
203
|
+
{
|
|
204
|
+
name: 'Alex',
|
|
205
|
+
segments: [
|
|
206
|
+
{ start: 1_000, end: 3_000 },
|
|
207
|
+
{ start: 4_000, end: 8_000 },
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
name: 'Sam',
|
|
212
|
+
segments: [
|
|
213
|
+
{ start: 2_000, end: 5_000 },
|
|
214
|
+
{ start: 6_000, end: 9_000 },
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
const windows = people.map((person): Window => {
|
|
220
|
+
const extent = extentOf(person.segments);
|
|
221
|
+
if (extent === null) throw new Error(`${person.name} has no segments`);
|
|
222
|
+
return extent;
|
|
223
|
+
});
|
|
224
|
+
const sharedTime = intersectionOf(windows); // { start: 2_000, end: 8_000 }
|
|
225
|
+
// A null result from intersectionOf means there is no shared time.
|
|
226
|
+
```
|
|
227
|
+
|
|
183
228
|
## Navigation and interaction
|
|
184
229
|
|
|
185
230
|
A `WindowSpec` is `{ span: 'day' | 'week' | 'month', anchorDate, timezone, wkst? }`
|
package/dist/src/core/index.d.ts
CHANGED
|
@@ -9,5 +9,6 @@ export type { LaneComparator } from './order';
|
|
|
9
9
|
export { byCoverage, byLabel } from './order';
|
|
10
10
|
export { snapToStep, timeAtX, timeAtY } from './snap';
|
|
11
11
|
export { hasSource } from './source';
|
|
12
|
+
export { extentOf, intersectionOf, unionOf } from './spans';
|
|
12
13
|
export type { Coverage, DayColumn, Gap, Interval, Lane, LaneFlag, LaneGeometry, Layer, LayerRole, LayerStyle, Projection, Rect, Source, Transition, Weekday, Window, } from './types';
|
|
13
14
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,YAAY,EACV,QAAQ,EACR,SAAS,EACT,GAAG,EACH,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,YAAY,EACZ,KAAK,EACL,SAAS,EACT,UAAU,EACV,UAAU,EACV,IAAI,EACJ,MAAM,EACN,UAAU,EACV,OAAO,EACP,MAAM,GACP,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAC5D,YAAY,EACV,QAAQ,EACR,SAAS,EACT,GAAG,EACH,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,YAAY,EACZ,KAAK,EACL,SAAS,EACT,UAAU,EACV,UAAU,EACV,IAAI,EACJ,MAAM,EACN,UAAU,EACV,OAAO,EACP,MAAM,GACP,MAAM,SAAS,CAAC"}
|
package/dist/src/core/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.hasSource = exports.timeAtY = exports.timeAtX = exports.snapToStep = exports.byLabel = exports.byCoverage = exports.layoutLane = exports.flagFor = exports.coverageFor = exports.dayColumnsFor = exports.resetStats = exports.layoutStats = exports.coverageStats = exports.clearLayoutCache = exports.clearCoverageCache = exports.windowFor = exports.today = exports.prev = exports.next = void 0;
|
|
3
|
+
exports.unionOf = exports.intersectionOf = exports.extentOf = exports.hasSource = exports.timeAtY = exports.timeAtX = exports.snapToStep = exports.byLabel = exports.byCoverage = exports.layoutLane = exports.flagFor = exports.coverageFor = exports.dayColumnsFor = exports.resetStats = exports.layoutStats = exports.coverageStats = exports.clearLayoutCache = exports.clearCoverageCache = exports.windowFor = exports.today = exports.prev = exports.next = void 0;
|
|
4
4
|
var axis_1 = require("./axis");
|
|
5
5
|
Object.defineProperty(exports, "next", { enumerable: true, get: function () { return axis_1.next; } });
|
|
6
6
|
Object.defineProperty(exports, "prev", { enumerable: true, get: function () { return axis_1.prev; } });
|
|
@@ -29,4 +29,8 @@ Object.defineProperty(exports, "timeAtX", { enumerable: true, get: function () {
|
|
|
29
29
|
Object.defineProperty(exports, "timeAtY", { enumerable: true, get: function () { return snap_1.timeAtY; } });
|
|
30
30
|
var source_1 = require("./source");
|
|
31
31
|
Object.defineProperty(exports, "hasSource", { enumerable: true, get: function () { return source_1.hasSource; } });
|
|
32
|
+
var spans_1 = require("./spans");
|
|
33
|
+
Object.defineProperty(exports, "extentOf", { enumerable: true, get: function () { return spans_1.extentOf; } });
|
|
34
|
+
Object.defineProperty(exports, "intersectionOf", { enumerable: true, get: function () { return spans_1.intersectionOf; } });
|
|
35
|
+
Object.defineProperty(exports, "unionOf", { enumerable: true, get: function () { return spans_1.unionOf; } });
|
|
32
36
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":";;;AACA,+BAAsD;AAA7C,4FAAA,IAAI,OAAA;AAAE,4FAAA,IAAI,OAAA;AAAE,6FAAA,KAAK,OAAA;AAAE,iGAAA,SAAS,OAAA;AACrC,iCAMiB;AALf,2GAAA,kBAAkB,OAAA;AAClB,yGAAA,gBAAgB,OAAA;AAChB,sGAAA,aAAa,OAAA;AACb,oGAAA,WAAW,OAAA;AACX,mGAAA,UAAU,OAAA;AAEZ,qCAA0C;AAAjC,wGAAA,aAAa,OAAA;AACtB,uCAAyC;AAAhC,uGAAA,WAAW,OAAA;AACpB,+BAAiC;AAAxB,+FAAA,OAAO,OAAA;AAChB,mCAAsC;AAA7B,oGAAA,UAAU,OAAA;AAEnB,iCAA8C;AAArC,mGAAA,UAAU,OAAA;AAAE,gGAAA,OAAO,OAAA;AAC5B,+BAAsD;AAA7C,kGAAA,UAAU,OAAA;AAAE,+FAAA,OAAO,OAAA;AAAE,+FAAA,OAAO,OAAA;AACrC,mCAAqC;AAA5B,mGAAA,SAAS,OAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":";;;AACA,+BAAsD;AAA7C,4FAAA,IAAI,OAAA;AAAE,4FAAA,IAAI,OAAA;AAAE,6FAAA,KAAK,OAAA;AAAE,iGAAA,SAAS,OAAA;AACrC,iCAMiB;AALf,2GAAA,kBAAkB,OAAA;AAClB,yGAAA,gBAAgB,OAAA;AAChB,sGAAA,aAAa,OAAA;AACb,oGAAA,WAAW,OAAA;AACX,mGAAA,UAAU,OAAA;AAEZ,qCAA0C;AAAjC,wGAAA,aAAa,OAAA;AACtB,uCAAyC;AAAhC,uGAAA,WAAW,OAAA;AACpB,+BAAiC;AAAxB,+FAAA,OAAO,OAAA;AAChB,mCAAsC;AAA7B,oGAAA,UAAU,OAAA;AAEnB,iCAA8C;AAArC,mGAAA,UAAU,OAAA;AAAE,gGAAA,OAAO,OAAA;AAC5B,+BAAsD;AAA7C,kGAAA,UAAU,OAAA;AAAE,+FAAA,OAAO,OAAA;AAAE,+FAAA,OAAO,OAAA;AACrC,mCAAqC;AAA5B,mGAAA,SAAS,OAAA;AAClB,iCAA4D;AAAnD,iGAAA,QAAQ,OAAA;AAAE,uGAAA,cAAc,OAAA;AAAE,gGAAA,OAAO,OAAA"}
|
package/dist/src/core/spans.d.ts
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
1
|
import type { Interval, Window } from './types';
|
|
2
2
|
export declare function sourceSpans(intervals: Interval[], window: Window): Interval[];
|
|
3
|
+
/**
|
|
4
|
+
* Earliest start through latest end in epoch milliseconds, end-exclusive; empty input returns null.
|
|
5
|
+
* Throws RangeError for non-finite bounds or end <= start.
|
|
6
|
+
* Inputs may be unsorted, overlapping, nested, or duplicated; inputs are not mutated.
|
|
7
|
+
*/
|
|
8
|
+
export declare function extentOf(segments: readonly Window[]): Window | null;
|
|
9
|
+
/**
|
|
10
|
+
* Shared epoch milliseconds, end-exclusive; empty input or an empty intersection returns null.
|
|
11
|
+
* Throws RangeError for non-finite bounds or end <= start.
|
|
12
|
+
* Inputs may be unsorted, overlapping, nested, or duplicated; inputs are not mutated.
|
|
13
|
+
*/
|
|
14
|
+
export declare function intersectionOf(spans: readonly Window[]): Window | null;
|
|
15
|
+
/**
|
|
16
|
+
* Merged end-exclusive epoch windows sorted by start; empty input returns an empty array.
|
|
17
|
+
* Overlapping or touching spans merge; disjoint spans remain separate.
|
|
18
|
+
* Throws RangeError for non-finite bounds or end <= start.
|
|
19
|
+
* Returns new objects without mutating inputs.
|
|
20
|
+
*/
|
|
21
|
+
export declare function unionOf(spans: readonly Window[]): Window[];
|
|
3
22
|
//# sourceMappingURL=spans.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spans.d.ts","sourceRoot":"","sources":["../../../src/core/spans.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"spans.d.ts","sourceRoot":"","sources":["../../../src/core/spans.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAU,MAAM,EAAE,MAAM,SAAS,CAAC;AAIxD,wBAAgB,WAAW,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,EAAE,CAmD7E;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CASnE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAUtE;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAgB1D"}
|
package/dist/src/core/spans.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.sourceSpans = sourceSpans;
|
|
4
|
+
exports.extentOf = extentOf;
|
|
5
|
+
exports.intersectionOf = intersectionOf;
|
|
6
|
+
exports.unionOf = unionOf;
|
|
4
7
|
function sourceSpans(intervals, window) {
|
|
5
8
|
const boundaries = [];
|
|
6
9
|
for (const interval of intervals) {
|
|
@@ -57,4 +60,65 @@ function sourceSpans(intervals, window) {
|
|
|
57
60
|
}
|
|
58
61
|
return spans;
|
|
59
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Earliest start through latest end in epoch milliseconds, end-exclusive; empty input returns null.
|
|
65
|
+
* Throws RangeError for non-finite bounds or end <= start.
|
|
66
|
+
* Inputs may be unsorted, overlapping, nested, or duplicated; inputs are not mutated.
|
|
67
|
+
*/
|
|
68
|
+
function extentOf(segments) {
|
|
69
|
+
let start = Infinity;
|
|
70
|
+
let end = -Infinity;
|
|
71
|
+
for (const segment of segments) {
|
|
72
|
+
validateSpan(segment);
|
|
73
|
+
start = Math.min(start, segment.start);
|
|
74
|
+
end = Math.max(end, segment.end);
|
|
75
|
+
}
|
|
76
|
+
return start < end ? { start, end } : null;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Shared epoch milliseconds, end-exclusive; empty input or an empty intersection returns null.
|
|
80
|
+
* Throws RangeError for non-finite bounds or end <= start.
|
|
81
|
+
* Inputs may be unsorted, overlapping, nested, or duplicated; inputs are not mutated.
|
|
82
|
+
*/
|
|
83
|
+
function intersectionOf(spans) {
|
|
84
|
+
if (spans.length === 0)
|
|
85
|
+
return null;
|
|
86
|
+
let start = -Infinity;
|
|
87
|
+
let end = Infinity;
|
|
88
|
+
for (const span of spans) {
|
|
89
|
+
validateSpan(span);
|
|
90
|
+
start = Math.max(start, span.start);
|
|
91
|
+
end = Math.min(end, span.end);
|
|
92
|
+
}
|
|
93
|
+
return start < end ? { start, end } : null;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Merged end-exclusive epoch windows sorted by start; empty input returns an empty array.
|
|
97
|
+
* Overlapping or touching spans merge; disjoint spans remain separate.
|
|
98
|
+
* Throws RangeError for non-finite bounds or end <= start.
|
|
99
|
+
* Returns new objects without mutating inputs.
|
|
100
|
+
*/
|
|
101
|
+
function unionOf(spans) {
|
|
102
|
+
const sorted = spans.map((span) => {
|
|
103
|
+
validateSpan(span);
|
|
104
|
+
return { start: span.start, end: span.end };
|
|
105
|
+
});
|
|
106
|
+
sorted.sort((a, b) => a.start - b.start);
|
|
107
|
+
const merged = [];
|
|
108
|
+
for (const span of sorted) {
|
|
109
|
+
const last = merged.at(-1);
|
|
110
|
+
if (last && span.start <= last.end) {
|
|
111
|
+
last.end = Math.max(last.end, span.end);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
merged.push(span);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return merged;
|
|
118
|
+
}
|
|
119
|
+
function validateSpan(span) {
|
|
120
|
+
if (!Number.isFinite(span.start) || !Number.isFinite(span.end) || span.end <= span.start) {
|
|
121
|
+
throw new RangeError('Span bounds must be finite with end greater than start');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
60
124
|
//# sourceMappingURL=spans.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spans.js","sourceRoot":"","sources":["../../../src/core/spans.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"spans.js","sourceRoot":"","sources":["../../../src/core/spans.ts"],"names":[],"mappings":"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"}
|
package/llms.txt
CHANGED
|
@@ -145,10 +145,27 @@ show the incompleteLabel; do not describe incomplete empty time as definitive.
|
|
|
145
145
|
| Import | Runtime exports and purpose |
|
|
146
146
|
| --- | --- |
|
|
147
147
|
| react-native-roster | Roster, Schedule, hooks, default slot components, RosterSelectionPopover, PortalHost, Portal, and core re-exports. |
|
|
148
|
-
| react-native-roster/core | layoutLane, coverageFor, flagFor, windowFor, prev, next, today, dayColumnsFor, timeAtX, timeAtY, snapToStep, byLabel, byCoverage, and core counters/caches. |
|
|
148
|
+
| react-native-roster/core | layoutLane, coverageFor, flagFor, extentOf, intersectionOf, unionOf, windowFor, prev, next, today, dayColumnsFor, timeAtX, timeAtY, snapToStep, byLabel, byCoverage, and core counters/caches. |
|
|
149
149
|
| react-native-roster/rrule | expandRuleSet, envelopeFor, expandStats, resetExpandStats, clearExpandCache; RuleSet, RosterRule, RosterDate, ExpandOptions, ExpandResult, and ExpandStats types. |
|
|
150
150
|
| react-native-roster/nativewind | Side-effect registration of Roster and Schedule with NativeWind's cssInterop, plus the registered Roster and Schedule and the rosterClassNames/scheduleClassNames mappings. Imports the optional nativewind peer. |
|
|
151
151
|
|
|
152
|
+
## Interval helpers
|
|
153
|
+
|
|
154
|
+
extentOf(segments: readonly Window[]): Window | null returns earliest start through
|
|
155
|
+
latest end, bridging disjoint segments. intersectionOf(spans: readonly Window[]):
|
|
156
|
+
Window | null returns latest start through earliest end. Both use end-exclusive
|
|
157
|
+
epoch milliseconds and return null for empty input.
|
|
158
|
+
Inputs may be unsorted, overlapping, nested, or duplicated; inputs are not mutated.
|
|
159
|
+
An empty intersection, including touching bounds, returns null. Every input is
|
|
160
|
+
validated; non-finite bounds or end <= start throw RangeError. Window is the bare
|
|
161
|
+
{ start, end } type; Span remains the axis choice type.
|
|
162
|
+
|
|
163
|
+
unionOf(spans: readonly Window[]): Window[] merges overlapping or touching
|
|
164
|
+
end-exclusive windows, sorts them by start, and keeps disjoint windows separate.
|
|
165
|
+
Empty input returns []; every returned window is a new object. The input array
|
|
166
|
+
and its windows remain untouched. Every span is validated with the same
|
|
167
|
+
RangeError rules as extentOf and intersectionOf.
|
|
168
|
+
|
|
152
169
|
No default exports. Root exports renderer inputs/models/slot input types as well as
|
|
153
170
|
core types. Core imports standard JS and Intl only. Only /rrule imports
|
|
154
171
|
rrule-temporal 1.6.0 and @js-temporal/polyfill 0.5.1. Those are installed runtime
|
package/package.json
CHANGED
package/src/core/README.md
CHANGED
|
@@ -6,6 +6,9 @@ no React Native, no dependencies.
|
|
|
6
6
|
- Public subpath: `react-native-roster/core`; the root entry re-exports it
|
|
7
7
|
- Authoritative types: `types.ts` (`Lane`, `Layer`, `Interval`, `Gap`,
|
|
8
8
|
`Source`, `Window`, `Projection`, `Rect`, `Coverage`, `LaneGeometry`)
|
|
9
|
+
- Interval helpers: `extentOf(segments: readonly Window[]): Window | null`,
|
|
10
|
+
`intersectionOf(spans: readonly Window[]): Window | null`, and
|
|
11
|
+
`unionOf(spans: readonly Window[]): Window[]`
|
|
9
12
|
- Barrel: `index.ts` only
|
|
10
13
|
|
|
11
14
|
| File | Role |
|
|
@@ -14,7 +17,7 @@ no React Native, no dependencies.
|
|
|
14
17
|
| `layout.ts` | rect geometry for one lane in either projection |
|
|
15
18
|
| `coverage.ts` | covered and removed minutes per lane |
|
|
16
19
|
| `source.ts` | `hasSource(sources, source)` matches kind and id, ignoring labels |
|
|
17
|
-
| `spans.ts` | the provenance sweep
|
|
20
|
+
| `spans.ts` | `extentOf`, `intersectionOf`, `unionOf`, and the provenance sweep with exact sources per merged span |
|
|
18
21
|
| `columns.ts` | day columns, transitions, and skipped dates for the columns projection |
|
|
19
22
|
| `zone.ts` | Intl-only timezone math (offsets, first instants, transitions) |
|
|
20
23
|
| `scale.ts`, `snap.ts` | pixel and time conversion, pointer snapping |
|
|
@@ -22,6 +25,16 @@ no React Native, no dependencies.
|
|
|
22
25
|
| `order.ts`, `flag.ts` | lane comparators and lane flags |
|
|
23
26
|
| `cache.ts`, `hash.ts` | layout and coverage caches keyed by lane content |
|
|
24
27
|
|
|
28
|
+
All interval helpers reuse the bare `{ start, end }` `Window` type with
|
|
29
|
+
end-exclusive epoch millisecond bounds. `extentOf` bridges disjoint segments;
|
|
30
|
+
`intersectionOf` returns only their shared span. Both return `null` for empty
|
|
31
|
+
input; an empty intersection, including touching bounds, also returns `null`.
|
|
32
|
+
`unionOf` merges overlapping or touching windows, sorts by start, and keeps
|
|
33
|
+
disjoint windows separate. It returns `[]` for empty input and new window objects.
|
|
34
|
+
Every input must have finite bounds and `end > start`; otherwise the helpers throw `RangeError`.
|
|
35
|
+
Inputs may be unsorted, overlapping, nested, or duplicated; inputs are not mutated.
|
|
36
|
+
The existing `Span` type names axis choices only.
|
|
37
|
+
|
|
25
38
|
Reference: [design](../../docs/design.md), [timezones](../../docs/timezones.md),
|
|
26
39
|
[caches](../../docs/caches.md). Tests: `test/core`.
|
|
27
40
|
|
package/src/core/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type { LaneComparator } from './order';
|
|
|
15
15
|
export { byCoverage, byLabel } from './order';
|
|
16
16
|
export { snapToStep, timeAtX, timeAtY } from './snap';
|
|
17
17
|
export { hasSource } from './source';
|
|
18
|
+
export { extentOf, intersectionOf, unionOf } from './spans';
|
|
18
19
|
export type {
|
|
19
20
|
Coverage,
|
|
20
21
|
DayColumn,
|
package/src/core/spans.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
1
|
+
// Provenance sweep logic and the extentOf, intersectionOf, and unionOf interval helpers.
|
|
2
|
+
// The sweep uses reference counts to preserve provenance through nested intervals
|
|
3
|
+
// and equal-time ends/starts. Labels never affect source identity.
|
|
3
4
|
import type { Interval, Source, Window } from './types';
|
|
4
5
|
|
|
5
6
|
type Boundary = { at: number; delta: number; sources: Source[] };
|
|
@@ -56,3 +57,66 @@ export function sourceSpans(intervals: Interval[], window: Window): Interval[] {
|
|
|
56
57
|
}
|
|
57
58
|
return spans;
|
|
58
59
|
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Earliest start through latest end in epoch milliseconds, end-exclusive; empty input returns null.
|
|
63
|
+
* Throws RangeError for non-finite bounds or end <= start.
|
|
64
|
+
* Inputs may be unsorted, overlapping, nested, or duplicated; inputs are not mutated.
|
|
65
|
+
*/
|
|
66
|
+
export function extentOf(segments: readonly Window[]): Window | null {
|
|
67
|
+
let start = Infinity;
|
|
68
|
+
let end = -Infinity;
|
|
69
|
+
for (const segment of segments) {
|
|
70
|
+
validateSpan(segment);
|
|
71
|
+
start = Math.min(start, segment.start);
|
|
72
|
+
end = Math.max(end, segment.end);
|
|
73
|
+
}
|
|
74
|
+
return start < end ? { start, end } : null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Shared epoch milliseconds, end-exclusive; empty input or an empty intersection returns null.
|
|
79
|
+
* Throws RangeError for non-finite bounds or end <= start.
|
|
80
|
+
* Inputs may be unsorted, overlapping, nested, or duplicated; inputs are not mutated.
|
|
81
|
+
*/
|
|
82
|
+
export function intersectionOf(spans: readonly Window[]): Window | null {
|
|
83
|
+
if (spans.length === 0) return null;
|
|
84
|
+
let start = -Infinity;
|
|
85
|
+
let end = Infinity;
|
|
86
|
+
for (const span of spans) {
|
|
87
|
+
validateSpan(span);
|
|
88
|
+
start = Math.max(start, span.start);
|
|
89
|
+
end = Math.min(end, span.end);
|
|
90
|
+
}
|
|
91
|
+
return start < end ? { start, end } : null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Merged end-exclusive epoch windows sorted by start; empty input returns an empty array.
|
|
96
|
+
* Overlapping or touching spans merge; disjoint spans remain separate.
|
|
97
|
+
* Throws RangeError for non-finite bounds or end <= start.
|
|
98
|
+
* Returns new objects without mutating inputs.
|
|
99
|
+
*/
|
|
100
|
+
export function unionOf(spans: readonly Window[]): Window[] {
|
|
101
|
+
const sorted = spans.map((span) => {
|
|
102
|
+
validateSpan(span);
|
|
103
|
+
return { start: span.start, end: span.end };
|
|
104
|
+
});
|
|
105
|
+
sorted.sort((a, b) => a.start - b.start);
|
|
106
|
+
const merged: Window[] = [];
|
|
107
|
+
for (const span of sorted) {
|
|
108
|
+
const last = merged.at(-1);
|
|
109
|
+
if (last && span.start <= last.end) {
|
|
110
|
+
last.end = Math.max(last.end, span.end);
|
|
111
|
+
} else {
|
|
112
|
+
merged.push(span);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return merged;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function validateSpan(span: Window): void {
|
|
119
|
+
if (!Number.isFinite(span.start) || !Number.isFinite(span.end) || span.end <= span.start) {
|
|
120
|
+
throw new RangeError('Span bounds must be finite with end greater than start');
|
|
121
|
+
}
|
|
122
|
+
}
|