@farm.js/react 0.1.0-beta.2 → 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[];
@@ -134,6 +151,7 @@ interface CompilerComponentBlockProps {
134
151
  interface CompilerBlockRuntime {
135
152
  Conditional: React.ComponentType<CompilerConditionalBlockProps>;
136
153
  HostConditional: React.ComponentType<CompilerHostConditionalBlockProps>;
154
+ ConditionalRanges: React.ComponentType<CompilerConditionalRangesBlockProps>;
137
155
  KeyedList: React.ComponentType<CompilerKeyedListBlockProps>;
138
156
  KeyedRows: React.ComponentType<CompilerKeyedRowsBlockProps>;
139
157
  KeyedRanges: React.ComponentType<CompilerKeyedRangesBlockProps>;
@@ -160,4 +178,4 @@ interface CompiledComponentDefinition<Props> {
160
178
  */
161
179
  declare function createCompiledComponent<Props>(definition: CompiledComponentDefinition<Props>): React.ComponentType<Props>;
162
180
 
163
- 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 CompilerKeyedRange, type CompilerKeyedRangesBlockProps, 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[];
@@ -134,6 +151,7 @@ interface CompilerComponentBlockProps {
134
151
  interface CompilerBlockRuntime {
135
152
  Conditional: React.ComponentType<CompilerConditionalBlockProps>;
136
153
  HostConditional: React.ComponentType<CompilerHostConditionalBlockProps>;
154
+ ConditionalRanges: React.ComponentType<CompilerConditionalRangesBlockProps>;
137
155
  KeyedList: React.ComponentType<CompilerKeyedListBlockProps>;
138
156
  KeyedRows: React.ComponentType<CompilerKeyedRowsBlockProps>;
139
157
  KeyedRanges: React.ComponentType<CompilerKeyedRangesBlockProps>;
@@ -160,4 +178,4 @@ interface CompiledComponentDefinition<Props> {
160
178
  */
161
179
  declare function createCompiledComponent<Props>(definition: CompiledComponentDefinition<Props>): React.ComponentType<Props>;
162
180
 
163
- 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 CompilerKeyedRange, type CompilerKeyedRangesBlockProps, 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);
@@ -1203,6 +1378,9 @@ function createCompiledComponent(definition) {
1203
1378
  HostConditional: createHostConditionalBlockComponent({
1204
1379
  subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
1205
1380
  }),
1381
+ ConditionalRanges: createConditionalRangesBlockComponent({
1382
+ subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
1383
+ }),
1206
1384
  KeyedList: createKeyedListBlockComponent({
1207
1385
  subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
1208
1386
  }),
@@ -1379,6 +1557,12 @@ function createCompiledComponent(definition) {
1379
1557
  rootRef: this.captureRoot
1380
1558
  });
1381
1559
  }
1560
+ if (element.type === this.blockRuntime.ConditionalRanges) {
1561
+ return React.cloneElement(
1562
+ element,
1563
+ { rootRef: this.captureRoot }
1564
+ );
1565
+ }
1382
1566
  if (typeof element.type !== "string") {
1383
1567
  throw new TypeError(
1384
1568
  `Compiled component ${currentDefinition.displayName} must return one host element.`