react-native-roster 0.0.1 → 0.1.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 +108 -31
- package/dist/src/components/roster/body-layout.d.ts +9 -0
- package/dist/src/components/roster/body-layout.d.ts.map +1 -0
- package/dist/src/components/roster/body-layout.js +9 -0
- package/dist/src/components/roster/body-layout.js.map +1 -0
- package/dist/src/components/roster/index.d.ts +1 -1
- package/dist/src/components/roster/index.d.ts.map +1 -1
- package/dist/src/components/roster/index.js +10 -26
- package/dist/src/components/roster/index.js.map +1 -1
- package/dist/src/components/roster/lanes/lane.d.ts +2 -2
- package/dist/src/components/roster/lanes/lane.d.ts.map +1 -1
- package/dist/src/components/roster/lanes/lane.js +3 -8
- package/dist/src/components/roster/lanes/lane.js.map +1 -1
- package/dist/src/components/roster/parts/body.d.ts +3 -3
- package/dist/src/components/roster/parts/body.d.ts.map +1 -1
- package/dist/src/components/roster/parts/body.js +5 -20
- package/dist/src/components/roster/parts/body.js.map +1 -1
- package/dist/src/components/roster/parts/header-cell.d.ts +2 -4
- package/dist/src/components/roster/parts/header-cell.d.ts.map +1 -1
- package/dist/src/components/roster/parts/header-cell.js.map +1 -1
- package/dist/src/components/roster/parts/header.d.ts +1 -1
- package/dist/src/components/roster/parts/header.d.ts.map +1 -1
- package/dist/src/components/roster/parts/header.js +2 -2
- package/dist/src/components/roster/parts/header.js.map +1 -1
- package/dist/src/components/roster/parts/lane-label-column.d.ts +1 -1
- package/dist/src/components/roster/parts/lane-label-column.d.ts.map +1 -1
- package/dist/src/components/roster/parts/lane-label-column.js +2 -2
- package/dist/src/components/roster/parts/lane-label-column.js.map +1 -1
- package/dist/src/components/roster/parts/lane-list.d.ts +7 -0
- package/dist/src/components/roster/parts/lane-list.d.ts.map +1 -0
- package/dist/src/components/roster/parts/lane-list.js +25 -0
- package/dist/src/components/roster/parts/lane-list.js.map +1 -0
- package/dist/src/components/roster/roster.types.d.ts +19 -20
- package/dist/src/components/roster/roster.types.d.ts.map +1 -1
- package/dist/src/components/roster/utils/body-content-key.d.ts +1 -1
- package/dist/src/components/roster/utils/body-content-key.d.ts.map +1 -1
- package/dist/src/components/roster/utils/body-content-key.js +10 -10
- package/dist/src/components/roster/utils/body-content-key.js.map +1 -1
- package/dist/src/components/schedule/days/day.d.ts +2 -2
- package/dist/src/components/schedule/days/day.d.ts.map +1 -1
- package/dist/src/components/schedule/days/day.js +2 -11
- package/dist/src/components/schedule/days/day.js.map +1 -1
- package/dist/src/components/schedule/days/header-layout.d.ts +1 -1
- package/dist/src/components/schedule/days/header-layout.d.ts.map +1 -1
- package/dist/src/components/schedule/days/parts/column.d.ts +1 -1
- package/dist/src/components/schedule/days/parts/column.d.ts.map +1 -1
- package/dist/src/components/schedule/days/parts/column.js +3 -8
- package/dist/src/components/schedule/days/parts/column.js.map +1 -1
- package/dist/src/components/schedule/index.d.ts.map +1 -1
- package/dist/src/components/schedule/index.js +7 -8
- package/dist/src/components/schedule/index.js.map +1 -1
- package/dist/src/components/schedule/schedule.types.d.ts +17 -17
- package/dist/src/components/schedule/schedule.types.d.ts.map +1 -1
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/nativewind/index.d.ts +24 -26
- package/dist/src/nativewind/index.d.ts.map +1 -1
- package/docs/migrations.md +61 -0
- package/llms.txt +46 -29
- package/package.json +3 -2
- package/src/components/layers/README.md +7 -3
- package/src/components/roster/README.md +15 -4
- package/src/components/roster/body-layout.tsx +32 -0
- package/src/components/roster/index.tsx +63 -45
- package/src/components/roster/lanes/lane.tsx +16 -11
- package/src/components/roster/parts/body.tsx +17 -84
- package/src/components/roster/parts/header-cell.tsx +2 -2
- package/src/components/roster/parts/header.tsx +7 -2
- package/src/components/roster/parts/lane-label-column.tsx +2 -2
- package/src/components/roster/parts/lane-list.tsx +74 -0
- package/src/components/roster/roster.types.ts +17 -16
- package/src/components/roster/utils/body-content-key.ts +13 -13
- package/src/components/schedule/README.md +8 -3
- package/src/components/schedule/days/day.tsx +31 -18
- package/src/components/schedule/days/header-layout.tsx +1 -1
- package/src/components/schedule/days/parts/column.tsx +10 -10
- package/src/components/schedule/index.tsx +20 -21
- package/src/components/schedule/schedule.types.ts +13 -13
- package/src/index.ts +3 -0
|
@@ -11,18 +11,16 @@ export declare const scheduleClassNames: Mapping<ScheduleStyleProps>;
|
|
|
11
11
|
export declare const Roster: import("react").ComponentType<import("../components/roster/roster.types").RosterInput & RosterStyleProps & {
|
|
12
12
|
incompleteLabel?: string;
|
|
13
13
|
neverSetLabel?: string;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
laneLabelColumnZone?: (input: import("../components/roster/roster.types").LabelColumnInput) => import("react").ReactNode;
|
|
25
|
-
bodyZone?: (input: import("../components/roster/roster.types").BodyInput) => import("react").ReactNode;
|
|
14
|
+
laneLabelComponent?: import("react").ComponentType<import("../components/roster/roster.types").LaneLabelInput>;
|
|
15
|
+
headerCellComponent?: import("react").ComponentType<import("../components/roster/roster.types").HeaderCellInput>;
|
|
16
|
+
intervalComponent?: import("react").ComponentType<import("..").IntervalInput>;
|
|
17
|
+
gapComponent?: import("react").ComponentType<import("..").GapInput>;
|
|
18
|
+
gridComponent?: import("react").ComponentType<import("../components/roster/roster.types").GridInput>;
|
|
19
|
+
emptyZone?: import("react").ReactNode;
|
|
20
|
+
cornerZone?: import("react").ReactNode;
|
|
21
|
+
headerComponent?: import("react").ComponentType<import("../components/roster/roster.types").HeaderInput>;
|
|
22
|
+
laneLabelColumnComponent?: import("react").ComponentType<import("../components/roster/roster.types").LabelColumnInput>;
|
|
23
|
+
bodyComponent?: import("react").ComponentType<import("../components/roster/roster.types").BodyInput>;
|
|
26
24
|
} & {
|
|
27
25
|
bodyClassName?: string | undefined;
|
|
28
26
|
className?: string | undefined;
|
|
@@ -31,26 +29,26 @@ export declare const Roster: import("react").ComponentType<import("../components
|
|
|
31
29
|
}>;
|
|
32
30
|
export declare const Schedule: import("react").ComponentType<import("../components/schedule/schedule.types").ScheduleInput & ScheduleStyleProps & {
|
|
33
31
|
incompleteLabel?: string;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
gutterComponent?: import("react").ComponentType<import("../components/schedule/schedule.types").ScheduleHoursInput>;
|
|
33
|
+
gridComponent?: import("react").ComponentType<import("../components/schedule/schedule.types").ScheduleHoursInput>;
|
|
34
|
+
dayHeaderComponent?: import("react").ComponentType<{
|
|
37
35
|
day: import("..").DayColumn;
|
|
38
|
-
}
|
|
39
|
-
|
|
36
|
+
}>;
|
|
37
|
+
skippedDateComponent?: import("react").ComponentType<{
|
|
40
38
|
localDate: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
}>;
|
|
40
|
+
columnComponent?: import("react").ComponentType<import("../components/schedule/schedule.types").ScheduleColumnInput>;
|
|
41
|
+
transitionComponent?: import("react").ComponentType<import("../components/schedule/schedule.types").ScheduleTransitionInput>;
|
|
42
|
+
nowLineComponent?: import("react").ComponentType<{
|
|
45
43
|
y: number;
|
|
46
44
|
column: number;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
}>;
|
|
46
|
+
intervalComponent?: import("react").ComponentType<import("..").IntervalInput>;
|
|
47
|
+
gapComponent?: import("react").ComponentType<import("..").GapInput>;
|
|
48
|
+
incompleteComponent?: import("react").ComponentType<{
|
|
51
49
|
lane: import("..").Lane;
|
|
52
50
|
label: string;
|
|
53
|
-
}
|
|
51
|
+
}>;
|
|
54
52
|
} & {
|
|
55
53
|
className?: string | undefined;
|
|
56
54
|
daysClassName?: string | undefined;
|
|
@@ -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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Migrations
|
|
2
|
+
|
|
3
|
+
Changes to public props that need consumer edits, by the version that ships them.
|
|
4
|
+
The changelog lists every change; this page carries only the ones with a
|
|
5
|
+
migration step.
|
|
6
|
+
|
|
7
|
+
## 0.1.0: slot components replace render functions
|
|
8
|
+
|
|
9
|
+
Every Roster and Schedule prop that used to take a render function now takes a
|
|
10
|
+
component type or a node. Props ending in `Component` accept
|
|
11
|
+
`ComponentType<Input>` and are mounted by React with the same input as props.
|
|
12
|
+
Props ending in `Zone` accept `ReactNode`. Press callbacks (`onIntervalPress`,
|
|
13
|
+
`onGapPress`, `onCellPress`, `onIntervalHover`, `onNavigate`) are unchanged.
|
|
14
|
+
|
|
15
|
+
| Before (0.0.x) | After (0.1.0) | Input |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| `emptyZone={() => <Empty />}` | `emptyZone={<Empty />}` | none |
|
|
18
|
+
| `cornerZone={() => <Corner />}` | `cornerZone={<Corner />}` | none |
|
|
19
|
+
| `headerZone={(input) => ...}` | `headerComponent={Header}` | `HeaderInput` |
|
|
20
|
+
| `laneLabelColumnZone={(input) => ...}` | `laneLabelColumnComponent={LabelColumn}` | `LabelColumnInput` |
|
|
21
|
+
| `bodyZone={(input) => ...}` | `bodyComponent={Body}` | `BodyInput` |
|
|
22
|
+
| `headerCellZone={({ tick }) => ...}` | `headerCellComponent={HeaderCell}` | `HeaderCellInput` |
|
|
23
|
+
| `laneLabelZone={(input) => ...}` | `laneLabelComponent={LaneLabel}` | `LaneLabelInput` |
|
|
24
|
+
| `intervalZone={(input) => ...}` | `intervalComponent={Interval}` | `IntervalInput` |
|
|
25
|
+
| `gapZone={(input) => ...}` | `gapComponent={Gap}` | `GapInput` |
|
|
26
|
+
| `gridZone={(input) => ...}` | `gridComponent={Grid}` | `GridInput` |
|
|
27
|
+
|
|
28
|
+
Schedule follows the same rule: `gutterZone`, `gridZone`, `dayHeaderZone`,
|
|
29
|
+
`skippedDateZone`, `columnZone`, `transitionZone`, `nowLineZone`, `intervalZone`,
|
|
30
|
+
`gapZone`, and `incompleteZone` become the matching `Component` props with the
|
|
31
|
+
same inputs.
|
|
32
|
+
|
|
33
|
+
Nested contracts renamed with them: `HeaderInput.headerCellZone` is now
|
|
34
|
+
`headerCellComponent`, `LabelColumnInput.laneLabelZone` is now
|
|
35
|
+
`laneLabelComponent`, and `BodyInput` carries `intervalComponent`,
|
|
36
|
+
`gapComponent`, and `gridComponent`.
|
|
37
|
+
|
|
38
|
+
Mechanical rewrite for a render function that was already a plain function of
|
|
39
|
+
its input:
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
// Before
|
|
43
|
+
<Roster intervalZone={({ rect, layer }) => <Bar rect={rect} layer={layer} />} />
|
|
44
|
+
|
|
45
|
+
// After: the same function, declared once at module scope and passed as a type
|
|
46
|
+
function Interval({ rect, layer }: IntervalInput) {
|
|
47
|
+
return <Bar rect={rect} layer={layer} />;
|
|
48
|
+
}
|
|
49
|
+
<Roster intervalComponent={Interval} />
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Declare slot components at module scope. An inline arrow creates a new
|
|
53
|
+
component type every render, which remounts every interval and defeats the
|
|
54
|
+
body's content key.
|
|
55
|
+
|
|
56
|
+
The default parts are exported under their existing names (`RosterInterval`,
|
|
57
|
+
`RosterGap`, `RosterLaneLabel`, `RosterHeaderCell`, `RosterGrid`, `RosterHeader`,
|
|
58
|
+
`RosterLaneLabelColumn`, `RosterBody`, `RosterEmpty`, `RosterCorner`) and can be
|
|
59
|
+
composed inside a replacement. `RosterBody` is now built from `RosterBodyLayout`
|
|
60
|
+
(arranges `gridZone` and `listZone`) and `RosterLaneList` (owns the virtualized
|
|
61
|
+
lane list); both are exported for consumers that replace the body.
|
package/llms.txt
CHANGED
|
@@ -144,12 +144,12 @@ 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, useRoster, useSchedule, default
|
|
147
|
+
| react-native-roster | Roster, Schedule, useRoster, useSchedule, default slot components, and core re-exports. |
|
|
148
148
|
| react-native-roster/core | layoutLane, coverageFor, flagFor, 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
|
-
No default exports. Root exports renderer inputs/models/
|
|
152
|
+
No default exports. Root exports renderer inputs/models/slot input types as well as
|
|
153
153
|
core types. Core imports standard JS and Intl only. Only /rrule imports
|
|
154
154
|
rrule-temporal 1.6.0 and @js-temporal/polyfill 0.5.1. Those are installed runtime
|
|
155
155
|
dependencies but cannot enter root/core bundles through library imports.
|
|
@@ -160,6 +160,14 @@ from dist/src; declarations use the types condition. Build before Node export
|
|
|
160
160
|
tests. Root needs a native renderer or web bundler; direct Node use of root is
|
|
161
161
|
not a substitute for a React Native app. Core and /rrule work in Node/Bun.
|
|
162
162
|
|
|
163
|
+
## 0.1.0 slot prop rename
|
|
164
|
+
|
|
165
|
+
0.1.0 renamed every input-bearing render-function Zone prop to a Component prop;
|
|
166
|
+
use the current names below instead of the 0.0.x props. Singleton emptyZone and
|
|
167
|
+
cornerZone keep their names and now take nodes. See [docs/migrations.md](./docs/migrations.md).
|
|
168
|
+
Zone props are ReactNode.
|
|
169
|
+
Component props are ComponentType<Input>.
|
|
170
|
+
|
|
163
171
|
## Renderer contracts
|
|
164
172
|
|
|
165
173
|
Roster required props: lanes: Lane[], windowSpec: WindowSpec.
|
|
@@ -198,44 +206,53 @@ The chassis measures width, reserves a 48 px gutter, and fits all actual day
|
|
|
198
206
|
columns without horizontal scrolling; the body scrolls vertically. now updates
|
|
199
207
|
every minute and is null outside the selected window.
|
|
200
208
|
|
|
201
|
-
|
|
202
|
-
|
|
209
|
+
Props ending in Component accept ComponentType<Input>, mounted as React elements.
|
|
210
|
+
Props ending in Zone accept ReactNode; pass null to suppress a singleton default.
|
|
211
|
+
Define component types at module scope; hooks and class components work normally.
|
|
212
|
+
Use exported defaults inside replacements to preserve geometry, scrolling, and
|
|
213
|
+
interaction. Input types, including HeaderCellInput, are exported from root.
|
|
214
|
+
Mount a React context provider above Roster for extra consumer data, and read it with useContext in a module-scope slot component.
|
|
215
|
+
Avoid inline closures or memoized factories returning a new component per render; new interval types remount every interval and defeat the body's content key.
|
|
216
|
+
|
|
217
|
+
RosterBody composes the exported RosterBodyLayout (gridZone/listZone nodes with
|
|
218
|
+
scroll wiring) and RosterLaneList (LegendList, geometryFor, and LaneRow mounting).
|
|
219
|
+
Keep its viewport gate and measured estimatedListSize in custom compositions.
|
|
203
220
|
|
|
204
221
|
## Roster zones
|
|
205
222
|
|
|
206
|
-
|
|
|
223
|
+
| Slot | Component inputs or node | Default and behavior |
|
|
207
224
|
| --- | --- | --- |
|
|
208
|
-
| `emptyZone` |
|
|
209
|
-
| `
|
|
210
|
-
| `
|
|
211
|
-
| `
|
|
212
|
-
| `
|
|
213
|
-
| `
|
|
214
|
-
| `cornerZone` |
|
|
215
|
-
| `
|
|
216
|
-
| `
|
|
217
|
-
| `
|
|
225
|
+
| `emptyZone` | `ReactNode` | `RosterEmpty`: No lanes. |
|
|
226
|
+
| `laneLabelComponent` | `lane`, `flag`, `complete`, `viewTimezone`, localized labels | `RosterLaneLabel`: label, differing IANA zone badge, and notices. |
|
|
227
|
+
| `headerCellComponent` | `HeaderCellInput` (`tick`) | `RosterHeaderCell`: tick label. |
|
|
228
|
+
| `intervalComponent` | `rect`, `layer`, `lane`, `highlighted` | `RosterInterval`: positioned colored rect, with final inset bounds. |
|
|
229
|
+
| `gapComponent` | `rect`, `layer`, `lane` | `RosterGap`: no visible content; the row supplies pressable bounds. |
|
|
230
|
+
| `gridComponent` | `ticks`, `contentWidth` | `RosterGrid`: one hairline per tick behind every lane. |
|
|
231
|
+
| `cornerZone` | `ReactNode` | `RosterCorner`: nothing; the cell above the labels, laneLabelWidth wide. |
|
|
232
|
+
| `headerComponent` | `ticks`, `projection`, `scroll`, `contentWidth`, `headerCellComponent` | `RosterHeader`: frozen header following horizontal offset. |
|
|
233
|
+
| `laneLabelColumnComponent` | `labels: LaneLabelInput[]`, `projection`, `scroll`, `laneLabelComponent` | `RosterLaneLabelColumn`: frozen labels following vertical offset. |
|
|
234
|
+
| `bodyComponent` | Ordered `lanes`, `window`, `geometryFor`, `projection`, `scroll`, `press`, `ticks`, `viewport`, `contentWidth`, highlight/hover, incomplete label, and rect components | `RosterBody`: virtualized lanes. |
|
|
218
235
|
|
|
219
236
|
## Schedule zones
|
|
220
237
|
|
|
221
|
-
|
|
|
238
|
+
| Slot | Component inputs or node | Default and behavior |
|
|
222
239
|
| --- | --- | --- |
|
|
223
|
-
| `
|
|
224
|
-
| `
|
|
225
|
-
| `
|
|
226
|
-
| `
|
|
227
|
-
| `
|
|
228
|
-
| `
|
|
229
|
-
| `
|
|
230
|
-
| `
|
|
231
|
-
| `
|
|
240
|
+
| `gutterComponent` | `hours`, `pxPerHour` | `ScheduleGutter`: 24 frozen hour labels. |
|
|
241
|
+
| `gridComponent` | `hours`, `pxPerHour` | `ScheduleGrid`: 24 bordered hour bands behind each day's rects. |
|
|
242
|
+
| `dayHeaderComponent` | `day` | `ScheduleDayHeader`: weekday, localDate, and transition badge. |
|
|
243
|
+
| `skippedDateComponent` | `localDate` | `ScheduleSkippedDate`: zero-width header marker for a wholly skipped date. |
|
|
244
|
+
| `columnComponent` | `day`, `rects`, `gapRects`, `lane`, `highlightSource`, `press`, interval/gap components | `ScheduleColumn`: final rect bounds in layer order. |
|
|
245
|
+
| `transitionComponent` | `day`, `transition`, `y`, `dividerY`, `height`, `width` | `ScheduleTransition`: skipped-time hatch, or repeat divider and again label. |
|
|
246
|
+
| `nowLineComponent` | `y`, `column` | `ScheduleNowLine`: line in the current day's column, updated each minute. |
|
|
247
|
+
| `intervalComponent`, `gapComponent` | Same inputs as Roster | Shared `RosterInterval` and `RosterGap`. |
|
|
248
|
+
| `incompleteComponent` | `lane`, `label` | `ScheduleIncomplete`: notice in reserved space above the grid when incomplete. |
|
|
232
249
|
|
|
233
250
|
Position custom interval content at rect.x/y with width/height/z and
|
|
234
251
|
pointerEvents="none". Gap content already sits in a positioned pressable.
|
|
235
252
|
Column x coordinates are local; select the column through rect.column.
|
|
236
|
-
|
|
253
|
+
Slot components are plain React Native views; under NativeWind they take className
|
|
237
254
|
directly. The default RosterLaneLabel owns its row's bottom border, so a custom
|
|
238
|
-
|
|
255
|
+
laneLabelComponent draws its own separators.
|
|
239
256
|
|
|
240
257
|
## Navigation and zones
|
|
241
258
|
|
|
@@ -255,7 +272,7 @@ Schedule always uses 24 equal wall-hour bands. Positive deltaMinutes means a
|
|
|
255
272
|
skipped empty region; negative means two half-height repeat regions. Rects split
|
|
256
273
|
at column edges, source changes, and every scale boundary. For repeat at T with
|
|
257
274
|
magnitude d, boundaries are T-d, T, and T+d. A wholly skipped date produces no
|
|
258
|
-
column;
|
|
275
|
+
column; skippedDateComponent labels the header gap (Apia 2011-12-30).
|
|
259
276
|
Each column spans the first instant of its date to the first instant of the next.
|
|
260
277
|
Skipped wall spans belong to the date they interrupt. Its transitions include a
|
|
261
278
|
skip at the exclusive end when it empties that date's final wall bands (Nuuk
|
|
@@ -457,7 +474,7 @@ W: seed 1318, 200 lanes, week, two layers, 24-lane layout batch, 15-minute ticks
|
|
|
457
474
|
Bun 1.4.0, 2026-09-08T05:18:00Z, commit
|
|
458
475
|
1c4ac0e8580b1d46c043f946aa7ebc1ee316d0fa. Median of 11 target-cold samples after
|
|
459
476
|
five runtime warmups. Each timing must stay below 16 ms; CI also enforces <=1.5x
|
|
460
|
-
its baseline. Size gates: core <15 kB, root <
|
|
477
|
+
its baseline. Size gates: core <15 kB, root <44 kB minified/uncompressed, peers
|
|
461
478
|
external. Phone fps and cold-layout numbers are not yet captured; the paired
|
|
462
479
|
react-big-scheduler comparison is not yet measured. Do not claim relative speed.
|
|
463
480
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-roster",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.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",
|
|
@@ -66,7 +66,8 @@
|
|
|
66
66
|
"README.md",
|
|
67
67
|
"LICENSE",
|
|
68
68
|
"NOTICE.md",
|
|
69
|
-
"llms.txt"
|
|
69
|
+
"llms.txt",
|
|
70
|
+
"docs/migrations.md"
|
|
70
71
|
],
|
|
71
72
|
"scripts": {
|
|
72
73
|
"clean": "rm -rf dist",
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# layers
|
|
2
2
|
|
|
3
3
|
This area is about a layer; its children are intervals and gaps. Both
|
|
4
|
-
projections draw the same
|
|
4
|
+
projections draw the same components, so they live above Roster and Schedule.
|
|
5
5
|
|
|
6
6
|
- Root entry exports: `RosterInterval`, `RosterGap`, `IntervalInput`, `GapInput`
|
|
7
|
-
- `layers.types.ts`: the
|
|
8
|
-
- `parts/interval.tsx`, `parts/gap.tsx`: the default
|
|
7
|
+
- `layers.types.ts`: the component input contract both projections pass
|
|
8
|
+
- `parts/interval.tsx`, `parts/gap.tsx`: the default components
|
|
9
9
|
- `utils/styles.ts`: content-keyed style objects per layer
|
|
10
10
|
|
|
11
11
|
Imports core only. Tests: `test/components/layers`.
|
|
12
|
+
|
|
13
|
+
Both projections accept intervalComponent and gapComponent as ComponentType inputs.
|
|
14
|
+
Their data-owning lane or column mounts the component with the final rect, layer,
|
|
15
|
+
and lane; interval inputs also carry highlighted. No renderer calls a slot function.
|
|
@@ -2,17 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
This feature is about a roster; its children are lanes.
|
|
4
4
|
|
|
5
|
-
- Root entry exports: `Roster`, `useRoster`, the `RosterXxx` parts, and every
|
|
6
|
-
`XxxInput`
|
|
5
|
+
- Root entry exports: `Roster`, `useRoster`, `RosterBodyLayout`, `RosterLaneList`, the other `RosterXxx` parts, and every
|
|
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
9
|
- `use-roster.ts`: the hook; owns window, projection, scroll, geometry, and press
|
|
10
10
|
- `layout.tsx`: arranges corner, header, label column, and body regions only
|
|
11
|
-
- `roster.types.ts`: props, model, and every
|
|
11
|
+
- `roster.types.ts`: props, model, and every slot input type
|
|
12
|
+
- `body-layout.tsx`: arranges grid and list nodes with horizontal scroll wiring
|
|
13
|
+
- `parts/lane-list.tsx`: `RosterLaneList` owns LegendList and its lane render callback
|
|
12
14
|
- `parts/`: collection-level parts (body, header, header cell, grid, corner, label column, empty)
|
|
13
15
|
- `lanes/`: `LaneRow`, the interval-hover platform pair, and lane-local parts
|
|
14
16
|
- `utils/`: ticks and the body content key
|
|
15
17
|
|
|
16
|
-
Interval and gap
|
|
18
|
+
Interval and gap components live in `../layers`; press geometry and `regionStyle`
|
|
17
19
|
in `../primitives`. Zone contracts are documented in the README's Roster zones
|
|
18
20
|
section and in `llms.txt`. Tests: `test/components/roster`.
|
|
21
|
+
|
|
22
|
+
This feature is about a roster body; its children are lanes. `RosterBody` gates
|
|
23
|
+
measurement and composes the body layout and lane list. The `onRowRender` prop on
|
|
24
|
+
`RosterBody` and `RosterLaneList` is a development-only Profiler hook used by the
|
|
25
|
+
gallery, not a supported customization point. Production Profiler callbacks are disabled.
|
|
26
|
+
|
|
27
|
+
Input-bearing slots accept component types and mount in the data-owning parts.
|
|
28
|
+
The chassis binds defaults once; emptyZone and cornerZone accept nodes. The body
|
|
29
|
+
content key tracks interval and gap component identity, including class components.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { ScrollView, View } from 'react-native';
|
|
3
|
+
import type { BodyInput } from './roster.types';
|
|
4
|
+
|
|
5
|
+
export function RosterBodyLayout({
|
|
6
|
+
scroll,
|
|
7
|
+
contentWidth,
|
|
8
|
+
viewport,
|
|
9
|
+
gridZone,
|
|
10
|
+
listZone,
|
|
11
|
+
}: Pick<BodyInput, 'scroll' | 'contentWidth' | 'viewport'> & {
|
|
12
|
+
/** Noninteractive grid behind the lane list. */
|
|
13
|
+
gridZone: ReactNode;
|
|
14
|
+
/** Virtualized lane collection within the measured viewport. */
|
|
15
|
+
listZone: ReactNode;
|
|
16
|
+
}) {
|
|
17
|
+
return (
|
|
18
|
+
<ScrollView
|
|
19
|
+
testID="roster-horizontal-scroll"
|
|
20
|
+
ref={scroll.bodyRef}
|
|
21
|
+
horizontal
|
|
22
|
+
onScroll={scroll.onBodyScroll}
|
|
23
|
+
scrollEventThrottle={16}
|
|
24
|
+
style={{ flex: 1 }}
|
|
25
|
+
>
|
|
26
|
+
<View style={{ width: contentWidth, height: viewport.height }}>
|
|
27
|
+
{gridZone}
|
|
28
|
+
{listZone}
|
|
29
|
+
</View>
|
|
30
|
+
</ScrollView>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -12,6 +12,9 @@ import { RosterLaneLabelColumn } from './parts/lane-label-column';
|
|
|
12
12
|
import type { RosterProps } from './roster.types';
|
|
13
13
|
import { useRoster } from './use-roster';
|
|
14
14
|
|
|
15
|
+
const defaultEmptyZone = <RosterEmpty />;
|
|
16
|
+
const defaultCornerZone = <RosterCorner />;
|
|
17
|
+
|
|
15
18
|
export function Roster(props: RosterProps) {
|
|
16
19
|
const {
|
|
17
20
|
status,
|
|
@@ -28,20 +31,20 @@ export function Roster(props: RosterProps) {
|
|
|
28
31
|
onLayout,
|
|
29
32
|
} = useRoster(props);
|
|
30
33
|
const {
|
|
31
|
-
emptyZone =
|
|
32
|
-
cornerZone =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
emptyZone = defaultEmptyZone,
|
|
35
|
+
cornerZone = defaultCornerZone,
|
|
36
|
+
headerComponent: HeaderComponent = RosterHeader,
|
|
37
|
+
laneLabelColumnComponent: LaneLabelColumnComponent = RosterLaneLabelColumn,
|
|
38
|
+
bodyComponent: BodyComponent = RosterBody,
|
|
39
|
+
headerCellComponent = RosterHeaderCell,
|
|
40
|
+
laneLabelComponent = RosterLaneLabel,
|
|
41
|
+
intervalComponent = RosterInterval,
|
|
42
|
+
gapComponent = RosterGap,
|
|
43
|
+
gridComponent = RosterGrid,
|
|
41
44
|
incompleteLabel = 'Availability may be incomplete',
|
|
42
45
|
neverSetLabel = 'No availability set',
|
|
43
46
|
} = props;
|
|
44
|
-
if (status === 'empty') return emptyZone
|
|
47
|
+
if (status === 'empty') return emptyZone;
|
|
45
48
|
if (window.start === window.end) return null;
|
|
46
49
|
return (
|
|
47
50
|
<RosterLayout
|
|
@@ -51,40 +54,55 @@ export function Roster(props: RosterProps) {
|
|
|
51
54
|
bodyStyle={props.bodyStyle}
|
|
52
55
|
laneLabelWidth={props.laneLabelWidth}
|
|
53
56
|
onLayout={onLayout}
|
|
54
|
-
cornerZone={cornerZone
|
|
55
|
-
headerZone={
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
57
|
+
cornerZone={cornerZone}
|
|
58
|
+
headerZone={
|
|
59
|
+
<HeaderComponent
|
|
60
|
+
ticks={ticks}
|
|
61
|
+
projection={projection}
|
|
62
|
+
scroll={scroll}
|
|
63
|
+
contentWidth={contentWidth}
|
|
64
|
+
headerCellComponent={headerCellComponent}
|
|
65
|
+
/>
|
|
66
|
+
}
|
|
67
|
+
laneLabelColumnZone={
|
|
68
|
+
<LaneLabelColumnComponent
|
|
69
|
+
labels={orderedLanes.map((lane) => {
|
|
70
|
+
const state = laneState.get(lane.id) ?? {
|
|
71
|
+
flag: 'none',
|
|
72
|
+
complete: true,
|
|
73
|
+
};
|
|
74
|
+
return {
|
|
75
|
+
lane,
|
|
76
|
+
...state,
|
|
77
|
+
viewTimezone: projection.viewTimezone,
|
|
78
|
+
incompleteLabel,
|
|
79
|
+
neverSetLabel,
|
|
80
|
+
};
|
|
81
|
+
})}
|
|
82
|
+
projection={projection}
|
|
83
|
+
scroll={scroll}
|
|
84
|
+
laneLabelComponent={laneLabelComponent}
|
|
85
|
+
/>
|
|
86
|
+
}
|
|
87
|
+
bodyZone={
|
|
88
|
+
<BodyComponent
|
|
89
|
+
lanes={orderedLanes}
|
|
90
|
+
window={window}
|
|
91
|
+
geometryFor={geometryFor}
|
|
92
|
+
projection={projection}
|
|
93
|
+
scroll={scroll}
|
|
94
|
+
press={press}
|
|
95
|
+
ticks={ticks}
|
|
96
|
+
contentWidth={contentWidth}
|
|
97
|
+
viewport={viewport}
|
|
98
|
+
intervalComponent={intervalComponent}
|
|
99
|
+
gapComponent={gapComponent}
|
|
100
|
+
gridComponent={gridComponent}
|
|
101
|
+
highlightSource={props.highlightSource}
|
|
102
|
+
onIntervalHover={props.onIntervalHover}
|
|
103
|
+
incompleteLabel={incompleteLabel}
|
|
104
|
+
/>
|
|
105
|
+
}
|
|
88
106
|
/>
|
|
89
107
|
);
|
|
90
108
|
}
|
|
@@ -8,7 +8,12 @@ import { RosterIncomplete } from './parts/incomplete';
|
|
|
8
8
|
|
|
9
9
|
export type LaneRowProps = Pick<
|
|
10
10
|
BodyInput,
|
|
11
|
-
|
|
11
|
+
| 'press'
|
|
12
|
+
| 'intervalComponent'
|
|
13
|
+
| 'gapComponent'
|
|
14
|
+
| 'highlightSource'
|
|
15
|
+
| 'onIntervalHover'
|
|
16
|
+
| 'incompleteLabel'
|
|
12
17
|
> & {
|
|
13
18
|
lane: Lane;
|
|
14
19
|
geometry: LaneGeometry;
|
|
@@ -22,8 +27,8 @@ export function LaneRow({
|
|
|
22
27
|
width,
|
|
23
28
|
rowHeight,
|
|
24
29
|
press,
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
intervalComponent: IntervalComponent,
|
|
31
|
+
gapComponent: GapComponent,
|
|
27
32
|
highlightSource,
|
|
28
33
|
onIntervalHover,
|
|
29
34
|
incompleteLabel = 'Availability may be incomplete',
|
|
@@ -47,15 +52,15 @@ export function LaneRow({
|
|
|
47
52
|
.filter((rect) => rect.layerId === layer.id)
|
|
48
53
|
.map((rect) => (
|
|
49
54
|
<Fragment key={`interval-${rect.x}:${rect.width}`}>
|
|
50
|
-
|
|
51
|
-
rect
|
|
52
|
-
layer
|
|
53
|
-
lane
|
|
54
|
-
highlighted
|
|
55
|
+
<IntervalComponent
|
|
56
|
+
rect={rect}
|
|
57
|
+
layer={layer}
|
|
58
|
+
lane={lane}
|
|
59
|
+
highlighted={rect.sources.some(
|
|
55
60
|
(source) =>
|
|
56
61
|
source.kind === highlightSource?.kind && source.id === highlightSource.id,
|
|
57
|
-
)
|
|
58
|
-
|
|
62
|
+
)}
|
|
63
|
+
/>
|
|
59
64
|
</Fragment>
|
|
60
65
|
))}
|
|
61
66
|
{geometry.gapRects
|
|
@@ -78,7 +83,7 @@ export function LaneRow({
|
|
|
78
83
|
zIndex: rect.z,
|
|
79
84
|
}}
|
|
80
85
|
>
|
|
81
|
-
|
|
86
|
+
<GapComponent rect={rect} layer={layer} lane={lane} />
|
|
82
87
|
</Pressable>
|
|
83
88
|
))}
|
|
84
89
|
</Fragment>
|