@farm.js/react 0.1.0-beta.1 → 0.1.0-beta.3

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.
@@ -95,6 +95,23 @@ interface CompilerHostConditionalBlockProps {
95
95
  truthy?: CompilerHostConditionalBranch;
96
96
  falsy?: CompilerHostConditionalBranch;
97
97
  }
98
+ interface CompilerConditionalRange {
99
+ /** Number of static direct host siblings between this slot and the previous slot. */
100
+ before: number;
101
+ test(): unknown;
102
+ /** Logical && can produce a visible number instead of an empty branch. */
103
+ logical?: boolean;
104
+ truthy?: CompilerHostConditionalBranch;
105
+ falsy?: CompilerHostConditionalBranch;
106
+ }
107
+ interface CompilerConditionalRangesBlockProps {
108
+ id: number;
109
+ render(): React.ReactElement;
110
+ rootRef?: React.RefCallback<Element>;
111
+ ranges: readonly CompilerConditionalRange[];
112
+ /** Number of static direct host siblings after the final conditional slot. */
113
+ trailing: number;
114
+ }
98
115
  interface CompilerKeyedRowBinding {
99
116
  kind: "text" | "attribute" | "style";
100
117
  path: readonly number[];
@@ -111,6 +128,22 @@ interface CompilerKeyedRowsBlockProps {
111
128
  events?: readonly CompilerKeyedRowEvent[];
112
129
  conditionals?: readonly CompilerKeyedRowConditional[];
113
130
  }
131
+ interface CompilerKeyedRange {
132
+ /** Number of static direct host siblings between this range and the previous range. */
133
+ before: number;
134
+ items(): Iterable<unknown> | null | undefined;
135
+ rowKey(item: unknown, index: number): React.Key;
136
+ create(item: unknown, index: number): CompilerKeyedRowElement;
137
+ bindings: readonly CompilerKeyedRowBinding[];
138
+ }
139
+ interface CompilerKeyedRangesBlockProps {
140
+ id: number;
141
+ render(): React.ReactElement;
142
+ rootRef?: React.RefCallback<Element>;
143
+ ranges: readonly CompilerKeyedRange[];
144
+ /** Number of static direct host siblings after the final keyed range. */
145
+ trailing: number;
146
+ }
114
147
  interface CompilerComponentBlockProps {
115
148
  id: number;
116
149
  render(): React.ReactNode;
@@ -118,8 +151,10 @@ interface CompilerComponentBlockProps {
118
151
  interface CompilerBlockRuntime {
119
152
  Conditional: React.ComponentType<CompilerConditionalBlockProps>;
120
153
  HostConditional: React.ComponentType<CompilerHostConditionalBlockProps>;
154
+ ConditionalRanges: React.ComponentType<CompilerConditionalRangesBlockProps>;
121
155
  KeyedList: React.ComponentType<CompilerKeyedListBlockProps>;
122
156
  KeyedRows: React.ComponentType<CompilerKeyedRowsBlockProps>;
157
+ KeyedRanges: React.ComponentType<CompilerKeyedRangesBlockProps>;
123
158
  Component: React.ComponentType<CompilerComponentBlockProps>;
124
159
  target(id: number): React.RefCallback<Element>;
125
160
  }
@@ -143,4 +178,4 @@ interface CompiledComponentDefinition<Props> {
143
178
  */
144
179
  declare function createCompiledComponent<Props>(definition: CompiledComponentDefinition<Props>): React.ComponentType<Props>;
145
180
 
146
- export { type CompiledComponentDefinition, type CompilerAttributeBinding, type CompilerBinding, type CompilerBlockRuntime, type CompilerCell, type CompilerComponentBlockProps, type CompilerConditionalBlockBinding, type CompilerConditionalBlockProps, type CompilerHostConditionalBinding, type CompilerHostConditionalBlockProps, type CompilerHostConditionalBranch, type CompilerHostElement, type CompilerKeyedListBlockProps, type CompilerKeyedRowBinding, type CompilerKeyedRowConditional, type CompilerKeyedRowConditionalBranch, type CompilerKeyedRowElement, type CompilerKeyedRowEvent, type CompilerKeyedRowsBlockProps, type CompilerStateUpdater, type CompilerStyleBinding, type CompilerTextBinding, createCompiledComponent };
181
+ export { type CompiledComponentDefinition, type CompilerAttributeBinding, type CompilerBinding, type CompilerBlockRuntime, type CompilerCell, type CompilerComponentBlockProps, type CompilerConditionalBlockBinding, type CompilerConditionalBlockProps, type CompilerConditionalRange, type CompilerConditionalRangesBlockProps, type CompilerHostConditionalBinding, type CompilerHostConditionalBlockProps, type CompilerHostConditionalBranch, type CompilerHostElement, type CompilerKeyedListBlockProps, type CompilerKeyedRange, type CompilerKeyedRangesBlockProps, type CompilerKeyedRowBinding, type CompilerKeyedRowConditional, type CompilerKeyedRowConditionalBranch, type CompilerKeyedRowElement, type CompilerKeyedRowEvent, type CompilerKeyedRowsBlockProps, type CompilerStateUpdater, type CompilerStyleBinding, type CompilerTextBinding, createCompiledComponent };
@@ -95,6 +95,23 @@ interface CompilerHostConditionalBlockProps {
95
95
  truthy?: CompilerHostConditionalBranch;
96
96
  falsy?: CompilerHostConditionalBranch;
97
97
  }
98
+ interface CompilerConditionalRange {
99
+ /** Number of static direct host siblings between this slot and the previous slot. */
100
+ before: number;
101
+ test(): unknown;
102
+ /** Logical && can produce a visible number instead of an empty branch. */
103
+ logical?: boolean;
104
+ truthy?: CompilerHostConditionalBranch;
105
+ falsy?: CompilerHostConditionalBranch;
106
+ }
107
+ interface CompilerConditionalRangesBlockProps {
108
+ id: number;
109
+ render(): React.ReactElement;
110
+ rootRef?: React.RefCallback<Element>;
111
+ ranges: readonly CompilerConditionalRange[];
112
+ /** Number of static direct host siblings after the final conditional slot. */
113
+ trailing: number;
114
+ }
98
115
  interface CompilerKeyedRowBinding {
99
116
  kind: "text" | "attribute" | "style";
100
117
  path: readonly number[];
@@ -111,6 +128,22 @@ interface CompilerKeyedRowsBlockProps {
111
128
  events?: readonly CompilerKeyedRowEvent[];
112
129
  conditionals?: readonly CompilerKeyedRowConditional[];
113
130
  }
131
+ interface CompilerKeyedRange {
132
+ /** Number of static direct host siblings between this range and the previous range. */
133
+ before: number;
134
+ items(): Iterable<unknown> | null | undefined;
135
+ rowKey(item: unknown, index: number): React.Key;
136
+ create(item: unknown, index: number): CompilerKeyedRowElement;
137
+ bindings: readonly CompilerKeyedRowBinding[];
138
+ }
139
+ interface CompilerKeyedRangesBlockProps {
140
+ id: number;
141
+ render(): React.ReactElement;
142
+ rootRef?: React.RefCallback<Element>;
143
+ ranges: readonly CompilerKeyedRange[];
144
+ /** Number of static direct host siblings after the final keyed range. */
145
+ trailing: number;
146
+ }
114
147
  interface CompilerComponentBlockProps {
115
148
  id: number;
116
149
  render(): React.ReactNode;
@@ -118,8 +151,10 @@ interface CompilerComponentBlockProps {
118
151
  interface CompilerBlockRuntime {
119
152
  Conditional: React.ComponentType<CompilerConditionalBlockProps>;
120
153
  HostConditional: React.ComponentType<CompilerHostConditionalBlockProps>;
154
+ ConditionalRanges: React.ComponentType<CompilerConditionalRangesBlockProps>;
121
155
  KeyedList: React.ComponentType<CompilerKeyedListBlockProps>;
122
156
  KeyedRows: React.ComponentType<CompilerKeyedRowsBlockProps>;
157
+ KeyedRanges: React.ComponentType<CompilerKeyedRangesBlockProps>;
123
158
  Component: React.ComponentType<CompilerComponentBlockProps>;
124
159
  target(id: number): React.RefCallback<Element>;
125
160
  }
@@ -143,4 +178,4 @@ interface CompiledComponentDefinition<Props> {
143
178
  */
144
179
  declare function createCompiledComponent<Props>(definition: CompiledComponentDefinition<Props>): React.ComponentType<Props>;
145
180
 
146
- export { type CompiledComponentDefinition, type CompilerAttributeBinding, type CompilerBinding, type CompilerBlockRuntime, type CompilerCell, type CompilerComponentBlockProps, type CompilerConditionalBlockBinding, type CompilerConditionalBlockProps, type CompilerHostConditionalBinding, type CompilerHostConditionalBlockProps, type CompilerHostConditionalBranch, type CompilerHostElement, type CompilerKeyedListBlockProps, type CompilerKeyedRowBinding, type CompilerKeyedRowConditional, type CompilerKeyedRowConditionalBranch, type CompilerKeyedRowElement, type CompilerKeyedRowEvent, type CompilerKeyedRowsBlockProps, type CompilerStateUpdater, type CompilerStyleBinding, type CompilerTextBinding, createCompiledComponent };
181
+ export { type CompiledComponentDefinition, type CompilerAttributeBinding, type CompilerBinding, type CompilerBlockRuntime, type CompilerCell, type CompilerComponentBlockProps, type CompilerConditionalBlockBinding, type CompilerConditionalBlockProps, type CompilerConditionalRange, type CompilerConditionalRangesBlockProps, type CompilerHostConditionalBinding, type CompilerHostConditionalBlockProps, type CompilerHostConditionalBranch, type CompilerHostElement, type CompilerKeyedListBlockProps, type CompilerKeyedRange, type CompilerKeyedRangesBlockProps, type CompilerKeyedRowBinding, type CompilerKeyedRowConditional, type CompilerKeyedRowConditionalBranch, type CompilerKeyedRowElement, type CompilerKeyedRowEvent, type CompilerKeyedRowsBlockProps, type CompilerStateUpdater, type CompilerStyleBinding, type CompilerTextBinding, createCompiledComponent };
@@ -504,6 +504,181 @@ function createHostConditionalBlockComponent(owner) {
504
504
  FarmHostConditionalBlock.displayName = "FarmCompiledHostConditional";
505
505
  return FarmHostConditionalBlock;
506
506
  }
507
+ function createConditionalRangesBlockComponent(owner) {
508
+ class FarmConditionalRangesBlock extends React.Component {
509
+ constructor() {
510
+ super(...arguments);
511
+ this.state = { fallback: false };
512
+ this.root = null;
513
+ this.mounted = false;
514
+ this.fallbackRequested = false;
515
+ this.fallbackVersion = 0;
516
+ this.propFallbackQueued = false;
517
+ this.currentProps = this.props;
518
+ this.rangeInstances = [];
519
+ this.staticSegments = [];
520
+ this.captureRoot = (root) => {
521
+ this.root = root;
522
+ this.currentProps.rootRef?.(root);
523
+ };
524
+ this.refresh = (afterCommit) => {
525
+ this.reconcile(afterCommit);
526
+ };
527
+ }
528
+ readSelections(props) {
529
+ const selections = [];
530
+ for (const range of props.ranges) {
531
+ const selection = hostConditionalSelection(range);
532
+ if (selection.kind === "fallback") return null;
533
+ selections.push(selection);
534
+ }
535
+ return selections;
536
+ }
537
+ adopt(props = this.currentProps) {
538
+ if (!this.root || !Number.isSafeInteger(props.trailing) || props.trailing < 0) return false;
539
+ const selections = this.readSelections(props);
540
+ if (!selections) return false;
541
+ const elements = [...this.root.children];
542
+ const staticSegments = [];
543
+ const instances = [];
544
+ let cursor = 0;
545
+ for (let rangeIndex = 0; rangeIndex < props.ranges.length; rangeIndex += 1) {
546
+ const range = props.ranges[rangeIndex];
547
+ if (!Number.isSafeInteger(range.before) || range.before < 0) return false;
548
+ const staticEnd = cursor + range.before;
549
+ if (staticEnd > elements.length) return false;
550
+ staticSegments.push(elements.slice(cursor, staticEnd));
551
+ cursor = staticEnd;
552
+ const selection = selections[rangeIndex];
553
+ if (selection.kind === "empty") {
554
+ instances.push(null);
555
+ continue;
556
+ }
557
+ const element = elements[cursor];
558
+ if (!element) return false;
559
+ const descriptor = selection.branch.create();
560
+ if (!matchesCompilerHostElement(element, descriptor)) return false;
561
+ instances.push({
562
+ key: selection.key,
563
+ host: {
564
+ element,
565
+ values: readHostConditionalBindingValues(selection.branch)
566
+ }
567
+ });
568
+ cursor += 1;
569
+ }
570
+ if (cursor + props.trailing !== elements.length) return false;
571
+ staticSegments.push(elements.slice(cursor));
572
+ this.staticSegments = staticSegments;
573
+ this.rangeInstances = instances;
574
+ return true;
575
+ }
576
+ anchorAfter(rangeIndex) {
577
+ for (let next = rangeIndex + 1; next < this.rangeInstances.length; next += 1) {
578
+ const staticAnchor = this.staticSegments[next]?.[0];
579
+ if (staticAnchor) return staticAnchor;
580
+ const rangeAnchor = this.rangeInstances[next]?.host.element;
581
+ if (rangeAnchor) return rangeAnchor;
582
+ }
583
+ return this.staticSegments[this.rangeInstances.length]?.[0] || null;
584
+ }
585
+ reconcileRange(rangeIndex, selection) {
586
+ if (!this.root) return;
587
+ const previous = this.rangeInstances[rangeIndex];
588
+ if (selection.kind === "empty") {
589
+ previous?.host.element.remove();
590
+ this.rangeInstances[rangeIndex] = null;
591
+ return;
592
+ }
593
+ if (previous?.key === selection.key) {
594
+ applyHostConditionalBindings(selection.branch, previous.host);
595
+ return;
596
+ }
597
+ const element = createCompilerHostElement(this.root.ownerDocument, selection.branch.create());
598
+ if (previous) previous.host.element.replaceWith(element);
599
+ else this.root.insertBefore(element, this.anchorAfter(rangeIndex));
600
+ this.rangeInstances[rangeIndex] = {
601
+ key: selection.key,
602
+ host: {
603
+ element,
604
+ values: readHostConditionalBindingValues(selection.branch)
605
+ }
606
+ };
607
+ }
608
+ reconcile(afterCommit) {
609
+ if (!this.mounted || !this.root) {
610
+ afterCommit?.();
611
+ return;
612
+ }
613
+ if (this.state.fallback) {
614
+ this.fallbackVersion += 1;
615
+ this.forceUpdate(afterCommit);
616
+ return;
617
+ }
618
+ if (this.currentProps.ranges.length !== this.rangeInstances.length) {
619
+ this.activateFallback(afterCommit);
620
+ return;
621
+ }
622
+ const selections = this.readSelections(this.currentProps);
623
+ if (!selections) {
624
+ this.activateFallback(afterCommit);
625
+ return;
626
+ }
627
+ for (let rangeIndex = this.rangeInstances.length - 1; rangeIndex >= 0; rangeIndex -= 1) {
628
+ this.reconcileRange(rangeIndex, selections[rangeIndex]);
629
+ }
630
+ afterCommit?.();
631
+ }
632
+ activateFallback(afterCommit) {
633
+ if (!this.mounted || this.state.fallback || this.fallbackRequested) {
634
+ afterCommit?.();
635
+ return;
636
+ }
637
+ this.fallbackRequested = true;
638
+ this.setState({ fallback: true }, afterCommit);
639
+ }
640
+ schedulePropFallback() {
641
+ if (this.propFallbackQueued) return;
642
+ this.propFallbackQueued = true;
643
+ queueMicrotask(() => {
644
+ this.propFallbackQueued = false;
645
+ if (this.mounted && !this.state.fallback) this.activateFallback();
646
+ });
647
+ }
648
+ shouldComponentUpdate(nextProps, nextState) {
649
+ this.currentProps = nextProps;
650
+ if (nextState.fallback || this.state.fallback) return true;
651
+ this.schedulePropFallback();
652
+ return false;
653
+ }
654
+ componentDidMount() {
655
+ this.mounted = true;
656
+ this.unsubscribe = owner.subscribe(this.props.id, this.refresh);
657
+ if (!this.adopt()) this.activateFallback();
658
+ }
659
+ componentWillUnmount() {
660
+ this.mounted = false;
661
+ this.unsubscribe?.();
662
+ this.root = null;
663
+ this.rangeInstances = [];
664
+ this.staticSegments = [];
665
+ }
666
+ render() {
667
+ const container = this.currentProps.render();
668
+ if (!React.isValidElement(container) || typeof container.type !== "string") {
669
+ throw new TypeError(
670
+ `Compiled conditional ranges ${this.currentProps.id} must own one host container.`
671
+ );
672
+ }
673
+ return React.cloneElement(container, {
674
+ key: this.state.fallback ? `react-${this.fallbackVersion}` : "compiled",
675
+ ref: this.captureRoot
676
+ });
677
+ }
678
+ }
679
+ FarmConditionalRangesBlock.displayName = "FarmCompiledConditionalRanges";
680
+ return FarmConditionalRangesBlock;
681
+ }
507
682
  function longestIncreasingSubsequencePositions(sequence) {
508
683
  const tails = [];
509
684
  const previous = Array.from({ length: sequence.length }, () => -1);
@@ -900,6 +1075,219 @@ function createKeyedRowsBlockComponent(owner) {
900
1075
  FarmKeyedRowsBlock.displayName = "FarmCompiledKeyedRows";
901
1076
  return FarmKeyedRowsBlock;
902
1077
  }
1078
+ function createKeyedRangesBlockComponent(owner) {
1079
+ class FarmKeyedRangesBlock extends React.Component {
1080
+ constructor() {
1081
+ super(...arguments);
1082
+ this.state = { fallback: false };
1083
+ this.root = null;
1084
+ this.mounted = false;
1085
+ this.fallbackRequested = false;
1086
+ this.fallbackVersion = 0;
1087
+ this.propFallbackQueued = false;
1088
+ this.currentProps = this.props;
1089
+ this.rangeInstances = [];
1090
+ this.staticSegments = [];
1091
+ this.captureRoot = (root) => {
1092
+ this.root = root;
1093
+ this.currentProps.rootRef?.(root);
1094
+ };
1095
+ this.refresh = (afterCommit) => {
1096
+ this.reconcile(afterCommit);
1097
+ };
1098
+ }
1099
+ readRange(range) {
1100
+ const source = range.items();
1101
+ const items = source ? Array.from(source) : [];
1102
+ const keys = items.map((item, index) => keyedRowIdentity(range.rowKey(item, index)));
1103
+ if (new Set(keys).size !== keys.length) return null;
1104
+ return { items, keys };
1105
+ }
1106
+ readRanges(props) {
1107
+ const ranges = [];
1108
+ for (const range of props.ranges) {
1109
+ const rows = this.readRange(range);
1110
+ if (!rows) return null;
1111
+ ranges.push(rows);
1112
+ }
1113
+ return ranges;
1114
+ }
1115
+ adopt(props = this.currentProps) {
1116
+ if (!this.root || !Number.isSafeInteger(props.trailing) || props.trailing < 0) return false;
1117
+ const rowsByRange = this.readRanges(props);
1118
+ if (!rowsByRange) return false;
1119
+ const elements = [...this.root.children];
1120
+ const staticSegments = [];
1121
+ const instancesByRange = [];
1122
+ let cursor = 0;
1123
+ for (let rangeIndex = 0; rangeIndex < props.ranges.length; rangeIndex += 1) {
1124
+ const range = props.ranges[rangeIndex];
1125
+ const rows = rowsByRange[rangeIndex];
1126
+ if (!Number.isSafeInteger(range.before) || range.before < 0) return false;
1127
+ const staticEnd = cursor + range.before;
1128
+ if (staticEnd > elements.length) return false;
1129
+ staticSegments.push(elements.slice(cursor, staticEnd));
1130
+ cursor = staticEnd;
1131
+ const rowEnd = cursor + rows.items.length;
1132
+ if (rowEnd > elements.length) return false;
1133
+ const instances = /* @__PURE__ */ new Map();
1134
+ for (let index = 0; index < rows.items.length; index += 1) {
1135
+ const element = elements[cursor + index];
1136
+ if (!matchesCompilerHostElement(element, range.create(rows.items[index], index))) {
1137
+ return false;
1138
+ }
1139
+ instances.set(rows.keys[index], {
1140
+ key: rows.keys[index],
1141
+ element,
1142
+ values: readKeyedRowBindingValues(range, rows.items[index], index),
1143
+ item: rows.items[index],
1144
+ index,
1145
+ conditionalValues: /* @__PURE__ */ new Map()
1146
+ });
1147
+ }
1148
+ instancesByRange.push(instances);
1149
+ cursor = rowEnd;
1150
+ }
1151
+ if (cursor + props.trailing !== elements.length) return false;
1152
+ staticSegments.push(elements.slice(cursor));
1153
+ this.staticSegments = staticSegments;
1154
+ this.rangeInstances = instancesByRange;
1155
+ return true;
1156
+ }
1157
+ anchorAfter(rangeIndex) {
1158
+ for (let next = rangeIndex + 1; next < this.rangeInstances.length; next += 1) {
1159
+ const staticAnchor = this.staticSegments[next]?.[0];
1160
+ if (staticAnchor) return staticAnchor;
1161
+ const rowAnchor = this.rangeInstances[next].values().next().value?.element;
1162
+ if (rowAnchor) return rowAnchor;
1163
+ }
1164
+ return this.staticSegments[this.rangeInstances.length]?.[0] || null;
1165
+ }
1166
+ reconcileRange(rangeIndex, rows) {
1167
+ if (!this.root) return;
1168
+ const range = this.currentProps.ranges[rangeIndex];
1169
+ const previous = this.rangeInstances[rangeIndex];
1170
+ const oldIndices = /* @__PURE__ */ new Map();
1171
+ [...previous.keys()].forEach((key, index) => oldIndices.set(key, index));
1172
+ const nextKeys = new Set(rows.keys);
1173
+ for (const [key, instance] of previous) {
1174
+ if (!nextKeys.has(key)) instance.element.remove();
1175
+ }
1176
+ const sequence = rows.keys.map((key) => oldIndices.get(key) ?? -1);
1177
+ const stablePositions = longestIncreasingSubsequencePositions(sequence);
1178
+ const nextInstances = [];
1179
+ for (let index = 0; index < rows.items.length; index += 1) {
1180
+ const key = rows.keys[index];
1181
+ const existing = previous.get(key);
1182
+ if (existing) {
1183
+ applyKeyedRowBindings(range, existing, rows.items[index], index);
1184
+ existing.item = rows.items[index];
1185
+ existing.index = index;
1186
+ nextInstances.push(existing);
1187
+ continue;
1188
+ }
1189
+ const element = createCompilerHostElement(
1190
+ this.root.ownerDocument,
1191
+ range.create(rows.items[index], index)
1192
+ );
1193
+ nextInstances.push({
1194
+ key,
1195
+ element,
1196
+ values: readKeyedRowBindingValues(range, rows.items[index], index),
1197
+ item: rows.items[index],
1198
+ index,
1199
+ conditionalValues: /* @__PURE__ */ new Map()
1200
+ });
1201
+ }
1202
+ let anchor = this.anchorAfter(rangeIndex);
1203
+ for (let index = nextInstances.length - 1; index >= 0; index -= 1) {
1204
+ const instance = nextInstances[index];
1205
+ if (sequence[index] < 0) {
1206
+ this.root.insertBefore(instance.element, anchor);
1207
+ } else if (!stablePositions.has(index) && instance.element.nextSibling !== anchor) {
1208
+ this.root.insertBefore(instance.element, anchor);
1209
+ }
1210
+ anchor = instance.element;
1211
+ }
1212
+ this.rangeInstances[rangeIndex] = new Map(
1213
+ nextInstances.map((instance) => [instance.key, instance])
1214
+ );
1215
+ }
1216
+ reconcile(afterCommit) {
1217
+ if (!this.mounted || !this.root) {
1218
+ afterCommit?.();
1219
+ return;
1220
+ }
1221
+ if (this.state.fallback) {
1222
+ this.fallbackVersion += 1;
1223
+ this.forceUpdate(afterCommit);
1224
+ return;
1225
+ }
1226
+ const rowsByRange = this.readRanges(this.currentProps);
1227
+ if (!rowsByRange || rowsByRange.length !== this.rangeInstances.length) {
1228
+ this.activateFallback(afterCommit);
1229
+ return;
1230
+ }
1231
+ const activeElement = this.root.ownerDocument.activeElement;
1232
+ const restoreFocus = Boolean(activeElement && this.root.contains(activeElement));
1233
+ for (let rangeIndex = rowsByRange.length - 1; rangeIndex >= 0; rangeIndex -= 1) {
1234
+ this.reconcileRange(rangeIndex, rowsByRange[rangeIndex]);
1235
+ }
1236
+ if (restoreFocus && activeElement?.isConnected && activeElement.ownerDocument.activeElement !== activeElement && "focus" in activeElement) {
1237
+ activeElement.focus({ preventScroll: true });
1238
+ }
1239
+ afterCommit?.();
1240
+ }
1241
+ activateFallback(afterCommit) {
1242
+ if (!this.mounted || this.state.fallback || this.fallbackRequested) {
1243
+ afterCommit?.();
1244
+ return;
1245
+ }
1246
+ this.fallbackRequested = true;
1247
+ this.setState({ fallback: true }, afterCommit);
1248
+ }
1249
+ schedulePropFallback() {
1250
+ if (this.propFallbackQueued) return;
1251
+ this.propFallbackQueued = true;
1252
+ queueMicrotask(() => {
1253
+ this.propFallbackQueued = false;
1254
+ if (this.mounted && !this.state.fallback) this.activateFallback();
1255
+ });
1256
+ }
1257
+ shouldComponentUpdate(nextProps, nextState) {
1258
+ this.currentProps = nextProps;
1259
+ if (nextState.fallback || this.state.fallback) return true;
1260
+ this.schedulePropFallback();
1261
+ return false;
1262
+ }
1263
+ componentDidMount() {
1264
+ this.mounted = true;
1265
+ this.unsubscribe = owner.subscribe(this.props.id, this.refresh);
1266
+ if (!this.adopt()) this.activateFallback();
1267
+ }
1268
+ componentWillUnmount() {
1269
+ this.mounted = false;
1270
+ this.unsubscribe?.();
1271
+ this.root = null;
1272
+ this.rangeInstances = [];
1273
+ this.staticSegments = [];
1274
+ }
1275
+ render() {
1276
+ const container = this.currentProps.render();
1277
+ if (!React.isValidElement(container) || typeof container.type !== "string") {
1278
+ throw new TypeError(
1279
+ `Compiled keyed ranges ${this.currentProps.id} must own one host container.`
1280
+ );
1281
+ }
1282
+ return React.cloneElement(container, {
1283
+ key: this.state.fallback ? `react-${this.fallbackVersion}` : "compiled",
1284
+ ref: this.captureRoot
1285
+ });
1286
+ }
1287
+ }
1288
+ FarmKeyedRangesBlock.displayName = "FarmCompiledKeyedRanges";
1289
+ return FarmKeyedRangesBlock;
1290
+ }
903
1291
  function createComponentBlockComponent(owner) {
904
1292
  class FarmComponentBlock extends React.Component {
905
1293
  constructor() {
@@ -990,12 +1378,18 @@ function createCompiledComponent(definition) {
990
1378
  HostConditional: createHostConditionalBlockComponent({
991
1379
  subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
992
1380
  }),
1381
+ ConditionalRanges: createConditionalRangesBlockComponent({
1382
+ subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
1383
+ }),
993
1384
  KeyedList: createKeyedListBlockComponent({
994
1385
  subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
995
1386
  }),
996
1387
  KeyedRows: createKeyedRowsBlockComponent({
997
1388
  subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
998
1389
  }),
1390
+ KeyedRanges: createKeyedRangesBlockComponent({
1391
+ subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
1392
+ }),
999
1393
  Component: createComponentBlockComponent({
1000
1394
  subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
1001
1395
  }),
@@ -1153,7 +1547,23 @@ function createCompiledComponent(definition) {
1153
1547
  if (this.hasBindingError) throw this.bindingError;
1154
1548
  const currentDefinition = definitionReference.current;
1155
1549
  const element = currentDefinition.render(this.props, this.cells, this.blockRuntime);
1156
- if (!React.isValidElement(element) || typeof element.type !== "string") {
1550
+ if (!React.isValidElement(element)) {
1551
+ throw new TypeError(
1552
+ `Compiled component ${currentDefinition.displayName} must return one host element.`
1553
+ );
1554
+ }
1555
+ if (element.type === this.blockRuntime.KeyedRanges) {
1556
+ return React.cloneElement(element, {
1557
+ rootRef: this.captureRoot
1558
+ });
1559
+ }
1560
+ if (element.type === this.blockRuntime.ConditionalRanges) {
1561
+ return React.cloneElement(
1562
+ element,
1563
+ { rootRef: this.captureRoot }
1564
+ );
1565
+ }
1566
+ if (typeof element.type !== "string") {
1157
1567
  throw new TypeError(
1158
1568
  `Compiled component ${currentDefinition.displayName} must return one host element.`
1159
1569
  );