react-native-roster 0.4.4 → 0.4.6

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.
Files changed (3) hide show
  1. package/README.md +242 -247
  2. package/llms.txt +20 -0
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,303 +1,298 @@
1
- # react-native-roster
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-4f46e5?style=for-the-badge" alt="Live demo" />
10
+ </a>
11
+ </p>
2
12
 
3
- **Resource timeline and schedule components for React Native and web, with layered intervals, coverage, provenance, and explicit daylight-saving handling.**
13
+ # react-native-roster
4
14
 
5
15
  [![npm version](https://img.shields.io/npm/v/react-native-roster?color=cb3837&logo=npm)](https://www.npmjs.com/package/react-native-roster)
6
16
  [![CI](https://github.com/simiancraft/react-native-roster/actions/workflows/ci.yml/badge.svg)](https://github.com/simiancraft/react-native-roster/actions/workflows/ci.yml)
7
17
  [![Coverage](https://img.shields.io/codecov/c/github/simiancraft/react-native-roster?logo=codecov)](https://codecov.io/github/simiancraft/react-native-roster)
8
- [![Types: included](https://img.shields.io/badge/types-included-3178c6?logo=typescript)](#contract)
9
- [![Core under 15 kB](https://img.shields.io/badge/core-%3C15%20kB-0f766e)](./.size-limit.json)
10
- [![Expo SDK 54](https://img.shields.io/badge/Expo-SDK%2054-000020?logo=expo)](./demo/package.json)
11
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
18
+ [![Types: included](https://img.shields.io/npm/types/react-native-roster?color=3178c6&logo=typescript)](https://www.npmjs.com/package/react-native-roster)
12
19
  [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/simiancraft/react-native-roster/badge)](https://securityscorecards.dev/viewer/?uri=github.com/simiancraft/react-native-roster)
20
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/simiancraft/react-native-roster/blob/main/LICENSE)
21
+
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.
13
23
 
14
- **Live demo: [explore the gallery](https://simiancraft.github.io/react-native-roster/)
15
- or [try the showcase](https://simiancraft.github.io/react-native-roster/showcase).**
24
+ ```tsx
25
+ <Roster
26
+ lanes={lanes}
27
+ windowSpec={windowSpec}
28
+ onGapPress={(rect) => console.log(rect.sources)} // [{ kind: 'date', id: 'dentist' }]
29
+ />
30
+ ```
16
31
 
17
- <!-- HERO GIF PLACEHOLDER: insert a recorded gallery or showcase tour here when available. -->
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.
18
36
 
19
- Give it lanes of absolute intervals and their sources. Get a resource timeline
20
- across many lanes, or a day-column schedule for one lane, with coverage totals
21
- and press callbacks that explain exactly which sources produced each span.
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.
22
40
 
23
- **It renders who is on, when, and why.** Press any covered or removed span and
24
- get back the rules, rows, or exclusions behind it; removed time keeps its
25
- exclusion sources. Daylight-saving transitions are drawn honestly: skipped hours
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.
41
+ `Roster` draws elapsed time, so the week the clocks change is 167 or 169 hours
42
+ wide; `Schedule` hatches the hour they skipped.
29
43
 
30
- Consumers own creation, dragging, resizing, and persistence. It ships
31
- virtualized roster lanes, replaceable components, NativeWind chrome, and a small
32
- standard JavaScript and `Intl` core with a [15 kB size gate](./.size-limit.json).
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).
33
47
 
34
- ## Install
48
+ [Browse every example](https://simiancraft.github.io/react-native-roster/) in the gallery.
35
49
 
36
- ```sh
37
- bun add react-native-roster @legendapp/list
38
- bunx expo install react-native-reanimated --bun
39
- ```
50
+ ## Quickstart
51
+
52
+ ### Install
53
+
54
+ These steps assume an Expo app.
55
+
56
+ 1. Add the package and LegendList:
57
+
58
+ ```sh
59
+ bun add react-native-roster @legendapp/list
60
+ ```
61
+
62
+ 2. Add Reanimated for your Expo SDK:
63
+
64
+ ```sh
65
+ bunx expo install react-native-reanimated --bun
66
+ ```
40
67
 
41
- For web, also install React DOM and React Native Web versions compatible with
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.
68
+ 3. For web, add React DOM, React Native Web, and Radix Popover; every web build needs all three:
48
69
 
49
- Peer ranges: React >=18.2, React Native >=0.74, LegendList >=2, Reanimated >=3.19,
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.
70
+ ```sh
71
+ bunx expo install react-dom react-native-web --bun
72
+ bun add '@radix-ui/react-popover@^1.1.23'
73
+ ```
55
74
 
56
- On Metro before React Native 0.79, enable `resolver.unstable_enablePackageExports`
57
- so the subpaths resolve. Keep one React copy in workspaces.
58
- `rrule-temporal` and `@js-temporal/polyfill` install as ordinary dependencies;
59
- only `/rrule` imports them. Import isolation does not reduce installation size.
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.
60
78
 
61
- ## Quick start: static intervals
79
+ ### Roster
62
80
 
63
- Give the component a bounded height. No adapter is needed for absolute intervals.
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 lanes: Lane[] = [{
70
- id: 'one', label: 'Lane one', layers: [{
71
- id: 'open', role: 'availability', z: 0,
72
- style: { color: '#4f9478' },
73
- intervals: [{
74
- start: Date.UTC(2024, 0, 1, 9), end: Date.UTC(2024, 0, 1, 17),
75
- sources: [{ kind: 'table', id: 'row-one' }],
76
- }],
77
- }],
78
- }];
79
-
80
- export function Example() {
81
- return <Roster lanes={lanes}
82
- style={{ height: 480, flex: undefined }}
83
- windowSpec={{ span: 'week', anchorDate: '2024-01-01', timezone: 'UTC' }}
84
- onIntervalPress={(rect, lane) => console.log(lane.id, rect.sources)} />;
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
- Press the interval to receive its `table / row-one` source. `minuteStep`
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.
122
+ Add lanes for more people. Add layers and they stack by `z`.
93
123
 
94
- ## Quick start: recurring local hours
124
+ ### Schedule
95
125
 
96
- Import `expandRuleSet` from `/rrule` to turn daily, weekly, or monthly local
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.
126
+ Hand `Schedule` that same `lane` and it draws days across and hours down. It
127
+ takes day and week windows.
102
128
 
103
- ## Contract
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
+ ```
104
142
 
105
- | Term | Meaning |
106
- | --- | --- |
107
- | roster | Shared time axis, lanes, and layers. |
108
- | lane | One person or resource row; `id`, `label`, `layers`, and optional display metadata. |
109
- | layer | Ordered intervals and gaps with `id`, `role`, `z`, and `style`. |
110
- | interval | Covered `[start, end)` in epoch milliseconds, with `sources: Source[]`. |
111
- | gap | Removed `[start, end)` with exclusion sources; survives complete subtraction. |
112
- | source | `{ kind, id, label? }`; identity is `(kind, id)`, never the label. |
113
- | window | Absolute `{ start, end }` only; span and resolution belong to the axis. |
114
- | projection | Horizontal elapsed time, or day columns with wall-clock hours. |
115
- | adapter | Consumer-side conversion from upstream data to lanes and layers. |
116
- | schedule | One lane projected into days across and hours down. |
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. |
143
+ ### Recurrence from rrule
138
144
 
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.
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`.
143
149
 
144
- ## Caches
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
+ ```
145
211
 
146
- Bump a supplied `lane.version` whenever layers change; otherwise structural layer
147
- content supplies the key. Treat returned references as read-only. Consumers own
148
- cache lifetime: clear layout, coverage, and expansion caches when discarding old
149
- windows. Coverage runs for all lanes; geometry runs for mounted lanes.
150
- [Cache keys, counters, and clearing](./docs/caches.md) explain target-warm reuse.
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.
151
216
 
152
- ## Core
217
+ ### NativeWind
153
218
 
154
- ### Interval helpers
219
+ react-native-roster supports [NativeWind](https://www.nativewind.dev/).
155
220
 
156
- `extentOf`, `intersectionOf`, and `unionOf` operate on end-exclusive absolute
157
- windows. Extents bridge gaps; unions preserve disjoint spans. See the
158
- [helper walkthrough](./src/core/README.md#interval-helper-walkthrough).
221
+ 1. Install NativeWind 4.1 or newer and finish its
222
+ [setup](https://www.nativewind.dev/docs/getting-started/installation):
159
223
 
160
- ## Navigation and interaction
224
+ ```sh
225
+ bun add 'nativewind@^4.1'
226
+ ```
161
227
 
162
- A `WindowSpec` is `{ span: 'day' | 'week' | 'month', anchorDate, timezone, wkst? }`
163
- or `{ span: 'custom', window, timezone }`. `anchorDate` is a local `YYYY-MM-DD`.
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.
228
+ 2. Register the components once, in your app's root module:
167
229
 
168
- Three zones take part in a render: the rule timezone interprets local hours in
169
- the adapter, the lane timezone is a badge, and `windowSpec.timezone` drives the
170
- projection. Changing the view zone preserves the local anchor and usually reuses
171
- expanded occurrences. Column spans, rollbacks across midnight, and pointer
172
- inversion are documented in [timezones](./docs/timezones.md).
230
+ ```tsx
231
+ import 'react-native-roster/nativewind';
232
+ ```
173
233
 
174
- Presses resolve one result: intervals before gaps, highest z first, later
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.
234
+ 3. Style with class props:
181
235
 
182
- ## Now line
236
+ ```tsx
237
+ <Roster
238
+ className="rounded-xl bg-white"
239
+ headerClassName="bg-slate-100"
240
+ laneLabelColumnClassName="bg-slate-100"
241
+ {...rest}
242
+ />
243
+ ```
183
244
 
184
- Pass controlled `now={timestamp}` to Roster; `null` (the default) draws nothing.
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.
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.
187
248
 
188
- ## Roster zones
249
+ ## Customization
189
250
 
190
- Props ending in `Component` accept component types; props ending in `Zone`
191
- accept React nodes. Define slot components at module scope to preserve state.
192
- Use exported defaults inside replacements to retain geometry and interaction.
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.
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).
196
256
 
197
- ### Selection
257
+ ## Size and support
198
258
 
199
- Add `intervalDetailComponent` for pressed-interval details. Pressing the same
200
- interval dismisses it; pressing another switches selection. Cell and gap presses
201
- also dismiss, preserving their callbacks. Web outside press and Escape dismiss.
202
- Schedule does not support selection. Custom `selectionLayout` implementations
203
- must follow the [layout and portal contract](./docs/customization.md#selection),
204
- including unique native host names and provider placement above the host.
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.
205
265
 
206
- ### Consumer data in slot components
266
+ ## Reference
207
267
 
208
- Use context with stable slot types; see the [complete recipe](./docs/customization.md#consumer-data-in-slot-components).
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. |
209
274
 
210
- ## Schedule and its zones
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.
211
282
 
212
- Pass the same lane to `<Schedule lane={lane} windowSpec={spec} />` with a bounded
213
- height. Schedule accepts day and week specs, fits columns to measured width,
214
- and reserves a 48 px gutter. `pxPerHour` defaults to 48 and must be positive and
215
- finite. Its automatic now line updates each minute. See the
216
- [example and slot table](./docs/customization.md#schedule-zones).
283
+ ## Contributing
217
284
 
218
- ## NativeWind
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).
219
289
 
220
- Install NativeWind >=4.1 and configure it for your app, then import
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).
290
+ ## License
226
291
 
227
- ## Gallery and platform support
292
+ MIT © [the-simian](https://github.com/the-simian). See [LICENSE](https://github.com/simiancraft/react-native-roster/blob/main/LICENSE).
228
293
 
229
- The gallery covers lane counts, layer precedence, coverage sorting, provenance,
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).
294
+ <sub>Looking for a React Native resource timeline, staff scheduler, shift calendar, or Gantt-style roster and landed here another way? The package name is **react-native-roster**.</sub>
234
295
 
235
- | Platform | Support and evidence |
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
- ```
296
+ <p align="center"><sub>Crafted with care by <a href="https://simiancraft.com">Simiancraft</a>.</sub></p>
281
297
 
282
- `check` runs Biome, library, test, and demo typechecks, React Compiler safety,
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).
298
+ <p align="center"><sub>Recurrence in <code>/rrule</code> runs on <a href="https://github.com/ggaabe/rrule-temporal">rrule-temporal</a> and <a href="https://github.com/js-temporal/temporal-polyfill">@js-temporal/polyfill</a>; lanes virtualize through <a href="https://github.com/LegendApp/legend-list">LegendList</a>. Every person and organization in the demo is generated. Full attributions: <a href="https://github.com/simiancraft/react-native-roster/blob/main/NOTICE.md">NOTICE.md</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.4",
3
+ "version": "0.4.6",
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",