react-native-roster 0.0.0 → 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.
Files changed (373) hide show
  1. package/NOTICE.md +29 -0
  2. package/README.md +455 -2
  3. package/dist/src/adapters/rrule/cache.d.ts +15 -0
  4. package/dist/src/adapters/rrule/cache.d.ts.map +1 -0
  5. package/dist/src/adapters/rrule/cache.js +53 -0
  6. package/dist/src/adapters/rrule/cache.js.map +1 -0
  7. package/dist/src/adapters/rrule/envelope.d.ts +3 -0
  8. package/dist/src/adapters/rrule/envelope.d.ts.map +1 -0
  9. package/dist/src/adapters/rrule/envelope.js +9 -0
  10. package/dist/src/adapters/rrule/envelope.js.map +1 -0
  11. package/dist/src/adapters/rrule/expand.d.ts +4 -0
  12. package/dist/src/adapters/rrule/expand.d.ts.map +1 -0
  13. package/dist/src/adapters/rrule/expand.js +92 -0
  14. package/dist/src/adapters/rrule/expand.js.map +1 -0
  15. package/dist/src/adapters/rrule/hash.d.ts +3 -0
  16. package/dist/src/adapters/rrule/hash.d.ts.map +1 -0
  17. package/dist/src/adapters/rrule/hash.js +15 -0
  18. package/dist/src/adapters/rrule/hash.js.map +1 -0
  19. package/dist/src/adapters/rrule/index.d.ts +5 -0
  20. package/dist/src/adapters/rrule/index.d.ts.map +1 -0
  21. package/dist/src/adapters/rrule/index.js +12 -0
  22. package/dist/src/adapters/rrule/index.js.map +1 -0
  23. package/dist/src/adapters/rrule/net.d.ts +6 -0
  24. package/dist/src/adapters/rrule/net.d.ts.map +1 -0
  25. package/dist/src/adapters/rrule/net.js +31 -0
  26. package/dist/src/adapters/rrule/net.js.map +1 -0
  27. package/dist/src/adapters/rrule/occurrences.d.ts +6 -0
  28. package/dist/src/adapters/rrule/occurrences.d.ts.map +1 -0
  29. package/dist/src/adapters/rrule/occurrences.js +252 -0
  30. package/dist/src/adapters/rrule/occurrences.js.map +1 -0
  31. package/dist/src/adapters/rrule/types.d.ts +62 -0
  32. package/dist/src/adapters/rrule/types.d.ts.map +1 -0
  33. package/dist/src/adapters/rrule/types.js +3 -0
  34. package/dist/src/adapters/rrule/types.js.map +1 -0
  35. package/dist/src/adapters/rrule/validate.d.ts +6 -0
  36. package/dist/src/adapters/rrule/validate.d.ts.map +1 -0
  37. package/dist/src/adapters/rrule/validate.js +64 -0
  38. package/dist/src/adapters/rrule/validate.js.map +1 -0
  39. package/dist/src/components/layers/layers.types.d.ts +15 -0
  40. package/dist/src/components/layers/layers.types.d.ts.map +1 -0
  41. package/dist/src/components/layers/layers.types.js +3 -0
  42. package/dist/src/components/layers/layers.types.js.map +1 -0
  43. package/dist/src/components/layers/parts/gap.d.ts +3 -0
  44. package/dist/src/components/layers/parts/gap.d.ts.map +1 -0
  45. package/dist/src/components/layers/parts/gap.js +7 -0
  46. package/dist/src/components/layers/parts/gap.js.map +1 -0
  47. package/dist/src/components/layers/parts/interval.d.ts +3 -0
  48. package/dist/src/components/layers/parts/interval.d.ts.map +1 -0
  49. package/dist/src/components/layers/parts/interval.js +22 -0
  50. package/dist/src/components/layers/parts/interval.js.map +1 -0
  51. package/dist/src/components/layers/utils/styles.d.ts +7 -0
  52. package/dist/src/components/layers/utils/styles.d.ts.map +1 -0
  53. package/dist/src/components/layers/utils/styles.js +24 -0
  54. package/dist/src/components/layers/utils/styles.js.map +1 -0
  55. package/dist/src/components/primitives/press-point.d.ts +3 -0
  56. package/dist/src/components/primitives/press-point.d.ts.map +1 -0
  57. package/dist/src/components/primitives/press-point.js +7 -0
  58. package/dist/src/components/primitives/press-point.js.map +1 -0
  59. package/dist/src/components/primitives/press-point.types.d.ts +14 -0
  60. package/dist/src/components/primitives/press-point.types.d.ts.map +1 -0
  61. package/dist/src/components/primitives/press-point.types.js +3 -0
  62. package/dist/src/components/primitives/press-point.types.js.map +1 -0
  63. package/dist/src/components/primitives/press-point.web.d.ts +3 -0
  64. package/dist/src/components/primitives/press-point.web.d.ts.map +1 -0
  65. package/dist/src/components/primitives/press-point.web.js +17 -0
  66. package/dist/src/components/primitives/press-point.web.js.map +1 -0
  67. package/dist/src/components/primitives/region-style.d.ts +9 -0
  68. package/dist/src/components/primitives/region-style.d.ts.map +1 -0
  69. package/dist/src/components/primitives/region-style.js +20 -0
  70. package/dist/src/components/primitives/region-style.js.map +1 -0
  71. package/dist/src/components/roster/body-layout.d.ts +9 -0
  72. package/dist/src/components/roster/body-layout.d.ts.map +1 -0
  73. package/dist/src/components/roster/body-layout.js +9 -0
  74. package/dist/src/components/roster/body-layout.js.map +1 -0
  75. package/dist/src/components/roster/index.d.ts +3 -0
  76. package/dist/src/components/roster/index.d.ts.map +1 -0
  77. package/dist/src/components/roster/index.js +40 -0
  78. package/dist/src/components/roster/index.js.map +1 -0
  79. package/dist/src/components/roster/lanes/interval-hover.d.ts +3 -0
  80. package/dist/src/components/roster/lanes/interval-hover.d.ts.map +1 -0
  81. package/dist/src/components/roster/lanes/interval-hover.js +7 -0
  82. package/dist/src/components/roster/lanes/interval-hover.js.map +1 -0
  83. package/dist/src/components/roster/lanes/interval-hover.types.d.ts +15 -0
  84. package/dist/src/components/roster/lanes/interval-hover.types.d.ts.map +1 -0
  85. package/dist/src/components/roster/lanes/interval-hover.types.js +3 -0
  86. package/dist/src/components/roster/lanes/interval-hover.types.js.map +1 -0
  87. package/dist/src/components/roster/lanes/interval-hover.web.d.ts +3 -0
  88. package/dist/src/components/roster/lanes/interval-hover.web.d.ts.map +1 -0
  89. package/dist/src/components/roster/lanes/interval-hover.web.js +20 -0
  90. package/dist/src/components/roster/lanes/interval-hover.web.js.map +1 -0
  91. package/dist/src/components/roster/lanes/lane.d.ts +10 -0
  92. package/dist/src/components/roster/lanes/lane.d.ts.map +1 -0
  93. package/dist/src/components/roster/lanes/lane.js +32 -0
  94. package/dist/src/components/roster/lanes/lane.js.map +1 -0
  95. package/dist/src/components/roster/lanes/parts/incomplete.d.ts +8 -0
  96. package/dist/src/components/roster/lanes/parts/incomplete.d.ts.map +1 -0
  97. package/dist/src/components/roster/lanes/parts/incomplete.js +30 -0
  98. package/dist/src/components/roster/lanes/parts/incomplete.js.map +1 -0
  99. package/dist/src/components/roster/lanes/parts/lane-label.d.ts +3 -0
  100. package/dist/src/components/roster/lanes/parts/lane-label.d.ts.map +1 -0
  101. package/dist/src/components/roster/lanes/parts/lane-label.js +18 -0
  102. package/dist/src/components/roster/lanes/parts/lane-label.js.map +1 -0
  103. package/dist/src/components/roster/layout.d.ts +17 -0
  104. package/dist/src/components/roster/layout.d.ts.map +1 -0
  105. package/dist/src/components/roster/layout.js +24 -0
  106. package/dist/src/components/roster/layout.js.map +1 -0
  107. package/dist/src/components/roster/parts/body.d.ts +7 -0
  108. package/dist/src/components/roster/parts/body.d.ts.map +1 -0
  109. package/dist/src/components/roster/parts/body.js +14 -0
  110. package/dist/src/components/roster/parts/body.js.map +1 -0
  111. package/dist/src/components/roster/parts/corner.d.ts +2 -0
  112. package/dist/src/components/roster/parts/corner.d.ts.map +1 -0
  113. package/dist/src/components/roster/parts/corner.js +7 -0
  114. package/dist/src/components/roster/parts/corner.js.map +1 -0
  115. package/dist/src/components/roster/parts/empty.d.ts +2 -0
  116. package/dist/src/components/roster/parts/empty.d.ts.map +1 -0
  117. package/dist/src/components/roster/parts/empty.js +9 -0
  118. package/dist/src/components/roster/parts/empty.js.map +1 -0
  119. package/dist/src/components/roster/parts/grid.d.ts +3 -0
  120. package/dist/src/components/roster/parts/grid.d.ts.map +1 -0
  121. package/dist/src/components/roster/parts/grid.js +16 -0
  122. package/dist/src/components/roster/parts/grid.js.map +1 -0
  123. package/dist/src/components/roster/parts/header-cell.d.ts +3 -0
  124. package/dist/src/components/roster/parts/header-cell.d.ts.map +1 -0
  125. package/dist/src/components/roster/parts/header-cell.js +9 -0
  126. package/dist/src/components/roster/parts/header-cell.js.map +1 -0
  127. package/dist/src/components/roster/parts/header.d.ts +3 -0
  128. package/dist/src/components/roster/parts/header.d.ts.map +1 -0
  129. package/dist/src/components/roster/parts/header.js +13 -0
  130. package/dist/src/components/roster/parts/header.js.map +1 -0
  131. package/dist/src/components/roster/parts/lane-label-column.d.ts +3 -0
  132. package/dist/src/components/roster/parts/lane-label-column.d.ts.map +1 -0
  133. package/dist/src/components/roster/parts/lane-label-column.js +18 -0
  134. package/dist/src/components/roster/parts/lane-label-column.js.map +1 -0
  135. package/dist/src/components/roster/parts/lane-list.d.ts +7 -0
  136. package/dist/src/components/roster/parts/lane-list.d.ts.map +1 -0
  137. package/dist/src/components/roster/parts/lane-list.js +25 -0
  138. package/dist/src/components/roster/parts/lane-list.js.map +1 -0
  139. package/dist/src/components/roster/roster.types.d.ts +145 -0
  140. package/dist/src/components/roster/roster.types.d.ts.map +1 -0
  141. package/dist/src/components/roster/roster.types.js +3 -0
  142. package/dist/src/components/roster/roster.types.js.map +1 -0
  143. package/dist/src/components/roster/use-roster.d.ts +3 -0
  144. package/dist/src/components/roster/use-roster.d.ts.map +1 -0
  145. package/dist/src/components/roster/use-roster.js +106 -0
  146. package/dist/src/components/roster/use-roster.js.map +1 -0
  147. package/dist/src/components/roster/utils/body-content-key.d.ts +3 -0
  148. package/dist/src/components/roster/utils/body-content-key.d.ts.map +1 -0
  149. package/dist/src/components/roster/utils/body-content-key.js +29 -0
  150. package/dist/src/components/roster/utils/body-content-key.js.map +1 -0
  151. package/dist/src/components/roster/utils/ticks.d.ts +4 -0
  152. package/dist/src/components/roster/utils/ticks.d.ts.map +1 -0
  153. package/dist/src/components/roster/utils/ticks.js +60 -0
  154. package/dist/src/components/roster/utils/ticks.js.map +1 -0
  155. package/dist/src/components/schedule/days/day.d.ts +20 -0
  156. package/dist/src/components/schedule/days/day.d.ts.map +1 -0
  157. package/dist/src/components/schedule/days/day.js +17 -0
  158. package/dist/src/components/schedule/days/day.js.map +1 -0
  159. package/dist/src/components/schedule/days/header-layout.d.ts +9 -0
  160. package/dist/src/components/schedule/days/header-layout.d.ts.map +1 -0
  161. package/dist/src/components/schedule/days/header-layout.js +9 -0
  162. package/dist/src/components/schedule/days/header-layout.js.map +1 -0
  163. package/dist/src/components/schedule/days/layout.d.ts +18 -0
  164. package/dist/src/components/schedule/days/layout.d.ts.map +1 -0
  165. package/dist/src/components/schedule/days/layout.js +9 -0
  166. package/dist/src/components/schedule/days/layout.js.map +1 -0
  167. package/dist/src/components/schedule/days/parts/column.d.ts +3 -0
  168. package/dist/src/components/schedule/days/parts/column.d.ts.map +1 -0
  169. package/dist/src/components/schedule/days/parts/column.js +28 -0
  170. package/dist/src/components/schedule/days/parts/column.js.map +1 -0
  171. package/dist/src/components/schedule/days/parts/day-header.d.ts +5 -0
  172. package/dist/src/components/schedule/days/parts/day-header.d.ts.map +1 -0
  173. package/dist/src/components/schedule/days/parts/day-header.js +10 -0
  174. package/dist/src/components/schedule/days/parts/day-header.js.map +1 -0
  175. package/dist/src/components/schedule/days/parts/grid.d.ts +3 -0
  176. package/dist/src/components/schedule/days/parts/grid.d.ts.map +1 -0
  177. package/dist/src/components/schedule/days/parts/grid.js +14 -0
  178. package/dist/src/components/schedule/days/parts/grid.js.map +1 -0
  179. package/dist/src/components/schedule/days/parts/now-line.d.ts +5 -0
  180. package/dist/src/components/schedule/days/parts/now-line.d.ts.map +1 -0
  181. package/dist/src/components/schedule/days/parts/now-line.js +9 -0
  182. package/dist/src/components/schedule/days/parts/now-line.js.map +1 -0
  183. package/dist/src/components/schedule/days/parts/skipped-date.d.ts +4 -0
  184. package/dist/src/components/schedule/days/parts/skipped-date.d.ts.map +1 -0
  185. package/dist/src/components/schedule/days/parts/skipped-date.js +17 -0
  186. package/dist/src/components/schedule/days/parts/skipped-date.js.map +1 -0
  187. package/dist/src/components/schedule/days/parts/transition.d.ts +3 -0
  188. package/dist/src/components/schedule/days/parts/transition.d.ts.map +1 -0
  189. package/dist/src/components/schedule/days/parts/transition.js +42 -0
  190. package/dist/src/components/schedule/days/parts/transition.js.map +1 -0
  191. package/dist/src/components/schedule/index.d.ts +3 -0
  192. package/dist/src/components/schedule/index.d.ts.map +1 -0
  193. package/dist/src/components/schedule/index.js +44 -0
  194. package/dist/src/components/schedule/index.js.map +1 -0
  195. package/dist/src/components/schedule/layout.d.ts +15 -0
  196. package/dist/src/components/schedule/layout.d.ts.map +1 -0
  197. package/dist/src/components/schedule/layout.js +10 -0
  198. package/dist/src/components/schedule/layout.js.map +1 -0
  199. package/dist/src/components/schedule/parts/gutter.d.ts +3 -0
  200. package/dist/src/components/schedule/parts/gutter.d.ts.map +1 -0
  201. package/dist/src/components/schedule/parts/gutter.js +9 -0
  202. package/dist/src/components/schedule/parts/gutter.js.map +1 -0
  203. package/dist/src/components/schedule/parts/incomplete.d.ts +6 -0
  204. package/dist/src/components/schedule/parts/incomplete.d.ts.map +1 -0
  205. package/dist/src/components/schedule/parts/incomplete.js +9 -0
  206. package/dist/src/components/schedule/parts/incomplete.js.map +1 -0
  207. package/dist/src/components/schedule/schedule.types.d.ts +109 -0
  208. package/dist/src/components/schedule/schedule.types.d.ts.map +1 -0
  209. package/dist/src/components/schedule/schedule.types.js +3 -0
  210. package/dist/src/components/schedule/schedule.types.js.map +1 -0
  211. package/dist/src/components/schedule/use-schedule-viewport.d.ts +7 -0
  212. package/dist/src/components/schedule/use-schedule-viewport.d.ts.map +1 -0
  213. package/dist/src/components/schedule/use-schedule-viewport.js +15 -0
  214. package/dist/src/components/schedule/use-schedule-viewport.js.map +1 -0
  215. package/dist/src/components/schedule/use-schedule.d.ts +3 -0
  216. package/dist/src/components/schedule/use-schedule.d.ts.map +1 -0
  217. package/dist/src/components/schedule/use-schedule.js +71 -0
  218. package/dist/src/components/schedule/use-schedule.js.map +1 -0
  219. package/dist/src/components/schedule/utils/days.d.ts +17 -0
  220. package/dist/src/components/schedule/utils/days.d.ts.map +1 -0
  221. package/dist/src/components/schedule/utils/days.js +49 -0
  222. package/dist/src/components/schedule/utils/days.js.map +1 -0
  223. package/dist/src/core/axis.d.ts +18 -0
  224. package/dist/src/core/axis.d.ts.map +1 -0
  225. package/dist/src/core/axis.js +58 -0
  226. package/dist/src/core/axis.js.map +1 -0
  227. package/dist/src/core/cache.d.ts +29 -0
  228. package/dist/src/core/cache.d.ts.map +1 -0
  229. package/dist/src/core/cache.js +33 -0
  230. package/dist/src/core/cache.js.map +1 -0
  231. package/dist/src/core/columns.d.ts +3 -0
  232. package/dist/src/core/columns.d.ts.map +1 -0
  233. package/dist/src/core/columns.js +45 -0
  234. package/dist/src/core/columns.js.map +1 -0
  235. package/dist/src/core/coverage.d.ts +3 -0
  236. package/dist/src/core/coverage.d.ts.map +1 -0
  237. package/dist/src/core/coverage.js +60 -0
  238. package/dist/src/core/coverage.js.map +1 -0
  239. package/dist/src/core/flag.d.ts +3 -0
  240. package/dist/src/core/flag.d.ts.map +1 -0
  241. package/dist/src/core/flag.js +17 -0
  242. package/dist/src/core/flag.js.map +1 -0
  243. package/dist/src/core/hash.d.ts +5 -0
  244. package/dist/src/core/hash.d.ts.map +1 -0
  245. package/dist/src/core/hash.js +29 -0
  246. package/dist/src/core/hash.js.map +1 -0
  247. package/dist/src/core/hit-test.d.ts +6 -0
  248. package/dist/src/core/hit-test.d.ts.map +1 -0
  249. package/dist/src/core/hit-test.js +23 -0
  250. package/dist/src/core/hit-test.js.map +1 -0
  251. package/dist/src/core/index.d.ts +12 -0
  252. package/dist/src/core/index.d.ts.map +1 -0
  253. package/dist/src/core/index.js +30 -0
  254. package/dist/src/core/index.js.map +1 -0
  255. package/dist/src/core/layout.d.ts +3 -0
  256. package/dist/src/core/layout.d.ts.map +1 -0
  257. package/dist/src/core/layout.js +96 -0
  258. package/dist/src/core/layout.js.map +1 -0
  259. package/dist/src/core/order.d.ts +7 -0
  260. package/dist/src/core/order.d.ts.map +1 -0
  261. package/dist/src/core/order.js +12 -0
  262. package/dist/src/core/order.js.map +1 -0
  263. package/dist/src/core/scale.d.ts +9 -0
  264. package/dist/src/core/scale.d.ts.map +1 -0
  265. package/dist/src/core/scale.js +41 -0
  266. package/dist/src/core/scale.js.map +1 -0
  267. package/dist/src/core/snap.d.ts +10 -0
  268. package/dist/src/core/snap.d.ts.map +1 -0
  269. package/dist/src/core/snap.js +43 -0
  270. package/dist/src/core/snap.js.map +1 -0
  271. package/dist/src/core/spans.d.ts +3 -0
  272. package/dist/src/core/spans.d.ts.map +1 -0
  273. package/dist/src/core/spans.js +60 -0
  274. package/dist/src/core/spans.js.map +1 -0
  275. package/dist/src/core/types.d.ts +104 -0
  276. package/dist/src/core/types.d.ts.map +1 -0
  277. package/dist/src/core/types.js +3 -0
  278. package/dist/src/core/types.js.map +1 -0
  279. package/dist/src/core/zone.d.ts +10 -0
  280. package/dist/src/core/zone.d.ts.map +1 -0
  281. package/dist/src/core/zone.js +103 -0
  282. package/dist/src/core/zone.js.map +1 -0
  283. package/dist/src/index.d.ts +29 -0
  284. package/dist/src/index.d.ts.map +1 -0
  285. package/dist/src/index.js +67 -0
  286. package/dist/src/index.js.map +1 -0
  287. package/dist/src/nativewind/index.d.ts +59 -0
  288. package/dist/src/nativewind/index.d.ts.map +1 -0
  289. package/dist/src/nativewind/index.js +24 -0
  290. package/dist/src/nativewind/index.js.map +1 -0
  291. package/docs/migrations.md +61 -0
  292. package/llms.txt +498 -0
  293. package/package.json +155 -8
  294. package/src/adapters/rrule/README.md +41 -0
  295. package/src/adapters/rrule/cache.ts +50 -0
  296. package/src/adapters/rrule/envelope.ts +7 -0
  297. package/src/adapters/rrule/expand.ts +105 -0
  298. package/src/adapters/rrule/hash.ts +13 -0
  299. package/src/adapters/rrule/index.ts +11 -0
  300. package/src/adapters/rrule/net.ts +30 -0
  301. package/src/adapters/rrule/occurrences.ts +288 -0
  302. package/src/adapters/rrule/types.ts +52 -0
  303. package/src/adapters/rrule/validate.ts +78 -0
  304. package/src/components/layers/README.md +15 -0
  305. package/src/components/layers/layers.types.ts +6 -0
  306. package/src/components/layers/parts/gap.tsx +5 -0
  307. package/src/components/layers/parts/interval.tsx +24 -0
  308. package/src/components/layers/utils/styles.ts +24 -0
  309. package/src/components/primitives/README.md +12 -0
  310. package/src/components/primitives/press-point.tsx +5 -0
  311. package/src/components/primitives/press-point.types.ts +5 -0
  312. package/src/components/primitives/press-point.web.tsx +19 -0
  313. package/src/components/primitives/region-style.ts +21 -0
  314. package/src/components/roster/README.md +29 -0
  315. package/src/components/roster/body-layout.tsx +32 -0
  316. package/src/components/roster/index.tsx +108 -0
  317. package/src/components/roster/lanes/interval-hover.tsx +5 -0
  318. package/src/components/roster/lanes/interval-hover.types.ts +11 -0
  319. package/src/components/roster/lanes/interval-hover.web.tsx +19 -0
  320. package/src/components/roster/lanes/lane.tsx +93 -0
  321. package/src/components/roster/lanes/parts/incomplete.tsx +44 -0
  322. package/src/components/roster/lanes/parts/lane-label.tsx +41 -0
  323. package/src/components/roster/layout.tsx +68 -0
  324. package/src/components/roster/parts/body.tsx +24 -0
  325. package/src/components/roster/parts/corner.tsx +3 -0
  326. package/src/components/roster/parts/empty.tsx +5 -0
  327. package/src/components/roster/parts/grid.tsx +26 -0
  328. package/src/components/roster/parts/header-cell.tsx +10 -0
  329. package/src/components/roster/parts/header.tsx +23 -0
  330. package/src/components/roster/parts/lane-label-column.tsx +31 -0
  331. package/src/components/roster/parts/lane-list.tsx +74 -0
  332. package/src/components/roster/roster.types.ts +156 -0
  333. package/src/components/roster/use-roster.ts +133 -0
  334. package/src/components/roster/utils/body-content-key.ts +46 -0
  335. package/src/components/roster/utils/ticks.ts +62 -0
  336. package/src/components/schedule/README.md +23 -0
  337. package/src/components/schedule/days/day.tsx +89 -0
  338. package/src/components/schedule/days/header-layout.tsx +12 -0
  339. package/src/components/schedule/days/layout.tsx +38 -0
  340. package/src/components/schedule/days/parts/column.tsx +59 -0
  341. package/src/components/schedule/days/parts/day-header.tsx +18 -0
  342. package/src/components/schedule/days/parts/grid.tsx +21 -0
  343. package/src/components/schedule/days/parts/now-line.tsx +11 -0
  344. package/src/components/schedule/days/parts/skipped-date.tsx +22 -0
  345. package/src/components/schedule/days/parts/transition.tsx +70 -0
  346. package/src/components/schedule/index.tsx +108 -0
  347. package/src/components/schedule/layout.tsx +42 -0
  348. package/src/components/schedule/parts/gutter.tsx +16 -0
  349. package/src/components/schedule/parts/incomplete.tsx +13 -0
  350. package/src/components/schedule/schedule.types.ts +103 -0
  351. package/src/components/schedule/use-schedule-viewport.ts +13 -0
  352. package/src/components/schedule/use-schedule.ts +75 -0
  353. package/src/components/schedule/utils/days.ts +54 -0
  354. package/src/core/README.md +25 -0
  355. package/src/core/axis.ts +58 -0
  356. package/src/core/cache.ts +38 -0
  357. package/src/core/columns.ts +49 -0
  358. package/src/core/coverage.ts +56 -0
  359. package/src/core/flag.ts +14 -0
  360. package/src/core/hash.ts +28 -0
  361. package/src/core/hit-test.ts +27 -0
  362. package/src/core/index.ts +34 -0
  363. package/src/core/layout.ts +101 -0
  364. package/src/core/order.ts +18 -0
  365. package/src/core/scale.ts +45 -0
  366. package/src/core/snap.ts +52 -0
  367. package/src/core/spans.ts +58 -0
  368. package/src/core/types.ts +92 -0
  369. package/src/core/zone.ts +100 -0
  370. package/src/index.ts +52 -0
  371. package/src/nativewind/README.md +14 -0
  372. package/src/nativewind/index.ts +30 -0
  373. package/index.js +0 -4
package/NOTICE.md ADDED
@@ -0,0 +1,29 @@
1
+ # Notices and attributions
2
+
3
+ react-native-roster uses the MIT license; see [LICENSE](./LICENSE).
4
+
5
+ ## Adapter dependencies
6
+
7
+ - **rrule-temporal**, ggaabe, MIT: <https://github.com/ggaabe/rrule-temporal>.
8
+ Version 1.6.0 supplies the recurrence engine.
9
+ - **@js-temporal/polyfill**, Temporal polyfill contributors, ISC:
10
+ <https://github.com/js-temporal/temporal-polyfill>. Version 0.5.1 supplies
11
+ the adapter's absolute-time conversion.
12
+
13
+ These dependencies are imported only through `./rrule`; root and core consumers
14
+ do not load them. They are installed as dependencies and are not bundled.
15
+
16
+ ## Rendering peer
17
+
18
+ - **@legendapp/list**, Legend, MIT: <https://github.com/LegendApp/legend-list>.
19
+ A peer dependency, with 2.0.19 used in development; Roster uses it to virtualize lanes.
20
+
21
+ ## Predecessor
22
+
23
+ [react-big-scheduler](https://github.com/StephenChou1017/react-big-scheduler),
24
+ Stephen Chou, is the predecessor this package replaces for the roster read surface.
25
+ react-native-roster is an independent implementation, not a fork; no predecessor
26
+ source is copied into this package.
27
+
28
+ Retain upstream licenses when adding third-party source or assets. Add new
29
+ attributions here in the same change. References do not imply endorsement.
package/README.md CHANGED
@@ -1,5 +1,458 @@
1
1
  # react-native-roster
2
2
 
3
- Layered intervals with provenance across lanes and a shared time axis for React Native.
3
+ **Lanes, layers, and sources on one time axis.**
4
4
 
5
- This is a placeholder release that reserves the package name; it contains no usable code. The first usable version will be published from https://github.com/simiancraft/react-native-roster. A live demo runs at https://simiancraft.github.io/react-native-roster/.
5
+ [![Gallery](https://img.shields.io/badge/▶%20Gallery-lanes%2C%20layers%2C%20and%20sources-4f46e5?style=for-the-badge)](https://simiancraft.github.io/react-native-roster/)
6
+
7
+ [![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)
8
+ [![Coverage](https://img.shields.io/codecov/c/github/simiancraft/react-native-roster?logo=codecov)](https://codecov.io/github/simiancraft/react-native-roster)
9
+ [![Types: included](https://img.shields.io/badge/types-included-3178c6?logo=typescript)](#contract)
10
+ [![Core under 15 kB](https://img.shields.io/badge/core-%3C15%20kB-0f766e)](./.size-limit.json)
11
+ [![Expo SDK 54](https://img.shields.io/badge/Expo-SDK%2054-000020?logo=expo)](./demo/package.json)
12
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
13
+ [![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)
14
+
15
+ A read surface for React Native and web that renders who is on, when, and why.
16
+ Feed it absolute intervals with provenance; press any filled or removed span and
17
+ get back the exact rules, rows, or exclusions that produced it. Daylight-saving
18
+ transitions are drawn honestly: skipped hours are hatched, repeated hours appear
19
+ twice. `Roster` stacks many lanes horizontally; `Schedule` opens one lane as a
20
+ week of wall-clock columns. The core is standard JavaScript plus `Intl`, under
21
+ 15 kB, and recurrence expansion is an optional subpath.
22
+
23
+ It is not a calendar, a booking system, or a drag-to-create editor; you own
24
+ mutations and persistence. If you want an event grid with gestures, an event
25
+ calendar library is the better fit. If you need to explain coverage across two
26
+ hundred people without lying about time zones, this is the one.
27
+
28
+ - **Provenance on every span.** Intervals and gaps carry `sources`; presses
29
+ return the winning span's `(kind, id)` list, including the exclusion that
30
+ removed time.
31
+ - **Honest time.** Elapsed-time roster weeks are 167 or 169 hours wide across a
32
+ transition; schedule columns hatch skipped hours and draw repeated hours twice.
33
+ - **Layers with roles.** `availability`, `booking`, and `custom` layers stack by
34
+ z-order; coverage math unions availability, subtracts bookings, and sorts.
35
+ - **Recurrence adapter.** Weekly, daily, and monthly rules with exclusions,
36
+ caps, and content-keyed caches, kept out of the core.
37
+ - **Zones everywhere.** Input-bearing regions accept component types, singletons accept nodes, and
38
+ every chrome region takes `style` plus a NativeWind `className` twin.
39
+ - **Virtualized and measured.** Geometry runs only for mounted lanes; CI gates
40
+ layout, coverage, bundle size, and browser action budgets.
41
+
42
+ ## Install
43
+
44
+ ```sh
45
+ bun add react-native-roster @legendapp/list
46
+ bunx expo install react-native-reanimated --bun
47
+ ```
48
+
49
+ To try an unreleased checkout instead, pack it with `bun pm pack --destination
50
+ .cache` and `bun add` the printed tarball path. Keep React, React Native, and
51
+ Expo aligned with your SDK; the
52
+ tested demo uses Expo SDK 54, React 19.1, React Native 0.81.5, LegendList
53
+ 2.0.19, and Reanimated 3.19.5. Web also needs React DOM and React Native Web.
54
+ Peer ranges are React 18.2+, React Native 0.74+, and LegendList 2+; Reanimated
55
+ is optional for core-only use and required by `Roster`. On Metro before React
56
+ Native 0.79, enable `resolver.unstable_enablePackageExports` so the `/core`,
57
+ `/rrule`, and `/nativewind` subpaths resolve.
58
+
59
+ ## Quick start: static intervals
60
+
61
+ Twenty lines, no adapter. Give the component a bounded height.
62
+
63
+ ```tsx
64
+ import { Roster } from 'react-native-roster';
65
+ import type { Lane } from 'react-native-roster/core';
66
+
67
+ const lanes: Lane[] = [{
68
+ id: 'one', label: 'Lane one', layers: [{
69
+ id: 'open', role: 'availability', z: 0,
70
+ style: { color: '#4f9478' },
71
+ intervals: [{
72
+ start: Date.UTC(2024, 0, 1, 9), end: Date.UTC(2024, 0, 1, 17),
73
+ sources: [{ kind: 'table', id: 'row-one' }],
74
+ }],
75
+ }],
76
+ }];
77
+
78
+ export function Example() {
79
+ return <Roster lanes={lanes}
80
+ style={{ height: 480, flex: undefined }}
81
+ windowSpec={{ span: 'week', anchorDate: '2024-01-01', timezone: 'UTC' }}
82
+ onIntervalPress={(rect, lane) => console.log(lane.id, rect.sources)} />;
83
+ }
84
+ ```
85
+
86
+ Press the interval to receive its `table / row-one` source. `minuteStep`
87
+ defaults to 60 and accepts any positive divisor of 60; it changes ticks and
88
+ empty-space snapping, never interval boundaries. `rowHeight` defaults to 48 and
89
+ `pxPerMinute` to a minimum of 0.5, growing to fit wider viewports.
90
+
91
+ ## Quick start: recurring local hours
92
+
93
+ ```tsx
94
+ import { Roster } from 'react-native-roster';
95
+ import type { Lane, WindowSpec } from 'react-native-roster/core';
96
+ import { windowFor } from 'react-native-roster/core';
97
+ import { expandRuleSet } from 'react-native-roster/rrule';
98
+ import type { RuleSet } from 'react-native-roster/rrule';
99
+
100
+ const spec: WindowSpec = {
101
+ span: 'week', anchorDate: '2024-01-01', timezone: 'America/Chicago',
102
+ };
103
+ const set: RuleSet = {
104
+ rules: [{
105
+ id: 'weekday-hours', kind: 'include', frequency: 'WEEKLY',
106
+ dtstart: '2024-01-01', byweekday: [0, 1, 2, 3, 4],
107
+ hourstart: 9, hourend: 17, timezone: 'America/Chicago',
108
+ }],
109
+ dates: [{
110
+ id: 'closed', kind: 'exclude', date: '2024-01-02',
111
+ timezone: 'America/Chicago', note: 'Closed all day',
112
+ }],
113
+ };
114
+ const result = expandRuleSet(set, windowFor(spec));
115
+ const lane: Lane = {
116
+ id: 'one', label: 'Lane one', timezone: 'America/Chicago',
117
+ complete: result.complete,
118
+ layers: [{
119
+ id: 'open', role: 'availability', z: 0, style: { color: '#4f9478' },
120
+ intervals: result.intervals, gaps: result.gaps,
121
+ }],
122
+ };
123
+
124
+ export function RecurringExample() {
125
+ return <Roster lanes={[lane]} windowSpec={spec}
126
+ style={{ height: 480, flex: undefined }}
127
+ onGapPress={(rect) => console.log(rect.sources)} />;
128
+ }
129
+ ```
130
+
131
+ Tuesday is a gap carrying the dated exclusion; the other weekdays carry the
132
+ include rule. Expand once per `windowFor(spec)` in your screen's hook, never
133
+ inside a slot component; content-keyed caches reuse unchanged occurrences. The
134
+ [adapter guide](./docs/adapters.md) covers validation, caps, cache lifetime,
135
+ and mapping tables or feeds without a recurrence dependency;
136
+ [recurrence semantics](./docs/recurrence.md) records the exact anchor, UNTIL,
137
+ COUNT, and BYSETPOS rules.
138
+
139
+ ## Contract
140
+
141
+ | Term | Meaning |
142
+ | --- | --- |
143
+ | roster | Shared time axis, lanes, and layers. |
144
+ | lane | One person or resource row; `id`, `label`, `layers`, and optional display metadata. |
145
+ | layer | Ordered intervals and gaps with `id`, `role`, `z`, and `style`. |
146
+ | interval | Covered `[start, end)` in epoch milliseconds, with `sources: Source[]`. |
147
+ | gap | Removed `[start, end)` with exclusion sources; survives complete subtraction. |
148
+ | source | `{ kind, id, label? }`; identity is `(kind, id)`, never the label. |
149
+ | window | Absolute `{ start, end }` only; span and resolution belong to the axis. |
150
+ | projection | Horizontal elapsed time, or day columns with wall-clock hours. |
151
+ | adapter | Consumer-side conversion from upstream data to lanes and layers. |
152
+ | schedule | One lane projected into days across and hours down. |
153
+
154
+ `LayerRole` is `'availability' | 'booking' | 'custom'`. The first two feed
155
+ coverage math (`availabilityMinutes`, `bookingMinutes`, and
156
+ `availabilityMinusBookingMinutes`); use `custom` for anything else. `LayerStyle`
157
+ is `{ color, highlightColor?, inset?, opacity? }`; the engine applies inset on
158
+ the cross axis and returns final bounds for drawing and hit testing.
159
+
160
+ `Lane` also accepts optional data-quality fields: `version` (bump when layers
161
+ change), `timezone` (a badge), `flag: 'never-set'` for lanes you know were never
162
+ configured, `complete: false` when an adapter cap truncated the result, and
163
+ `meta` for your own display data. The notices default to "No availability set"
164
+ and "Availability may be incomplete"; both are localizable props.
165
+
166
+ ## Entry points
167
+
168
+ | Import | Shipped surface |
169
+ | --- | --- |
170
+ | `react-native-roster` | `Roster`, `Schedule`, `useRoster`, `useSchedule`, slot components, and all core exports. |
171
+ | `react-native-roster/core` | Types, `layoutLane`, `coverageFor`, `flagFor`, axis helpers, comparators, and counters. Standard JavaScript and `Intl` only. |
172
+ | `react-native-roster/rrule` | `expandRuleSet`, `envelopeFor`, types, and expansion counters and caches. Uses pinned `rrule-temporal` and `@js-temporal/polyfill`. |
173
+ | `react-native-roster/nativewind` | Registers `Roster` and `Schedule` with NativeWind so their `className` props resolve; re-exports the registered components. |
174
+
175
+ Root and core never import recurrence dependencies. Metro selects TypeScript
176
+ source through the `react-native` export condition; other bundlers select
177
+ emitted CommonJS with declarations in `dist/src`. The emit is CommonJS, so
178
+ importing one function from the root costs the whole root bundle; import from
179
+ `/core` for a core-only bundle. Cache keys, counters, and clearing are in
180
+ [caches](./docs/caches.md).
181
+
182
+ ## Navigation and interaction
183
+
184
+ A `WindowSpec` is `{ span: 'day' | 'week' | 'month', anchorDate, timezone, wkst? }`
185
+ or `{ span: 'custom', window, timezone }`. `anchorDate` is a local `YYYY-MM-DD`.
186
+ `windowFor`, `prev`, `next`, and `today` handle bounds and navigation; weeks
187
+ start Monday (weekday 0) unless `wkst` says otherwise. Components accept
188
+ `onNavigate` but ship no toolbar; your controls update the controlled spec.
189
+
190
+ Three zones take part in a render: the rule timezone interprets local hours in
191
+ the adapter, the lane timezone is a badge, and `windowSpec.timezone` drives the
192
+ projection. Changing the view zone preserves the local anchor and usually reuses
193
+ expanded occurrences. Column spans, rollbacks across midnight, and pointer
194
+ inversion are documented in [timezones](./docs/timezones.md).
195
+
196
+ Presses resolve one result: intervals before gaps, highest z first, later
197
+ layers winning equal-z ties. `onIntervalPress(rect, lane)` and
198
+ `onGapPress(rect, lane)` return the contributing sources; `onCellPress(lane,
199
+ time)` returns snapped absolute time for empty space. `onIntervalHover` is
200
+ Roster's web-only pointer callback. `highlightSource={{ kind, id }}` applies
201
+ `highlightColor` to every matching rect without new geometry. Sorting defaults
202
+ to `byLabel`; `byCoverage({ measure })` sorts by coverage descending.
203
+
204
+ ## Roster zones
205
+
206
+ Props ending in `Component` accept `ComponentType<Input>` and are mounted by React.
207
+ Props ending in `Zone` accept `ReactNode`. Define components at module scope so
208
+ state survives rerenders; hooks and class components are supported. Compose the
209
+ exported default components inside replacements to retain scrolling, geometry,
210
+ or press behavior. Pass `null` to a node slot to suppress its default.
211
+
212
+ | Slot | Component inputs or node | Default and behavior |
213
+ | --- | --- | --- |
214
+ | `emptyZone` | `ReactNode` | `RosterEmpty`: No lanes. |
215
+ | `cornerZone` | `ReactNode` | `RosterCorner`: nothing; the cell above the labels, `laneLabelWidth` wide. |
216
+ | `laneLabelComponent` | `lane`, `flag`, `complete`, `viewTimezone`, localized labels | `RosterLaneLabel`: label, differing IANA zone badge, and notices. |
217
+ | `headerCellComponent` | `HeaderCellInput` (`tick`) | `RosterHeaderCell`: tick label. |
218
+ | `intervalComponent` | `rect`, `layer`, `lane`, `highlighted` | `RosterInterval`: positioned colored rect, with final inset bounds. |
219
+ | `gapComponent` | `rect`, `layer`, `lane` | `RosterGap`: no visible content; the row supplies pressable bounds. |
220
+ | `gridComponent` | `ticks`, `contentWidth` | `RosterGrid`: one hairline per tick behind every lane. |
221
+ | `headerComponent` | `ticks`, `projection`, `scroll`, `contentWidth`, `headerCellComponent` | `RosterHeader`: frozen header following horizontal offset. |
222
+ | `laneLabelColumnComponent` | `labels`, `projection`, `scroll`, `laneLabelComponent` | `RosterLaneLabelColumn`: frozen labels following vertical offset. |
223
+ | `bodyComponent` | Ordered `lanes`, `window`, `geometryFor`, `projection`, `scroll`, `press`, `ticks`, `viewport`, `contentWidth`, highlight and hover, incomplete label, and rect components | `RosterBody`: virtualized lanes. |
224
+
225
+ `RosterBody` composes `RosterBodyLayout`, which arranges `gridZone` and `listZone`
226
+ nodes with scroll wiring, and `RosterLaneList`, which owns LegendList and its
227
+ per-lane callback. The body waits for viewport measurement before mounting the list.
228
+
229
+ ```tsx
230
+ import type { LaneLabelInput } from 'react-native-roster';
231
+ import { Text } from 'react-native';
232
+
233
+ function LaneLabel({ lane }: LaneLabelInput) {
234
+ return <Text>{lane.label}</Text>;
235
+ }
236
+
237
+ <Roster lanes={lanes} windowSpec={windowSpec}
238
+ laneLabelComponent={LaneLabel} cornerZone={<Text>People</Text>} />
239
+ ```
240
+
241
+ A custom interval component positions at `rect.x/y`, uses `rect.width/height/z`,
242
+ and sets `pointerEvents="none"` so the parent hit-test walk owns presses. Gap
243
+ fillers are already inside positioned pressables. `useRoster` exposes ordered
244
+ lanes, coverage, lane state, ticks, `geometryFor`, shared scrolling, `press`,
245
+ viewport measurement, navigation, and status for fully custom layouts.
246
+
247
+ Chrome regions take style props: `style`, `headerStyle` (the 40 px header row
248
+ holding the corner and ticks), `laneLabelColumnStyle`, and `bodyStyle`.
249
+ `laneLabelWidth` sizes the corner and label column, default 180. Each style
250
+ prop has a `className` twin; see [NativeWind](#nativewind).
251
+
252
+ ### Consumer data in slot components
253
+
254
+ For consumer data beyond a slot's Input, mount a React context provider above
255
+ `Roster` and read it with `useContext` in a module-scope slot component. This
256
+ example supplies density and locale while keeping the header cell type stable:
257
+
258
+ ```tsx
259
+ import { createContext, useContext } from 'react';
260
+ import { Text } from 'react-native';
261
+ import type { HeaderCellInput, RosterProps } from 'react-native-roster';
262
+ import { Roster } from 'react-native-roster';
263
+
264
+ type SlotPreferences = {
265
+ density: 'compact' | 'comfortable';
266
+ locale: string;
267
+ };
268
+
269
+ const SlotPreferencesContext = createContext<SlotPreferences>({
270
+ density: 'comfortable',
271
+ locale: 'en-US',
272
+ });
273
+
274
+ function LocalizedHeaderCell({ tick }: HeaderCellInput) {
275
+ const { density, locale } = useContext(SlotPreferencesContext);
276
+ const label = new Intl.DateTimeFormat(locale, {
277
+ timeZone: 'UTC',
278
+ ...(tick.kind === 'day'
279
+ ? { month: 'short', day: 'numeric' }
280
+ : { hour: 'numeric', minute: '2-digit' }),
281
+ }).format(tick.time);
282
+
283
+ return (
284
+ <Text numberOfLines={1} style={{ padding: density === 'compact' ? 2 : 6 }}>
285
+ {label}
286
+ </Text>
287
+ );
288
+ }
289
+
290
+ export function ConsumerRoster({ lanes }: Pick<RosterProps, 'lanes'>) {
291
+ return (
292
+ <SlotPreferencesContext.Provider value={{ density: 'compact', locale: 'en-GB' }}>
293
+ <Roster
294
+ lanes={lanes}
295
+ windowSpec={{ span: 'week', anchorDate: '2024-01-01', timezone: 'UTC' }}
296
+ style={{ height: 480, flex: undefined }}
297
+ headerCellComponent={LocalizedHeaderCell}
298
+ />
299
+ </SlotPreferencesContext.Provider>
300
+ );
301
+ }
302
+ ```
303
+
304
+ An inline closure or a memoized factory that returns a new component per render
305
+ is not the recommended path. A new interval component type remounts every interval
306
+ and defeats the body's content key. Context supplies changing consumer data while
307
+ preserving the module-scope component type.
308
+
309
+ ## Schedule and its zones
310
+
311
+ ```tsx
312
+ import { Schedule } from 'react-native-roster';
313
+ import type { Lane } from 'react-native-roster/core';
314
+
315
+ export function Week({ lane }: { lane: Lane }) {
316
+ return <Schedule lane={lane} minuteStep={60}
317
+ style={{ height: 600, flex: undefined }}
318
+ windowSpec={{ span: 'week', anchorDate: '2024-01-01', timezone: 'UTC' }}
319
+ onIntervalPress={(rect) => console.log(rect.sources)} />;
320
+ }
321
+ ```
322
+
323
+ Only day and week specs are accepted. The component measures its width,
324
+ reserves a 48 px gutter, and fits the day columns without horizontal scrolling.
325
+ `pxPerHour` defaults to 48. Chrome takes `style`, `headerStyle`, `gutterStyle`,
326
+ and `daysStyle`, each with a `className` twin.
327
+
328
+ | Slot | Component inputs or node | Default and behavior |
329
+ | --- | --- | --- |
330
+ | `gutterComponent` | `hours`, `pxPerHour` | `ScheduleGutter`: 24 frozen hour labels. |
331
+ | `gridComponent` | `hours`, `pxPerHour` | `ScheduleGrid`: 24 bordered hour bands behind each day's rects. |
332
+ | `dayHeaderComponent` | `day` | `ScheduleDayHeader`: weekday, localDate, and transition badge. |
333
+ | `skippedDateComponent` | `localDate` | `ScheduleSkippedDate`: zero-width header marker for a wholly skipped date. |
334
+ | `columnComponent` | `day`, `rects`, `gapRects`, `lane`, `highlightSource`, `press`, interval and gap components | `ScheduleColumn`: final rect bounds in layer order. |
335
+ | `transitionComponent` | `day`, `transition`, `y`, `dividerY`, `height`, `width` | `ScheduleTransition`: skipped-time hatch, or repeat divider and again label. |
336
+ | `nowLineComponent` | `y`, `column` | `ScheduleNowLine`: line in the current day's column, updated each minute. |
337
+ | `intervalComponent`, `gapComponent` | Same inputs as Roster | Shared `RosterInterval` and `RosterGap`. |
338
+ | `incompleteComponent` | `lane`, `label` | `ScheduleIncomplete`: notice above the grid when the lane is incomplete. |
339
+
340
+ ## NativeWind
341
+
342
+ `className` is a first-class prop on `Roster` and `Schedule`, alongside `style`.
343
+ Every chrome style prop has a class twin: `className`, `headerClassName`,
344
+ `laneLabelColumnClassName`, and `bodyClassName` on Roster; `className`,
345
+ `headerClassName`, `gutterClassName`, and `daysClassName` on Schedule. A class
346
+ on a region replaces that region's default paint (background and border colors)
347
+ while its structure (size, flex, overflow) stays. This is deliberate: React
348
+ Native Web renders object styles inline, and inline paint would otherwise beat
349
+ any class.
350
+
351
+ Set up NativeWind 4 as usual (the demo's [babel.config.js](./demo/babel.config.js),
352
+ [metro.config.js](./demo/metro.config.js), [tailwind.config.js](./demo/tailwind.config.js),
353
+ and [global.css](./demo/global.css) are a working reference), add the optional
354
+ peer with `bun add nativewind`, and register the components once at your app
355
+ root:
356
+
357
+ ```tsx
358
+ // app/_layout.tsx
359
+ import '../global.css';
360
+ import 'react-native-roster/nativewind';
361
+ ```
362
+
363
+ ```tsx
364
+ import { Roster } from 'react-native-roster';
365
+
366
+ <Roster
367
+ lanes={lanes}
368
+ windowSpec={windowSpec}
369
+ className="flex-1 rounded-xl bg-background"
370
+ headerClassName="border-b border-border bg-card"
371
+ laneLabelColumnClassName="border-r border-border bg-card"
372
+ laneLabelWidth={220}
373
+ />
374
+ ```
375
+
376
+ Slot components are ordinary React Native views, so a custom `laneLabelComponent` or
377
+ `intervalComponent` uses `className` on `View` and `Text` directly. Without the
378
+ entry point, class props are ignored and style props still work. The entry
379
+ point is the package's only module with side effects and is listed in
380
+ `sideEffects`. The [showcase route](./demo/app/showcase.tsx) styles every region
381
+ and zone this way in light and dark palettes.
382
+
383
+ ## Gallery and platform support
384
+
385
+ The [Expo gallery](https://simiancraft.github.io/react-native-roster/) is the
386
+ demo; there is no Storybook. Run it locally with `bun install --frozen-lockfile`,
387
+ `bun run build`, and `bun run demo:web` (add `EXPO_OFFLINE=1` in a restricted
388
+ network). The home page lists every fixture route:
389
+
390
+ - Empty, one, 20, and 200 lanes; inset layers; default and replaced zones.
391
+ - Day, week, and month routes at 15, 30, and 60-minute steps.
392
+ - Full-day gaps, lane flags, equal-z precedence, highlight, coverage sorting,
393
+ and incomplete expansion.
394
+ - Chicago DST weeks, mixed rule, lane, and view zones, and editable adapter routes.
395
+ - Schedule empty, layered, excluded, spring, fall, Lord Howe, Apia, incomplete,
396
+ midnight, side-by-side projections, and every zone.
397
+ - The showcase at `/showcase`: twelve generated people across seven zones,
398
+ weekly hours and exclusions from the recurrence adapter, booked events, day
399
+ and week spans, sorting, filtering, and a per-person Schedule, styled with
400
+ NativeWind class props and slot components, with a sun and moon theme toggle.
401
+ Names, titles, and the organization come from `@faker-js/faker` with a fixed
402
+ seed; any resemblance to real people is coincidental.
403
+
404
+ | Platform | Support and evidence |
405
+ | --- | --- |
406
+ | Web via React Native Web | Static export and Chromium gallery checks; first-class target. |
407
+ | iOS | React Native implementation; device rendering and Hermes behavior not yet verified here. |
408
+ | Android | React Native implementation; device rendering and release performance not yet captured. |
409
+ | Node and Bun | Built core and adapter work without a renderer; root components require native peers or a web bundler. |
410
+
411
+ ## Performance
412
+
413
+ Workload W is 200 lanes, one week, two layers, a 24-lane viewport, and
414
+ 15-minute ticks: 63 interval rects and 7 gap rects per lane. The
415
+ [committed CI baseline](./test/performance/baseline.json) on GitHub Actions
416
+ ubuntu-latest records target-cold layout of 24 lanes at 2.070 ms and coverage of
417
+ all 200 lanes at 1.063 ms, gated below 16 ms everywhere and within 1.5 times the
418
+ baseline in CI. Size gates hold core under 15 kB and root under 44 kB. Device
419
+ captures and the paired comparison against other libraries are described, not
420
+ yet recorded, in the [performance guide](./docs/performance.md); CI timings are
421
+ not phone frame rates.
422
+
423
+ ## What this isn't
424
+
425
+ This is a read surface. Consumers own creation, dragging, resizing,
426
+ persistence, and permission checks through their own interactions. It is not a
427
+ month grid or an arbitrary event list, and it does not expand rules inside the
428
+ core. The short [design note](./docs/design.md) explains the reasoning.
429
+
430
+ ## Development and reference
431
+
432
+ ```sh
433
+ bunx playwright install chromium
434
+ bun run check
435
+ ```
436
+
437
+ `check` runs Biome, library, test, and demo typechecks, React Compiler safety,
438
+ the library build, the static web export, tests with coverage, knip, strict
439
+ publint, size-limit, and Playwright.
440
+
441
+ - [Migrations](https://github.com/simiancraft/react-native-roster/blob/main/docs/migrations.md) lists prop renames by version with before-and-after examples.
442
+ - [Adapter guide](./docs/adapters.md), [recurrence semantics](./docs/recurrence.md),
443
+ [timezones](./docs/timezones.md), [caches](./docs/caches.md), and the
444
+ [performance guide](./docs/performance.md).
445
+ - [Design note](./docs/design.md): precompute, then render geometry.
446
+ - Area landing pages: [core](./src/core/README.md), [Roster](./src/components/roster/README.md),
447
+ [Schedule](./src/components/schedule/README.md), [layers](./src/components/layers/README.md),
448
+ [primitives](./src/components/primitives/README.md), [rrule adapter](./src/adapters/rrule/README.md),
449
+ [nativewind](./src/nativewind/README.md), the [gallery](./demo/components/gallery/README.md), and the
450
+ [showcase](./demo/components/team-roster/README.md), and the [theme](./demo/components/theme/README.md);
451
+ [adding an adapter](./docs/adding-an-adapter.md)
452
+ is the contributor recipe.
453
+ - [llms.txt](./llms.txt): integration instructions for agents.
454
+ - [CONTRIBUTING.md](./CONTRIBUTING.md) and [AGENTS.md](./AGENTS.md): contributor workflow.
455
+ - [CHANGELOG.md](./CHANGELOG.md): release history, maintained by semantic-release.
456
+ - [SECURITY.md](./SECURITY.md), [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md), and [NOTICE.md](./NOTICE.md).
457
+
458
+ MIT, copyright 2026 Jesse Harlin (the-simian). See [LICENSE](./LICENSE).
@@ -0,0 +1,15 @@
1
+ import type { Window } from '../../core';
2
+ import type { ExpandStats } from './types';
3
+ export type Occurrences = {
4
+ spans: Window[];
5
+ capped: boolean;
6
+ };
7
+ export declare const occurrences: Map<string, Occurrences>;
8
+ export declare function touch<T>(cache: Map<string, T>, key: string, value: T): T;
9
+ export declare function trim<T>(cache: Map<string, T>, limit: number): void;
10
+ export declare function retainedEnvelope(window: Window, limit: number): Window;
11
+ export declare function recordStats(stats: ExpandStats): void;
12
+ export declare function expandStats(): ExpandStats;
13
+ export declare function resetExpandStats(): void;
14
+ export declare function clearExpandCache(): void;
15
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../../src/adapters/rrule/cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,MAAM,MAAM,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAC/D,eAAO,MAAM,WAAW,0BAAiC,CAAC;AAM1D,wBAAgB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAIxE;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAElE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAWtE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAEpD;AAED,wBAAgB,WAAW,IAAI,WAAW,CAEzC;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAGvC"}
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.occurrences = void 0;
4
+ exports.touch = touch;
5
+ exports.trim = trim;
6
+ exports.retainedEnvelope = retainedEnvelope;
7
+ exports.recordStats = recordStats;
8
+ exports.expandStats = expandStats;
9
+ exports.resetExpandStats = resetExpandStats;
10
+ exports.clearExpandCache = clearExpandCache;
11
+ const envelope_1 = require("./envelope");
12
+ exports.occurrences = new Map();
13
+ const envelopes = new Map();
14
+ const counts = { rules: 0, dates: 0, expanded: 0, cacheHits: 0, cacheMisses: 0 };
15
+ // Map insertion order is the LRU: reads move entries to the newest end.
16
+ // Envelope retention selects bounds only; no netted result is retained.
17
+ function touch(cache, key, value) {
18
+ cache.delete(key);
19
+ cache.set(key, value);
20
+ return value;
21
+ }
22
+ function trim(cache, limit) {
23
+ while (cache.size > limit)
24
+ cache.delete(cache.keys().next().value);
25
+ }
26
+ function retainedEnvelope(window, limit) {
27
+ trim(envelopes, limit);
28
+ for (const [id, envelope] of [...envelopes].reverse()) {
29
+ if (envelope.start <= window.start && envelope.end >= window.end) {
30
+ return touch(envelopes, id, envelope);
31
+ }
32
+ }
33
+ const envelope = (0, envelope_1.envelopeFor)(window);
34
+ touch(envelopes, JSON.stringify(envelope), envelope);
35
+ trim(envelopes, limit);
36
+ return envelope;
37
+ }
38
+ function recordStats(stats) {
39
+ for (const key of Object.keys(counts))
40
+ counts[key] += stats[key];
41
+ }
42
+ function expandStats() {
43
+ return { ...counts };
44
+ }
45
+ function resetExpandStats() {
46
+ for (const key of Object.keys(counts))
47
+ counts[key] = 0;
48
+ }
49
+ function clearExpandCache() {
50
+ exports.occurrences.clear();
51
+ envelopes.clear();
52
+ }
53
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../../src/adapters/rrule/cache.ts"],"names":[],"mappings":";;;;;;;;;;AACA,yCAAyC;AAI5B,QAAA,WAAW,GAAG,IAAI,GAAG,EAAuB,CAAC;AAC1D,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;AAC5C,MAAM,MAAM,GAAgB,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;AAE9F,wEAAwE;AACxE,wEAAwE;AACxE,eAAyB,KAAqB,EAAE,GAAW,EAAE,KAAQ;IACnE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAClB,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACtB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,cAAwB,KAAqB,EAAE,KAAa;IAC1D,OAAO,KAAK,CAAC,IAAI,GAAG,KAAK;QAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAe,CAAC,CAAC;AAC/E,CAAC;AAED,0BAAiC,MAAc,EAAE,KAAa;IAC5D,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACvB,KAAK,MAAM,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QACtD,IAAI,QAAQ,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,IAAI,QAAQ,CAAC,GAAG,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC;YACjE,OAAO,KAAK,CAAC,SAAS,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,IAAA,sBAAW,EAAC,MAAM,CAAC,CAAC;IACrC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;IACrD,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACvB,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,qBAA4B,KAAkB;IAC5C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAA6B;QAAE,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC;AAC/F,CAAC;AAED;IACE,OAAO,EAAE,GAAG,MAAM,EAAE,CAAC;AACvB,CAAC;AAED;IACE,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAA6B;QAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACrF,CAAC;AAED;IACE,QAAA,WAAW,CAAC,KAAK,EAAE,CAAC;IACpB,SAAS,CAAC,KAAK,EAAE,CAAC;AACpB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Window } from '../../core';
2
+ export declare function envelopeFor(window: Window): Window;
3
+ //# sourceMappingURL=envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../../../src/adapters/rrule/envelope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAIzC,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElD"}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.envelopeFor = envelopeFor;
4
+ // Absolute 48-hour padding exceeds the 26-hour difference between view zones.
5
+ // Includes, excludes, and dates share these bounds before subtraction.
6
+ function envelopeFor(window) {
7
+ return { start: window.start - 48 * 3_600_000, end: window.end + 48 * 3_600_000 };
8
+ }
9
+ //# sourceMappingURL=envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../../../src/adapters/rrule/envelope.ts"],"names":[],"mappings":";;;AAEA,8EAA8E;AAC9E,uEAAuE;AACvE,qBAA4B,MAAc;IACxC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,EAAE,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,GAAG,EAAE,GAAG,SAAS,EAAE,CAAC;AACpF,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Window } from '../../core';
2
+ import type { ExpandOptions, ExpandResult, RuleSet } from './types';
3
+ export declare function expandRuleSet(set: RuleSet, window: Window, options?: ExpandOptions): ExpandResult;
4
+ //# sourceMappingURL=expand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expand.d.ts","sourceRoot":"","sources":["../../../../src/adapters/rrule/expand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,MAAM,EAAE,MAAM,YAAY,CAAC;AAM3D,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EAIZ,OAAO,EACR,MAAM,SAAS,CAAC;AAGjB,wBAAgB,aAAa,CAC3B,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,aAAkB,GAC1B,YAAY,CAgFd"}