react-native-roster 0.0.1 → 0.2.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 (122) hide show
  1. package/README.md +209 -32
  2. package/dist/src/components/primitives/portal.d.ts +12 -0
  3. package/dist/src/components/primitives/portal.d.ts.map +1 -0
  4. package/dist/src/components/primitives/portal.js +41 -0
  5. package/dist/src/components/primitives/portal.js.map +1 -0
  6. package/dist/src/components/roster/body-layout.d.ts +9 -0
  7. package/dist/src/components/roster/body-layout.d.ts.map +1 -0
  8. package/dist/src/components/roster/body-layout.js +15 -0
  9. package/dist/src/components/roster/body-layout.js.map +1 -0
  10. package/dist/src/components/roster/index.d.ts +2 -1
  11. package/dist/src/components/roster/index.d.ts.map +1 -1
  12. package/dist/src/components/roster/index.js +34 -27
  13. package/dist/src/components/roster/index.js.map +1 -1
  14. package/dist/src/components/roster/lanes/lane.d.ts +2 -2
  15. package/dist/src/components/roster/lanes/lane.d.ts.map +1 -1
  16. package/dist/src/components/roster/lanes/lane.js +4 -8
  17. package/dist/src/components/roster/lanes/lane.js.map +1 -1
  18. package/dist/src/components/roster/parts/body.d.ts +3 -3
  19. package/dist/src/components/roster/parts/body.d.ts.map +1 -1
  20. package/dist/src/components/roster/parts/body.js +5 -20
  21. package/dist/src/components/roster/parts/body.js.map +1 -1
  22. package/dist/src/components/roster/parts/header-cell.d.ts +2 -4
  23. package/dist/src/components/roster/parts/header-cell.d.ts.map +1 -1
  24. package/dist/src/components/roster/parts/header-cell.js.map +1 -1
  25. package/dist/src/components/roster/parts/header.d.ts +1 -1
  26. package/dist/src/components/roster/parts/header.d.ts.map +1 -1
  27. package/dist/src/components/roster/parts/header.js +2 -2
  28. package/dist/src/components/roster/parts/header.js.map +1 -1
  29. package/dist/src/components/roster/parts/lane-label-column.d.ts +1 -1
  30. package/dist/src/components/roster/parts/lane-label-column.d.ts.map +1 -1
  31. package/dist/src/components/roster/parts/lane-label-column.js +2 -2
  32. package/dist/src/components/roster/parts/lane-label-column.js.map +1 -1
  33. package/dist/src/components/roster/parts/lane-list.d.ts +7 -0
  34. package/dist/src/components/roster/parts/lane-list.d.ts.map +1 -0
  35. package/dist/src/components/roster/parts/lane-list.js +26 -0
  36. package/dist/src/components/roster/parts/lane-list.js.map +1 -0
  37. package/dist/src/components/roster/roster.types.d.ts +48 -21
  38. package/dist/src/components/roster/roster.types.d.ts.map +1 -1
  39. package/dist/src/components/roster/selection/selection-layout.d.ts +4 -0
  40. package/dist/src/components/roster/selection/selection-layout.d.ts.map +1 -0
  41. package/dist/src/components/roster/selection/selection-layout.js +82 -0
  42. package/dist/src/components/roster/selection/selection-layout.js.map +1 -0
  43. package/dist/src/components/roster/selection/selection-layout.types.d.ts +25 -0
  44. package/dist/src/components/roster/selection/selection-layout.types.d.ts.map +1 -0
  45. package/dist/src/components/roster/selection/selection-layout.types.js +3 -0
  46. package/dist/src/components/roster/selection/selection-layout.types.js.map +1 -0
  47. package/dist/src/components/roster/selection/selection-layout.web.d.ts +4 -0
  48. package/dist/src/components/roster/selection/selection-layout.web.d.ts.map +1 -0
  49. package/dist/src/components/roster/selection/selection-layout.web.js +52 -0
  50. package/dist/src/components/roster/selection/selection-layout.web.js.map +1 -0
  51. package/dist/src/components/roster/use-roster-press.d.ts +6 -0
  52. package/dist/src/components/roster/use-roster-press.d.ts.map +1 -0
  53. package/dist/src/components/roster/use-roster-press.js +61 -0
  54. package/dist/src/components/roster/use-roster-press.js.map +1 -0
  55. package/dist/src/components/roster/use-roster.d.ts.map +1 -1
  56. package/dist/src/components/roster/use-roster.js +40 -30
  57. package/dist/src/components/roster/use-roster.js.map +1 -1
  58. package/dist/src/components/roster/utils/body-content-key.d.ts +1 -1
  59. package/dist/src/components/roster/utils/body-content-key.d.ts.map +1 -1
  60. package/dist/src/components/roster/utils/body-content-key.js +10 -10
  61. package/dist/src/components/roster/utils/body-content-key.js.map +1 -1
  62. package/dist/src/components/schedule/days/day.d.ts +2 -2
  63. package/dist/src/components/schedule/days/day.d.ts.map +1 -1
  64. package/dist/src/components/schedule/days/day.js +2 -11
  65. package/dist/src/components/schedule/days/day.js.map +1 -1
  66. package/dist/src/components/schedule/days/header-layout.d.ts +1 -1
  67. package/dist/src/components/schedule/days/header-layout.d.ts.map +1 -1
  68. package/dist/src/components/schedule/days/parts/column.d.ts +1 -1
  69. package/dist/src/components/schedule/days/parts/column.d.ts.map +1 -1
  70. package/dist/src/components/schedule/days/parts/column.js +4 -8
  71. package/dist/src/components/schedule/days/parts/column.js.map +1 -1
  72. package/dist/src/components/schedule/index.d.ts.map +1 -1
  73. package/dist/src/components/schedule/index.js +7 -8
  74. package/dist/src/components/schedule/index.js.map +1 -1
  75. package/dist/src/components/schedule/schedule.types.d.ts +17 -17
  76. package/dist/src/components/schedule/schedule.types.d.ts.map +1 -1
  77. package/dist/src/core/index.d.ts +1 -0
  78. package/dist/src/core/index.d.ts.map +1 -1
  79. package/dist/src/core/index.js +3 -1
  80. package/dist/src/core/index.js.map +1 -1
  81. package/dist/src/core/source.d.ts +4 -0
  82. package/dist/src/core/source.d.ts.map +1 -0
  83. package/dist/src/core/source.js +9 -0
  84. package/dist/src/core/source.js.map +1 -0
  85. package/dist/src/index.d.ts +6 -1
  86. package/dist/src/index.d.ts.map +1 -1
  87. package/dist/src/index.js +10 -1
  88. package/dist/src/index.js.map +1 -1
  89. package/dist/src/nativewind/index.d.ts +28 -27
  90. package/dist/src/nativewind/index.d.ts.map +1 -1
  91. package/docs/migrations.md +61 -0
  92. package/llms.txt +112 -30
  93. package/package.json +12 -5
  94. package/src/components/layers/README.md +7 -3
  95. package/src/components/primitives/README.md +13 -2
  96. package/src/components/primitives/portal.tsx +48 -0
  97. package/src/components/roster/README.md +62 -5
  98. package/src/components/roster/body-layout.tsx +38 -0
  99. package/src/components/roster/index.tsx +109 -46
  100. package/src/components/roster/lanes/lane.tsx +16 -13
  101. package/src/components/roster/parts/body.tsx +17 -84
  102. package/src/components/roster/parts/header-cell.tsx +2 -2
  103. package/src/components/roster/parts/header.tsx +7 -2
  104. package/src/components/roster/parts/lane-label-column.tsx +2 -2
  105. package/src/components/roster/parts/lane-list.tsx +76 -0
  106. package/src/components/roster/roster.types.ts +47 -17
  107. package/src/components/roster/selection/selection-layout.tsx +90 -0
  108. package/src/components/roster/selection/selection-layout.types.ts +20 -0
  109. package/src/components/roster/selection/selection-layout.web.tsx +88 -0
  110. package/src/components/roster/use-roster-press.ts +74 -0
  111. package/src/components/roster/use-roster.ts +57 -44
  112. package/src/components/roster/utils/body-content-key.ts +13 -13
  113. package/src/components/schedule/README.md +8 -3
  114. package/src/components/schedule/days/day.tsx +31 -18
  115. package/src/components/schedule/days/header-layout.tsx +1 -1
  116. package/src/components/schedule/days/parts/column.tsx +10 -12
  117. package/src/components/schedule/index.tsx +20 -21
  118. package/src/components/schedule/schedule.types.ts +13 -13
  119. package/src/core/README.md +4 -0
  120. package/src/core/index.ts +1 -0
  121. package/src/core/source.ts +9 -0
  122. package/src/index.ts +7 -0
@@ -0,0 +1,4 @@
1
+ import type { Source } from './types';
2
+ /** Match source identity by kind and id, ignoring display metadata. */
3
+ export declare function hasSource(sources: readonly Source[], source: Source | undefined): boolean;
4
+ //# sourceMappingURL=source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../../src/core/source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEtC,uEAAuE;AACvE,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAKzF"}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasSource = hasSource;
4
+ /** Match source identity by kind and id, ignoring display metadata. */
5
+ function hasSource(sources, source) {
6
+ return (source !== undefined &&
7
+ sources.some((item) => item.kind === source.kind && item.id === source.id));
8
+ }
9
+ //# sourceMappingURL=source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.js","sourceRoot":"","sources":["../../../src/core/source.ts"],"names":[],"mappings":";;;AAEA,uEAAuE;AACvE,mBAA0B,OAA0B,EAAE,MAA0B;IAC9E,OAAO,CACL,MAAM,KAAK,SAAS;QACpB,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC,CAC3E,CAAC;AACJ,CAAC"}
@@ -1,7 +1,9 @@
1
1
  export type { GapInput, IntervalInput } from './components/layers/layers.types';
2
2
  export { RosterGap } from './components/layers/parts/gap';
3
3
  export { RosterInterval } from './components/layers/parts/interval';
4
+ export { Portal, PortalHost } from './components/primitives/portal';
4
5
  export { Roster } from './components/roster';
6
+ export { RosterBodyLayout } from './components/roster/body-layout';
5
7
  export { RosterLaneLabel } from './components/roster/lanes/parts/lane-label';
6
8
  export { RosterBody } from './components/roster/parts/body';
7
9
  export { RosterCorner } from './components/roster/parts/corner';
@@ -10,7 +12,10 @@ export { RosterGrid } from './components/roster/parts/grid';
10
12
  export { RosterHeader } from './components/roster/parts/header';
11
13
  export { RosterHeaderCell } from './components/roster/parts/header-cell';
12
14
  export { RosterLaneLabelColumn } from './components/roster/parts/lane-label-column';
13
- export type { BodyInput, GridInput, HeaderInput, LabelColumnInput, LaneLabelInput, RosterInput, RosterModel, RosterProjection, RosterProps, RosterScroll, RosterStyleProps, RosterTick, } from './components/roster/roster.types';
15
+ export { RosterLaneList } from './components/roster/parts/lane-list';
16
+ export type { BodyInput, GridInput, HeaderCellInput, HeaderInput, IntervalDetailInput, LabelColumnInput, LaneLabelInput, RosterInput, RosterModel, RosterProjection, RosterProps, RosterScroll, RosterStyleProps, RosterTick, } from './components/roster/roster.types';
17
+ export { RosterSelectionPopover } from './components/roster/selection/selection-layout';
18
+ export type { SelectionLayoutProps } from './components/roster/selection/selection-layout.types';
14
19
  export { useRoster } from './components/roster/use-roster';
15
20
  export { Schedule } from './components/schedule';
16
21
  export { ScheduleColumn } from './components/schedule/days/parts/column';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACpF,YAAY,EACV,SAAS,EACT,SAAS,EACT,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,UAAU,GACX,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6CAA6C,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,cAAc,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACpF,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AACrE,YAAY,EACV,SAAS,EACT,SAAS,EACT,eAAe,EACf,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,UAAU,GACX,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,gDAAgD,CAAC;AACxF,YAAY,EAAE,oBAAoB,EAAE,MAAM,sDAAsD,CAAC;AACjG,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6CAA6C,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,cAAc,QAAQ,CAAC"}
package/dist/src/index.js CHANGED
@@ -14,13 +14,18 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.useSchedule = exports.ScheduleIncomplete = exports.ScheduleGutter = exports.ScheduleTransition = exports.ScheduleSkippedDate = exports.ScheduleNowLine = exports.ScheduleGrid = exports.ScheduleDayHeader = exports.ScheduleColumn = exports.Schedule = exports.useRoster = exports.RosterLaneLabelColumn = exports.RosterHeaderCell = exports.RosterHeader = exports.RosterGrid = exports.RosterEmpty = exports.RosterCorner = exports.RosterBody = exports.RosterLaneLabel = exports.Roster = exports.RosterInterval = exports.RosterGap = void 0;
17
+ exports.useSchedule = exports.ScheduleIncomplete = exports.ScheduleGutter = exports.ScheduleTransition = exports.ScheduleSkippedDate = exports.ScheduleNowLine = exports.ScheduleGrid = exports.ScheduleDayHeader = exports.ScheduleColumn = exports.Schedule = exports.useRoster = exports.RosterSelectionPopover = exports.RosterLaneList = exports.RosterLaneLabelColumn = exports.RosterHeaderCell = exports.RosterHeader = exports.RosterGrid = exports.RosterEmpty = exports.RosterCorner = exports.RosterBody = exports.RosterLaneLabel = exports.RosterBodyLayout = exports.Roster = exports.PortalHost = exports.Portal = exports.RosterInterval = exports.RosterGap = void 0;
18
18
  var gap_1 = require("./components/layers/parts/gap");
19
19
  Object.defineProperty(exports, "RosterGap", { enumerable: true, get: function () { return gap_1.RosterGap; } });
20
20
  var interval_1 = require("./components/layers/parts/interval");
21
21
  Object.defineProperty(exports, "RosterInterval", { enumerable: true, get: function () { return interval_1.RosterInterval; } });
22
+ var portal_1 = require("./components/primitives/portal");
23
+ Object.defineProperty(exports, "Portal", { enumerable: true, get: function () { return portal_1.Portal; } });
24
+ Object.defineProperty(exports, "PortalHost", { enumerable: true, get: function () { return portal_1.PortalHost; } });
22
25
  var roster_1 = require("./components/roster");
23
26
  Object.defineProperty(exports, "Roster", { enumerable: true, get: function () { return roster_1.Roster; } });
27
+ var body_layout_1 = require("./components/roster/body-layout");
28
+ Object.defineProperty(exports, "RosterBodyLayout", { enumerable: true, get: function () { return body_layout_1.RosterBodyLayout; } });
24
29
  var lane_label_1 = require("./components/roster/lanes/parts/lane-label");
25
30
  Object.defineProperty(exports, "RosterLaneLabel", { enumerable: true, get: function () { return lane_label_1.RosterLaneLabel; } });
26
31
  var body_1 = require("./components/roster/parts/body");
@@ -37,6 +42,10 @@ var header_cell_1 = require("./components/roster/parts/header-cell");
37
42
  Object.defineProperty(exports, "RosterHeaderCell", { enumerable: true, get: function () { return header_cell_1.RosterHeaderCell; } });
38
43
  var lane_label_column_1 = require("./components/roster/parts/lane-label-column");
39
44
  Object.defineProperty(exports, "RosterLaneLabelColumn", { enumerable: true, get: function () { return lane_label_column_1.RosterLaneLabelColumn; } });
45
+ var lane_list_1 = require("./components/roster/parts/lane-list");
46
+ Object.defineProperty(exports, "RosterLaneList", { enumerable: true, get: function () { return lane_list_1.RosterLaneList; } });
47
+ var selection_layout_1 = require("./components/roster/selection/selection-layout");
48
+ Object.defineProperty(exports, "RosterSelectionPopover", { enumerable: true, get: function () { return selection_layout_1.RosterSelectionPopover; } });
40
49
  var use_roster_1 = require("./components/roster/use-roster");
41
50
  Object.defineProperty(exports, "useRoster", { enumerable: true, get: function () { return use_roster_1.useRoster; } });
42
51
  var schedule_1 = require("./components/schedule");
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AACA,qDAA0D;AAAjD,gGAAA,SAAS,OAAA;AAClB,+DAAoE;AAA3D,0GAAA,cAAc,OAAA;AACvB,8CAA6C;AAApC,gGAAA,MAAM,OAAA;AACf,yEAA6E;AAApE,6GAAA,eAAe,OAAA;AACxB,uDAA4D;AAAnD,kGAAA,UAAU,OAAA;AACnB,2DAAgE;AAAvD,sGAAA,YAAY,OAAA;AACrB,yDAA8D;AAArD,oGAAA,WAAW,OAAA;AACpB,uDAA4D;AAAnD,kGAAA,UAAU,OAAA;AACnB,2DAAgE;AAAvD,sGAAA,YAAY,OAAA;AACrB,qEAAyE;AAAhE,+GAAA,gBAAgB,OAAA;AACzB,iFAAoF;AAA3E,0HAAA,qBAAqB,OAAA;AAe9B,6DAA2D;AAAlD,uGAAA,SAAS,OAAA;AAClB,kDAAiD;AAAxC,oGAAA,QAAQ,OAAA;AACjB,kEAAyE;AAAhE,wGAAA,cAAc,OAAA;AACvB,0EAAgF;AAAvE,+GAAA,iBAAiB,OAAA;AAC1B,8DAAqE;AAA5D,oGAAA,YAAY,OAAA;AACrB,sEAA4E;AAAnE,2GAAA,eAAe,OAAA;AACxB,8EAAoF;AAA3E,mHAAA,mBAAmB,OAAA;AAC5B,0EAAiF;AAAxE,gHAAA,kBAAkB,OAAA;AAC3B,6DAAoE;AAA3D,wGAAA,cAAc,OAAA;AACvB,qEAA4E;AAAnE,gHAAA,kBAAkB,OAAA;AAY3B,mEAAiE;AAAxD,2GAAA,WAAW,OAAA;AACpB,yCAAuB"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AACA,qDAA0D;AAAjD,gGAAA,SAAS,OAAA;AAClB,+DAAoE;AAA3D,0GAAA,cAAc,OAAA;AACvB,yDAAoE;AAA3D,gGAAA,MAAM,OAAA;AAAE,oGAAA,UAAU,OAAA;AAC3B,8CAA6C;AAApC,gGAAA,MAAM,OAAA;AACf,+DAAmE;AAA1D,+GAAA,gBAAgB,OAAA;AACzB,yEAA6E;AAApE,6GAAA,eAAe,OAAA;AACxB,uDAA4D;AAAnD,kGAAA,UAAU,OAAA;AACnB,2DAAgE;AAAvD,sGAAA,YAAY,OAAA;AACrB,yDAA8D;AAArD,oGAAA,WAAW,OAAA;AACpB,uDAA4D;AAAnD,kGAAA,UAAU,OAAA;AACnB,2DAAgE;AAAvD,sGAAA,YAAY,OAAA;AACrB,qEAAyE;AAAhE,+GAAA,gBAAgB,OAAA;AACzB,iFAAoF;AAA3E,0HAAA,qBAAqB,OAAA;AAC9B,iEAAqE;AAA5D,2GAAA,cAAc,OAAA;AAiBvB,mFAAwF;AAA/E,0HAAA,sBAAsB,OAAA;AAE/B,6DAA2D;AAAlD,uGAAA,SAAS,OAAA;AAClB,kDAAiD;AAAxC,oGAAA,QAAQ,OAAA;AACjB,kEAAyE;AAAhE,wGAAA,cAAc,OAAA;AACvB,0EAAgF;AAAvE,+GAAA,iBAAiB,OAAA;AAC1B,8DAAqE;AAA5D,oGAAA,YAAY,OAAA;AACrB,sEAA4E;AAAnE,2GAAA,eAAe,OAAA;AACxB,8EAAoF;AAA3E,mHAAA,mBAAmB,OAAA;AAC5B,0EAAiF;AAAxE,gHAAA,kBAAkB,OAAA;AAC3B,6DAAoE;AAA3D,wGAAA,cAAc,OAAA;AACvB,qEAA4E;AAAnE,gHAAA,kBAAkB,OAAA;AAY3B,mEAAiE;AAAxD,2GAAA,WAAW,OAAA;AACpB,yCAAuB"}
@@ -8,21 +8,22 @@ type Mapping<T> = {
8
8
  /** Each class prop resolves into the style prop of the same chrome region. */
9
9
  export declare const rosterClassNames: Mapping<RosterStyleProps>;
10
10
  export declare const scheduleClassNames: Mapping<ScheduleStyleProps>;
11
- export declare const Roster: import("react").ComponentType<import("../components/roster/roster.types").RosterInput & RosterStyleProps & {
11
+ export declare const Roster: import("react").ComponentType<Omit<import("../components/roster/roster.types").RosterInput, "selectable"> & RosterStyleProps & {
12
+ intervalDetailComponent?: import("react").ComponentType<import("../components/roster/roster.types").IntervalDetailInput>;
13
+ selectionLayout?: import("react").ComponentType<import("..").SelectionLayoutProps>;
14
+ portalHost?: string;
12
15
  incompleteLabel?: string;
13
16
  neverSetLabel?: string;
14
- laneLabelZone?: (input: import("../components/roster/roster.types").LaneLabelInput) => import("react").ReactNode;
15
- headerCellZone?: (input: {
16
- tick: import("../components/roster/roster.types").RosterTick;
17
- }) => import("react").ReactNode;
18
- intervalZone?: (input: import("..").IntervalInput) => import("react").ReactNode;
19
- gapZone?: (input: import("..").GapInput) => import("react").ReactNode;
20
- gridZone?: (input: import("../components/roster/roster.types").GridInput) => import("react").ReactNode;
21
- emptyZone?: () => import("react").ReactNode;
22
- cornerZone?: () => import("react").ReactNode;
23
- headerZone?: (input: import("../components/roster/roster.types").HeaderInput) => import("react").ReactNode;
24
- laneLabelColumnZone?: (input: import("../components/roster/roster.types").LabelColumnInput) => import("react").ReactNode;
25
- bodyZone?: (input: import("../components/roster/roster.types").BodyInput) => import("react").ReactNode;
17
+ laneLabelComponent?: import("react").ComponentType<import("../components/roster/roster.types").LaneLabelInput>;
18
+ headerCellComponent?: import("react").ComponentType<import("../components/roster/roster.types").HeaderCellInput>;
19
+ intervalComponent?: import("react").ComponentType<import("..").IntervalInput>;
20
+ gapComponent?: import("react").ComponentType<import("..").GapInput>;
21
+ gridComponent?: import("react").ComponentType<import("../components/roster/roster.types").GridInput>;
22
+ emptyZone?: import("react").ReactNode;
23
+ cornerZone?: import("react").ReactNode;
24
+ headerComponent?: import("react").ComponentType<import("../components/roster/roster.types").HeaderInput>;
25
+ laneLabelColumnComponent?: import("react").ComponentType<import("../components/roster/roster.types").LabelColumnInput>;
26
+ bodyComponent?: import("react").ComponentType<import("../components/roster/roster.types").BodyInput>;
26
27
  } & {
27
28
  bodyClassName?: string | undefined;
28
29
  className?: string | undefined;
@@ -31,26 +32,26 @@ export declare const Roster: import("react").ComponentType<import("../components
31
32
  }>;
32
33
  export declare const Schedule: import("react").ComponentType<import("../components/schedule/schedule.types").ScheduleInput & ScheduleStyleProps & {
33
34
  incompleteLabel?: string;
34
- gutterZone?: (input: import("../components/schedule/schedule.types").ScheduleHoursInput) => import("react").ReactNode;
35
- gridZone?: (input: import("../components/schedule/schedule.types").ScheduleHoursInput) => import("react").ReactNode;
36
- dayHeaderZone?: (input: {
35
+ gutterComponent?: import("react").ComponentType<import("../components/schedule/schedule.types").ScheduleHoursInput>;
36
+ gridComponent?: import("react").ComponentType<import("../components/schedule/schedule.types").ScheduleHoursInput>;
37
+ dayHeaderComponent?: import("react").ComponentType<{
37
38
  day: import("..").DayColumn;
38
- }) => import("react").ReactNode;
39
- skippedDateZone?: (input: {
39
+ }>;
40
+ skippedDateComponent?: import("react").ComponentType<{
40
41
  localDate: string;
41
- }) => import("react").ReactNode;
42
- columnZone?: (input: import("../components/schedule/schedule.types").ScheduleColumnInput) => import("react").ReactNode;
43
- transitionZone?: (input: import("../components/schedule/schedule.types").ScheduleTransitionInput) => import("react").ReactNode;
44
- nowLineZone?: (input: {
42
+ }>;
43
+ columnComponent?: import("react").ComponentType<import("../components/schedule/schedule.types").ScheduleColumnInput>;
44
+ transitionComponent?: import("react").ComponentType<import("../components/schedule/schedule.types").ScheduleTransitionInput>;
45
+ nowLineComponent?: import("react").ComponentType<{
45
46
  y: number;
46
47
  column: number;
47
- }) => import("react").ReactNode;
48
- intervalZone?: (input: import("..").IntervalInput) => import("react").ReactNode;
49
- gapZone?: (input: import("..").GapInput) => import("react").ReactNode;
50
- incompleteZone?: (input: {
48
+ }>;
49
+ intervalComponent?: import("react").ComponentType<import("..").IntervalInput>;
50
+ gapComponent?: import("react").ComponentType<import("..").GapInput>;
51
+ incompleteComponent?: import("react").ComponentType<{
51
52
  lane: import("..").Lane;
52
53
  label: string;
53
- }) => import("react").ReactNode;
54
+ }>;
54
55
  } & {
55
56
  className?: string | undefined;
56
57
  daysClassName?: string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/nativewind/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAE1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAEhF,KAAK,aAAa,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,WAAW,GAAG,GAAG,MAAM,WAAW,CAAC,CAAC;AACtF,KAAK,WAAW,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,MAAM,WAAW,GACrE,GAAG,MAAM,OAAO,GAChB,OAAO,CAAC;AACZ,KAAK,OAAO,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC;CAAE,CAAC;AAEhE,8EAA8E;AAC9E,eAAO,MAAM,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,CAKtD,CAAC;AACF,eAAO,MAAM,kBAAkB,EAAE,OAAO,CAAC,kBAAkB,CAK1D,CAAC;AAIF,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;EAA2C,CAAC;AAC/D,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;EAA+C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/nativewind/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAE1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAEhF,KAAK,aAAa,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,WAAW,GAAG,GAAG,MAAM,WAAW,CAAC,CAAC;AACtF,KAAK,WAAW,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,MAAM,WAAW,GACrE,GAAG,MAAM,OAAO,GAChB,OAAO,CAAC;AACZ,KAAK,OAAO,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC;CAAE,CAAC;AAEhE,8EAA8E;AAC9E,eAAO,MAAM,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,CAKtD,CAAC;AACF,eAAO,MAAM,kBAAkB,EAAE,OAAO,CAAC,kBAAkB,CAK1D,CAAC;AAIF,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;EAA2C,CAAC;AAC/D,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;EAA+C,CAAC"}
@@ -0,0 +1,61 @@
1
+ # Migrations
2
+
3
+ Changes to public props that need consumer edits, by the version that ships them.
4
+ The changelog lists every change; this page carries only the ones with a
5
+ migration step.
6
+
7
+ ## 0.1.0: slot components replace render functions
8
+
9
+ Every Roster and Schedule prop that used to take a render function now takes a
10
+ component type or a node. Props ending in `Component` accept
11
+ `ComponentType<Input>` and are mounted by React with the same input as props.
12
+ Props ending in `Zone` accept `ReactNode`. Press callbacks (`onIntervalPress`,
13
+ `onGapPress`, `onCellPress`, `onIntervalHover`, `onNavigate`) are unchanged.
14
+
15
+ | Before (0.0.x) | After (0.1.0) | Input |
16
+ | --- | --- | --- |
17
+ | `emptyZone={() => <Empty />}` | `emptyZone={<Empty />}` | none |
18
+ | `cornerZone={() => <Corner />}` | `cornerZone={<Corner />}` | none |
19
+ | `headerZone={(input) => ...}` | `headerComponent={Header}` | `HeaderInput` |
20
+ | `laneLabelColumnZone={(input) => ...}` | `laneLabelColumnComponent={LabelColumn}` | `LabelColumnInput` |
21
+ | `bodyZone={(input) => ...}` | `bodyComponent={Body}` | `BodyInput` |
22
+ | `headerCellZone={({ tick }) => ...}` | `headerCellComponent={HeaderCell}` | `HeaderCellInput` |
23
+ | `laneLabelZone={(input) => ...}` | `laneLabelComponent={LaneLabel}` | `LaneLabelInput` |
24
+ | `intervalZone={(input) => ...}` | `intervalComponent={Interval}` | `IntervalInput` |
25
+ | `gapZone={(input) => ...}` | `gapComponent={Gap}` | `GapInput` |
26
+ | `gridZone={(input) => ...}` | `gridComponent={Grid}` | `GridInput` |
27
+
28
+ Schedule follows the same rule: `gutterZone`, `gridZone`, `dayHeaderZone`,
29
+ `skippedDateZone`, `columnZone`, `transitionZone`, `nowLineZone`, `intervalZone`,
30
+ `gapZone`, and `incompleteZone` become the matching `Component` props with the
31
+ same inputs.
32
+
33
+ Nested contracts renamed with them: `HeaderInput.headerCellZone` is now
34
+ `headerCellComponent`, `LabelColumnInput.laneLabelZone` is now
35
+ `laneLabelComponent`, and `BodyInput` carries `intervalComponent`,
36
+ `gapComponent`, and `gridComponent`.
37
+
38
+ Mechanical rewrite for a render function that was already a plain function of
39
+ its input:
40
+
41
+ ```tsx
42
+ // Before
43
+ <Roster intervalZone={({ rect, layer }) => <Bar rect={rect} layer={layer} />} />
44
+
45
+ // After: the same function, declared once at module scope and passed as a type
46
+ function Interval({ rect, layer }: IntervalInput) {
47
+ return <Bar rect={rect} layer={layer} />;
48
+ }
49
+ <Roster intervalComponent={Interval} />
50
+ ```
51
+
52
+ Declare slot components at module scope. An inline arrow creates a new
53
+ component type every render, which remounts every interval and defeats the
54
+ body's content key.
55
+
56
+ The default parts are exported under their existing names (`RosterInterval`,
57
+ `RosterGap`, `RosterLaneLabel`, `RosterHeaderCell`, `RosterGrid`, `RosterHeader`,
58
+ `RosterLaneLabelColumn`, `RosterBody`, `RosterEmpty`, `RosterCorner`) and can be
59
+ composed inside a replacement. `RosterBody` is now built from `RosterBodyLayout`
60
+ (arranges `gridZone` and `listZone`) and `RosterLaneList` (owns the virtualized
61
+ lane list); both are exported for consumers that replace the body.
package/llms.txt CHANGED
@@ -144,12 +144,12 @@ show the incompleteLabel; do not describe incomplete empty time as definitive.
144
144
 
145
145
  | Import | Runtime exports and purpose |
146
146
  | --- | --- |
147
- | react-native-roster | Roster, Schedule, useRoster, useSchedule, default zone fillers, and core re-exports. |
147
+ | react-native-roster | Roster, Schedule, hooks, default slot components, RosterSelectionPopover, PortalHost, Portal, and core re-exports. |
148
148
  | react-native-roster/core | layoutLane, coverageFor, flagFor, windowFor, prev, next, today, dayColumnsFor, timeAtX, timeAtY, snapToStep, byLabel, byCoverage, and core counters/caches. |
149
149
  | react-native-roster/rrule | expandRuleSet, envelopeFor, expandStats, resetExpandStats, clearExpandCache; RuleSet, RosterRule, RosterDate, ExpandOptions, ExpandResult, and ExpandStats types. |
150
150
  | react-native-roster/nativewind | Side-effect registration of Roster and Schedule with NativeWind's cssInterop, plus the registered Roster and Schedule and the rosterClassNames/scheduleClassNames mappings. Imports the optional nativewind peer. |
151
151
 
152
- No default exports. Root exports renderer inputs/models/zone types as well as
152
+ No default exports. Root exports renderer inputs/models/slot input types as well as
153
153
  core types. Core imports standard JS and Intl only. Only /rrule imports
154
154
  rrule-temporal 1.6.0 and @js-temporal/polyfill 0.5.1. Those are installed runtime
155
155
  dependencies but cannot enter root/core bundles through library imports.
@@ -160,6 +160,14 @@ from dist/src; declarations use the types condition. Build before Node export
160
160
  tests. Root needs a native renderer or web bundler; direct Node use of root is
161
161
  not a substitute for a React Native app. Core and /rrule work in Node/Bun.
162
162
 
163
+ ## 0.1.0 slot prop rename
164
+
165
+ 0.1.0 renamed every input-bearing render-function Zone prop to a Component prop;
166
+ use the current names below instead of the 0.0.x props. Singleton emptyZone and
167
+ cornerZone keep their names and now take nodes. See [docs/migrations.md](./docs/migrations.md).
168
+ Zone props are ReactNode.
169
+ Component props are ComponentType<Input>.
170
+
163
171
  ## Renderer contracts
164
172
 
165
173
  Roster required props: lanes: Lane[], windowSpec: WindowSpec.
@@ -178,7 +186,7 @@ outside React state.
178
186
 
179
187
  useRoster returns window, projection, orderedLanes, coverage, laneState,
180
188
  geometryFor(lane), scroll, press(lane,x,y), ticks, contentWidth, viewport,
181
- onLayout, navigate, and status ('empty' or 'ready'). There is no visibleRange
189
+ onLayout, navigate, selection, dismissSelection, and status ('empty' or 'ready'). There is no visibleRange
182
190
  field in the shipped public model. Custom layouts must wire onLayout. Custom
183
191
  bodies compute geometry only for mounted lanes, preserving virtualization.
184
192
  A zero-duration window has contentWidth=0 and renders no Roster body. Positive
@@ -198,44 +206,118 @@ The chassis measures width, reserves a 48 px gutter, and fits all actual day
198
206
  columns without horizontal scrolling; the body scrolls vertically. now updates
199
207
  every minute and is null outside the selected window.
200
208
 
201
- Zone props are optional render functions. Use exported fillers inside replacements
202
- to preserve geometry, scrolling, and interaction. Types are exported from root.
209
+ Props ending in Component accept ComponentType<Input>, mounted as React elements.
210
+ Props ending in Zone accept ReactNode; pass null to suppress a singleton default.
211
+ Define component types at module scope; hooks and class components work normally.
212
+ Use exported defaults inside replacements to preserve geometry, scrolling, and
213
+ interaction. Input types, including HeaderCellInput, are exported from root.
214
+ Mount a React context provider above Roster for extra consumer data, and read it with useContext in a module-scope slot component.
215
+ Avoid inline closures or memoized factories returning a new component per render; new interval types remount every interval and defeat the body's content key.
216
+
217
+ RosterBody composes the exported RosterBodyLayout (gridZone/listZone nodes with
218
+ scroll wiring) and RosterLaneList (LegendList, geometryFor, and LaneRow mounting).
219
+ Keep its viewport gate and measured estimatedListSize in custom compositions.
203
220
 
204
221
  ## Roster zones
205
222
 
206
- | Zone | Receives | Default filler and behavior |
223
+ | Slot | Component inputs or node | Default and behavior |
207
224
  | --- | --- | --- |
208
- | `emptyZone` | Nothing | `RosterEmpty`: No lanes. |
209
- | `laneLabelZone` | `lane`, `flag`, `complete`, `viewTimezone`, localized labels | `RosterLaneLabel`: label, differing IANA zone badge, and notices. |
210
- | `headerCellZone` | `tick` | `RosterHeaderCell`: tick label. |
211
- | `intervalZone` | `rect`, `layer`, `lane`, `highlighted` | `RosterInterval`: positioned colored rect, with final inset bounds. |
212
- | `gapZone` | `rect`, `layer`, `lane` | `RosterGap`: no visible content; the row supplies pressable bounds. |
213
- | `gridZone` | `ticks`, `contentWidth` | `RosterGrid`: one hairline per tick behind every lane. |
214
- | `cornerZone` | Nothing | `RosterCorner`: nothing; the cell above the labels, laneLabelWidth wide. |
215
- | `headerZone` | `ticks`, `projection`, `scroll`, `contentWidth`, `headerCellZone` | `RosterHeader`: frozen header following horizontal offset. |
216
- | `laneLabelColumnZone` | `labels: LaneLabelInput[]`, `projection`, `scroll`, `laneLabelZone` | `RosterLaneLabelColumn`: frozen labels following vertical offset. |
217
- | `bodyZone` | Ordered `lanes`, `window`, `geometryFor`, `projection`, `scroll`, `press`, `ticks`, `viewport`, `contentWidth`, highlight/hover, incomplete label, and rect zones | `RosterBody`: virtualized lanes. |
225
+ | `emptyZone` | `ReactNode` | `RosterEmpty`: No lanes. |
226
+ | `laneLabelComponent` | `lane`, `flag`, `complete`, `viewTimezone`, localized labels | `RosterLaneLabel`: label, differing IANA zone badge, and notices. |
227
+ | `headerCellComponent` | `HeaderCellInput` (`tick`) | `RosterHeaderCell`: tick label. |
228
+ | `intervalComponent` | `rect`, `layer`, `lane`, `highlighted` | `RosterInterval`: positioned colored rect, with final inset bounds. |
229
+ | `gapComponent` | `rect`, `layer`, `lane` | `RosterGap`: no visible content; the row supplies pressable bounds. |
230
+ | `gridComponent` | `ticks`, `contentWidth` | `RosterGrid`: one hairline per tick behind every lane. |
231
+ | `cornerZone` | `ReactNode` | `RosterCorner`: nothing; the cell above the labels, laneLabelWidth wide. |
232
+ | `headerComponent` | `ticks`, `projection`, `scroll`, `contentWidth`, `headerCellComponent` | `RosterHeader`: frozen header following horizontal offset. |
233
+ | `laneLabelColumnComponent` | `labels: LaneLabelInput[]`, `projection`, `scroll`, `laneLabelComponent` | `RosterLaneLabelColumn`: frozen labels following vertical offset. |
234
+ | `bodyComponent` | Ordered `lanes`, `window`, `geometryFor`, `projection`, `scroll`, `press`, `ticks`, `viewport`, `contentWidth`, highlight/hover, incomplete label, and rect components | `RosterBody`: virtualized lanes. |
235
+
236
+ ## Selection
237
+
238
+ Roster only; Schedule does not yet support selection and will gain it in a later change.
239
+ intervalDetailComponent?: ComponentType<IntervalDetailInput> enables selection on interval
240
+ press while preserving onIntervalPress. Absent content retains no selection.
241
+ Pressing the selected interval again dismisses it; pressing another interval
242
+ switches selection. Cell and gap presses dismiss selection while still firing
243
+ `onCellPress` and `onGapPress`. These rules live in the hook and apply on native
244
+ and web; web outside press and Escape still dismiss.
245
+ IntervalDetailInput is IntervalInput plus absolute start and end and viewTimezone.
246
+ useRoster owns selection: {rect,layer,lane,start,end}|null and dismissSelection(). It resolves
247
+ current lane/layer/rect references and clears removed lane or absolute rect bounds.
248
+ Resizing and sorting retain valid selection with current geometry; reconciliation
249
+ uses the private reconcileSelection helper to match source identity sets and choose
250
+ nearest bounds with a total difference of at most 1 ms for projection roundoff.
251
+ selectable?: boolean belongs only to the hook input, RosterInput, and defaults to false.
252
+ It is excluded from RosterProps; hook consumers pass
253
+ selectable: true to retain selection. intervalDetailComponent, selectionLayout, and
254
+ portalHost belong to RosterProps because the chassis mounts content and the layout.
255
+ The chassis passes selectable: Boolean(intervalDetailComponent) to useRoster.
256
+ Custom chassis mount their content and layout using the returned model.
257
+
258
+ selectionLayout?: ComponentType<SelectionLayoutProps> defaults to the root-exported
259
+ RosterSelectionPopover. It is the explicit naming exception to the Component suffix.
260
+ The chassis mounts the strategy once around the body node, then passes that node
261
+ to RosterLayout.bodyZone. SelectionLayoutProps is exported: anchorZone and contentZone
262
+ are ReactNode; targetBounds is {x,y,width,height}|null in body-content coordinates, including
263
+ the lane offset and interval inset; open is
264
+ boolean; onDismiss is () => void; portalHost is string; scroll is Pick<RosterScroll, 'x' | 'y' | 'headerStyle' | 'labelStyle'>.
265
+ Layouts receive no lane, layer, or rect. Render anchorZone once and arrange contentZone
266
+ above or beside it. The interval-detail fixture demonstrates a runtime inspector toggle.
267
+
268
+ Native selection-layout.tsx uses the root-exported PortalHost and Portal from
269
+ components/primitives/portal.tsx, a module-level Map store with useSyncExternalStore.
270
+ The default layout owns a host named from the roster's useId; portalHost overrides
271
+ that name. Use distinct names for independent rosters and never mount the same host
272
+ twice. Custom layouts may target an ancestor host they own. Caller context does not
273
+ cross a store portal; provide context above the host or inside the portaled node.
274
+ Native hardware back dismisses details. The detail card flips above
275
+ when that fits, or uses the top edge when neither vertical placement fits. It clamps
276
+ left using measured viewport and content widths. Shared x/y values position the
277
+ overlay in viewport coordinates, never React scroll state. Selection identity
278
+ must not enter the body content key or cause mounted rows to render. The stable press
279
+ ref lives in use-roster-press.ts so React Compiler can retain derived body inputs in useRoster.
280
+
281
+ Native has no intercepting dismissal overlay; body presses reach the hook, and the detail
282
+ card captures its own presses. Web excludes the body wrapper from Radix outside
283
+ dismissal, leaving body presses to the hook while preserving true outside presses and Escape.
284
+ The hook toggles the selected interval closed, switches to another interval, and dismisses
285
+ on cell or gap presses while preserving their callbacks. The body restores horizontal
286
+ and vertical offsets from shared values on remount when selectionLayout changes.
287
+ A mount effect calls the horizontal ScrollView ref's scrollTo without animation;
288
+ native also retains contentOffset. LegendList restores initialScrollOffset through
289
+ its own web mount effect and native initial offset.
290
+
291
+ selection-layout.web.tsx uses @radix-ui/react-popover, required by the web root entry
292
+ but an optional peer for native/core-only consumers. Source web bundlers (Vite,
293
+ Storybook, or Metro web) require it even with selection disabled because the web
294
+ layout is in the module graph. Install with Bun. The emitted
295
+ browser remap must travel with the shared selection-layout.types.ts contract, just
296
+ like press-point and interval-hover. Its zero-size, pointer-transparent anchor follows
297
+ the shared translation; Radix's portal follows anchor movement and owns outside press,
298
+ Escape, and collisions. The layout returns focus to the previously focused element
299
+ only after Escape.
218
300
 
219
301
  ## Schedule zones
220
302
 
221
- | Zone | Receives | Default filler and behavior |
303
+ | Slot | Component inputs or node | Default and behavior |
222
304
  | --- | --- | --- |
223
- | `gutterZone` | `hours`, `pxPerHour` | `ScheduleGutter`: 24 frozen hour labels. |
224
- | `gridZone` | `hours`, `pxPerHour` | `ScheduleGrid`: 24 bordered hour bands behind each day's rects. |
225
- | `dayHeaderZone` | `day` | `ScheduleDayHeader`: weekday, localDate, and transition badge. |
226
- | `skippedDateZone` | `localDate` | `ScheduleSkippedDate`: zero-width header marker for a wholly skipped date. |
227
- | `columnZone` | `day`, `rects`, `gapRects`, `lane`, `highlightSource`, `press`, interval/gap zones | `ScheduleColumn`: final rect bounds in layer order. |
228
- | `transitionZone` | `day`, `transition`, `y`, `dividerY`, `height`, `width` | `ScheduleTransition`: skipped-time hatch, or repeat divider and again label. |
229
- | `nowLineZone` | `y`, `column` | `ScheduleNowLine`: line in the current day's column, updated each minute. |
230
- | `intervalZone`, `gapZone` | Same inputs as Roster | Shared `RosterInterval` and `RosterGap`. |
231
- | `incompleteZone` | `lane`, `label` | `ScheduleIncomplete`: notice in reserved space above the grid when incomplete. |
305
+ | `gutterComponent` | `hours`, `pxPerHour` | `ScheduleGutter`: 24 frozen hour labels. |
306
+ | `gridComponent` | `hours`, `pxPerHour` | `ScheduleGrid`: 24 bordered hour bands behind each day's rects. |
307
+ | `dayHeaderComponent` | `day` | `ScheduleDayHeader`: weekday, localDate, and transition badge. |
308
+ | `skippedDateComponent` | `localDate` | `ScheduleSkippedDate`: zero-width header marker for a wholly skipped date. |
309
+ | `columnComponent` | `day`, `rects`, `gapRects`, `lane`, `highlightSource`, `press`, interval/gap components | `ScheduleColumn`: final rect bounds in layer order. |
310
+ | `transitionComponent` | `day`, `transition`, `y`, `dividerY`, `height`, `width` | `ScheduleTransition`: skipped-time hatch, or repeat divider and again label. |
311
+ | `nowLineComponent` | `y`, `column` | `ScheduleNowLine`: line in the current day's column, updated each minute. |
312
+ | `intervalComponent`, `gapComponent` | Same inputs as Roster | Shared `RosterInterval` and `RosterGap`. |
313
+ | `incompleteComponent` | `lane`, `label` | `ScheduleIncomplete`: notice in reserved space above the grid when incomplete. |
232
314
 
233
315
  Position custom interval content at rect.x/y with width/height/z and
234
316
  pointerEvents="none". Gap content already sits in a positioned pressable.
235
317
  Column x coordinates are local; select the column through rect.column.
236
- Zone fillers are plain React Native views; under NativeWind they take className
318
+ Slot components are plain React Native views; under NativeWind they take className
237
319
  directly. The default RosterLaneLabel owns its row's bottom border, so a custom
238
- laneLabelZone draws its own separators.
320
+ laneLabelComponent draws its own separators.
239
321
 
240
322
  ## Navigation and zones
241
323
 
@@ -255,7 +337,7 @@ Schedule always uses 24 equal wall-hour bands. Positive deltaMinutes means a
255
337
  skipped empty region; negative means two half-height repeat regions. Rects split
256
338
  at column edges, source changes, and every scale boundary. For repeat at T with
257
339
  magnitude d, boundaries are T-d, T, and T+d. A wholly skipped date produces no
258
- column; skippedDateZone labels the header gap (Apia 2011-12-30).
340
+ column; skippedDateComponent labels the header gap (Apia 2011-12-30).
259
341
  Each column spans the first instant of its date to the first instant of the next.
260
342
  Skipped wall spans belong to the date they interrupt. Its transitions include a
261
343
  skip at the exclusive end when it empties that date's final wall bands (Nuuk
@@ -457,7 +539,7 @@ W: seed 1318, 200 lanes, week, two layers, 24-lane layout batch, 15-minute ticks
457
539
  Bun 1.4.0, 2026-09-08T05:18:00Z, commit
458
540
  1c4ac0e8580b1d46c043f946aa7ebc1ee316d0fa. Median of 11 target-cold samples after
459
541
  five runtime warmups. Each timing must stay below 16 ms; CI also enforces <=1.5x
460
- its baseline. Size gates: core <15 kB, root <40 kB minified/uncompressed, peers
542
+ its baseline. Size gates: core <15 kB, root <44 kB minified/uncompressed, peers
461
543
  external. Phone fps and cold-layout numbers are not yet captured; the paired
462
544
  react-big-scheduler comparison is not yet measured. Do not claim relative speed.
463
545
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-roster",
3
- "version": "0.0.1",
3
+ "version": "0.2.0",
4
4
  "description": "Layered intervals with provenance across lanes and a shared time axis for React Native.",
5
5
  "keywords": [
6
6
  "react-native",
@@ -31,7 +31,8 @@
31
31
  "types": "./dist/src/index.d.ts",
32
32
  "browser": {
33
33
  "./dist/src/components/primitives/press-point.js": "./dist/src/components/primitives/press-point.web.js",
34
- "./dist/src/components/roster/lanes/interval-hover.js": "./dist/src/components/roster/lanes/interval-hover.web.js"
34
+ "./dist/src/components/roster/lanes/interval-hover.js": "./dist/src/components/roster/lanes/interval-hover.web.js",
35
+ "./dist/src/components/roster/selection/selection-layout.js": "./dist/src/components/roster/selection/selection-layout.web.js"
35
36
  },
36
37
  "exports": {
37
38
  ".": {
@@ -66,7 +67,8 @@
66
67
  "README.md",
67
68
  "LICENSE",
68
69
  "NOTICE.md",
69
- "llms.txt"
70
+ "llms.txt",
71
+ "docs/migrations.md"
70
72
  ],
71
73
  "scripts": {
72
74
  "clean": "rm -rf dist",
@@ -100,7 +102,8 @@
100
102
  "nativewind": ">=4.1.0",
101
103
  "react": ">=18.2.0",
102
104
  "react-native": ">=0.74.0",
103
- "react-native-reanimated": ">=3.19.0"
105
+ "react-native-reanimated": ">=3.19.0",
106
+ "@radix-ui/react-popover": "^1.1.23"
104
107
  },
105
108
  "peerDependenciesMeta": {
106
109
  "expo": {
@@ -111,6 +114,9 @@
111
114
  },
112
115
  "react-native-reanimated": {
113
116
  "optional": true
117
+ },
118
+ "@radix-ui/react-popover": {
119
+ "optional": true
114
120
  }
115
121
  },
116
122
  "dependencies": {
@@ -149,7 +155,8 @@
149
155
  "semantic-release": "^25.0.3",
150
156
  "semver": "^7.7.0",
151
157
  "size-limit": "^13.0.3",
152
- "temporal-spec": "1.0.1"
158
+ "temporal-spec": "1.0.1",
159
+ "@radix-ui/react-popover": "1.1.23"
153
160
  },
154
161
  "publishConfig": {
155
162
  "access": "public",
@@ -1,11 +1,15 @@
1
1
  # layers
2
2
 
3
3
  This area is about a layer; its children are intervals and gaps. Both
4
- projections draw the same fillers, so they live above Roster and Schedule.
4
+ projections draw the same components, so they live above Roster and Schedule.
5
5
 
6
6
  - Root entry exports: `RosterInterval`, `RosterGap`, `IntervalInput`, `GapInput`
7
- - `layers.types.ts`: the filler input contract both projections pass
8
- - `parts/interval.tsx`, `parts/gap.tsx`: the default fillers
7
+ - `layers.types.ts`: the component input contract both projections pass
8
+ - `parts/interval.tsx`, `parts/gap.tsx`: the default components
9
9
  - `utils/styles.ts`: content-keyed style objects per layer
10
10
 
11
11
  Imports core only. Tests: `test/components/layers`.
12
+
13
+ Both projections accept intervalComponent and gapComponent as ComponentType inputs.
14
+ Their data-owning lane or column mounts the component with the final rect, layer,
15
+ and lane; interval inputs also carry highlighted. No renderer calls a slot function.
@@ -1,7 +1,6 @@
1
1
  # primitives
2
2
 
3
- Hookless building blocks shared by Roster and Schedule. Nothing here is a
4
- public export.
3
+ Building blocks shared by the projections, plus the native portal store for Roster selection.
5
4
 
6
5
  - `press-point.tsx`, `press-point.web.tsx`, `press-point.types.ts`: pointer
7
6
  coordinates relative to the pressed surface; the web file is selected by the
@@ -10,3 +9,15 @@ public export.
10
9
  default paint when the override carries a NativeWind class entry
11
10
 
12
11
  Tests: `test/components/primitives`.
12
+
13
+ - `portal.tsx`: root-exported `PortalHost({name})` and
14
+ `Portal({hostName,name,children})`; a module-level Map of hosts to portal nodes,
15
+ subscribed through useSyncExternalStore, registered and removed through lifecycle effects
16
+
17
+ Mount one host per destination name. The default Roster selection layout owns its
18
+ per-instance host; custom layouts can target an ancestor host they own. Caller
19
+ context does not cross the store; provide it above the host or inside the node.
20
+ Schedule does not yet support selection.
21
+
22
+ As a primitive, `Portal` takes `children` rather than a named zone; named zones belong
23
+ to the machine and feature tiers.
@@ -0,0 +1,48 @@
1
+ import { Fragment, type ReactNode, useEffect, useSyncExternalStore } from 'react';
2
+
3
+ const hosts = new Map<string, Map<string, ReactNode>>();
4
+ const listeners = new Set<() => void>();
5
+ const empty = new Map<string, ReactNode>();
6
+
7
+ function subscribe(listener: () => void) {
8
+ listeners.add(listener);
9
+ return () => listeners.delete(listener);
10
+ }
11
+ function publish(hostName: string, nodes: Map<string, ReactNode>) {
12
+ if (nodes.size) hosts.set(hostName, nodes);
13
+ else hosts.delete(hostName);
14
+ for (const listener of listeners) listener();
15
+ }
16
+
17
+ /** Mount once at the destination for a named native portal. */
18
+ export function PortalHost({ name }: { name: string }) {
19
+ const nodes = useSyncExternalStore(
20
+ subscribe,
21
+ () => hosts.get(name) ?? empty,
22
+ () => empty,
23
+ );
24
+ return Array.from(nodes, ([key, node]) => <Fragment key={key}>{node}</Fragment>);
25
+ }
26
+
27
+ /** Register content at a named native host; caller context does not cross the store. */
28
+ export function Portal({
29
+ hostName,
30
+ name,
31
+ children,
32
+ }: {
33
+ hostName: string;
34
+ name: string;
35
+ children: ReactNode;
36
+ }) {
37
+ useEffect(() => {
38
+ const nodes = new Map(hosts.get(hostName));
39
+ nodes.set(name, children);
40
+ publish(hostName, nodes);
41
+ return () => {
42
+ const nodes = new Map(hosts.get(hostName));
43
+ nodes.delete(name);
44
+ publish(hostName, nodes);
45
+ };
46
+ }, [hostName, name, children]);
47
+ return null;
48
+ }