react-native-roster 0.1.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 +148 -3
- package/dist/src/components/primitives/portal.d.ts +12 -0
- package/dist/src/components/primitives/portal.d.ts.map +1 -0
- package/dist/src/components/primitives/portal.js +41 -0
- package/dist/src/components/primitives/portal.js.map +1 -0
- package/dist/src/components/roster/body-layout.d.ts +1 -1
- package/dist/src/components/roster/body-layout.d.ts.map +1 -1
- package/dist/src/components/roster/body-layout.js +7 -1
- package/dist/src/components/roster/body-layout.js.map +1 -1
- package/dist/src/components/roster/index.d.ts +2 -1
- package/dist/src/components/roster/index.d.ts.map +1 -1
- package/dist/src/components/roster/index.js +26 -3
- package/dist/src/components/roster/index.js.map +1 -1
- package/dist/src/components/roster/lanes/lane.d.ts.map +1 -1
- package/dist/src/components/roster/lanes/lane.js +2 -1
- package/dist/src/components/roster/lanes/lane.js.map +1 -1
- package/dist/src/components/roster/parts/lane-list.d.ts.map +1 -1
- package/dist/src/components/roster/parts/lane-list.js +2 -1
- package/dist/src/components/roster/parts/lane-list.js.map +1 -1
- package/dist/src/components/roster/roster.types.d.ts +29 -1
- package/dist/src/components/roster/roster.types.d.ts.map +1 -1
- package/dist/src/components/roster/selection/selection-layout.d.ts +4 -0
- package/dist/src/components/roster/selection/selection-layout.d.ts.map +1 -0
- package/dist/src/components/roster/selection/selection-layout.js +82 -0
- package/dist/src/components/roster/selection/selection-layout.js.map +1 -0
- package/dist/src/components/roster/selection/selection-layout.types.d.ts +25 -0
- package/dist/src/components/roster/selection/selection-layout.types.d.ts.map +1 -0
- package/dist/src/components/roster/selection/selection-layout.types.js +3 -0
- package/dist/src/components/roster/selection/selection-layout.types.js.map +1 -0
- package/dist/src/components/roster/selection/selection-layout.web.d.ts +4 -0
- package/dist/src/components/roster/selection/selection-layout.web.d.ts.map +1 -0
- package/dist/src/components/roster/selection/selection-layout.web.js +52 -0
- package/dist/src/components/roster/selection/selection-layout.web.js.map +1 -0
- package/dist/src/components/roster/use-roster-press.d.ts +6 -0
- package/dist/src/components/roster/use-roster-press.d.ts.map +1 -0
- package/dist/src/components/roster/use-roster-press.js +61 -0
- package/dist/src/components/roster/use-roster-press.js.map +1 -0
- package/dist/src/components/roster/use-roster.d.ts.map +1 -1
- package/dist/src/components/roster/use-roster.js +40 -30
- package/dist/src/components/roster/use-roster.js.map +1 -1
- package/dist/src/components/schedule/days/parts/column.d.ts.map +1 -1
- package/dist/src/components/schedule/days/parts/column.js +2 -1
- package/dist/src/components/schedule/days/parts/column.js.map +1 -1
- package/dist/src/core/index.d.ts +2 -0
- package/dist/src/core/index.d.ts.map +1 -1
- package/dist/src/core/index.js +7 -1
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/core/source.d.ts +4 -0
- package/dist/src/core/source.d.ts.map +1 -0
- package/dist/src/core/source.js +9 -0
- package/dist/src/core/source.js.map +1 -0
- 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/dist/src/index.d.ts +4 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/nativewind/index.d.ts +4 -1
- package/dist/src/nativewind/index.d.ts.map +1 -1
- package/llms.txt +85 -3
- package/package.json +10 -4
- package/src/components/primitives/README.md +13 -2
- package/src/components/primitives/portal.tsx +48 -0
- package/src/components/roster/README.md +48 -2
- package/src/components/roster/body-layout.tsx +7 -1
- package/src/components/roster/index.tsx +62 -17
- package/src/components/roster/lanes/lane.tsx +2 -4
- package/src/components/roster/parts/lane-list.tsx +3 -1
- package/src/components/roster/roster.types.ts +30 -1
- package/src/components/roster/selection/selection-layout.tsx +90 -0
- package/src/components/roster/selection/selection-layout.types.ts +20 -0
- package/src/components/roster/selection/selection-layout.web.tsx +88 -0
- package/src/components/roster/use-roster-press.ts +74 -0
- package/src/components/roster/use-roster.ts +57 -44
- package/src/components/schedule/days/parts/column.tsx +2 -4
- package/src/core/README.md +18 -1
- package/src/core/index.ts +2 -0
- package/src/core/source.ts +9 -0
- package/src/core/spans.ts +66 -2
- package/src/index.ts +4 -0
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/dist/src/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type { GapInput, IntervalInput } from './components/layers/layers.types';
|
|
2
2
|
export { RosterGap } from './components/layers/parts/gap';
|
|
3
3
|
export { RosterInterval } from './components/layers/parts/interval';
|
|
4
|
+
export { Portal, PortalHost } from './components/primitives/portal';
|
|
4
5
|
export { Roster } from './components/roster';
|
|
5
6
|
export { RosterBodyLayout } from './components/roster/body-layout';
|
|
6
7
|
export { RosterLaneLabel } from './components/roster/lanes/parts/lane-label';
|
|
@@ -12,7 +13,9 @@ export { RosterHeader } from './components/roster/parts/header';
|
|
|
12
13
|
export { RosterHeaderCell } from './components/roster/parts/header-cell';
|
|
13
14
|
export { RosterLaneLabelColumn } from './components/roster/parts/lane-label-column';
|
|
14
15
|
export { RosterLaneList } from './components/roster/parts/lane-list';
|
|
15
|
-
export type { BodyInput, GridInput, HeaderCellInput, HeaderInput, LabelColumnInput, LaneLabelInput, RosterInput, RosterModel, RosterProjection, RosterProps, RosterScroll, RosterStyleProps, RosterTick, } from './components/roster/roster.types';
|
|
16
|
+
export type { BodyInput, GridInput, HeaderCellInput, HeaderInput, IntervalDetailInput, LabelColumnInput, LaneLabelInput, RosterInput, RosterModel, RosterProjection, RosterProps, RosterScroll, RosterStyleProps, RosterTick, } from './components/roster/roster.types';
|
|
17
|
+
export { RosterSelectionPopover } from './components/roster/selection/selection-layout';
|
|
18
|
+
export type { SelectionLayoutProps } from './components/roster/selection/selection-layout.types';
|
|
16
19
|
export { useRoster } from './components/roster/use-roster';
|
|
17
20
|
export { Schedule } from './components/schedule';
|
|
18
21
|
export { ScheduleColumn } from './components/schedule/days/parts/column';
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACpF,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AACrE,YAAY,EACV,SAAS,EACT,SAAS,EACT,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,UAAU,GACX,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6CAA6C,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,cAAc,QAAQ,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACpF,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AACrE,YAAY,EACV,SAAS,EACT,SAAS,EACT,eAAe,EACf,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,UAAU,GACX,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,gDAAgD,CAAC;AACxF,YAAY,EAAE,oBAAoB,EAAE,MAAM,sDAAsD,CAAC;AACjG,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6CAA6C,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,cAAc,QAAQ,CAAC"}
|
package/dist/src/index.js
CHANGED
|
@@ -14,11 +14,14 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.useSchedule = exports.ScheduleIncomplete = exports.ScheduleGutter = exports.ScheduleTransition = exports.ScheduleSkippedDate = exports.ScheduleNowLine = exports.ScheduleGrid = exports.ScheduleDayHeader = exports.ScheduleColumn = exports.Schedule = exports.useRoster = exports.RosterLaneList = exports.RosterLaneLabelColumn = exports.RosterHeaderCell = exports.RosterHeader = exports.RosterGrid = exports.RosterEmpty = exports.RosterCorner = exports.RosterBody = exports.RosterLaneLabel = exports.RosterBodyLayout = exports.Roster = exports.RosterInterval = exports.RosterGap = void 0;
|
|
17
|
+
exports.useSchedule = exports.ScheduleIncomplete = exports.ScheduleGutter = exports.ScheduleTransition = exports.ScheduleSkippedDate = exports.ScheduleNowLine = exports.ScheduleGrid = exports.ScheduleDayHeader = exports.ScheduleColumn = exports.Schedule = exports.useRoster = exports.RosterSelectionPopover = exports.RosterLaneList = exports.RosterLaneLabelColumn = exports.RosterHeaderCell = exports.RosterHeader = exports.RosterGrid = exports.RosterEmpty = exports.RosterCorner = exports.RosterBody = exports.RosterLaneLabel = exports.RosterBodyLayout = exports.Roster = exports.PortalHost = exports.Portal = exports.RosterInterval = exports.RosterGap = void 0;
|
|
18
18
|
var gap_1 = require("./components/layers/parts/gap");
|
|
19
19
|
Object.defineProperty(exports, "RosterGap", { enumerable: true, get: function () { return gap_1.RosterGap; } });
|
|
20
20
|
var interval_1 = require("./components/layers/parts/interval");
|
|
21
21
|
Object.defineProperty(exports, "RosterInterval", { enumerable: true, get: function () { return interval_1.RosterInterval; } });
|
|
22
|
+
var portal_1 = require("./components/primitives/portal");
|
|
23
|
+
Object.defineProperty(exports, "Portal", { enumerable: true, get: function () { return portal_1.Portal; } });
|
|
24
|
+
Object.defineProperty(exports, "PortalHost", { enumerable: true, get: function () { return portal_1.PortalHost; } });
|
|
22
25
|
var roster_1 = require("./components/roster");
|
|
23
26
|
Object.defineProperty(exports, "Roster", { enumerable: true, get: function () { return roster_1.Roster; } });
|
|
24
27
|
var body_layout_1 = require("./components/roster/body-layout");
|
|
@@ -41,6 +44,8 @@ var lane_label_column_1 = require("./components/roster/parts/lane-label-column")
|
|
|
41
44
|
Object.defineProperty(exports, "RosterLaneLabelColumn", { enumerable: true, get: function () { return lane_label_column_1.RosterLaneLabelColumn; } });
|
|
42
45
|
var lane_list_1 = require("./components/roster/parts/lane-list");
|
|
43
46
|
Object.defineProperty(exports, "RosterLaneList", { enumerable: true, get: function () { return lane_list_1.RosterLaneList; } });
|
|
47
|
+
var selection_layout_1 = require("./components/roster/selection/selection-layout");
|
|
48
|
+
Object.defineProperty(exports, "RosterSelectionPopover", { enumerable: true, get: function () { return selection_layout_1.RosterSelectionPopover; } });
|
|
44
49
|
var use_roster_1 = require("./components/roster/use-roster");
|
|
45
50
|
Object.defineProperty(exports, "useRoster", { enumerable: true, get: function () { return use_roster_1.useRoster; } });
|
|
46
51
|
var schedule_1 = require("./components/schedule");
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AACA,qDAA0D;AAAjD,gGAAA,SAAS,OAAA;AAClB,+DAAoE;AAA3D,0GAAA,cAAc,OAAA;AACvB,8CAA6C;AAApC,gGAAA,MAAM,OAAA;AACf,+DAAmE;AAA1D,+GAAA,gBAAgB,OAAA;AACzB,yEAA6E;AAApE,6GAAA,eAAe,OAAA;AACxB,uDAA4D;AAAnD,kGAAA,UAAU,OAAA;AACnB,2DAAgE;AAAvD,sGAAA,YAAY,OAAA;AACrB,yDAA8D;AAArD,oGAAA,WAAW,OAAA;AACpB,uDAA4D;AAAnD,kGAAA,UAAU,OAAA;AACnB,2DAAgE;AAAvD,sGAAA,YAAY,OAAA;AACrB,qEAAyE;AAAhE,+GAAA,gBAAgB,OAAA;AACzB,iFAAoF;AAA3E,0HAAA,qBAAqB,OAAA;AAC9B,iEAAqE;AAA5D,2GAAA,cAAc,OAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AACA,qDAA0D;AAAjD,gGAAA,SAAS,OAAA;AAClB,+DAAoE;AAA3D,0GAAA,cAAc,OAAA;AACvB,yDAAoE;AAA3D,gGAAA,MAAM,OAAA;AAAE,oGAAA,UAAU,OAAA;AAC3B,8CAA6C;AAApC,gGAAA,MAAM,OAAA;AACf,+DAAmE;AAA1D,+GAAA,gBAAgB,OAAA;AACzB,yEAA6E;AAApE,6GAAA,eAAe,OAAA;AACxB,uDAA4D;AAAnD,kGAAA,UAAU,OAAA;AACnB,2DAAgE;AAAvD,sGAAA,YAAY,OAAA;AACrB,yDAA8D;AAArD,oGAAA,WAAW,OAAA;AACpB,uDAA4D;AAAnD,kGAAA,UAAU,OAAA;AACnB,2DAAgE;AAAvD,sGAAA,YAAY,OAAA;AACrB,qEAAyE;AAAhE,+GAAA,gBAAgB,OAAA;AACzB,iFAAoF;AAA3E,0HAAA,qBAAqB,OAAA;AAC9B,iEAAqE;AAA5D,2GAAA,cAAc,OAAA;AAiBvB,mFAAwF;AAA/E,0HAAA,sBAAsB,OAAA;AAE/B,6DAA2D;AAAlD,uGAAA,SAAS,OAAA;AAClB,kDAAiD;AAAxC,oGAAA,QAAQ,OAAA;AACjB,kEAAyE;AAAhE,wGAAA,cAAc,OAAA;AACvB,0EAAgF;AAAvE,+GAAA,iBAAiB,OAAA;AAC1B,8DAAqE;AAA5D,oGAAA,YAAY,OAAA;AACrB,sEAA4E;AAAnE,2GAAA,eAAe,OAAA;AACxB,8EAAoF;AAA3E,mHAAA,mBAAmB,OAAA;AAC5B,0EAAiF;AAAxE,gHAAA,kBAAkB,OAAA;AAC3B,6DAAoE;AAA3D,wGAAA,cAAc,OAAA;AACvB,qEAA4E;AAAnE,gHAAA,kBAAkB,OAAA;AAY3B,mEAAiE;AAAxD,2GAAA,WAAW,OAAA;AACpB,yCAAuB"}
|
|
@@ -8,7 +8,10 @@ type Mapping<T> = {
|
|
|
8
8
|
/** Each class prop resolves into the style prop of the same chrome region. */
|
|
9
9
|
export declare const rosterClassNames: Mapping<RosterStyleProps>;
|
|
10
10
|
export declare const scheduleClassNames: Mapping<ScheduleStyleProps>;
|
|
11
|
-
export declare const Roster: import("react").ComponentType<import("../components/roster/roster.types").RosterInput & RosterStyleProps & {
|
|
11
|
+
export declare const Roster: import("react").ComponentType<Omit<import("../components/roster/roster.types").RosterInput, "selectable"> & RosterStyleProps & {
|
|
12
|
+
intervalDetailComponent?: import("react").ComponentType<import("../components/roster/roster.types").IntervalDetailInput>;
|
|
13
|
+
selectionLayout?: import("react").ComponentType<import("..").SelectionLayoutProps>;
|
|
14
|
+
portalHost?: string;
|
|
12
15
|
incompleteLabel?: string;
|
|
13
16
|
neverSetLabel?: string;
|
|
14
17
|
laneLabelComponent?: import("react").ComponentType<import("../components/roster/roster.types").LaneLabelInput>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/nativewind/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAE1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAEhF,KAAK,aAAa,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,WAAW,GAAG,GAAG,MAAM,WAAW,CAAC,CAAC;AACtF,KAAK,WAAW,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,MAAM,WAAW,GACrE,GAAG,MAAM,OAAO,GAChB,OAAO,CAAC;AACZ,KAAK,OAAO,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC;CAAE,CAAC;AAEhE,8EAA8E;AAC9E,eAAO,MAAM,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,CAKtD,CAAC;AACF,eAAO,MAAM,kBAAkB,EAAE,OAAO,CAAC,kBAAkB,CAK1D,CAAC;AAIF,eAAO,MAAM,MAAM
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/nativewind/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAE1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAEhF,KAAK,aAAa,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,WAAW,GAAG,GAAG,MAAM,WAAW,CAAC,CAAC;AACtF,KAAK,WAAW,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,MAAM,WAAW,GACrE,GAAG,MAAM,OAAO,GAChB,OAAO,CAAC;AACZ,KAAK,OAAO,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC;CAAE,CAAC;AAEhE,8EAA8E;AAC9E,eAAO,MAAM,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,CAKtD,CAAC;AACF,eAAO,MAAM,kBAAkB,EAAE,OAAO,CAAC,kBAAkB,CAK1D,CAAC;AAIF,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;EAA2C,CAAC;AAC/D,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;EAA+C,CAAC"}
|
package/llms.txt
CHANGED
|
@@ -144,11 +144,28 @@ show the incompleteLabel; do not describe incomplete empty time as definitive.
|
|
|
144
144
|
|
|
145
145
|
| Import | Runtime exports and purpose |
|
|
146
146
|
| --- | --- |
|
|
147
|
-
| react-native-roster | Roster, Schedule,
|
|
148
|
-
| react-native-roster/core | layoutLane, coverageFor, flagFor, windowFor, prev, next, today, dayColumnsFor, timeAtX, timeAtY, snapToStep, byLabel, byCoverage, and core counters/caches. |
|
|
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, 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
|
|
@@ -186,7 +203,7 @@ outside React state.
|
|
|
186
203
|
|
|
187
204
|
useRoster returns window, projection, orderedLanes, coverage, laneState,
|
|
188
205
|
geometryFor(lane), scroll, press(lane,x,y), ticks, contentWidth, viewport,
|
|
189
|
-
onLayout, navigate, and status ('empty' or 'ready'). There is no visibleRange
|
|
206
|
+
onLayout, navigate, selection, dismissSelection, and status ('empty' or 'ready'). There is no visibleRange
|
|
190
207
|
field in the shipped public model. Custom layouts must wire onLayout. Custom
|
|
191
208
|
bodies compute geometry only for mounted lanes, preserving virtualization.
|
|
192
209
|
A zero-duration window has contentWidth=0 and renders no Roster body. Positive
|
|
@@ -233,6 +250,71 @@ Keep its viewport gate and measured estimatedListSize in custom compositions.
|
|
|
233
250
|
| `laneLabelColumnComponent` | `labels: LaneLabelInput[]`, `projection`, `scroll`, `laneLabelComponent` | `RosterLaneLabelColumn`: frozen labels following vertical offset. |
|
|
234
251
|
| `bodyComponent` | Ordered `lanes`, `window`, `geometryFor`, `projection`, `scroll`, `press`, `ticks`, `viewport`, `contentWidth`, highlight/hover, incomplete label, and rect components | `RosterBody`: virtualized lanes. |
|
|
235
252
|
|
|
253
|
+
## Selection
|
|
254
|
+
|
|
255
|
+
Roster only; Schedule does not yet support selection and will gain it in a later change.
|
|
256
|
+
intervalDetailComponent?: ComponentType<IntervalDetailInput> enables selection on interval
|
|
257
|
+
press while preserving onIntervalPress. Absent content retains no selection.
|
|
258
|
+
Pressing the selected interval again dismisses it; pressing another interval
|
|
259
|
+
switches selection. Cell and gap presses dismiss selection while still firing
|
|
260
|
+
`onCellPress` and `onGapPress`. These rules live in the hook and apply on native
|
|
261
|
+
and web; web outside press and Escape still dismiss.
|
|
262
|
+
IntervalDetailInput is IntervalInput plus absolute start and end and viewTimezone.
|
|
263
|
+
useRoster owns selection: {rect,layer,lane,start,end}|null and dismissSelection(). It resolves
|
|
264
|
+
current lane/layer/rect references and clears removed lane or absolute rect bounds.
|
|
265
|
+
Resizing and sorting retain valid selection with current geometry; reconciliation
|
|
266
|
+
uses the private reconcileSelection helper to match source identity sets and choose
|
|
267
|
+
nearest bounds with a total difference of at most 1 ms for projection roundoff.
|
|
268
|
+
selectable?: boolean belongs only to the hook input, RosterInput, and defaults to false.
|
|
269
|
+
It is excluded from RosterProps; hook consumers pass
|
|
270
|
+
selectable: true to retain selection. intervalDetailComponent, selectionLayout, and
|
|
271
|
+
portalHost belong to RosterProps because the chassis mounts content and the layout.
|
|
272
|
+
The chassis passes selectable: Boolean(intervalDetailComponent) to useRoster.
|
|
273
|
+
Custom chassis mount their content and layout using the returned model.
|
|
274
|
+
|
|
275
|
+
selectionLayout?: ComponentType<SelectionLayoutProps> defaults to the root-exported
|
|
276
|
+
RosterSelectionPopover. It is the explicit naming exception to the Component suffix.
|
|
277
|
+
The chassis mounts the strategy once around the body node, then passes that node
|
|
278
|
+
to RosterLayout.bodyZone. SelectionLayoutProps is exported: anchorZone and contentZone
|
|
279
|
+
are ReactNode; targetBounds is {x,y,width,height}|null in body-content coordinates, including
|
|
280
|
+
the lane offset and interval inset; open is
|
|
281
|
+
boolean; onDismiss is () => void; portalHost is string; scroll is Pick<RosterScroll, 'x' | 'y' | 'headerStyle' | 'labelStyle'>.
|
|
282
|
+
Layouts receive no lane, layer, or rect. Render anchorZone once and arrange contentZone
|
|
283
|
+
above or beside it. The interval-detail fixture demonstrates a runtime inspector toggle.
|
|
284
|
+
|
|
285
|
+
Native selection-layout.tsx uses the root-exported PortalHost and Portal from
|
|
286
|
+
components/primitives/portal.tsx, a module-level Map store with useSyncExternalStore.
|
|
287
|
+
The default layout owns a host named from the roster's useId; portalHost overrides
|
|
288
|
+
that name. Use distinct names for independent rosters and never mount the same host
|
|
289
|
+
twice. Custom layouts may target an ancestor host they own. Caller context does not
|
|
290
|
+
cross a store portal; provide context above the host or inside the portaled node.
|
|
291
|
+
Native hardware back dismisses details. The detail card flips above
|
|
292
|
+
when that fits, or uses the top edge when neither vertical placement fits. It clamps
|
|
293
|
+
left using measured viewport and content widths. Shared x/y values position the
|
|
294
|
+
overlay in viewport coordinates, never React scroll state. Selection identity
|
|
295
|
+
must not enter the body content key or cause mounted rows to render. The stable press
|
|
296
|
+
ref lives in use-roster-press.ts so React Compiler can retain derived body inputs in useRoster.
|
|
297
|
+
|
|
298
|
+
Native has no intercepting dismissal overlay; body presses reach the hook, and the detail
|
|
299
|
+
card captures its own presses. Web excludes the body wrapper from Radix outside
|
|
300
|
+
dismissal, leaving body presses to the hook while preserving true outside presses and Escape.
|
|
301
|
+
The hook toggles the selected interval closed, switches to another interval, and dismisses
|
|
302
|
+
on cell or gap presses while preserving their callbacks. The body restores horizontal
|
|
303
|
+
and vertical offsets from shared values on remount when selectionLayout changes.
|
|
304
|
+
A mount effect calls the horizontal ScrollView ref's scrollTo without animation;
|
|
305
|
+
native also retains contentOffset. LegendList restores initialScrollOffset through
|
|
306
|
+
its own web mount effect and native initial offset.
|
|
307
|
+
|
|
308
|
+
selection-layout.web.tsx uses @radix-ui/react-popover, required by the web root entry
|
|
309
|
+
but an optional peer for native/core-only consumers. Source web bundlers (Vite,
|
|
310
|
+
Storybook, or Metro web) require it even with selection disabled because the web
|
|
311
|
+
layout is in the module graph. Install with Bun. The emitted
|
|
312
|
+
browser remap must travel with the shared selection-layout.types.ts contract, just
|
|
313
|
+
like press-point and interval-hover. Its zero-size, pointer-transparent anchor follows
|
|
314
|
+
the shared translation; Radix's portal follows anchor movement and owns outside press,
|
|
315
|
+
Escape, and collisions. The layout returns focus to the previously focused element
|
|
316
|
+
only after Escape.
|
|
317
|
+
|
|
236
318
|
## Schedule zones
|
|
237
319
|
|
|
238
320
|
| Slot | Component inputs or node | Default and behavior |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-roster",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Layered intervals with provenance across lanes and a shared time axis for React Native.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"types": "./dist/src/index.d.ts",
|
|
32
32
|
"browser": {
|
|
33
33
|
"./dist/src/components/primitives/press-point.js": "./dist/src/components/primitives/press-point.web.js",
|
|
34
|
-
"./dist/src/components/roster/lanes/interval-hover.js": "./dist/src/components/roster/lanes/interval-hover.web.js"
|
|
34
|
+
"./dist/src/components/roster/lanes/interval-hover.js": "./dist/src/components/roster/lanes/interval-hover.web.js",
|
|
35
|
+
"./dist/src/components/roster/selection/selection-layout.js": "./dist/src/components/roster/selection/selection-layout.web.js"
|
|
35
36
|
},
|
|
36
37
|
"exports": {
|
|
37
38
|
".": {
|
|
@@ -101,7 +102,8 @@
|
|
|
101
102
|
"nativewind": ">=4.1.0",
|
|
102
103
|
"react": ">=18.2.0",
|
|
103
104
|
"react-native": ">=0.74.0",
|
|
104
|
-
"react-native-reanimated": ">=3.19.0"
|
|
105
|
+
"react-native-reanimated": ">=3.19.0",
|
|
106
|
+
"@radix-ui/react-popover": "^1.1.23"
|
|
105
107
|
},
|
|
106
108
|
"peerDependenciesMeta": {
|
|
107
109
|
"expo": {
|
|
@@ -112,6 +114,9 @@
|
|
|
112
114
|
},
|
|
113
115
|
"react-native-reanimated": {
|
|
114
116
|
"optional": true
|
|
117
|
+
},
|
|
118
|
+
"@radix-ui/react-popover": {
|
|
119
|
+
"optional": true
|
|
115
120
|
}
|
|
116
121
|
},
|
|
117
122
|
"dependencies": {
|
|
@@ -150,7 +155,8 @@
|
|
|
150
155
|
"semantic-release": "^25.0.3",
|
|
151
156
|
"semver": "^7.7.0",
|
|
152
157
|
"size-limit": "^13.0.3",
|
|
153
|
-
"temporal-spec": "1.0.1"
|
|
158
|
+
"temporal-spec": "1.0.1",
|
|
159
|
+
"@radix-ui/react-popover": "1.1.23"
|
|
154
160
|
},
|
|
155
161
|
"publishConfig": {
|
|
156
162
|
"access": "public",
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# primitives
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
public export.
|
|
3
|
+
Building blocks shared by the projections, plus the native portal store for Roster selection.
|
|
5
4
|
|
|
6
5
|
- `press-point.tsx`, `press-point.web.tsx`, `press-point.types.ts`: pointer
|
|
7
6
|
coordinates relative to the pressed surface; the web file is selected by the
|
|
@@ -10,3 +9,15 @@ public export.
|
|
|
10
9
|
default paint when the override carries a NativeWind class entry
|
|
11
10
|
|
|
12
11
|
Tests: `test/components/primitives`.
|
|
12
|
+
|
|
13
|
+
- `portal.tsx`: root-exported `PortalHost({name})` and
|
|
14
|
+
`Portal({hostName,name,children})`; a module-level Map of hosts to portal nodes,
|
|
15
|
+
subscribed through useSyncExternalStore, registered and removed through lifecycle effects
|
|
16
|
+
|
|
17
|
+
Mount one host per destination name. The default Roster selection layout owns its
|
|
18
|
+
per-instance host; custom layouts can target an ancestor host they own. Caller
|
|
19
|
+
context does not cross the store; provide it above the host or inside the node.
|
|
20
|
+
Schedule does not yet support selection.
|
|
21
|
+
|
|
22
|
+
As a primitive, `Portal` takes `children` rather than a named zone; named zones belong
|
|
23
|
+
to the machine and feature tiers.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Fragment, type ReactNode, useEffect, useSyncExternalStore } from 'react';
|
|
2
|
+
|
|
3
|
+
const hosts = new Map<string, Map<string, ReactNode>>();
|
|
4
|
+
const listeners = new Set<() => void>();
|
|
5
|
+
const empty = new Map<string, ReactNode>();
|
|
6
|
+
|
|
7
|
+
function subscribe(listener: () => void) {
|
|
8
|
+
listeners.add(listener);
|
|
9
|
+
return () => listeners.delete(listener);
|
|
10
|
+
}
|
|
11
|
+
function publish(hostName: string, nodes: Map<string, ReactNode>) {
|
|
12
|
+
if (nodes.size) hosts.set(hostName, nodes);
|
|
13
|
+
else hosts.delete(hostName);
|
|
14
|
+
for (const listener of listeners) listener();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Mount once at the destination for a named native portal. */
|
|
18
|
+
export function PortalHost({ name }: { name: string }) {
|
|
19
|
+
const nodes = useSyncExternalStore(
|
|
20
|
+
subscribe,
|
|
21
|
+
() => hosts.get(name) ?? empty,
|
|
22
|
+
() => empty,
|
|
23
|
+
);
|
|
24
|
+
return Array.from(nodes, ([key, node]) => <Fragment key={key}>{node}</Fragment>);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Register content at a named native host; caller context does not cross the store. */
|
|
28
|
+
export function Portal({
|
|
29
|
+
hostName,
|
|
30
|
+
name,
|
|
31
|
+
children,
|
|
32
|
+
}: {
|
|
33
|
+
hostName: string;
|
|
34
|
+
name: string;
|
|
35
|
+
children: ReactNode;
|
|
36
|
+
}) {
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const nodes = new Map(hosts.get(hostName));
|
|
39
|
+
nodes.set(name, children);
|
|
40
|
+
publish(hostName, nodes);
|
|
41
|
+
return () => {
|
|
42
|
+
const nodes = new Map(hosts.get(hostName));
|
|
43
|
+
nodes.delete(name);
|
|
44
|
+
publish(hostName, nodes);
|
|
45
|
+
};
|
|
46
|
+
}, [hostName, name, children]);
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
@@ -6,7 +6,8 @@ This feature is about a roster; its children are lanes.
|
|
|
6
6
|
`XxxInput` slot input type from `roster.types.ts`
|
|
7
7
|
|
|
8
8
|
- `index.tsx`: the chassis; calls `useRoster`, branches on `status`, composes zones
|
|
9
|
-
- `use-roster.ts`: the hook; owns window, projection, scroll, geometry, and
|
|
9
|
+
- `use-roster.ts`: the hook; owns window, projection, scroll, geometry, and selection
|
|
10
|
+
- `use-roster-press.ts`: isolates the stable press ref so React Compiler can retain derived body inputs
|
|
10
11
|
- `layout.tsx`: arranges corner, header, label column, and body regions only
|
|
11
12
|
- `roster.types.ts`: props, model, and every slot input type
|
|
12
13
|
- `body-layout.tsx`: arranges grid and list nodes with horizontal scroll wiring
|
|
@@ -19,7 +20,7 @@ Interval and gap components live in `../layers`; press geometry and `regionStyle
|
|
|
19
20
|
in `../primitives`. Zone contracts are documented in the README's Roster zones
|
|
20
21
|
section and in `llms.txt`. Tests: `test/components/roster`.
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
`RosterBody` gates
|
|
23
24
|
measurement and composes the body layout and lane list. The `onRowRender` prop on
|
|
24
25
|
`RosterBody` and `RosterLaneList` is a development-only Profiler hook used by the
|
|
25
26
|
gallery, not a supported customization point. Production Profiler callbacks are disabled.
|
|
@@ -27,3 +28,48 @@ gallery, not a supported customization point. Production Profiler callbacks are
|
|
|
27
28
|
Input-bearing slots accept component types and mount in the data-owning parts.
|
|
28
29
|
The chassis binds defaults once; emptyZone and cornerZone accept nodes. The body
|
|
29
30
|
content key tracks interval and gap component identity, including class components.
|
|
31
|
+
|
|
32
|
+
`selection/` holds the runtime-swappable presentation strategies (native popover, web popover) for one `SelectionLayoutProps` contract.
|
|
33
|
+
|
|
34
|
+
- `selection/selection-layout.types.ts`: exported `SelectionLayoutProps`, with body
|
|
35
|
+
and detail nodes, targetBounds, open, dismissal, host name, and shared scroll
|
|
36
|
+
limited to x, y, headerStyle, and labelStyle
|
|
37
|
+
- `selection/selection-layout.tsx`: exported native `RosterSelectionPopover`, local
|
|
38
|
+
portal host, body press passthrough, hardware back, measured viewport clamping, and shared scroll positioning
|
|
39
|
+
- `selection/selection-layout.web.tsx`: Radix presentation with a browser remap
|
|
40
|
+
|
|
41
|
+
`intervalDetailComponent` receives `IntervalDetailInput`, the interval input plus absolute
|
|
42
|
+
`start`, `end`, and `viewTimezone`; presses still invoke onIntervalPress.
|
|
43
|
+
`selectable?: boolean` belongs only to the hook input, `RosterInput`, and defaults to false.
|
|
44
|
+
It is excluded from `RosterProps`.
|
|
45
|
+
`intervalDetailComponent`, `selectionLayout`, and `portalHost` belong to `RosterProps`
|
|
46
|
+
because the chassis mounts the content and layout. The chassis passes
|
|
47
|
+
`selectable: Boolean(intervalDetailComponent)` to `useRoster`.
|
|
48
|
+
`selectionLayout` defaults once in the chassis and wraps the body
|
|
49
|
+
before RosterLayout receives bodyZone. The selection never enters the body content
|
|
50
|
+
key. Missing lane/layer/bounds clear selection; valid selections use current data.
|
|
51
|
+
Pressing the selected interval again dismisses it; pressing another interval
|
|
52
|
+
switches selection. Cell and gap presses dismiss selection while still firing
|
|
53
|
+
`onCellPress` and `onGapPress`. These rules live in the hook and apply on native
|
|
54
|
+
and web; web outside press and Escape still dismiss.
|
|
55
|
+
On web, the layout captures document.activeElement before opening autofocus and
|
|
56
|
+
recaptures the focused lane when targetBounds changes while open. It uses onCloseAutoFocus to return focus after Escape only, leaving outside pointer focus intact. The browser's focus-visible ring styling is left to the host page.
|
|
57
|
+
`portalHost` defaults to a per-roster useId name. The interval-detail fixture switches
|
|
58
|
+
to an inspector column with the same node contract. Schedule does not yet support selection.
|
|
59
|
+
|
|
60
|
+
The private reconcileSelection helper resolves the stored absolute bounds against the
|
|
61
|
+
current lanes and window each render, matching source identity sets and choosing the
|
|
62
|
+
nearest bounds with a total difference of at most 1 ms for projection roundoff. Target bounds
|
|
63
|
+
include the lane offset and interval inset. Native details clamp horizontally and use the
|
|
64
|
+
top edge when neither below nor above fits. The browser gate isolates selection updates from Pressable state,
|
|
65
|
+
and separately exercises complete pointer presses and outside dismissal.
|
|
66
|
+
|
|
67
|
+
Native has no intercepting dismissal overlay; body presses reach the hook, and the detail
|
|
68
|
+
card captures its own presses. Web excludes the body wrapper from Radix outside
|
|
69
|
+
dismissal, leaving body presses to the hook while preserving true outside presses and Escape.
|
|
70
|
+
The hook toggles the selected interval closed, switches to another interval, and dismisses
|
|
71
|
+
on cell or gap presses while preserving their callbacks. The body restores horizontal
|
|
72
|
+
and vertical offsets from shared values on remount when selectionLayout changes.
|
|
73
|
+
A mount effect calls the horizontal ScrollView ref's scrollTo without animation;
|
|
74
|
+
native also retains contentOffset. LegendList restores initialScrollOffset through
|
|
75
|
+
its own web mount effect and native initial offset.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ReactNode, useEffect, useState } from 'react';
|
|
2
2
|
import { ScrollView, View } from 'react-native';
|
|
3
3
|
import type { BodyInput } from './roster.types';
|
|
4
4
|
|
|
@@ -14,10 +14,16 @@ export function RosterBodyLayout({
|
|
|
14
14
|
/** Virtualized lane collection within the measured viewport. */
|
|
15
15
|
listZone: ReactNode;
|
|
16
16
|
}) {
|
|
17
|
+
const [contentOffset] = useState(() => ({ x: scroll.x.get(), y: 0 }));
|
|
18
|
+
const bodyRef = scroll.bodyRef;
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
bodyRef.current?.scrollTo({ x: contentOffset.x, animated: false });
|
|
21
|
+
}, [bodyRef, contentOffset]);
|
|
17
22
|
return (
|
|
18
23
|
<ScrollView
|
|
19
24
|
testID="roster-horizontal-scroll"
|
|
20
25
|
ref={scroll.bodyRef}
|
|
26
|
+
contentOffset={contentOffset}
|
|
21
27
|
horizontal
|
|
22
28
|
onScroll={scroll.onBodyScroll}
|
|
23
29
|
scrollEventThrottle={16}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type ReactNode, useId } from 'react';
|
|
2
|
+
import { hasSource } from '../../core';
|
|
1
3
|
import { RosterGap } from '../layers/parts/gap';
|
|
2
4
|
import { RosterInterval } from '../layers/parts/interval';
|
|
3
5
|
import { RosterLaneLabel } from './lanes/parts/lane-label';
|
|
@@ -10,6 +12,8 @@ import { RosterHeader } from './parts/header';
|
|
|
10
12
|
import { RosterHeaderCell } from './parts/header-cell';
|
|
11
13
|
import { RosterLaneLabelColumn } from './parts/lane-label-column';
|
|
12
14
|
import type { RosterProps } from './roster.types';
|
|
15
|
+
import { RosterSelectionPopover } from './selection/selection-layout';
|
|
16
|
+
import type { SelectionLayoutProps } from './selection/selection-layout.types';
|
|
13
17
|
import { useRoster } from './use-roster';
|
|
14
18
|
|
|
15
19
|
const defaultEmptyZone = <RosterEmpty />;
|
|
@@ -17,6 +21,8 @@ const defaultCornerZone = <RosterCorner />;
|
|
|
17
21
|
|
|
18
22
|
export function Roster(props: RosterProps) {
|
|
19
23
|
const {
|
|
24
|
+
selection,
|
|
25
|
+
dismissSelection,
|
|
20
26
|
status,
|
|
21
27
|
orderedLanes,
|
|
22
28
|
laneState,
|
|
@@ -29,8 +35,10 @@ export function Roster(props: RosterProps) {
|
|
|
29
35
|
contentWidth,
|
|
30
36
|
viewport,
|
|
31
37
|
onLayout,
|
|
32
|
-
} = useRoster(props);
|
|
38
|
+
} = useRoster({ ...props, selectable: Boolean(props.intervalDetailComponent) });
|
|
33
39
|
const {
|
|
40
|
+
intervalDetailComponent: IntervalDetailComponent,
|
|
41
|
+
selectionLayout: SelectionLayout = RosterSelectionPopover,
|
|
34
42
|
emptyZone = defaultEmptyZone,
|
|
35
43
|
cornerZone = defaultCornerZone,
|
|
36
44
|
headerComponent: HeaderComponent = RosterHeader,
|
|
@@ -44,8 +52,30 @@ export function Roster(props: RosterProps) {
|
|
|
44
52
|
incompleteLabel = 'Availability may be incomplete',
|
|
45
53
|
neverSetLabel = 'No availability set',
|
|
46
54
|
} = props;
|
|
55
|
+
const hostId = useId();
|
|
47
56
|
if (status === 'empty') return emptyZone;
|
|
48
57
|
if (window.start === window.end) return null;
|
|
58
|
+
let targetBounds: SelectionLayoutProps['targetBounds'] = null;
|
|
59
|
+
if (selection) {
|
|
60
|
+
targetBounds = {
|
|
61
|
+
x: selection.rect.x,
|
|
62
|
+
y:
|
|
63
|
+
orderedLanes.findIndex((lane) => lane.id === selection.lane.id) * projection.rowHeight +
|
|
64
|
+
selection.rect.y,
|
|
65
|
+
width: selection.rect.width,
|
|
66
|
+
height: selection.rect.height,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
let contentZone: ReactNode = null;
|
|
70
|
+
if (selection && IntervalDetailComponent) {
|
|
71
|
+
contentZone = (
|
|
72
|
+
<IntervalDetailComponent
|
|
73
|
+
{...selection}
|
|
74
|
+
viewTimezone={projection.viewTimezone}
|
|
75
|
+
highlighted={hasSource(selection.rect.sources, props.highlightSource)}
|
|
76
|
+
/>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
49
79
|
return (
|
|
50
80
|
<RosterLayout
|
|
51
81
|
style={props.style}
|
|
@@ -85,22 +115,37 @@ export function Roster(props: RosterProps) {
|
|
|
85
115
|
/>
|
|
86
116
|
}
|
|
87
117
|
bodyZone={
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
118
|
+
<SelectionLayout
|
|
119
|
+
portalHost={props.portalHost ?? `roster-${hostId}`}
|
|
120
|
+
open={selection !== null}
|
|
121
|
+
onDismiss={dismissSelection}
|
|
122
|
+
scroll={{
|
|
123
|
+
x: scroll.x,
|
|
124
|
+
y: scroll.y,
|
|
125
|
+
headerStyle: scroll.headerStyle,
|
|
126
|
+
labelStyle: scroll.labelStyle,
|
|
127
|
+
}}
|
|
128
|
+
targetBounds={targetBounds}
|
|
129
|
+
contentZone={contentZone}
|
|
130
|
+
anchorZone={
|
|
131
|
+
<BodyComponent
|
|
132
|
+
lanes={orderedLanes}
|
|
133
|
+
window={window}
|
|
134
|
+
geometryFor={geometryFor}
|
|
135
|
+
projection={projection}
|
|
136
|
+
scroll={scroll}
|
|
137
|
+
press={press}
|
|
138
|
+
ticks={ticks}
|
|
139
|
+
contentWidth={contentWidth}
|
|
140
|
+
viewport={viewport}
|
|
141
|
+
intervalComponent={intervalComponent}
|
|
142
|
+
gapComponent={gapComponent}
|
|
143
|
+
gridComponent={gridComponent}
|
|
144
|
+
highlightSource={props.highlightSource}
|
|
145
|
+
onIntervalHover={props.onIntervalHover}
|
|
146
|
+
incompleteLabel={incompleteLabel}
|
|
147
|
+
/>
|
|
148
|
+
}
|
|
104
149
|
/>
|
|
105
150
|
}
|
|
106
151
|
/>
|