react-native-roster 0.1.0 → 0.3.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 (82) hide show
  1. package/README.md +148 -3
  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 +1 -1
  7. package/dist/src/components/roster/body-layout.d.ts.map +1 -1
  8. package/dist/src/components/roster/body-layout.js +7 -1
  9. package/dist/src/components/roster/body-layout.js.map +1 -1
  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 +26 -3
  13. package/dist/src/components/roster/index.js.map +1 -1
  14. package/dist/src/components/roster/lanes/lane.d.ts.map +1 -1
  15. package/dist/src/components/roster/lanes/lane.js +2 -1
  16. package/dist/src/components/roster/lanes/lane.js.map +1 -1
  17. package/dist/src/components/roster/parts/lane-list.d.ts.map +1 -1
  18. package/dist/src/components/roster/parts/lane-list.js +2 -1
  19. package/dist/src/components/roster/parts/lane-list.js.map +1 -1
  20. package/dist/src/components/roster/roster.types.d.ts +29 -1
  21. package/dist/src/components/roster/roster.types.d.ts.map +1 -1
  22. package/dist/src/components/roster/selection/selection-layout.d.ts +4 -0
  23. package/dist/src/components/roster/selection/selection-layout.d.ts.map +1 -0
  24. package/dist/src/components/roster/selection/selection-layout.js +82 -0
  25. package/dist/src/components/roster/selection/selection-layout.js.map +1 -0
  26. package/dist/src/components/roster/selection/selection-layout.types.d.ts +25 -0
  27. package/dist/src/components/roster/selection/selection-layout.types.d.ts.map +1 -0
  28. package/dist/src/components/roster/selection/selection-layout.types.js +3 -0
  29. package/dist/src/components/roster/selection/selection-layout.types.js.map +1 -0
  30. package/dist/src/components/roster/selection/selection-layout.web.d.ts +4 -0
  31. package/dist/src/components/roster/selection/selection-layout.web.d.ts.map +1 -0
  32. package/dist/src/components/roster/selection/selection-layout.web.js +52 -0
  33. package/dist/src/components/roster/selection/selection-layout.web.js.map +1 -0
  34. package/dist/src/components/roster/use-roster-press.d.ts +6 -0
  35. package/dist/src/components/roster/use-roster-press.d.ts.map +1 -0
  36. package/dist/src/components/roster/use-roster-press.js +61 -0
  37. package/dist/src/components/roster/use-roster-press.js.map +1 -0
  38. package/dist/src/components/roster/use-roster.d.ts.map +1 -1
  39. package/dist/src/components/roster/use-roster.js +40 -30
  40. package/dist/src/components/roster/use-roster.js.map +1 -1
  41. package/dist/src/components/schedule/days/parts/column.d.ts.map +1 -1
  42. package/dist/src/components/schedule/days/parts/column.js +2 -1
  43. package/dist/src/components/schedule/days/parts/column.js.map +1 -1
  44. package/dist/src/core/index.d.ts +2 -0
  45. package/dist/src/core/index.d.ts.map +1 -1
  46. package/dist/src/core/index.js +7 -1
  47. package/dist/src/core/index.js.map +1 -1
  48. package/dist/src/core/source.d.ts +4 -0
  49. package/dist/src/core/source.d.ts.map +1 -0
  50. package/dist/src/core/source.js +9 -0
  51. package/dist/src/core/source.js.map +1 -0
  52. package/dist/src/core/spans.d.ts +19 -0
  53. package/dist/src/core/spans.d.ts.map +1 -1
  54. package/dist/src/core/spans.js +64 -0
  55. package/dist/src/core/spans.js.map +1 -1
  56. package/dist/src/index.d.ts +4 -1
  57. package/dist/src/index.d.ts.map +1 -1
  58. package/dist/src/index.js +6 -1
  59. package/dist/src/index.js.map +1 -1
  60. package/dist/src/nativewind/index.d.ts +4 -1
  61. package/dist/src/nativewind/index.d.ts.map +1 -1
  62. package/llms.txt +85 -3
  63. package/package.json +10 -4
  64. package/src/components/primitives/README.md +13 -2
  65. package/src/components/primitives/portal.tsx +48 -0
  66. package/src/components/roster/README.md +48 -2
  67. package/src/components/roster/body-layout.tsx +7 -1
  68. package/src/components/roster/index.tsx +62 -17
  69. package/src/components/roster/lanes/lane.tsx +2 -4
  70. package/src/components/roster/parts/lane-list.tsx +3 -1
  71. package/src/components/roster/roster.types.ts +30 -1
  72. package/src/components/roster/selection/selection-layout.tsx +90 -0
  73. package/src/components/roster/selection/selection-layout.types.ts +20 -0
  74. package/src/components/roster/selection/selection-layout.web.tsx +88 -0
  75. package/src/components/roster/use-roster-press.ts +74 -0
  76. package/src/components/roster/use-roster.ts +57 -44
  77. package/src/components/schedule/days/parts/column.tsx +2 -4
  78. package/src/core/README.md +18 -1
  79. package/src/core/index.ts +2 -0
  80. package/src/core/source.ts +9 -0
  81. package/src/core/spans.ts +66 -2
  82. package/src/index.ts +4 -0
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.sourceSpans = sourceSpans;
4
+ exports.extentOf = extentOf;
5
+ exports.intersectionOf = intersectionOf;
6
+ exports.unionOf = unionOf;
4
7
  function sourceSpans(intervals, window) {
5
8
  const boundaries = [];
6
9
  for (const interval of intervals) {
@@ -57,4 +60,65 @@ function sourceSpans(intervals, window) {
57
60
  }
58
61
  return spans;
59
62
  }
63
+ /**
64
+ * Earliest start through latest end in epoch milliseconds, end-exclusive; empty input returns null.
65
+ * Throws RangeError for non-finite bounds or end <= start.
66
+ * Inputs may be unsorted, overlapping, nested, or duplicated; inputs are not mutated.
67
+ */
68
+ function extentOf(segments) {
69
+ let start = Infinity;
70
+ let end = -Infinity;
71
+ for (const segment of segments) {
72
+ validateSpan(segment);
73
+ start = Math.min(start, segment.start);
74
+ end = Math.max(end, segment.end);
75
+ }
76
+ return start < end ? { start, end } : null;
77
+ }
78
+ /**
79
+ * Shared epoch milliseconds, end-exclusive; empty input or an empty intersection returns null.
80
+ * Throws RangeError for non-finite bounds or end <= start.
81
+ * Inputs may be unsorted, overlapping, nested, or duplicated; inputs are not mutated.
82
+ */
83
+ function intersectionOf(spans) {
84
+ if (spans.length === 0)
85
+ return null;
86
+ let start = -Infinity;
87
+ let end = Infinity;
88
+ for (const span of spans) {
89
+ validateSpan(span);
90
+ start = Math.max(start, span.start);
91
+ end = Math.min(end, span.end);
92
+ }
93
+ return start < end ? { start, end } : null;
94
+ }
95
+ /**
96
+ * Merged end-exclusive epoch windows sorted by start; empty input returns an empty array.
97
+ * Overlapping or touching spans merge; disjoint spans remain separate.
98
+ * Throws RangeError for non-finite bounds or end <= start.
99
+ * Returns new objects without mutating inputs.
100
+ */
101
+ function unionOf(spans) {
102
+ const sorted = spans.map((span) => {
103
+ validateSpan(span);
104
+ return { start: span.start, end: span.end };
105
+ });
106
+ sorted.sort((a, b) => a.start - b.start);
107
+ const merged = [];
108
+ for (const span of sorted) {
109
+ const last = merged.at(-1);
110
+ if (last && span.start <= last.end) {
111
+ last.end = Math.max(last.end, span.end);
112
+ }
113
+ else {
114
+ merged.push(span);
115
+ }
116
+ }
117
+ return merged;
118
+ }
119
+ function validateSpan(span) {
120
+ if (!Number.isFinite(span.start) || !Number.isFinite(span.end) || span.end <= span.start) {
121
+ throw new RangeError('Span bounds must be finite with end greater than start');
122
+ }
123
+ }
60
124
  //# sourceMappingURL=spans.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"spans.js","sourceRoot":"","sources":["../../../src/core/spans.ts"],"names":[],"mappings":";;;AAMA,qBAA4B,SAAqB,EAAE,MAAc;IAC/D,MAAM,UAAU,GAAe,EAAE,CAAC;IAClC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;QAC/C,IAAI,KAAK,IAAI,GAAG;YAAE,SAAS;QAC3B,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;QACpE,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,IAAI,GAAG,EAA6C,CAAC;IACpE,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC;IAC5B,IAAI,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,GAAI,CAAC;QACxC,MAAM,EAAE,GAAI,UAAU,CAAC,CAAC,CAAc,CAAC,EAAE,CAAC;QAC1C,IAAI,KAAK,GAAG,CAAC,IAAI,QAAQ,GAAG,EAAE,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,KAAK,QAAQ,IAAI,gBAAgB,KAAK,CAAC,EAAE,CAAC;gBAC5D,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;YAChB,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,IAAI,CAAC;oBACT,KAAK,EAAE,QAAQ;oBACf,GAAG,EAAE,EAAE;oBACP,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC;iBACzD,CAAC,CAAC;gBACH,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;gBAClC,gBAAgB,GAAG,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;QACD,OAAO,CAAC,GAAG,UAAU,CAAC,MAAM,IAAK,UAAU,CAAC,CAAC,CAAc,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;YACtE,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,EAAE,CAAa,CAAC;YAC7C,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC;YACxB,KAAK,MAAM,MAAM,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACtC,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;gBACrD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAC7B,MAAM,KAAK,GAAG,CAAC,IAAI,EAAE,KAAK,IAAI,CAAC,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;gBAClD,sEAAsE;gBACtE,mEAAmE;gBACnE,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,EAAE,CAAC;oBAC3C,gBAAgB,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAC3E,CAAC;gBACD,IAAI,KAAK,KAAK,CAAC;oBAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;;oBAC/B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,MAAM,EAAE,CAAC,CAAC;YAClE,CAAC;QACH,CAAC;QACD,QAAQ,GAAG,EAAE,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"spans.js","sourceRoot":"","sources":["../../../src/core/spans.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
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';
5
6
  export { RosterBodyLayout } from './components/roster/body-layout';
6
7
  export { RosterLaneLabel } from './components/roster/lanes/parts/lane-label';
@@ -12,7 +13,9 @@ export { RosterHeader } from './components/roster/parts/header';
12
13
  export { RosterHeaderCell } from './components/roster/parts/header-cell';
13
14
  export { RosterLaneLabelColumn } from './components/roster/parts/lane-label-column';
14
15
  export { RosterLaneList } from './components/roster/parts/lane-list';
15
- export type { BodyInput, GridInput, HeaderCellInput, HeaderInput, LabelColumnInput, LaneLabelInput, RosterInput, RosterModel, RosterProjection, RosterProps, RosterScroll, RosterStyleProps, RosterTick, } from './components/roster/roster.types';
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';
16
19
  export { useRoster } from './components/roster/use-roster';
17
20
  export { Schedule } from './components/schedule';
18
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,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,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,11 +14,14 @@ 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.RosterLaneList = exports.RosterLaneLabelColumn = exports.RosterHeaderCell = exports.RosterHeader = exports.RosterGrid = exports.RosterEmpty = exports.RosterCorner = exports.RosterBody = exports.RosterLaneLabel = exports.RosterBodyLayout = 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; } });
24
27
  var body_layout_1 = require("./components/roster/body-layout");
@@ -41,6 +44,8 @@ var lane_label_column_1 = require("./components/roster/parts/lane-label-column")
41
44
  Object.defineProperty(exports, "RosterLaneLabelColumn", { enumerable: true, get: function () { return lane_label_column_1.RosterLaneLabelColumn; } });
42
45
  var lane_list_1 = require("./components/roster/parts/lane-list");
43
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; } });
44
49
  var use_roster_1 = require("./components/roster/use-roster");
45
50
  Object.defineProperty(exports, "useRoster", { enumerable: true, get: function () { return use_roster_1.useRoster; } });
46
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,+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;AAgBvB,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,7 +8,10 @@ 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
17
  laneLabelComponent?: import("react").ComponentType<import("../components/roster/roster.types").LaneLabelInput>;
@@ -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"}
package/llms.txt CHANGED
@@ -144,11 +144,28 @@ 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 slot components, and core re-exports. |
148
- | react-native-roster/core | layoutLane, coverageFor, flagFor, windowFor, prev, next, today, dayColumnsFor, timeAtX, timeAtY, snapToStep, byLabel, byCoverage, and core counters/caches. |
147
+ | react-native-roster | Roster, Schedule, hooks, default slot components, RosterSelectionPopover, PortalHost, Portal, and core re-exports. |
148
+ | react-native-roster/core | layoutLane, coverageFor, flagFor, extentOf, intersectionOf, unionOf, 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
+ ## Interval helpers
153
+
154
+ extentOf(segments: readonly Window[]): Window | null returns earliest start through
155
+ latest end, bridging disjoint segments. intersectionOf(spans: readonly Window[]):
156
+ Window | null returns latest start through earliest end. Both use end-exclusive
157
+ epoch milliseconds and return null for empty input.
158
+ Inputs may be unsorted, overlapping, nested, or duplicated; inputs are not mutated.
159
+ An empty intersection, including touching bounds, returns null. Every input is
160
+ validated; non-finite bounds or end <= start throw RangeError. Window is the bare
161
+ { start, end } type; Span remains the axis choice type.
162
+
163
+ unionOf(spans: readonly Window[]): Window[] merges overlapping or touching
164
+ end-exclusive windows, sorts them by start, and keeps disjoint windows separate.
165
+ Empty input returns []; every returned window is a new object. The input array
166
+ and its windows remain untouched. Every span is validated with the same
167
+ RangeError rules as extentOf and intersectionOf.
168
+
152
169
  No default exports. Root exports renderer inputs/models/slot input types as well as
153
170
  core types. Core imports standard JS and Intl only. Only /rrule imports
154
171
  rrule-temporal 1.6.0 and @js-temporal/polyfill 0.5.1. Those are installed runtime
@@ -186,7 +203,7 @@ outside React state.
186
203
 
187
204
  useRoster returns window, projection, orderedLanes, coverage, laneState,
188
205
  geometryFor(lane), scroll, press(lane,x,y), ticks, contentWidth, viewport,
189
- onLayout, navigate, and status ('empty' or 'ready'). There is no visibleRange
206
+ onLayout, navigate, selection, dismissSelection, and status ('empty' or 'ready'). There is no visibleRange
190
207
  field in the shipped public model. Custom layouts must wire onLayout. Custom
191
208
  bodies compute geometry only for mounted lanes, preserving virtualization.
192
209
  A zero-duration window has contentWidth=0 and renders no Roster body. Positive
@@ -233,6 +250,71 @@ Keep its viewport gate and measured estimatedListSize in custom compositions.
233
250
  | `laneLabelColumnComponent` | `labels: LaneLabelInput[]`, `projection`, `scroll`, `laneLabelComponent` | `RosterLaneLabelColumn`: frozen labels following vertical offset. |
234
251
  | `bodyComponent` | Ordered `lanes`, `window`, `geometryFor`, `projection`, `scroll`, `press`, `ticks`, `viewport`, `contentWidth`, highlight/hover, incomplete label, and rect components | `RosterBody`: virtualized lanes. |
235
252
 
253
+ ## Selection
254
+
255
+ Roster only; Schedule does not yet support selection and will gain it in a later change.
256
+ intervalDetailComponent?: ComponentType<IntervalDetailInput> enables selection on interval
257
+ press while preserving onIntervalPress. Absent content retains no selection.
258
+ Pressing the selected interval again dismisses it; pressing another interval
259
+ switches selection. Cell and gap presses dismiss selection while still firing
260
+ `onCellPress` and `onGapPress`. These rules live in the hook and apply on native
261
+ and web; web outside press and Escape still dismiss.
262
+ IntervalDetailInput is IntervalInput plus absolute start and end and viewTimezone.
263
+ useRoster owns selection: {rect,layer,lane,start,end}|null and dismissSelection(). It resolves
264
+ current lane/layer/rect references and clears removed lane or absolute rect bounds.
265
+ Resizing and sorting retain valid selection with current geometry; reconciliation
266
+ uses the private reconcileSelection helper to match source identity sets and choose
267
+ nearest bounds with a total difference of at most 1 ms for projection roundoff.
268
+ selectable?: boolean belongs only to the hook input, RosterInput, and defaults to false.
269
+ It is excluded from RosterProps; hook consumers pass
270
+ selectable: true to retain selection. intervalDetailComponent, selectionLayout, and
271
+ portalHost belong to RosterProps because the chassis mounts content and the layout.
272
+ The chassis passes selectable: Boolean(intervalDetailComponent) to useRoster.
273
+ Custom chassis mount their content and layout using the returned model.
274
+
275
+ selectionLayout?: ComponentType<SelectionLayoutProps> defaults to the root-exported
276
+ RosterSelectionPopover. It is the explicit naming exception to the Component suffix.
277
+ The chassis mounts the strategy once around the body node, then passes that node
278
+ to RosterLayout.bodyZone. SelectionLayoutProps is exported: anchorZone and contentZone
279
+ are ReactNode; targetBounds is {x,y,width,height}|null in body-content coordinates, including
280
+ the lane offset and interval inset; open is
281
+ boolean; onDismiss is () => void; portalHost is string; scroll is Pick<RosterScroll, 'x' | 'y' | 'headerStyle' | 'labelStyle'>.
282
+ Layouts receive no lane, layer, or rect. Render anchorZone once and arrange contentZone
283
+ above or beside it. The interval-detail fixture demonstrates a runtime inspector toggle.
284
+
285
+ Native selection-layout.tsx uses the root-exported PortalHost and Portal from
286
+ components/primitives/portal.tsx, a module-level Map store with useSyncExternalStore.
287
+ The default layout owns a host named from the roster's useId; portalHost overrides
288
+ that name. Use distinct names for independent rosters and never mount the same host
289
+ twice. Custom layouts may target an ancestor host they own. Caller context does not
290
+ cross a store portal; provide context above the host or inside the portaled node.
291
+ Native hardware back dismisses details. The detail card flips above
292
+ when that fits, or uses the top edge when neither vertical placement fits. It clamps
293
+ left using measured viewport and content widths. Shared x/y values position the
294
+ overlay in viewport coordinates, never React scroll state. Selection identity
295
+ must not enter the body content key or cause mounted rows to render. The stable press
296
+ ref lives in use-roster-press.ts so React Compiler can retain derived body inputs in useRoster.
297
+
298
+ Native has no intercepting dismissal overlay; body presses reach the hook, and the detail
299
+ card captures its own presses. Web excludes the body wrapper from Radix outside
300
+ dismissal, leaving body presses to the hook while preserving true outside presses and Escape.
301
+ The hook toggles the selected interval closed, switches to another interval, and dismisses
302
+ on cell or gap presses while preserving their callbacks. The body restores horizontal
303
+ and vertical offsets from shared values on remount when selectionLayout changes.
304
+ A mount effect calls the horizontal ScrollView ref's scrollTo without animation;
305
+ native also retains contentOffset. LegendList restores initialScrollOffset through
306
+ its own web mount effect and native initial offset.
307
+
308
+ selection-layout.web.tsx uses @radix-ui/react-popover, required by the web root entry
309
+ but an optional peer for native/core-only consumers. Source web bundlers (Vite,
310
+ Storybook, or Metro web) require it even with selection disabled because the web
311
+ layout is in the module graph. Install with Bun. The emitted
312
+ browser remap must travel with the shared selection-layout.types.ts contract, just
313
+ like press-point and interval-hover. Its zero-size, pointer-transparent anchor follows
314
+ the shared translation; Radix's portal follows anchor movement and owns outside press,
315
+ Escape, and collisions. The layout returns focus to the previously focused element
316
+ only after Escape.
317
+
236
318
  ## Schedule zones
237
319
 
238
320
  | Slot | Component inputs or node | Default and behavior |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-roster",
3
- "version": "0.1.0",
3
+ "version": "0.3.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
  ".": {
@@ -101,7 +102,8 @@
101
102
  "nativewind": ">=4.1.0",
102
103
  "react": ">=18.2.0",
103
104
  "react-native": ">=0.74.0",
104
- "react-native-reanimated": ">=3.19.0"
105
+ "react-native-reanimated": ">=3.19.0",
106
+ "@radix-ui/react-popover": "^1.1.23"
105
107
  },
106
108
  "peerDependenciesMeta": {
107
109
  "expo": {
@@ -112,6 +114,9 @@
112
114
  },
113
115
  "react-native-reanimated": {
114
116
  "optional": true
117
+ },
118
+ "@radix-ui/react-popover": {
119
+ "optional": true
115
120
  }
116
121
  },
117
122
  "dependencies": {
@@ -150,7 +155,8 @@
150
155
  "semantic-release": "^25.0.3",
151
156
  "semver": "^7.7.0",
152
157
  "size-limit": "^13.0.3",
153
- "temporal-spec": "1.0.1"
158
+ "temporal-spec": "1.0.1",
159
+ "@radix-ui/react-popover": "1.1.23"
154
160
  },
155
161
  "publishConfig": {
156
162
  "access": "public",
@@ -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
+ }
@@ -6,7 +6,8 @@ This feature is about a roster; its children are lanes.
6
6
  `XxxInput` slot input type from `roster.types.ts`
7
7
 
8
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
9
+ - `use-roster.ts`: the hook; owns window, projection, scroll, geometry, and selection
10
+ - `use-roster-press.ts`: isolates the stable press ref so React Compiler can retain derived body inputs
10
11
  - `layout.tsx`: arranges corner, header, label column, and body regions only
11
12
  - `roster.types.ts`: props, model, and every slot input type
12
13
  - `body-layout.tsx`: arranges grid and list nodes with horizontal scroll wiring
@@ -19,7 +20,7 @@ Interval and gap components live in `../layers`; press geometry and `regionStyle
19
20
  in `../primitives`. Zone contracts are documented in the README's Roster zones
20
21
  section and in `llms.txt`. Tests: `test/components/roster`.
21
22
 
22
- This feature is about a roster body; its children are lanes. `RosterBody` gates
23
+ `RosterBody` gates
23
24
  measurement and composes the body layout and lane list. The `onRowRender` prop on
24
25
  `RosterBody` and `RosterLaneList` is a development-only Profiler hook used by the
25
26
  gallery, not a supported customization point. Production Profiler callbacks are disabled.
@@ -27,3 +28,48 @@ gallery, not a supported customization point. Production Profiler callbacks are
27
28
  Input-bearing slots accept component types and mount in the data-owning parts.
28
29
  The chassis binds defaults once; emptyZone and cornerZone accept nodes. The body
29
30
  content key tracks interval and gap component identity, including class components.
31
+
32
+ `selection/` holds the runtime-swappable presentation strategies (native popover, web popover) for one `SelectionLayoutProps` contract.
33
+
34
+ - `selection/selection-layout.types.ts`: exported `SelectionLayoutProps`, with body
35
+ and detail nodes, targetBounds, open, dismissal, host name, and shared scroll
36
+ limited to x, y, headerStyle, and labelStyle
37
+ - `selection/selection-layout.tsx`: exported native `RosterSelectionPopover`, local
38
+ portal host, body press passthrough, hardware back, measured viewport clamping, and shared scroll positioning
39
+ - `selection/selection-layout.web.tsx`: Radix presentation with a browser remap
40
+
41
+ `intervalDetailComponent` receives `IntervalDetailInput`, the interval input plus absolute
42
+ `start`, `end`, and `viewTimezone`; presses still invoke onIntervalPress.
43
+ `selectable?: boolean` belongs only to the hook input, `RosterInput`, and defaults to false.
44
+ It is excluded from `RosterProps`.
45
+ `intervalDetailComponent`, `selectionLayout`, and `portalHost` belong to `RosterProps`
46
+ because the chassis mounts the content and layout. The chassis passes
47
+ `selectable: Boolean(intervalDetailComponent)` to `useRoster`.
48
+ `selectionLayout` defaults once in the chassis and wraps the body
49
+ before RosterLayout receives bodyZone. The selection never enters the body content
50
+ key. Missing lane/layer/bounds clear selection; valid selections use current data.
51
+ Pressing the selected interval again dismisses it; pressing another interval
52
+ switches selection. Cell and gap presses dismiss selection while still firing
53
+ `onCellPress` and `onGapPress`. These rules live in the hook and apply on native
54
+ and web; web outside press and Escape still dismiss.
55
+ On web, the layout captures document.activeElement before opening autofocus and
56
+ recaptures the focused lane when targetBounds changes while open. It uses onCloseAutoFocus to return focus after Escape only, leaving outside pointer focus intact. The browser's focus-visible ring styling is left to the host page.
57
+ `portalHost` defaults to a per-roster useId name. The interval-detail fixture switches
58
+ to an inspector column with the same node contract. Schedule does not yet support selection.
59
+
60
+ The private reconcileSelection helper resolves the stored absolute bounds against the
61
+ current lanes and window each render, matching source identity sets and choosing the
62
+ nearest bounds with a total difference of at most 1 ms for projection roundoff. Target bounds
63
+ include the lane offset and interval inset. Native details clamp horizontally and use the
64
+ top edge when neither below nor above fits. The browser gate isolates selection updates from Pressable state,
65
+ and separately exercises complete pointer presses and outside dismissal.
66
+
67
+ Native has no intercepting dismissal overlay; body presses reach the hook, and the detail
68
+ card captures its own presses. Web excludes the body wrapper from Radix outside
69
+ dismissal, leaving body presses to the hook while preserving true outside presses and Escape.
70
+ The hook toggles the selected interval closed, switches to another interval, and dismisses
71
+ on cell or gap presses while preserving their callbacks. The body restores horizontal
72
+ and vertical offsets from shared values on remount when selectionLayout changes.
73
+ A mount effect calls the horizontal ScrollView ref's scrollTo without animation;
74
+ native also retains contentOffset. LegendList restores initialScrollOffset through
75
+ its own web mount effect and native initial offset.
@@ -1,4 +1,4 @@
1
- import type { ReactNode } from 'react';
1
+ import { type ReactNode, useEffect, useState } from 'react';
2
2
  import { ScrollView, View } from 'react-native';
3
3
  import type { BodyInput } from './roster.types';
4
4
 
@@ -14,10 +14,16 @@ export function RosterBodyLayout({
14
14
  /** Virtualized lane collection within the measured viewport. */
15
15
  listZone: ReactNode;
16
16
  }) {
17
+ const [contentOffset] = useState(() => ({ x: scroll.x.get(), y: 0 }));
18
+ const bodyRef = scroll.bodyRef;
19
+ useEffect(() => {
20
+ bodyRef.current?.scrollTo({ x: contentOffset.x, animated: false });
21
+ }, [bodyRef, contentOffset]);
17
22
  return (
18
23
  <ScrollView
19
24
  testID="roster-horizontal-scroll"
20
25
  ref={scroll.bodyRef}
26
+ contentOffset={contentOffset}
21
27
  horizontal
22
28
  onScroll={scroll.onBodyScroll}
23
29
  scrollEventThrottle={16}
@@ -1,3 +1,5 @@
1
+ import { type ReactNode, useId } from 'react';
2
+ import { hasSource } from '../../core';
1
3
  import { RosterGap } from '../layers/parts/gap';
2
4
  import { RosterInterval } from '../layers/parts/interval';
3
5
  import { RosterLaneLabel } from './lanes/parts/lane-label';
@@ -10,6 +12,8 @@ import { RosterHeader } from './parts/header';
10
12
  import { RosterHeaderCell } from './parts/header-cell';
11
13
  import { RosterLaneLabelColumn } from './parts/lane-label-column';
12
14
  import type { RosterProps } from './roster.types';
15
+ import { RosterSelectionPopover } from './selection/selection-layout';
16
+ import type { SelectionLayoutProps } from './selection/selection-layout.types';
13
17
  import { useRoster } from './use-roster';
14
18
 
15
19
  const defaultEmptyZone = <RosterEmpty />;
@@ -17,6 +21,8 @@ const defaultCornerZone = <RosterCorner />;
17
21
 
18
22
  export function Roster(props: RosterProps) {
19
23
  const {
24
+ selection,
25
+ dismissSelection,
20
26
  status,
21
27
  orderedLanes,
22
28
  laneState,
@@ -29,8 +35,10 @@ export function Roster(props: RosterProps) {
29
35
  contentWidth,
30
36
  viewport,
31
37
  onLayout,
32
- } = useRoster(props);
38
+ } = useRoster({ ...props, selectable: Boolean(props.intervalDetailComponent) });
33
39
  const {
40
+ intervalDetailComponent: IntervalDetailComponent,
41
+ selectionLayout: SelectionLayout = RosterSelectionPopover,
34
42
  emptyZone = defaultEmptyZone,
35
43
  cornerZone = defaultCornerZone,
36
44
  headerComponent: HeaderComponent = RosterHeader,
@@ -44,8 +52,30 @@ export function Roster(props: RosterProps) {
44
52
  incompleteLabel = 'Availability may be incomplete',
45
53
  neverSetLabel = 'No availability set',
46
54
  } = props;
55
+ const hostId = useId();
47
56
  if (status === 'empty') return emptyZone;
48
57
  if (window.start === window.end) return null;
58
+ let targetBounds: SelectionLayoutProps['targetBounds'] = null;
59
+ if (selection) {
60
+ targetBounds = {
61
+ x: selection.rect.x,
62
+ y:
63
+ orderedLanes.findIndex((lane) => lane.id === selection.lane.id) * projection.rowHeight +
64
+ selection.rect.y,
65
+ width: selection.rect.width,
66
+ height: selection.rect.height,
67
+ };
68
+ }
69
+ let contentZone: ReactNode = null;
70
+ if (selection && IntervalDetailComponent) {
71
+ contentZone = (
72
+ <IntervalDetailComponent
73
+ {...selection}
74
+ viewTimezone={projection.viewTimezone}
75
+ highlighted={hasSource(selection.rect.sources, props.highlightSource)}
76
+ />
77
+ );
78
+ }
49
79
  return (
50
80
  <RosterLayout
51
81
  style={props.style}
@@ -85,22 +115,37 @@ export function Roster(props: RosterProps) {
85
115
  />
86
116
  }
87
117
  bodyZone={
88
- <BodyComponent
89
- lanes={orderedLanes}
90
- window={window}
91
- geometryFor={geometryFor}
92
- projection={projection}
93
- scroll={scroll}
94
- press={press}
95
- ticks={ticks}
96
- contentWidth={contentWidth}
97
- viewport={viewport}
98
- intervalComponent={intervalComponent}
99
- gapComponent={gapComponent}
100
- gridComponent={gridComponent}
101
- highlightSource={props.highlightSource}
102
- onIntervalHover={props.onIntervalHover}
103
- incompleteLabel={incompleteLabel}
118
+ <SelectionLayout
119
+ portalHost={props.portalHost ?? `roster-${hostId}`}
120
+ open={selection !== null}
121
+ onDismiss={dismissSelection}
122
+ scroll={{
123
+ x: scroll.x,
124
+ y: scroll.y,
125
+ headerStyle: scroll.headerStyle,
126
+ labelStyle: scroll.labelStyle,
127
+ }}
128
+ targetBounds={targetBounds}
129
+ contentZone={contentZone}
130
+ anchorZone={
131
+ <BodyComponent
132
+ lanes={orderedLanes}
133
+ window={window}
134
+ geometryFor={geometryFor}
135
+ projection={projection}
136
+ scroll={scroll}
137
+ press={press}
138
+ ticks={ticks}
139
+ contentWidth={contentWidth}
140
+ viewport={viewport}
141
+ intervalComponent={intervalComponent}
142
+ gapComponent={gapComponent}
143
+ gridComponent={gridComponent}
144
+ highlightSource={props.highlightSource}
145
+ onIntervalHover={props.onIntervalHover}
146
+ incompleteLabel={incompleteLabel}
147
+ />
148
+ }
104
149
  />
105
150
  }
106
151
  />