react-native-roster 0.0.0 → 0.0.1

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 (362) hide show
  1. package/NOTICE.md +29 -0
  2. package/README.md +378 -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/index.d.ts +3 -0
  72. package/dist/src/components/roster/index.d.ts.map +1 -0
  73. package/dist/src/components/roster/index.js +56 -0
  74. package/dist/src/components/roster/index.js.map +1 -0
  75. package/dist/src/components/roster/lanes/interval-hover.d.ts +3 -0
  76. package/dist/src/components/roster/lanes/interval-hover.d.ts.map +1 -0
  77. package/dist/src/components/roster/lanes/interval-hover.js +7 -0
  78. package/dist/src/components/roster/lanes/interval-hover.js.map +1 -0
  79. package/dist/src/components/roster/lanes/interval-hover.types.d.ts +15 -0
  80. package/dist/src/components/roster/lanes/interval-hover.types.d.ts.map +1 -0
  81. package/dist/src/components/roster/lanes/interval-hover.types.js +3 -0
  82. package/dist/src/components/roster/lanes/interval-hover.types.js.map +1 -0
  83. package/dist/src/components/roster/lanes/interval-hover.web.d.ts +3 -0
  84. package/dist/src/components/roster/lanes/interval-hover.web.d.ts.map +1 -0
  85. package/dist/src/components/roster/lanes/interval-hover.web.js +20 -0
  86. package/dist/src/components/roster/lanes/interval-hover.web.js.map +1 -0
  87. package/dist/src/components/roster/lanes/lane.d.ts +10 -0
  88. package/dist/src/components/roster/lanes/lane.d.ts.map +1 -0
  89. package/dist/src/components/roster/lanes/lane.js +37 -0
  90. package/dist/src/components/roster/lanes/lane.js.map +1 -0
  91. package/dist/src/components/roster/lanes/parts/incomplete.d.ts +8 -0
  92. package/dist/src/components/roster/lanes/parts/incomplete.d.ts.map +1 -0
  93. package/dist/src/components/roster/lanes/parts/incomplete.js +30 -0
  94. package/dist/src/components/roster/lanes/parts/incomplete.js.map +1 -0
  95. package/dist/src/components/roster/lanes/parts/lane-label.d.ts +3 -0
  96. package/dist/src/components/roster/lanes/parts/lane-label.d.ts.map +1 -0
  97. package/dist/src/components/roster/lanes/parts/lane-label.js +18 -0
  98. package/dist/src/components/roster/lanes/parts/lane-label.js.map +1 -0
  99. package/dist/src/components/roster/layout.d.ts +17 -0
  100. package/dist/src/components/roster/layout.d.ts.map +1 -0
  101. package/dist/src/components/roster/layout.js +24 -0
  102. package/dist/src/components/roster/layout.js.map +1 -0
  103. package/dist/src/components/roster/parts/body.d.ts +7 -0
  104. package/dist/src/components/roster/parts/body.d.ts.map +1 -0
  105. package/dist/src/components/roster/parts/body.js +29 -0
  106. package/dist/src/components/roster/parts/body.js.map +1 -0
  107. package/dist/src/components/roster/parts/corner.d.ts +2 -0
  108. package/dist/src/components/roster/parts/corner.d.ts.map +1 -0
  109. package/dist/src/components/roster/parts/corner.js +7 -0
  110. package/dist/src/components/roster/parts/corner.js.map +1 -0
  111. package/dist/src/components/roster/parts/empty.d.ts +2 -0
  112. package/dist/src/components/roster/parts/empty.d.ts.map +1 -0
  113. package/dist/src/components/roster/parts/empty.js +9 -0
  114. package/dist/src/components/roster/parts/empty.js.map +1 -0
  115. package/dist/src/components/roster/parts/grid.d.ts +3 -0
  116. package/dist/src/components/roster/parts/grid.d.ts.map +1 -0
  117. package/dist/src/components/roster/parts/grid.js +16 -0
  118. package/dist/src/components/roster/parts/grid.js.map +1 -0
  119. package/dist/src/components/roster/parts/header-cell.d.ts +5 -0
  120. package/dist/src/components/roster/parts/header-cell.d.ts.map +1 -0
  121. package/dist/src/components/roster/parts/header-cell.js +9 -0
  122. package/dist/src/components/roster/parts/header-cell.js.map +1 -0
  123. package/dist/src/components/roster/parts/header.d.ts +3 -0
  124. package/dist/src/components/roster/parts/header.d.ts.map +1 -0
  125. package/dist/src/components/roster/parts/header.js +13 -0
  126. package/dist/src/components/roster/parts/header.js.map +1 -0
  127. package/dist/src/components/roster/parts/lane-label-column.d.ts +3 -0
  128. package/dist/src/components/roster/parts/lane-label-column.d.ts.map +1 -0
  129. package/dist/src/components/roster/parts/lane-label-column.js +18 -0
  130. package/dist/src/components/roster/parts/lane-label-column.js.map +1 -0
  131. package/dist/src/components/roster/roster.types.d.ts +146 -0
  132. package/dist/src/components/roster/roster.types.d.ts.map +1 -0
  133. package/dist/src/components/roster/roster.types.js +3 -0
  134. package/dist/src/components/roster/roster.types.js.map +1 -0
  135. package/dist/src/components/roster/use-roster.d.ts +3 -0
  136. package/dist/src/components/roster/use-roster.d.ts.map +1 -0
  137. package/dist/src/components/roster/use-roster.js +106 -0
  138. package/dist/src/components/roster/use-roster.js.map +1 -0
  139. package/dist/src/components/roster/utils/body-content-key.d.ts +3 -0
  140. package/dist/src/components/roster/utils/body-content-key.d.ts.map +1 -0
  141. package/dist/src/components/roster/utils/body-content-key.js +29 -0
  142. package/dist/src/components/roster/utils/body-content-key.js.map +1 -0
  143. package/dist/src/components/roster/utils/ticks.d.ts +4 -0
  144. package/dist/src/components/roster/utils/ticks.d.ts.map +1 -0
  145. package/dist/src/components/roster/utils/ticks.js +60 -0
  146. package/dist/src/components/roster/utils/ticks.js.map +1 -0
  147. package/dist/src/components/schedule/days/day.d.ts +20 -0
  148. package/dist/src/components/schedule/days/day.d.ts.map +1 -0
  149. package/dist/src/components/schedule/days/day.js +26 -0
  150. package/dist/src/components/schedule/days/day.js.map +1 -0
  151. package/dist/src/components/schedule/days/header-layout.d.ts +9 -0
  152. package/dist/src/components/schedule/days/header-layout.d.ts.map +1 -0
  153. package/dist/src/components/schedule/days/header-layout.js +9 -0
  154. package/dist/src/components/schedule/days/header-layout.js.map +1 -0
  155. package/dist/src/components/schedule/days/layout.d.ts +18 -0
  156. package/dist/src/components/schedule/days/layout.d.ts.map +1 -0
  157. package/dist/src/components/schedule/days/layout.js +9 -0
  158. package/dist/src/components/schedule/days/layout.js.map +1 -0
  159. package/dist/src/components/schedule/days/parts/column.d.ts +3 -0
  160. package/dist/src/components/schedule/days/parts/column.d.ts.map +1 -0
  161. package/dist/src/components/schedule/days/parts/column.js +33 -0
  162. package/dist/src/components/schedule/days/parts/column.js.map +1 -0
  163. package/dist/src/components/schedule/days/parts/day-header.d.ts +5 -0
  164. package/dist/src/components/schedule/days/parts/day-header.d.ts.map +1 -0
  165. package/dist/src/components/schedule/days/parts/day-header.js +10 -0
  166. package/dist/src/components/schedule/days/parts/day-header.js.map +1 -0
  167. package/dist/src/components/schedule/days/parts/grid.d.ts +3 -0
  168. package/dist/src/components/schedule/days/parts/grid.d.ts.map +1 -0
  169. package/dist/src/components/schedule/days/parts/grid.js +14 -0
  170. package/dist/src/components/schedule/days/parts/grid.js.map +1 -0
  171. package/dist/src/components/schedule/days/parts/now-line.d.ts +5 -0
  172. package/dist/src/components/schedule/days/parts/now-line.d.ts.map +1 -0
  173. package/dist/src/components/schedule/days/parts/now-line.js +9 -0
  174. package/dist/src/components/schedule/days/parts/now-line.js.map +1 -0
  175. package/dist/src/components/schedule/days/parts/skipped-date.d.ts +4 -0
  176. package/dist/src/components/schedule/days/parts/skipped-date.d.ts.map +1 -0
  177. package/dist/src/components/schedule/days/parts/skipped-date.js +17 -0
  178. package/dist/src/components/schedule/days/parts/skipped-date.js.map +1 -0
  179. package/dist/src/components/schedule/days/parts/transition.d.ts +3 -0
  180. package/dist/src/components/schedule/days/parts/transition.d.ts.map +1 -0
  181. package/dist/src/components/schedule/days/parts/transition.js +42 -0
  182. package/dist/src/components/schedule/days/parts/transition.js.map +1 -0
  183. package/dist/src/components/schedule/index.d.ts +3 -0
  184. package/dist/src/components/schedule/index.d.ts.map +1 -0
  185. package/dist/src/components/schedule/index.js +45 -0
  186. package/dist/src/components/schedule/index.js.map +1 -0
  187. package/dist/src/components/schedule/layout.d.ts +15 -0
  188. package/dist/src/components/schedule/layout.d.ts.map +1 -0
  189. package/dist/src/components/schedule/layout.js +10 -0
  190. package/dist/src/components/schedule/layout.js.map +1 -0
  191. package/dist/src/components/schedule/parts/gutter.d.ts +3 -0
  192. package/dist/src/components/schedule/parts/gutter.d.ts.map +1 -0
  193. package/dist/src/components/schedule/parts/gutter.js +9 -0
  194. package/dist/src/components/schedule/parts/gutter.js.map +1 -0
  195. package/dist/src/components/schedule/parts/incomplete.d.ts +6 -0
  196. package/dist/src/components/schedule/parts/incomplete.d.ts.map +1 -0
  197. package/dist/src/components/schedule/parts/incomplete.js +9 -0
  198. package/dist/src/components/schedule/parts/incomplete.js.map +1 -0
  199. package/dist/src/components/schedule/schedule.types.d.ts +109 -0
  200. package/dist/src/components/schedule/schedule.types.d.ts.map +1 -0
  201. package/dist/src/components/schedule/schedule.types.js +3 -0
  202. package/dist/src/components/schedule/schedule.types.js.map +1 -0
  203. package/dist/src/components/schedule/use-schedule-viewport.d.ts +7 -0
  204. package/dist/src/components/schedule/use-schedule-viewport.d.ts.map +1 -0
  205. package/dist/src/components/schedule/use-schedule-viewport.js +15 -0
  206. package/dist/src/components/schedule/use-schedule-viewport.js.map +1 -0
  207. package/dist/src/components/schedule/use-schedule.d.ts +3 -0
  208. package/dist/src/components/schedule/use-schedule.d.ts.map +1 -0
  209. package/dist/src/components/schedule/use-schedule.js +71 -0
  210. package/dist/src/components/schedule/use-schedule.js.map +1 -0
  211. package/dist/src/components/schedule/utils/days.d.ts +17 -0
  212. package/dist/src/components/schedule/utils/days.d.ts.map +1 -0
  213. package/dist/src/components/schedule/utils/days.js +49 -0
  214. package/dist/src/components/schedule/utils/days.js.map +1 -0
  215. package/dist/src/core/axis.d.ts +18 -0
  216. package/dist/src/core/axis.d.ts.map +1 -0
  217. package/dist/src/core/axis.js +58 -0
  218. package/dist/src/core/axis.js.map +1 -0
  219. package/dist/src/core/cache.d.ts +29 -0
  220. package/dist/src/core/cache.d.ts.map +1 -0
  221. package/dist/src/core/cache.js +33 -0
  222. package/dist/src/core/cache.js.map +1 -0
  223. package/dist/src/core/columns.d.ts +3 -0
  224. package/dist/src/core/columns.d.ts.map +1 -0
  225. package/dist/src/core/columns.js +45 -0
  226. package/dist/src/core/columns.js.map +1 -0
  227. package/dist/src/core/coverage.d.ts +3 -0
  228. package/dist/src/core/coverage.d.ts.map +1 -0
  229. package/dist/src/core/coverage.js +60 -0
  230. package/dist/src/core/coverage.js.map +1 -0
  231. package/dist/src/core/flag.d.ts +3 -0
  232. package/dist/src/core/flag.d.ts.map +1 -0
  233. package/dist/src/core/flag.js +17 -0
  234. package/dist/src/core/flag.js.map +1 -0
  235. package/dist/src/core/hash.d.ts +5 -0
  236. package/dist/src/core/hash.d.ts.map +1 -0
  237. package/dist/src/core/hash.js +29 -0
  238. package/dist/src/core/hash.js.map +1 -0
  239. package/dist/src/core/hit-test.d.ts +6 -0
  240. package/dist/src/core/hit-test.d.ts.map +1 -0
  241. package/dist/src/core/hit-test.js +23 -0
  242. package/dist/src/core/hit-test.js.map +1 -0
  243. package/dist/src/core/index.d.ts +12 -0
  244. package/dist/src/core/index.d.ts.map +1 -0
  245. package/dist/src/core/index.js +30 -0
  246. package/dist/src/core/index.js.map +1 -0
  247. package/dist/src/core/layout.d.ts +3 -0
  248. package/dist/src/core/layout.d.ts.map +1 -0
  249. package/dist/src/core/layout.js +96 -0
  250. package/dist/src/core/layout.js.map +1 -0
  251. package/dist/src/core/order.d.ts +7 -0
  252. package/dist/src/core/order.d.ts.map +1 -0
  253. package/dist/src/core/order.js +12 -0
  254. package/dist/src/core/order.js.map +1 -0
  255. package/dist/src/core/scale.d.ts +9 -0
  256. package/dist/src/core/scale.d.ts.map +1 -0
  257. package/dist/src/core/scale.js +41 -0
  258. package/dist/src/core/scale.js.map +1 -0
  259. package/dist/src/core/snap.d.ts +10 -0
  260. package/dist/src/core/snap.d.ts.map +1 -0
  261. package/dist/src/core/snap.js +43 -0
  262. package/dist/src/core/snap.js.map +1 -0
  263. package/dist/src/core/spans.d.ts +3 -0
  264. package/dist/src/core/spans.d.ts.map +1 -0
  265. package/dist/src/core/spans.js +60 -0
  266. package/dist/src/core/spans.js.map +1 -0
  267. package/dist/src/core/types.d.ts +104 -0
  268. package/dist/src/core/types.d.ts.map +1 -0
  269. package/dist/src/core/types.js +3 -0
  270. package/dist/src/core/types.js.map +1 -0
  271. package/dist/src/core/zone.d.ts +10 -0
  272. package/dist/src/core/zone.d.ts.map +1 -0
  273. package/dist/src/core/zone.js +103 -0
  274. package/dist/src/core/zone.js.map +1 -0
  275. package/dist/src/index.d.ts +27 -0
  276. package/dist/src/index.d.ts.map +1 -0
  277. package/dist/src/index.js +63 -0
  278. package/dist/src/index.js.map +1 -0
  279. package/dist/src/nativewind/index.d.ts +61 -0
  280. package/dist/src/nativewind/index.d.ts.map +1 -0
  281. package/dist/src/nativewind/index.js +24 -0
  282. package/dist/src/nativewind/index.js.map +1 -0
  283. package/llms.txt +481 -0
  284. package/package.json +154 -8
  285. package/src/adapters/rrule/README.md +41 -0
  286. package/src/adapters/rrule/cache.ts +50 -0
  287. package/src/adapters/rrule/envelope.ts +7 -0
  288. package/src/adapters/rrule/expand.ts +105 -0
  289. package/src/adapters/rrule/hash.ts +13 -0
  290. package/src/adapters/rrule/index.ts +11 -0
  291. package/src/adapters/rrule/net.ts +30 -0
  292. package/src/adapters/rrule/occurrences.ts +288 -0
  293. package/src/adapters/rrule/types.ts +52 -0
  294. package/src/adapters/rrule/validate.ts +78 -0
  295. package/src/components/layers/README.md +11 -0
  296. package/src/components/layers/layers.types.ts +6 -0
  297. package/src/components/layers/parts/gap.tsx +5 -0
  298. package/src/components/layers/parts/interval.tsx +24 -0
  299. package/src/components/layers/utils/styles.ts +24 -0
  300. package/src/components/primitives/README.md +12 -0
  301. package/src/components/primitives/press-point.tsx +5 -0
  302. package/src/components/primitives/press-point.types.ts +5 -0
  303. package/src/components/primitives/press-point.web.tsx +19 -0
  304. package/src/components/primitives/region-style.ts +21 -0
  305. package/src/components/roster/README.md +18 -0
  306. package/src/components/roster/index.tsx +90 -0
  307. package/src/components/roster/lanes/interval-hover.tsx +5 -0
  308. package/src/components/roster/lanes/interval-hover.types.ts +11 -0
  309. package/src/components/roster/lanes/interval-hover.web.tsx +19 -0
  310. package/src/components/roster/lanes/lane.tsx +88 -0
  311. package/src/components/roster/lanes/parts/incomplete.tsx +44 -0
  312. package/src/components/roster/lanes/parts/lane-label.tsx +41 -0
  313. package/src/components/roster/layout.tsx +68 -0
  314. package/src/components/roster/parts/body.tsx +91 -0
  315. package/src/components/roster/parts/corner.tsx +3 -0
  316. package/src/components/roster/parts/empty.tsx +5 -0
  317. package/src/components/roster/parts/grid.tsx +26 -0
  318. package/src/components/roster/parts/header-cell.tsx +10 -0
  319. package/src/components/roster/parts/header.tsx +18 -0
  320. package/src/components/roster/parts/lane-label-column.tsx +31 -0
  321. package/src/components/roster/roster.types.ts +155 -0
  322. package/src/components/roster/use-roster.ts +133 -0
  323. package/src/components/roster/utils/body-content-key.ts +46 -0
  324. package/src/components/roster/utils/ticks.ts +62 -0
  325. package/src/components/schedule/README.md +18 -0
  326. package/src/components/schedule/days/day.tsx +76 -0
  327. package/src/components/schedule/days/header-layout.tsx +12 -0
  328. package/src/components/schedule/days/layout.tsx +38 -0
  329. package/src/components/schedule/days/parts/column.tsx +59 -0
  330. package/src/components/schedule/days/parts/day-header.tsx +18 -0
  331. package/src/components/schedule/days/parts/grid.tsx +21 -0
  332. package/src/components/schedule/days/parts/now-line.tsx +11 -0
  333. package/src/components/schedule/days/parts/skipped-date.tsx +22 -0
  334. package/src/components/schedule/days/parts/transition.tsx +70 -0
  335. package/src/components/schedule/index.tsx +109 -0
  336. package/src/components/schedule/layout.tsx +42 -0
  337. package/src/components/schedule/parts/gutter.tsx +16 -0
  338. package/src/components/schedule/parts/incomplete.tsx +13 -0
  339. package/src/components/schedule/schedule.types.ts +103 -0
  340. package/src/components/schedule/use-schedule-viewport.ts +13 -0
  341. package/src/components/schedule/use-schedule.ts +75 -0
  342. package/src/components/schedule/utils/days.ts +54 -0
  343. package/src/core/README.md +25 -0
  344. package/src/core/axis.ts +58 -0
  345. package/src/core/cache.ts +38 -0
  346. package/src/core/columns.ts +49 -0
  347. package/src/core/coverage.ts +56 -0
  348. package/src/core/flag.ts +14 -0
  349. package/src/core/hash.ts +28 -0
  350. package/src/core/hit-test.ts +27 -0
  351. package/src/core/index.ts +34 -0
  352. package/src/core/layout.ts +101 -0
  353. package/src/core/order.ts +18 -0
  354. package/src/core/scale.ts +45 -0
  355. package/src/core/snap.ts +52 -0
  356. package/src/core/spans.ts +58 -0
  357. package/src/core/types.ts +92 -0
  358. package/src/core/zone.ts +100 -0
  359. package/src/index.ts +49 -0
  360. package/src/nativewind/README.md +14 -0
  361. package/src/nativewind/index.ts +30 -0
  362. package/index.js +0 -4
@@ -0,0 +1,11 @@
1
+ # layers
2
+
3
+ This area is about a layer; its children are intervals and gaps. Both
4
+ projections draw the same fillers, so they live above Roster and Schedule.
5
+
6
+ - Root entry exports: `RosterInterval`, `RosterGap`, `IntervalInput`, `GapInput`
7
+ - `layers.types.ts`: the filler input contract both projections pass
8
+ - `parts/interval.tsx`, `parts/gap.tsx`: the default fillers
9
+ - `utils/styles.ts`: content-keyed style objects per layer
10
+
11
+ Imports core only. Tests: `test/components/layers`.
@@ -0,0 +1,6 @@
1
+ import type { Lane, Layer, Rect } from '../../core';
2
+
3
+ /** One covered span of a layer, positioned by the projection; `highlighted` follows the active source. */
4
+ export type IntervalInput = { rect: Rect; layer: Layer; lane: Lane; highlighted: boolean };
5
+ /** One removed span of a layer, positioned by the projection. */
6
+ export type GapInput = { rect: Rect; layer: Layer; lane: Lane };
@@ -0,0 +1,5 @@
1
+ import type { GapInput } from '../layers.types';
2
+
3
+ export function RosterGap(_input: GapInput) {
4
+ return null;
5
+ }
@@ -0,0 +1,24 @@
1
+ import { View } from 'react-native';
2
+ import type { IntervalInput } from '../layers.types';
3
+ import { stylesFor } from '../utils/styles';
4
+
5
+ export function RosterInterval({ rect, layer, highlighted }: IntervalInput) {
6
+ const styles = stylesFor(layer);
7
+ const fill = highlighted ? styles.highlighted : styles.normal;
8
+ return (
9
+ <View
10
+ pointerEvents="none"
11
+ style={[
12
+ fill,
13
+ {
14
+ position: 'absolute',
15
+ left: rect.x,
16
+ top: rect.y,
17
+ width: rect.width,
18
+ height: rect.height,
19
+ zIndex: rect.z,
20
+ },
21
+ ]}
22
+ />
23
+ );
24
+ }
@@ -0,0 +1,24 @@
1
+ import type { ViewStyle } from 'react-native';
2
+ import type { Layer } from '../../../core';
3
+ import { structuralKey } from '../../../core/hash';
4
+
5
+ const styles = new Map<string, { normal: ViewStyle; highlighted: ViewStyle }>();
6
+
7
+ export function stylesFor(layer: Layer): { normal: ViewStyle; highlighted: ViewStyle } {
8
+ const key = structuralKey([layer.id, layer.style]);
9
+ let result = styles.get(key);
10
+ if (!result) {
11
+ const normal: ViewStyle = {
12
+ backgroundColor: layer.style.color,
13
+ opacity: layer.style.opacity ?? 1,
14
+ width: '100%',
15
+ height: '100%',
16
+ };
17
+ result = {
18
+ normal,
19
+ highlighted: { ...normal, backgroundColor: layer.style.highlightColor ?? layer.style.color },
20
+ };
21
+ styles.set(key, result);
22
+ }
23
+ return result;
24
+ }
@@ -0,0 +1,12 @@
1
+ # primitives
2
+
3
+ Hookless building blocks shared by Roster and Schedule. Nothing here is a
4
+ public export.
5
+
6
+ - `press-point.tsx`, `press-point.web.tsx`, `press-point.types.ts`: pointer
7
+ coordinates relative to the pressed surface; the web file is selected by the
8
+ `browser` remap in package.json
9
+ - `region-style.ts`: `regionStyle(structure, paint, override)`, which drops the
10
+ default paint when the override carries a NativeWind class entry
11
+
12
+ Tests: `test/components/primitives`.
@@ -0,0 +1,5 @@
1
+ import type { PressPoint, PressPointInput } from './press-point.types';
2
+
3
+ export function pressPoint({ nativeEvent }: PressPointInput): PressPoint {
4
+ return { x: nativeEvent.locationX as number, y: nativeEvent.locationY as number };
5
+ }
@@ -0,0 +1,5 @@
1
+ export type PressPointInput = {
2
+ nativeEvent: { locationX?: number; locationY?: number; clientX?: number; clientY?: number };
3
+ currentTarget?: unknown;
4
+ };
5
+ export type PressPoint = { x: number; y: number };
@@ -0,0 +1,19 @@
1
+ import type { PressPoint, PressPointInput } from './press-point.types';
2
+
3
+ export function pressPoint({ nativeEvent, currentTarget }: PressPointInput): PressPoint {
4
+ // React Native Web passes a DOM click, not a native responder event, to onPress.
5
+ const target = currentTarget as {
6
+ getBoundingClientRect(): { left: number; top: number; width: number; height: number };
7
+ };
8
+ const bounds = target.getBoundingClientRect();
9
+ return {
10
+ x:
11
+ typeof nativeEvent.clientX === 'number'
12
+ ? nativeEvent.clientX - bounds.left
13
+ : bounds.width / 2,
14
+ y:
15
+ typeof nativeEvent.clientY === 'number'
16
+ ? nativeEvent.clientY - bounds.top
17
+ : bounds.height / 2,
18
+ };
19
+ }
@@ -0,0 +1,21 @@
1
+ import type { StyleProp, ViewStyle } from 'react-native';
2
+
3
+ /**
4
+ * Compose a chrome region's style. React Native Web renders object styles
5
+ * inline, and inline paint beats any class, so a class entry in the override
6
+ * (NativeWind's `$$css` marker) replaces the region's default paint entirely.
7
+ * Structure always applies; native style overrides simply merge last.
8
+ */
9
+ export function regionStyle(
10
+ structure: ViewStyle,
11
+ paint: ViewStyle,
12
+ override: StyleProp<ViewStyle>,
13
+ ): StyleProp<ViewStyle> {
14
+ return [structure, hasClassEntry(override) ? null : paint, override];
15
+ }
16
+
17
+ function hasClassEntry(style: unknown): boolean {
18
+ if (!style || typeof style !== 'object') return false;
19
+ if (Array.isArray(style)) return style.some((entry) => hasClassEntry(entry));
20
+ return '$$css' in style;
21
+ }
@@ -0,0 +1,18 @@
1
+ # Roster
2
+
3
+ This feature is about a roster; its children are lanes.
4
+
5
+ - Root entry exports: `Roster`, `useRoster`, the `RosterXxx` parts, and every
6
+ `XxxInput` zone type from `roster.types.ts`
7
+
8
+ - `index.tsx`: the chassis; calls `useRoster`, branches on `status`, composes zones
9
+ - `use-roster.ts`: the hook; owns window, projection, scroll, geometry, and press
10
+ - `layout.tsx`: arranges corner, header, label column, and body regions only
11
+ - `roster.types.ts`: props, model, and every zone input type
12
+ - `parts/`: collection-level parts (body, header, header cell, grid, corner, label column, empty)
13
+ - `lanes/`: `LaneRow`, the interval-hover platform pair, and lane-local parts
14
+ - `utils/`: ticks and the body content key
15
+
16
+ Interval and gap fillers live in `../layers`; press geometry and `regionStyle`
17
+ in `../primitives`. Zone contracts are documented in the README's Roster zones
18
+ section and in `llms.txt`. Tests: `test/components/roster`.
@@ -0,0 +1,90 @@
1
+ import { RosterGap } from '../layers/parts/gap';
2
+ import { RosterInterval } from '../layers/parts/interval';
3
+ import { RosterLaneLabel } from './lanes/parts/lane-label';
4
+ import { RosterLayout } from './layout';
5
+ import { RosterBody } from './parts/body';
6
+ import { RosterCorner } from './parts/corner';
7
+ import { RosterEmpty } from './parts/empty';
8
+ import { RosterGrid } from './parts/grid';
9
+ import { RosterHeader } from './parts/header';
10
+ import { RosterHeaderCell } from './parts/header-cell';
11
+ import { RosterLaneLabelColumn } from './parts/lane-label-column';
12
+ import type { RosterProps } from './roster.types';
13
+ import { useRoster } from './use-roster';
14
+
15
+ export function Roster(props: RosterProps) {
16
+ const {
17
+ status,
18
+ orderedLanes,
19
+ laneState,
20
+ window,
21
+ geometryFor,
22
+ projection,
23
+ scroll,
24
+ press,
25
+ ticks,
26
+ contentWidth,
27
+ viewport,
28
+ onLayout,
29
+ } = useRoster(props);
30
+ const {
31
+ emptyZone = RosterEmpty,
32
+ cornerZone = RosterCorner,
33
+ headerZone = RosterHeader,
34
+ laneLabelColumnZone = RosterLaneLabelColumn,
35
+ bodyZone = RosterBody,
36
+ headerCellZone = RosterHeaderCell,
37
+ laneLabelZone = RosterLaneLabel,
38
+ intervalZone = RosterInterval,
39
+ gapZone = RosterGap,
40
+ gridZone = RosterGrid,
41
+ incompleteLabel = 'Availability may be incomplete',
42
+ neverSetLabel = 'No availability set',
43
+ } = props;
44
+ if (status === 'empty') return emptyZone();
45
+ if (window.start === window.end) return null;
46
+ return (
47
+ <RosterLayout
48
+ style={props.style}
49
+ headerStyle={props.headerStyle}
50
+ laneLabelColumnStyle={props.laneLabelColumnStyle}
51
+ bodyStyle={props.bodyStyle}
52
+ laneLabelWidth={props.laneLabelWidth}
53
+ onLayout={onLayout}
54
+ cornerZone={cornerZone()}
55
+ headerZone={headerZone({ ticks, projection, scroll, contentWidth, headerCellZone })}
56
+ laneLabelColumnZone={laneLabelColumnZone({
57
+ labels: orderedLanes.map((lane) => {
58
+ const state = laneState.get(lane.id) ?? { flag: 'none', complete: true };
59
+ return {
60
+ lane,
61
+ ...state,
62
+ viewTimezone: projection.viewTimezone,
63
+ incompleteLabel,
64
+ neverSetLabel,
65
+ };
66
+ }),
67
+ projection,
68
+ scroll,
69
+ laneLabelZone,
70
+ })}
71
+ bodyZone={bodyZone({
72
+ lanes: orderedLanes,
73
+ window,
74
+ geometryFor,
75
+ projection,
76
+ scroll,
77
+ press,
78
+ ticks,
79
+ contentWidth,
80
+ viewport,
81
+ intervalZone,
82
+ gapZone,
83
+ gridZone,
84
+ highlightSource: props.highlightSource,
85
+ onIntervalHover: props.onIntervalHover,
86
+ incompleteLabel,
87
+ })}
88
+ />
89
+ );
90
+ }
@@ -0,0 +1,5 @@
1
+ import type { IntervalHoverInput, IntervalHoverProps } from './interval-hover.types';
2
+
3
+ export function intervalHoverProps(_input: IntervalHoverInput): IntervalHoverProps {
4
+ return {};
5
+ }
@@ -0,0 +1,11 @@
1
+ import type { Lane, LaneGeometry, Rect } from '../../../core';
2
+ import type { PressPointInput } from '../../primitives/press-point.types';
3
+
4
+ export type IntervalHoverInput = {
5
+ lane: Lane;
6
+ geometry: LaneGeometry;
7
+ onIntervalHover?: (rect: Rect, lane: Lane) => void;
8
+ };
9
+ export type IntervalHoverProps = {
10
+ onPointerMove?: (input: PressPointInput & { nativeEvent: { pointerType?: string } }) => void;
11
+ };
@@ -0,0 +1,19 @@
1
+ import { hitTest } from '../../../core/hit-test';
2
+ import { pressPoint } from '../../primitives/press-point.web';
3
+ import type { IntervalHoverInput, IntervalHoverProps } from './interval-hover.types';
4
+
5
+ export function intervalHoverProps({
6
+ lane,
7
+ geometry,
8
+ onIntervalHover,
9
+ }: IntervalHoverInput): IntervalHoverProps {
10
+ if (!onIntervalHover) return {};
11
+ return {
12
+ onPointerMove(input) {
13
+ if (input.nativeEvent.pointerType === 'touch') return;
14
+ const { x, y } = pressPoint(input);
15
+ const hit = hitTest(lane, geometry, x, y);
16
+ if (hit?.kind === 'interval') onIntervalHover(hit.rect, lane);
17
+ },
18
+ };
19
+ }
@@ -0,0 +1,88 @@
1
+ import { Fragment } from 'react';
2
+ import { Pressable } from 'react-native';
3
+ import type { Lane, LaneGeometry } from '../../../core';
4
+ import { pressPoint } from '../../primitives/press-point';
5
+ import type { BodyInput } from '../roster.types';
6
+ import { intervalHoverProps } from './interval-hover';
7
+ import { RosterIncomplete } from './parts/incomplete';
8
+
9
+ export type LaneRowProps = Pick<
10
+ BodyInput,
11
+ 'press' | 'intervalZone' | 'gapZone' | 'highlightSource' | 'onIntervalHover' | 'incompleteLabel'
12
+ > & {
13
+ lane: Lane;
14
+ geometry: LaneGeometry;
15
+ width: number;
16
+ rowHeight: number;
17
+ };
18
+
19
+ export function LaneRow({
20
+ lane,
21
+ geometry,
22
+ width,
23
+ rowHeight,
24
+ press,
25
+ intervalZone,
26
+ gapZone,
27
+ highlightSource,
28
+ onIntervalHover,
29
+ incompleteLabel = 'Availability may be incomplete',
30
+ }: LaneRowProps) {
31
+ const layers = [...lane.layers].sort((a, b) => a.z - b.z);
32
+ return (
33
+ <Pressable
34
+ {...intervalHoverProps({ lane, geometry, onIntervalHover })}
35
+ testID={`roster-lane-${lane.id}`}
36
+ accessibilityLabel={lane.label}
37
+ onPress={(input) => {
38
+ const point = pressPoint(input);
39
+ press(lane, point.x, point.y);
40
+ }}
41
+ style={{ width, height: rowHeight }}
42
+ >
43
+ <RosterIncomplete lane={lane} geometry={geometry} width={width} label={incompleteLabel} />
44
+ {layers.map((layer) => (
45
+ <Fragment key={layer.id}>
46
+ {geometry.rects
47
+ .filter((rect) => rect.layerId === layer.id)
48
+ .map((rect) => (
49
+ <Fragment key={`interval-${rect.x}:${rect.width}`}>
50
+ {intervalZone({
51
+ rect,
52
+ layer,
53
+ lane,
54
+ highlighted: rect.sources.some(
55
+ (source) =>
56
+ source.kind === highlightSource?.kind && source.id === highlightSource.id,
57
+ ),
58
+ })}
59
+ </Fragment>
60
+ ))}
61
+ {geometry.gapRects
62
+ .filter((rect) => rect.layerId === layer.id)
63
+ .map((rect) => (
64
+ <Pressable
65
+ key={`gap-${rect.x}:${rect.width}`}
66
+ accessibilityLabel={`${lane.label}: removed time`}
67
+ onPress={(input) => {
68
+ input.stopPropagation();
69
+ const point = pressPoint(input);
70
+ press(lane, rect.x + point.x, rect.y + point.y);
71
+ }}
72
+ style={{
73
+ position: 'absolute',
74
+ left: rect.x,
75
+ top: rect.y,
76
+ width: rect.width,
77
+ height: rect.height,
78
+ zIndex: rect.z,
79
+ }}
80
+ >
81
+ {gapZone({ rect, layer, lane })}
82
+ </Pressable>
83
+ ))}
84
+ </Fragment>
85
+ ))}
86
+ </Pressable>
87
+ );
88
+ }
@@ -0,0 +1,44 @@
1
+ import { Text } from 'react-native';
2
+ import type { Lane, LaneGeometry } from '../../../../core';
3
+
4
+ export function RosterIncomplete({
5
+ lane,
6
+ geometry,
7
+ width,
8
+ label,
9
+ }: {
10
+ lane: Lane;
11
+ geometry: LaneGeometry;
12
+ width: number;
13
+ label: string;
14
+ }) {
15
+ if (lane.complete !== false) return null;
16
+ // Choose the first empty span; the notice never covers an interval.
17
+ let left = 0;
18
+ let right = width;
19
+ for (const rect of [...geometry.rects].sort((a, b) => a.x - b.x)) {
20
+ if (rect.x > left) {
21
+ right = rect.x;
22
+ break;
23
+ }
24
+ left = Math.max(left, rect.x + rect.width);
25
+ }
26
+ if (left >= right) return null;
27
+ return (
28
+ <Text
29
+ testID={`roster-incomplete-${lane.id}`}
30
+ pointerEvents="none"
31
+ numberOfLines={1}
32
+ style={{
33
+ position: 'absolute',
34
+ left,
35
+ width: right - left,
36
+ top: 4,
37
+ color: '#64748b',
38
+ fontSize: 12,
39
+ }}
40
+ >
41
+ {label}
42
+ </Text>
43
+ );
44
+ }
@@ -0,0 +1,41 @@
1
+ import { Text, View } from 'react-native';
2
+ import type { LaneLabelInput } from '../../roster.types';
3
+
4
+ export function RosterLaneLabel({
5
+ lane,
6
+ flag,
7
+ complete,
8
+ viewTimezone,
9
+ incompleteLabel,
10
+ neverSetLabel,
11
+ }: LaneLabelInput) {
12
+ const zone =
13
+ lane.timezone && lane.timezone !== viewTimezone ? (
14
+ <Text style={{ fontSize: 10, color: '#64748b' }}>{lane.timezone}</Text>
15
+ ) : null;
16
+ const flagText =
17
+ flag === 'never-set' ? (
18
+ <Text style={{ fontSize: 10, color: '#9a3412' }}>{neverSetLabel}</Text>
19
+ ) : null;
20
+ const completeness = complete ? null : (
21
+ <Text style={{ fontSize: 10, color: '#9a3412' }}>{incompleteLabel}</Text>
22
+ );
23
+ return (
24
+ <View
25
+ style={{
26
+ flex: 1,
27
+ justifyContent: 'center',
28
+ paddingHorizontal: 8,
29
+ borderBottomWidth: 1,
30
+ borderBottomColor: '#e2e8f0',
31
+ }}
32
+ >
33
+ <Text numberOfLines={1} style={{ color: '#0f172a', fontSize: 12 }}>
34
+ {lane.label}
35
+ </Text>
36
+ {zone}
37
+ {flagText}
38
+ {completeness}
39
+ </View>
40
+ );
41
+ }
@@ -0,0 +1,68 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { LayoutChangeEvent } from 'react-native';
3
+ import { View } from 'react-native';
4
+ import { regionStyle } from '../primitives/region-style';
5
+ import type { RosterStyleProps } from './roster.types';
6
+
7
+ type RosterLayoutProps = Omit<RosterStyleProps, `${string}ClassName` | 'className'> & {
8
+ /** Cell above the lane labels, beside the header. */
9
+ cornerZone: ReactNode;
10
+ /** Frozen tick row; the layout clips translated header content. */
11
+ headerZone: ReactNode;
12
+ /** Frozen lane labels; the layout supplies the label column and clips vertical translation. */
13
+ laneLabelColumnZone: ReactNode;
14
+ /** Virtualized body with its horizontal scroller; fills the measured viewport. */
15
+ bodyZone: ReactNode;
16
+ onLayout: (input: LayoutChangeEvent) => void;
17
+ };
18
+
19
+ export function RosterLayout({
20
+ cornerZone,
21
+ headerZone,
22
+ laneLabelColumnZone,
23
+ bodyZone,
24
+ onLayout,
25
+ style,
26
+ headerStyle,
27
+ laneLabelColumnStyle,
28
+ bodyStyle,
29
+ laneLabelWidth = 180,
30
+ }: RosterLayoutProps) {
31
+ return (
32
+ <View style={regionStyle(structure.root, paint.root, style)}>
33
+ <View style={regionStyle(structure.header, paint.header, headerStyle)}>
34
+ <View style={{ width: laneLabelWidth }}>{cornerZone}</View>
35
+ <View style={structure.headerStrip}>{headerZone}</View>
36
+ </View>
37
+ <View style={structure.content}>
38
+ <View
39
+ style={regionStyle(
40
+ { ...structure.labels, width: laneLabelWidth },
41
+ paint.labels,
42
+ laneLabelColumnStyle,
43
+ )}
44
+ >
45
+ {laneLabelColumnZone}
46
+ </View>
47
+ <View onLayout={onLayout} style={regionStyle(structure.body, {}, bodyStyle)}>
48
+ {bodyZone}
49
+ </View>
50
+ </View>
51
+ </View>
52
+ );
53
+ }
54
+
55
+ const structure = {
56
+ root: { flex: 1, minHeight: 0, overflow: 'hidden' },
57
+ header: { flexDirection: 'row', height: 40, overflow: 'hidden' },
58
+ headerStrip: { flex: 1, minWidth: 0, overflow: 'hidden' },
59
+ content: { flex: 1, minHeight: 0, flexDirection: 'row' },
60
+ labels: { overflow: 'hidden' },
61
+ body: { flex: 1, minWidth: 0, overflow: 'hidden' },
62
+ } as const;
63
+ // Paint is what a class replaces; see regionStyle.
64
+ const paint = {
65
+ root: { backgroundColor: '#fff' },
66
+ header: { borderBottomWidth: 1, borderBottomColor: '#cbd5e1' },
67
+ labels: { borderRightWidth: 1, borderRightColor: '#cbd5e1' },
68
+ } as const;
@@ -0,0 +1,91 @@
1
+ import { LegendList } from '@legendapp/list';
2
+ import { Profiler, type ProfilerOnRenderCallback } from 'react';
3
+ import { ScrollView, View } from 'react-native';
4
+ import { LaneRow } from '../lanes/lane';
5
+ import type { BodyInput } from '../roster.types';
6
+ import { bodyContentKey } from '../utils/body-content-key';
7
+
8
+ export function RosterBody({
9
+ lanes,
10
+ geometryFor,
11
+ projection,
12
+ scroll,
13
+ press,
14
+ ticks,
15
+ contentWidth,
16
+ viewport,
17
+ window,
18
+ intervalZone,
19
+ gapZone,
20
+ gridZone,
21
+ highlightSource,
22
+ onIntervalHover,
23
+ incompleteLabel,
24
+ onRowRender,
25
+ }: BodyInput & {
26
+ /** Optional per-lane commit observer; used by the development gallery's Profiler gate. */
27
+ onRowRender?: ProfilerOnRenderCallback;
28
+ }) {
29
+ // LegendList requires a measured viewport and does not support static rendering.
30
+ if (viewport.width <= 0 || viewport.height <= 0) return null;
31
+ return (
32
+ <ScrollView
33
+ testID="roster-horizontal-scroll"
34
+ ref={scroll.bodyRef}
35
+ horizontal
36
+ onScroll={scroll.onBodyScroll}
37
+ scrollEventThrottle={16}
38
+ style={{ flex: 1 }}
39
+ >
40
+ <View style={{ width: contentWidth, height: viewport.height }}>
41
+ {gridZone({ ticks, contentWidth })}
42
+ <LegendList
43
+ testID="roster-vertical-scroll"
44
+ data={lanes}
45
+ extraData={bodyContentKey({
46
+ window,
47
+ projection,
48
+ highlightSource,
49
+ intervalZone,
50
+ gapZone,
51
+ onIntervalHover,
52
+ incompleteLabel,
53
+ })}
54
+ keyExtractor={(lane) => lane.id}
55
+ estimatedListSize={{ width: contentWidth, height: viewport.height }}
56
+ estimatedItemSize={projection.rowHeight}
57
+ getFixedItemSize={() => projection.rowHeight}
58
+ drawDistance={0}
59
+ recycleItems={false}
60
+ maintainVisibleContentPosition={false}
61
+ onScroll={scroll.onVerticalScroll}
62
+ scrollEventThrottle={16}
63
+ style={{ flex: 1 }}
64
+ renderItem={({ item }) => {
65
+ const row = (
66
+ <LaneRow
67
+ lane={item}
68
+ geometry={geometryFor(item)}
69
+ width={contentWidth}
70
+ rowHeight={projection.rowHeight}
71
+ press={press}
72
+ intervalZone={intervalZone}
73
+ gapZone={gapZone}
74
+ highlightSource={highlightSource}
75
+ onIntervalHover={onIntervalHover}
76
+ incompleteLabel={incompleteLabel}
77
+ />
78
+ );
79
+ if (onRowRender)
80
+ return (
81
+ <Profiler id={item.id} onRender={onRowRender}>
82
+ {row}
83
+ </Profiler>
84
+ );
85
+ return row;
86
+ }}
87
+ />
88
+ </View>
89
+ </ScrollView>
90
+ );
91
+ }
@@ -0,0 +1,3 @@
1
+ export function RosterCorner() {
2
+ return null;
3
+ }
@@ -0,0 +1,5 @@
1
+ import { Text } from 'react-native';
2
+
3
+ export function RosterEmpty() {
4
+ return <Text style={{ padding: 24, color: '#475569' }}>No lanes</Text>;
5
+ }
@@ -0,0 +1,26 @@
1
+ import { View } from 'react-native';
2
+ import type { GridInput } from '../roster.types';
3
+
4
+ export function RosterGrid({ ticks, contentWidth }: GridInput) {
5
+ return (
6
+ <View
7
+ testID="roster-grid"
8
+ pointerEvents="none"
9
+ style={{ position: 'absolute', width: contentWidth, height: '100%' }}
10
+ >
11
+ {ticks.map((tick) => (
12
+ <View
13
+ key={tick.time}
14
+ style={{
15
+ position: 'absolute',
16
+ left: tick.x,
17
+ top: 0,
18
+ bottom: 0,
19
+ width: 1,
20
+ backgroundColor: '#e2e8f0',
21
+ }}
22
+ />
23
+ ))}
24
+ </View>
25
+ );
26
+ }