react-native-roster 0.4.4 → 0.4.5
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 +241 -249
- package/llms.txt +20 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,303 +1,295 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://simiancraft.github.io/react-native-roster/showcase">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/simiancraft/react-native-roster/main/docs/assets/hero.png" alt="Twelve people as lanes on a shared day axis with a red now line, beside one person's week as a schedule" width="860" />
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://simiancraft.github.io/react-native-roster/showcase">
|
|
9
|
+
<img src="https://img.shields.io/badge/▶%20Live%20demo-press%20a%20gap%2C%20see%20why-4f46e5?style=for-the-badge" alt="Live demo" />
|
|
10
|
+
</a>
|
|
11
|
+
</p>
|
|
2
12
|
|
|
3
|
-
|
|
13
|
+
# react-native-roster
|
|
4
14
|
|
|
5
15
|
[](https://www.npmjs.com/package/react-native-roster)
|
|
6
16
|
[](https://github.com/simiancraft/react-native-roster/actions/workflows/ci.yml)
|
|
7
17
|
[](https://codecov.io/github/simiancraft/react-native-roster)
|
|
8
|
-
[](./.size-limit.json)
|
|
10
|
-
[](./demo/package.json)
|
|
11
|
-
[](./LICENSE)
|
|
18
|
+
[](https://www.npmjs.com/package/react-native-roster)
|
|
12
19
|
[](https://securityscorecards.dev/viewer/?uri=github.com/simiancraft/react-native-roster)
|
|
20
|
+
[](https://github.com/simiancraft/react-native-roster/blob/main/LICENSE)
|
|
13
21
|
|
|
14
|
-
**
|
|
15
|
-
or [try the showcase](https://simiancraft.github.io/react-native-roster/showcase).**
|
|
22
|
+
**"Why is nobody on Wednesday?"** Rosters and schedules for React Native and web. Press the empty Wednesday and it names the dentist appointment that emptied it.
|
|
16
23
|
|
|
17
|
-
|
|
24
|
+
```tsx
|
|
25
|
+
<Roster
|
|
26
|
+
lanes={lanes}
|
|
27
|
+
windowSpec={windowSpec}
|
|
28
|
+
onGapPress={(rect) => console.log(rect.sources)} // [{ kind: 'date', id: 'dentist' }]
|
|
29
|
+
/>
|
|
30
|
+
```
|
|
18
31
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
32
|
+
A staffing screen gets asked who is on right now, who is free at 3, and why
|
|
33
|
+
nobody is on Wednesday. The third question needs the rule behind the rectangle,
|
|
34
|
+
so every interval and gap keeps its sources. Press an interval to get the sources
|
|
35
|
+
that produced it; press a gap to get the sources that removed it.
|
|
22
36
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
are hatched, repeated hours have separate geometry, and a week containing a
|
|
27
|
-
transition is 167 or 169 hours wide. `Roster` and `Schedule` project the same
|
|
28
|
-
lane data; `useRoster` and `useSchedule` expose their models for custom layouts.
|
|
37
|
+
Give each person or resource a lane. `Roster` draws every lane on one time axis;
|
|
38
|
+
`Schedule` draws any one of them as a week, days across and hours down. The same
|
|
39
|
+
lane feeds both.
|
|
29
40
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
standard JavaScript and `Intl` core with a [15 kB size gate](./.size-limit.json).
|
|
41
|
+
`Roster` draws elapsed time, so the week the clocks change is 167 or 169 hours
|
|
42
|
+
wide; `Schedule` hatches the hour they skipped.
|
|
33
43
|
|
|
34
|
-
|
|
44
|
+
It does not create, drag, or resize anything, and it has no month grid; if you
|
|
45
|
+
need those, reach for [react-native-calendar-kit](https://github.com/howljs/react-native-calendar-kit)
|
|
46
|
+
or [react-native-big-calendar](https://github.com/acro5piano/react-native-big-calendar).
|
|
35
47
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
[Browse every example](https://simiancraft.github.io/react-native-roster/) in the gallery.
|
|
49
|
+
|
|
50
|
+
## Quickstart
|
|
51
|
+
|
|
52
|
+
### Install
|
|
40
53
|
|
|
41
|
-
|
|
42
|
-
your React/Expo setup, plus `bun add @radix-ui/react-popover@^1.1.23`.
|
|
43
|
-
**Radix is required whenever a web bundler resolves the root entry or
|
|
44
|
-
`/nativewind` to the web selection layout, even with selection disabled or only
|
|
45
|
-
`Schedule` imported.** That layout imports Radix unconditionally; the published
|
|
46
|
-
browser remap and Metro's web source resolution both reach it. Native and
|
|
47
|
-
`/core`-only or `/rrule`-only imports do not require Radix.
|
|
54
|
+
These steps assume an Expo app.
|
|
48
55
|
|
|
49
|
-
|
|
50
|
-
Expo >=51 (optional), NativeWind >=4.1 (optional), and Radix ^1.1.23 (web).
|
|
51
|
-
Reanimated is required by `Roster` and optional for core-only use. Keep React,
|
|
52
|
-
React Native, and Expo aligned with your SDK. The demo uses Expo SDK 54,
|
|
53
|
-
React 19.1, React Native 0.81.5, LegendList 2.0.19, and Reanimated 3.19.5;
|
|
54
|
-
these versions do not establish support for every peer-range combination.
|
|
56
|
+
1. Add the package and LegendList:
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
only `/rrule` imports them. Import isolation does not reduce installation size.
|
|
58
|
+
```sh
|
|
59
|
+
bun add react-native-roster @legendapp/list
|
|
60
|
+
```
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
2. Add Reanimated for your Expo SDK:
|
|
62
63
|
|
|
63
|
-
|
|
64
|
+
```sh
|
|
65
|
+
bunx expo install react-native-reanimated --bun
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
3. For web, add React DOM, React Native Web, and Radix Popover; every web build needs all three:
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
bunx expo install react-dom react-native-web --bun
|
|
72
|
+
bun add '@radix-ui/react-popover@^1.1.23'
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Without Expo, follow the [Reanimated install guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/)
|
|
76
|
+
for 3.19 or newer. On React Native older than 0.79, turn on
|
|
77
|
+
`resolver.unstable_enablePackageExports` in Metro.
|
|
78
|
+
|
|
79
|
+
### Roster
|
|
80
|
+
|
|
81
|
+
The smallest useful roster: Alex works 09:00 to 17:00 UTC on Monday, and a table
|
|
82
|
+
row says so. `Roster` fills its parent by default; here it gets a fixed height.
|
|
64
83
|
|
|
65
84
|
```tsx
|
|
66
85
|
import { Roster } from 'react-native-roster';
|
|
67
86
|
import type { Lane } from 'react-native-roster/core';
|
|
68
87
|
|
|
69
|
-
const
|
|
70
|
-
id: '
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
88
|
+
const lane: Lane = {
|
|
89
|
+
id: 'alex',
|
|
90
|
+
label: 'Alex',
|
|
91
|
+
layers: [
|
|
92
|
+
{
|
|
93
|
+
id: 'working-hours',
|
|
94
|
+
role: 'availability',
|
|
95
|
+
z: 0,
|
|
96
|
+
style: { color: '#4f9478' },
|
|
97
|
+
intervals: [
|
|
98
|
+
{
|
|
99
|
+
start: Date.UTC(2026, 8, 21, 9),
|
|
100
|
+
end: Date.UTC(2026, 8, 21, 17),
|
|
101
|
+
sources: [{ kind: 'table', id: 'alex-monday' }],
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export function RosterExample() {
|
|
109
|
+
return (
|
|
110
|
+
<Roster
|
|
111
|
+
lanes={[lane]}
|
|
112
|
+
style={{ height: 480, flex: undefined }}
|
|
113
|
+
windowSpec={{ span: 'day', anchorDate: '2026-09-21', timezone: 'UTC' }}
|
|
114
|
+
onIntervalPress={(rect, pressedLane) => {
|
|
115
|
+
console.log(pressedLane.label, rect.sources); // Alex [{ kind: 'table', id: 'alex-monday' }]
|
|
116
|
+
}}
|
|
117
|
+
/>
|
|
118
|
+
);
|
|
85
119
|
}
|
|
86
120
|
```
|
|
87
121
|
|
|
88
|
-
|
|
89
|
-
defaults to 60 and accepts any positive divisor of 60; it changes ticks and
|
|
90
|
-
empty-space snapping, never interval boundaries. `rowHeight` defaults to 48 and
|
|
91
|
-
`pxPerMinute` to 0.5, growing to fit wider viewports. Both scale and row height
|
|
92
|
-
must be positive and finite.
|
|
93
|
-
|
|
94
|
-
## Quick start: recurring local hours
|
|
122
|
+
Add lanes for more people. Add layers and they stack by `z`.
|
|
95
123
|
|
|
96
|
-
|
|
97
|
-
hours and dated exclusions into intervals and gaps. Expand for `windowFor(spec)`
|
|
98
|
-
in your screen's hook, and carry `result.complete` into `Lane.complete`.
|
|
99
|
-
The [runnable recurring example](./docs/adapters.md#quick-start-recurring-local-hours)
|
|
100
|
-
shows the complete mapping; [recurrence semantics](./docs/recurrence.md) covers
|
|
101
|
-
validation, COUNT, UNTIL, and BYSETPOS. Never expand inside a slot component.
|
|
124
|
+
### Schedule
|
|
102
125
|
|
|
103
|
-
|
|
126
|
+
Hand `Schedule` that same `lane` and it draws days across and hours down. It
|
|
127
|
+
takes day and week windows.
|
|
104
128
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
`LayerRole` is `'availability' | 'booking' | 'custom'`. The first two feed
|
|
119
|
-
coverage math (`availabilityMinutes`, `bookingMinutes`, and
|
|
120
|
-
`availabilityMinusBookingMinutes`); use `custom` for anything else. `LayerStyle`
|
|
121
|
-
is `{ color, highlightColor?, inset?, opacity? }`; the engine applies inset on
|
|
122
|
-
the cross axis and returns final bounds for drawing and hit testing.
|
|
123
|
-
|
|
124
|
-
`Lane` also accepts optional data-quality fields: `version` (bump when layers
|
|
125
|
-
change), `timezone` (a badge), `flag: 'never-set'` for lanes you know were never
|
|
126
|
-
configured, `complete: false` when an adapter cap truncated the result, and
|
|
127
|
-
`meta` for your own display data. The notices default to "No availability set"
|
|
128
|
-
and "Availability may be incomplete"; both are localizable props.
|
|
129
|
-
|
|
130
|
-
## Entry points
|
|
131
|
-
|
|
132
|
-
| Import | Shipped surface |
|
|
133
|
-
| --- | --- |
|
|
134
|
-
| `react-native-roster` | `Roster`, `Schedule`, hooks, slot components, `RosterSelectionPopover`, `PortalHost`, `Portal`, and all core exports. |
|
|
135
|
-
| `react-native-roster/core` | Types, `layoutLane`, `coverageFor`, `flagFor`, `extentOf`, `intersectionOf`, `unionOf`, axis helpers, comparators, and counters. Standard JavaScript and `Intl` only. |
|
|
136
|
-
| `react-native-roster/rrule` | `expandRuleSet`, `envelopeFor`, types, and expansion counters and caches. Uses pinned `rrule-temporal` and `@js-temporal/polyfill`. |
|
|
137
|
-
| `react-native-roster/nativewind` | Registers `Roster` and `Schedule` with NativeWind so their `className` props resolve; re-exports the registered components. |
|
|
138
|
-
|
|
139
|
-
Root and core never import recurrence dependencies. Metro selects TypeScript
|
|
140
|
-
source; the default condition selects CommonJS, and `types` selects declarations
|
|
141
|
-
in `dist/src`.
|
|
142
|
-
The CommonJS root pulls in the root bundle; use `/core` for core-only consumers.
|
|
129
|
+
```tsx
|
|
130
|
+
import { Schedule } from 'react-native-roster';
|
|
131
|
+
|
|
132
|
+
export function ScheduleExample() {
|
|
133
|
+
return (
|
|
134
|
+
<Schedule
|
|
135
|
+
lane={lane}
|
|
136
|
+
style={{ height: 480, flex: undefined }}
|
|
137
|
+
windowSpec={{ span: 'week', anchorDate: '2026-09-21', timezone: 'UTC' }}
|
|
138
|
+
/>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
```
|
|
143
142
|
|
|
144
|
-
|
|
143
|
+
### Recurrence from rrule
|
|
145
144
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
[Cache keys, counters, and clearing](./docs/caches.md) explain target-warm reuse.
|
|
145
|
+
The `/rrule` adapter turns daily, weekly, and monthly rules into intervals and
|
|
146
|
+
gaps. Here is the Wednesday from the top of this page: weekdays 09:00 to 17:00
|
|
147
|
+
in New York, and a dentist appointment on the 23rd. Press the empty day and it
|
|
148
|
+
names `dentist`.
|
|
151
149
|
|
|
152
|
-
|
|
150
|
+
```tsx
|
|
151
|
+
import { Roster } from 'react-native-roster';
|
|
152
|
+
import { windowFor } from 'react-native-roster/core';
|
|
153
|
+
import type { Lane, WindowSpec } from 'react-native-roster/core';
|
|
154
|
+
import { expandRuleSet } from 'react-native-roster/rrule';
|
|
155
|
+
import type { RuleSet } from 'react-native-roster/rrule';
|
|
156
|
+
|
|
157
|
+
const windowSpec: WindowSpec = {
|
|
158
|
+
span: 'week',
|
|
159
|
+
anchorDate: '2026-09-21',
|
|
160
|
+
timezone: 'America/New_York',
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const ruleSet: RuleSet = {
|
|
164
|
+
rules: [
|
|
165
|
+
{
|
|
166
|
+
id: 'weekday-hours',
|
|
167
|
+
kind: 'include',
|
|
168
|
+
frequency: 'WEEKLY',
|
|
169
|
+
dtstart: '2026-09-21',
|
|
170
|
+
byweekday: [0, 1, 2, 3, 4],
|
|
171
|
+
hourstart: 9,
|
|
172
|
+
hourend: 17,
|
|
173
|
+
timezone: 'America/New_York',
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
dates: [
|
|
177
|
+
{ id: 'dentist', kind: 'exclude', date: '2026-09-23', timezone: 'America/New_York' },
|
|
178
|
+
],
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const result = expandRuleSet(ruleSet, windowFor(windowSpec)); // 4 intervals, 1 gap
|
|
182
|
+
|
|
183
|
+
const recurringLane: Lane = {
|
|
184
|
+
id: 'alex',
|
|
185
|
+
label: 'Alex',
|
|
186
|
+
timezone: 'America/New_York',
|
|
187
|
+
complete: result.complete,
|
|
188
|
+
layers: [
|
|
189
|
+
{
|
|
190
|
+
id: 'working-hours',
|
|
191
|
+
role: 'availability',
|
|
192
|
+
z: 0,
|
|
193
|
+
style: { color: '#4f9478' },
|
|
194
|
+
intervals: result.intervals,
|
|
195
|
+
gaps: result.gaps,
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export function RecurringRosterExample() {
|
|
201
|
+
return (
|
|
202
|
+
<Roster
|
|
203
|
+
lanes={[recurringLane]}
|
|
204
|
+
windowSpec={windowSpec}
|
|
205
|
+
style={{ height: 480, flex: undefined }}
|
|
206
|
+
onGapPress={(rect) => console.log(rect.sources)} // [{ kind: 'date', id: 'dentist' }]
|
|
207
|
+
/>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
```
|
|
153
211
|
|
|
154
|
-
|
|
212
|
+
When the window can change, expand for the selected window in your screen's
|
|
213
|
+
hook. The [adapter guide](https://github.com/simiancraft/react-native-roster/blob/main/docs/adapters.md)
|
|
214
|
+
covers live data; the [recurrence reference](https://github.com/simiancraft/react-native-roster/blob/main/docs/recurrence.md)
|
|
215
|
+
lists the supported rules.
|
|
155
216
|
|
|
156
|
-
|
|
157
|
-
windows. Extents bridge gaps; unions preserve disjoint spans. See the
|
|
158
|
-
[helper walkthrough](./src/core/README.md#interval-helper-walkthrough).
|
|
217
|
+
### NativeWind
|
|
159
218
|
|
|
160
|
-
|
|
219
|
+
react-native-roster supports [NativeWind](https://www.nativewind.dev/).
|
|
161
220
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
`windowFor`, `prev`, `next`, and `today` handle bounds and navigation; weeks
|
|
165
|
-
start Monday (weekday 0) unless `wkst` says otherwise. Components accept
|
|
166
|
-
`onNavigate` but ship no toolbar; your controls update the controlled spec.
|
|
221
|
+
1. Install NativeWind 4.1 or newer and finish its
|
|
222
|
+
[setup](https://www.nativewind.dev/docs/getting-started/installation):
|
|
167
223
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
expanded occurrences. Column spans, rollbacks across midnight, and pointer
|
|
172
|
-
inversion are documented in [timezones](./docs/timezones.md).
|
|
224
|
+
```sh
|
|
225
|
+
bun add 'nativewind@^4.1'
|
|
226
|
+
```
|
|
173
227
|
|
|
174
|
-
|
|
175
|
-
layers winning equal-z ties. `onIntervalPress(rect, lane)` and
|
|
176
|
-
`onGapPress(rect, lane)` return the contributing sources; `onCellPress(lane,
|
|
177
|
-
time)` returns snapped absolute time for empty space. `onIntervalHover` is
|
|
178
|
-
Roster's web-only pointer callback. `highlightSource={{ kind, id }}` applies
|
|
179
|
-
`highlightColor` to every matching rect without new geometry. Sorting defaults
|
|
180
|
-
to `byLabel`; `byCoverage({ measure })` sorts by coverage descending.
|
|
228
|
+
2. Register the components once, in your app's root module:
|
|
181
229
|
|
|
182
|
-
|
|
230
|
+
```tsx
|
|
231
|
+
import 'react-native-roster/nativewind';
|
|
232
|
+
```
|
|
183
233
|
|
|
184
|
-
|
|
185
|
-
The caller owns clock updates. Only instants inside the end-exclusive window are visible. Replace `nowLineComponent` to style it; the
|
|
186
|
-
[Roster reference](./src/components/roster/README.md) covers hook and scroll behavior.
|
|
234
|
+
3. Style with class props:
|
|
187
235
|
|
|
188
|
-
|
|
236
|
+
```tsx
|
|
237
|
+
<Roster
|
|
238
|
+
className="rounded-xl bg-white"
|
|
239
|
+
headerClassName="bg-slate-100"
|
|
240
|
+
laneLabelColumnClassName="bg-slate-100"
|
|
241
|
+
{...rest}
|
|
242
|
+
/>
|
|
243
|
+
```
|
|
189
244
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
On web, translated header and lane-label regions use non-scrolling clips, so focusable
|
|
194
|
-
custom content cannot move them out of alignment with the roster body.
|
|
195
|
-
The [slot tables and examples](./docs/customization.md#roster-zones) cover every region.
|
|
245
|
+
Every style prop has a `className` twin; the
|
|
246
|
+
[NativeWind page](https://github.com/simiancraft/react-native-roster/blob/main/src/nativewind/README.md)
|
|
247
|
+
lists them.
|
|
196
248
|
|
|
197
|
-
|
|
249
|
+
## Customization
|
|
198
250
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
including unique native host names and provider placement above the host.
|
|
251
|
+
You can replace every region: header cells, lane labels, intervals, gaps, the
|
|
252
|
+
grid, the now line, and the detail popover. Props ending in `Component` take a
|
|
253
|
+
component type; props ending in `Zone` take a node. If you want your own layout
|
|
254
|
+
entirely, `useRoster` and `useSchedule` return the same models the components
|
|
255
|
+
render from. See [customization](https://github.com/simiancraft/react-native-roster/blob/main/docs/customization.md).
|
|
205
256
|
|
|
206
|
-
|
|
257
|
+
## Size and support
|
|
207
258
|
|
|
208
|
-
|
|
259
|
+
Minified, with peers external: `/core` is 13.6 kB and the root entry is 51.2 kB.
|
|
260
|
+
`/rrule` adds 12.9 kB of its own code plus its two dependencies, `rrule-temporal`
|
|
261
|
+
and `@js-temporal/polyfill`, which install with the package. Web runs in CI on
|
|
262
|
+
every ready pull request. iOS and Android are implemented but have not been
|
|
263
|
+
verified on devices yet. Tested against Expo SDK 54, React Native 0.81, and
|
|
264
|
+
Reanimated 3.19.
|
|
209
265
|
|
|
210
|
-
##
|
|
266
|
+
## Reference
|
|
211
267
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
268
|
+
| Import | What you get |
|
|
269
|
+
| --- | --- |
|
|
270
|
+
| `react-native-roster` | `Roster`, `Schedule`, `useRoster`, `useSchedule`, the default slot components, and everything in `/core`. |
|
|
271
|
+
| `react-native-roster/core` | Types, layout, coverage, interval helpers, and window navigation. Standard JavaScript and `Intl` only. |
|
|
272
|
+
| `react-native-roster/rrule` | `expandRuleSet`, `envelopeFor`, and their caches. The only entry that imports `rrule-temporal` and `@js-temporal/polyfill`. |
|
|
273
|
+
| `react-native-roster/nativewind` | Registers the components with NativeWind. |
|
|
217
274
|
|
|
218
|
-
|
|
275
|
+
- [Customization](https://github.com/simiancraft/react-native-roster/blob/main/docs/customization.md): slots, selection, and passing your data to slot components.
|
|
276
|
+
- [Adapters](https://github.com/simiancraft/react-native-roster/blob/main/docs/adapters.md) and [recurrence](https://github.com/simiancraft/react-native-roster/blob/main/docs/recurrence.md): getting upstream data into lanes.
|
|
277
|
+
- [Timezones](https://github.com/simiancraft/react-native-roster/blob/main/docs/timezones.md): rule, lane, and view timezones; skipped and repeated hours.
|
|
278
|
+
- [Caches](https://github.com/simiancraft/react-native-roster/blob/main/docs/caches.md): keys, versions, and clearing.
|
|
279
|
+
- [Design note](https://github.com/simiancraft/react-native-roster/blob/main/docs/design.md): why intervals, and why geometry is computed before render.
|
|
280
|
+
- [Migrations](https://github.com/simiancraft/react-native-roster/blob/main/docs/migrations.md): prop renames by version.
|
|
281
|
+
- [llms.txt](https://github.com/simiancraft/react-native-roster/blob/main/llms.txt): the full contract, defaults, and edge cases in one file, written for coding agents.
|
|
219
282
|
|
|
220
|
-
|
|
221
|
-
`react-native-roster/nativewind` once at the root. Every chrome style prop has a
|
|
222
|
-
`className` twin; classes replace default paint while preserving structure.
|
|
223
|
-
Slot components use `className` on ordinary views. Without registration, class
|
|
224
|
-
props are ignored. [Setup, examples, and demo configuration](./src/nativewind/README.md#setup-and-styling)
|
|
225
|
-
show all regions, including the [showcase route](./demo/app/showcase.tsx).
|
|
283
|
+
## Contributing
|
|
226
284
|
|
|
227
|
-
|
|
285
|
+
[CONTRIBUTING.md](https://github.com/simiancraft/react-native-roster/blob/main/CONTRIBUTING.md)
|
|
286
|
+
has setup and the commit rules; merging to `main` releases. Report security
|
|
287
|
+
issues through [SECURITY.md](https://github.com/simiancraft/react-native-roster/blob/main/SECURITY.md).
|
|
288
|
+
Release history is in the [changelog](https://github.com/simiancraft/react-native-roster/blob/main/CHANGELOG.md).
|
|
228
289
|
|
|
229
|
-
|
|
230
|
-
DST transitions, editable recurrence, and replaceable components. The showcase
|
|
231
|
-
combines filtering, per-lane schedules, and attendance details. See the
|
|
232
|
-
[fixture catalog and local setup](./demo/components/gallery/README.md#run-the-gallery-and-explore-fixtures)
|
|
233
|
-
and [showcase tour](./demo/components/team-roster/README.md#showcase-tour).
|
|
290
|
+
## License
|
|
234
291
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
| Web via React Native Web | Static export and Chromium gallery checks; first-class target. |
|
|
238
|
-
| iOS | React Native implementation; device rendering and Hermes behavior not yet verified here. |
|
|
239
|
-
| Android | React Native implementation; device rendering and release performance not yet captured. |
|
|
240
|
-
| Node and Bun | Built core and adapter work without a renderer; root components require native peers or a web bundler. |
|
|
241
|
-
|
|
242
|
-
## Performance
|
|
243
|
-
|
|
244
|
-
Workload W is 200 lanes, one week, two layers, a 24-lane viewport, and
|
|
245
|
-
15-minute ticks: 63 interval rects and 7 gap rects per lane. The
|
|
246
|
-
[committed CI baseline](./test/performance/baseline.json) on GitHub Actions
|
|
247
|
-
ubuntu-latest records target-cold layout of 24 lanes at 2.070 ms and coverage of
|
|
248
|
-
all 200 lanes at 1.063 ms, gated below 16 ms everywhere and within 1.5 times the
|
|
249
|
-
baseline in CI. Minified, uncompressed size gates with peers external hold core under 15 kB and root under 52 kB. Device
|
|
250
|
-
captures and the paired comparison against other libraries are described, not
|
|
251
|
-
yet recorded, in the [performance guide](./docs/performance.md); CI timings are
|
|
252
|
-
not phone frame rates.
|
|
253
|
-
|
|
254
|
-
## Compared to
|
|
255
|
-
|
|
256
|
-
| Alternative | Choose it for | What react-native-roster adds to that model |
|
|
257
|
-
| --- | --- | --- |
|
|
258
|
-
| [react-native-calendar-kit](https://howljs.github.io/react-native-calendar-kit/) | Creation/editing gestures and pinch-to-zoom in a React Native calendar. | Layer-role coverage math and exact source sets on covered and removed spans. |
|
|
259
|
-
| [react-native-big-calendar](https://github.com/acro5piano/react-native-big-calendar) | Day, week, and month views of events. | A many-resource timeline and a single-lane schedule over the same layered interval contract. |
|
|
260
|
-
| [FullCalendar's resource timeline](https://fullcalendar.io/docs/timeline-view) | A web resource timeline with [dragging and resizing](https://fullcalendar.io/docs/event-dragging-resizing). | React Native components plus a renderer-free coverage/provenance core and explicit skipped/repeated wall-time geometry. |
|
|
261
|
-
|
|
262
|
-
These are differences in the supplied models, not claims that an alternative
|
|
263
|
-
cannot be extended. Their linked documentation describes their features; this
|
|
264
|
-
package's [contract](./src/core/types.ts), [coverage](./src/core/coverage.ts), and
|
|
265
|
-
[geometry](./docs/timezones.md) document its side. No relative performance
|
|
266
|
-
comparison has been recorded; see [performance evidence](./docs/performance.md).
|
|
267
|
-
|
|
268
|
-
## What this isn't
|
|
269
|
-
|
|
270
|
-
This is a read surface. Consumers own creation, dragging, resizing,
|
|
271
|
-
persistence, and permission checks through their own interactions. It is not a
|
|
272
|
-
month grid or an arbitrary event list, and it does not expand rules inside the
|
|
273
|
-
core. The short [design note](./docs/design.md) explains the reasoning.
|
|
274
|
-
|
|
275
|
-
## Development and reference
|
|
276
|
-
|
|
277
|
-
```sh
|
|
278
|
-
bunx playwright install chromium
|
|
279
|
-
bun run check
|
|
280
|
-
```
|
|
292
|
+
MIT, copyright 2026 Jesse Harlin (the-simian). See [LICENSE](https://github.com/simiancraft/react-native-roster/blob/main/LICENSE)
|
|
293
|
+
and [NOTICE.md](https://github.com/simiancraft/react-native-roster/blob/main/NOTICE.md).
|
|
281
294
|
|
|
282
|
-
|
|
283
|
-
the library build, the static web export, tests with coverage, knip, strict
|
|
284
|
-
publint, size-limit, and Playwright.
|
|
285
|
-
|
|
286
|
-
- [Migrations](https://github.com/simiancraft/react-native-roster/blob/main/docs/migrations.md) lists prop renames by version with before-and-after examples.
|
|
287
|
-
- [Adapter guide](./docs/adapters.md), [recurrence semantics](./docs/recurrence.md),
|
|
288
|
-
[timezones](./docs/timezones.md), [caches](./docs/caches.md), and the
|
|
289
|
-
[performance guide](./docs/performance.md).
|
|
290
|
-
- [Design note](./docs/design.md): precompute, then render geometry.
|
|
291
|
-
- Area landing pages: [core](./src/core/README.md), [Roster](./src/components/roster/README.md),
|
|
292
|
-
[Schedule](./src/components/schedule/README.md), [layers](./src/components/layers/README.md),
|
|
293
|
-
[primitives](./src/components/primitives/README.md), [rrule adapter](./src/adapters/rrule/README.md),
|
|
294
|
-
[nativewind](./src/nativewind/README.md), the [gallery](./demo/components/gallery/README.md), the
|
|
295
|
-
[showcase](./demo/components/team-roster/README.md), and the [theme](./demo/components/theme/README.md);
|
|
296
|
-
[adding an adapter](./docs/adding-an-adapter.md)
|
|
297
|
-
is the contributor recipe.
|
|
298
|
-
- [llms.txt](./llms.txt): integration instructions for agents.
|
|
299
|
-
- [CONTRIBUTING.md](./CONTRIBUTING.md) and [AGENTS.md](./AGENTS.md): contributor workflow.
|
|
300
|
-
- [CHANGELOG.md](./CHANGELOG.md): release history, maintained by semantic-release.
|
|
301
|
-
- [SECURITY.md](./SECURITY.md), [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md), and [NOTICE.md](./NOTICE.md).
|
|
302
|
-
|
|
303
|
-
MIT, copyright 2026 Jesse Harlin (the-simian). See [LICENSE](./LICENSE).
|
|
295
|
+
<p align="center"><sub>Crafted with care by <a href="https://simiancraft.com">Simiancraft</a>.</sub></p>
|
package/llms.txt
CHANGED
|
@@ -592,6 +592,26 @@ The package does not create, drag, resize, persist, or authorize intervals; it
|
|
|
592
592
|
is not a general calendar. Consumers implement their own interactions. A rule
|
|
593
593
|
editor in the gallery is an integration example, not a rule-authoring product.
|
|
594
594
|
|
|
595
|
+
## Alternatives, evidence, and sizes
|
|
596
|
+
|
|
597
|
+
Choose react-native-calendar-kit for creation and editing gestures and pinch to
|
|
598
|
+
zoom; react-native-big-calendar for day, week, and month views of titled events;
|
|
599
|
+
FullCalendar's resource timeline for a web timeline with dragging and resizing.
|
|
600
|
+
This package adds layer-role coverage math, exact source sets on covered and
|
|
601
|
+
removed spans, one lane contract for a many-lane roster and a single-lane
|
|
602
|
+
schedule, and geometry for skipped and repeated wall time. It has no creation,
|
|
603
|
+
dragging, resizing, or month grid. No relative performance comparison exists.
|
|
604
|
+
|
|
605
|
+
Measured on GitHub Actions ubuntu-latest (test/performance/baseline.json):
|
|
606
|
+
target-cold layout of 24 lanes of a 200-lane week at 15-minute ticks takes
|
|
607
|
+
2.070 ms; coverage of all 200 lanes takes 1.063 ms; both are gated below 16 ms.
|
|
608
|
+
Minified, uncompressed, peers external: /core 13.6 kB (limit 15), /rrule 12.9 kB
|
|
609
|
+
(limit 15), root 51.2 kB (limit 52), /nativewind 47.1 kB (limit 48). Web is
|
|
610
|
+
checked in Chromium on every pull request; iOS and Android device rendering,
|
|
611
|
+
Hermes behavior, and phone frame rates are not yet recorded. The demo pins Expo
|
|
612
|
+
SDK 54, React 19.1, React Native 0.81.5, LegendList 2.0.19, and Reanimated
|
|
613
|
+
3.19.5; those versions do not establish every peer-range combination.
|
|
614
|
+
|
|
595
615
|
## Working from a checkout
|
|
596
616
|
|
|
597
617
|
Use Bun 1.4.2 and Node 22 for repository development. At the repository root:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-roster",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
4
4
|
"description": "Resource timeline and schedule components for React Native and web, with layered intervals, coverage, provenance, and explicit daylight-saving handling.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|