@farm.js/react 0.1.0-beta.1 → 0.1.0-beta.2
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.
- package/README.md +67 -9
- package/dist/compiler-runtime.cjs +227 -1
- package/dist/compiler-runtime.cjs.map +1 -1
- package/dist/compiler-runtime.d.mts +18 -1
- package/dist/compiler-runtime.d.ts +18 -1
- package/dist/compiler-runtime.mjs +227 -1
- package/dist/compiler-runtime.mjs.map +1 -1
- package/dist/vite.cjs +326 -20
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.mjs +326 -20
- package/dist/vite.mjs.map +1 -1
- package/package.json +3 -3
|
@@ -111,6 +111,22 @@ interface CompilerKeyedRowsBlockProps {
|
|
|
111
111
|
events?: readonly CompilerKeyedRowEvent[];
|
|
112
112
|
conditionals?: readonly CompilerKeyedRowConditional[];
|
|
113
113
|
}
|
|
114
|
+
interface CompilerKeyedRange {
|
|
115
|
+
/** Number of static direct host siblings between this range and the previous range. */
|
|
116
|
+
before: number;
|
|
117
|
+
items(): Iterable<unknown> | null | undefined;
|
|
118
|
+
rowKey(item: unknown, index: number): React.Key;
|
|
119
|
+
create(item: unknown, index: number): CompilerKeyedRowElement;
|
|
120
|
+
bindings: readonly CompilerKeyedRowBinding[];
|
|
121
|
+
}
|
|
122
|
+
interface CompilerKeyedRangesBlockProps {
|
|
123
|
+
id: number;
|
|
124
|
+
render(): React.ReactElement;
|
|
125
|
+
rootRef?: React.RefCallback<Element>;
|
|
126
|
+
ranges: readonly CompilerKeyedRange[];
|
|
127
|
+
/** Number of static direct host siblings after the final keyed range. */
|
|
128
|
+
trailing: number;
|
|
129
|
+
}
|
|
114
130
|
interface CompilerComponentBlockProps {
|
|
115
131
|
id: number;
|
|
116
132
|
render(): React.ReactNode;
|
|
@@ -120,6 +136,7 @@ interface CompilerBlockRuntime {
|
|
|
120
136
|
HostConditional: React.ComponentType<CompilerHostConditionalBlockProps>;
|
|
121
137
|
KeyedList: React.ComponentType<CompilerKeyedListBlockProps>;
|
|
122
138
|
KeyedRows: React.ComponentType<CompilerKeyedRowsBlockProps>;
|
|
139
|
+
KeyedRanges: React.ComponentType<CompilerKeyedRangesBlockProps>;
|
|
123
140
|
Component: React.ComponentType<CompilerComponentBlockProps>;
|
|
124
141
|
target(id: number): React.RefCallback<Element>;
|
|
125
142
|
}
|
|
@@ -143,4 +160,4 @@ interface CompiledComponentDefinition<Props> {
|
|
|
143
160
|
*/
|
|
144
161
|
declare function createCompiledComponent<Props>(definition: CompiledComponentDefinition<Props>): React.ComponentType<Props>;
|
|
145
162
|
|
|
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 };
|
|
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 };
|
|
@@ -900,6 +900,219 @@ function createKeyedRowsBlockComponent(owner) {
|
|
|
900
900
|
FarmKeyedRowsBlock.displayName = "FarmCompiledKeyedRows";
|
|
901
901
|
return FarmKeyedRowsBlock;
|
|
902
902
|
}
|
|
903
|
+
function createKeyedRangesBlockComponent(owner) {
|
|
904
|
+
class FarmKeyedRangesBlock extends React.Component {
|
|
905
|
+
constructor() {
|
|
906
|
+
super(...arguments);
|
|
907
|
+
this.state = { fallback: false };
|
|
908
|
+
this.root = null;
|
|
909
|
+
this.mounted = false;
|
|
910
|
+
this.fallbackRequested = false;
|
|
911
|
+
this.fallbackVersion = 0;
|
|
912
|
+
this.propFallbackQueued = false;
|
|
913
|
+
this.currentProps = this.props;
|
|
914
|
+
this.rangeInstances = [];
|
|
915
|
+
this.staticSegments = [];
|
|
916
|
+
this.captureRoot = (root) => {
|
|
917
|
+
this.root = root;
|
|
918
|
+
this.currentProps.rootRef?.(root);
|
|
919
|
+
};
|
|
920
|
+
this.refresh = (afterCommit) => {
|
|
921
|
+
this.reconcile(afterCommit);
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
readRange(range) {
|
|
925
|
+
const source = range.items();
|
|
926
|
+
const items = source ? Array.from(source) : [];
|
|
927
|
+
const keys = items.map((item, index) => keyedRowIdentity(range.rowKey(item, index)));
|
|
928
|
+
if (new Set(keys).size !== keys.length) return null;
|
|
929
|
+
return { items, keys };
|
|
930
|
+
}
|
|
931
|
+
readRanges(props) {
|
|
932
|
+
const ranges = [];
|
|
933
|
+
for (const range of props.ranges) {
|
|
934
|
+
const rows = this.readRange(range);
|
|
935
|
+
if (!rows) return null;
|
|
936
|
+
ranges.push(rows);
|
|
937
|
+
}
|
|
938
|
+
return ranges;
|
|
939
|
+
}
|
|
940
|
+
adopt(props = this.currentProps) {
|
|
941
|
+
if (!this.root || !Number.isSafeInteger(props.trailing) || props.trailing < 0) return false;
|
|
942
|
+
const rowsByRange = this.readRanges(props);
|
|
943
|
+
if (!rowsByRange) return false;
|
|
944
|
+
const elements = [...this.root.children];
|
|
945
|
+
const staticSegments = [];
|
|
946
|
+
const instancesByRange = [];
|
|
947
|
+
let cursor = 0;
|
|
948
|
+
for (let rangeIndex = 0; rangeIndex < props.ranges.length; rangeIndex += 1) {
|
|
949
|
+
const range = props.ranges[rangeIndex];
|
|
950
|
+
const rows = rowsByRange[rangeIndex];
|
|
951
|
+
if (!Number.isSafeInteger(range.before) || range.before < 0) return false;
|
|
952
|
+
const staticEnd = cursor + range.before;
|
|
953
|
+
if (staticEnd > elements.length) return false;
|
|
954
|
+
staticSegments.push(elements.slice(cursor, staticEnd));
|
|
955
|
+
cursor = staticEnd;
|
|
956
|
+
const rowEnd = cursor + rows.items.length;
|
|
957
|
+
if (rowEnd > elements.length) return false;
|
|
958
|
+
const instances = /* @__PURE__ */ new Map();
|
|
959
|
+
for (let index = 0; index < rows.items.length; index += 1) {
|
|
960
|
+
const element = elements[cursor + index];
|
|
961
|
+
if (!matchesCompilerHostElement(element, range.create(rows.items[index], index))) {
|
|
962
|
+
return false;
|
|
963
|
+
}
|
|
964
|
+
instances.set(rows.keys[index], {
|
|
965
|
+
key: rows.keys[index],
|
|
966
|
+
element,
|
|
967
|
+
values: readKeyedRowBindingValues(range, rows.items[index], index),
|
|
968
|
+
item: rows.items[index],
|
|
969
|
+
index,
|
|
970
|
+
conditionalValues: /* @__PURE__ */ new Map()
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
instancesByRange.push(instances);
|
|
974
|
+
cursor = rowEnd;
|
|
975
|
+
}
|
|
976
|
+
if (cursor + props.trailing !== elements.length) return false;
|
|
977
|
+
staticSegments.push(elements.slice(cursor));
|
|
978
|
+
this.staticSegments = staticSegments;
|
|
979
|
+
this.rangeInstances = instancesByRange;
|
|
980
|
+
return true;
|
|
981
|
+
}
|
|
982
|
+
anchorAfter(rangeIndex) {
|
|
983
|
+
for (let next = rangeIndex + 1; next < this.rangeInstances.length; next += 1) {
|
|
984
|
+
const staticAnchor = this.staticSegments[next]?.[0];
|
|
985
|
+
if (staticAnchor) return staticAnchor;
|
|
986
|
+
const rowAnchor = this.rangeInstances[next].values().next().value?.element;
|
|
987
|
+
if (rowAnchor) return rowAnchor;
|
|
988
|
+
}
|
|
989
|
+
return this.staticSegments[this.rangeInstances.length]?.[0] || null;
|
|
990
|
+
}
|
|
991
|
+
reconcileRange(rangeIndex, rows) {
|
|
992
|
+
if (!this.root) return;
|
|
993
|
+
const range = this.currentProps.ranges[rangeIndex];
|
|
994
|
+
const previous = this.rangeInstances[rangeIndex];
|
|
995
|
+
const oldIndices = /* @__PURE__ */ new Map();
|
|
996
|
+
[...previous.keys()].forEach((key, index) => oldIndices.set(key, index));
|
|
997
|
+
const nextKeys = new Set(rows.keys);
|
|
998
|
+
for (const [key, instance] of previous) {
|
|
999
|
+
if (!nextKeys.has(key)) instance.element.remove();
|
|
1000
|
+
}
|
|
1001
|
+
const sequence = rows.keys.map((key) => oldIndices.get(key) ?? -1);
|
|
1002
|
+
const stablePositions = longestIncreasingSubsequencePositions(sequence);
|
|
1003
|
+
const nextInstances = [];
|
|
1004
|
+
for (let index = 0; index < rows.items.length; index += 1) {
|
|
1005
|
+
const key = rows.keys[index];
|
|
1006
|
+
const existing = previous.get(key);
|
|
1007
|
+
if (existing) {
|
|
1008
|
+
applyKeyedRowBindings(range, existing, rows.items[index], index);
|
|
1009
|
+
existing.item = rows.items[index];
|
|
1010
|
+
existing.index = index;
|
|
1011
|
+
nextInstances.push(existing);
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
1014
|
+
const element = createCompilerHostElement(
|
|
1015
|
+
this.root.ownerDocument,
|
|
1016
|
+
range.create(rows.items[index], index)
|
|
1017
|
+
);
|
|
1018
|
+
nextInstances.push({
|
|
1019
|
+
key,
|
|
1020
|
+
element,
|
|
1021
|
+
values: readKeyedRowBindingValues(range, rows.items[index], index),
|
|
1022
|
+
item: rows.items[index],
|
|
1023
|
+
index,
|
|
1024
|
+
conditionalValues: /* @__PURE__ */ new Map()
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
let anchor = this.anchorAfter(rangeIndex);
|
|
1028
|
+
for (let index = nextInstances.length - 1; index >= 0; index -= 1) {
|
|
1029
|
+
const instance = nextInstances[index];
|
|
1030
|
+
if (sequence[index] < 0) {
|
|
1031
|
+
this.root.insertBefore(instance.element, anchor);
|
|
1032
|
+
} else if (!stablePositions.has(index) && instance.element.nextSibling !== anchor) {
|
|
1033
|
+
this.root.insertBefore(instance.element, anchor);
|
|
1034
|
+
}
|
|
1035
|
+
anchor = instance.element;
|
|
1036
|
+
}
|
|
1037
|
+
this.rangeInstances[rangeIndex] = new Map(
|
|
1038
|
+
nextInstances.map((instance) => [instance.key, instance])
|
|
1039
|
+
);
|
|
1040
|
+
}
|
|
1041
|
+
reconcile(afterCommit) {
|
|
1042
|
+
if (!this.mounted || !this.root) {
|
|
1043
|
+
afterCommit?.();
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
if (this.state.fallback) {
|
|
1047
|
+
this.fallbackVersion += 1;
|
|
1048
|
+
this.forceUpdate(afterCommit);
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
const rowsByRange = this.readRanges(this.currentProps);
|
|
1052
|
+
if (!rowsByRange || rowsByRange.length !== this.rangeInstances.length) {
|
|
1053
|
+
this.activateFallback(afterCommit);
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
const activeElement = this.root.ownerDocument.activeElement;
|
|
1057
|
+
const restoreFocus = Boolean(activeElement && this.root.contains(activeElement));
|
|
1058
|
+
for (let rangeIndex = rowsByRange.length - 1; rangeIndex >= 0; rangeIndex -= 1) {
|
|
1059
|
+
this.reconcileRange(rangeIndex, rowsByRange[rangeIndex]);
|
|
1060
|
+
}
|
|
1061
|
+
if (restoreFocus && activeElement?.isConnected && activeElement.ownerDocument.activeElement !== activeElement && "focus" in activeElement) {
|
|
1062
|
+
activeElement.focus({ preventScroll: true });
|
|
1063
|
+
}
|
|
1064
|
+
afterCommit?.();
|
|
1065
|
+
}
|
|
1066
|
+
activateFallback(afterCommit) {
|
|
1067
|
+
if (!this.mounted || this.state.fallback || this.fallbackRequested) {
|
|
1068
|
+
afterCommit?.();
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
this.fallbackRequested = true;
|
|
1072
|
+
this.setState({ fallback: true }, afterCommit);
|
|
1073
|
+
}
|
|
1074
|
+
schedulePropFallback() {
|
|
1075
|
+
if (this.propFallbackQueued) return;
|
|
1076
|
+
this.propFallbackQueued = true;
|
|
1077
|
+
queueMicrotask(() => {
|
|
1078
|
+
this.propFallbackQueued = false;
|
|
1079
|
+
if (this.mounted && !this.state.fallback) this.activateFallback();
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
shouldComponentUpdate(nextProps, nextState) {
|
|
1083
|
+
this.currentProps = nextProps;
|
|
1084
|
+
if (nextState.fallback || this.state.fallback) return true;
|
|
1085
|
+
this.schedulePropFallback();
|
|
1086
|
+
return false;
|
|
1087
|
+
}
|
|
1088
|
+
componentDidMount() {
|
|
1089
|
+
this.mounted = true;
|
|
1090
|
+
this.unsubscribe = owner.subscribe(this.props.id, this.refresh);
|
|
1091
|
+
if (!this.adopt()) this.activateFallback();
|
|
1092
|
+
}
|
|
1093
|
+
componentWillUnmount() {
|
|
1094
|
+
this.mounted = false;
|
|
1095
|
+
this.unsubscribe?.();
|
|
1096
|
+
this.root = null;
|
|
1097
|
+
this.rangeInstances = [];
|
|
1098
|
+
this.staticSegments = [];
|
|
1099
|
+
}
|
|
1100
|
+
render() {
|
|
1101
|
+
const container = this.currentProps.render();
|
|
1102
|
+
if (!React.isValidElement(container) || typeof container.type !== "string") {
|
|
1103
|
+
throw new TypeError(
|
|
1104
|
+
`Compiled keyed ranges ${this.currentProps.id} must own one host container.`
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
1107
|
+
return React.cloneElement(container, {
|
|
1108
|
+
key: this.state.fallback ? `react-${this.fallbackVersion}` : "compiled",
|
|
1109
|
+
ref: this.captureRoot
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
FarmKeyedRangesBlock.displayName = "FarmCompiledKeyedRanges";
|
|
1114
|
+
return FarmKeyedRangesBlock;
|
|
1115
|
+
}
|
|
903
1116
|
function createComponentBlockComponent(owner) {
|
|
904
1117
|
class FarmComponentBlock extends React.Component {
|
|
905
1118
|
constructor() {
|
|
@@ -996,6 +1209,9 @@ function createCompiledComponent(definition) {
|
|
|
996
1209
|
KeyedRows: createKeyedRowsBlockComponent({
|
|
997
1210
|
subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
|
|
998
1211
|
}),
|
|
1212
|
+
KeyedRanges: createKeyedRangesBlockComponent({
|
|
1213
|
+
subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
|
|
1214
|
+
}),
|
|
999
1215
|
Component: createComponentBlockComponent({
|
|
1000
1216
|
subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
|
|
1001
1217
|
}),
|
|
@@ -1153,7 +1369,17 @@ function createCompiledComponent(definition) {
|
|
|
1153
1369
|
if (this.hasBindingError) throw this.bindingError;
|
|
1154
1370
|
const currentDefinition = definitionReference.current;
|
|
1155
1371
|
const element = currentDefinition.render(this.props, this.cells, this.blockRuntime);
|
|
1156
|
-
if (!React.isValidElement(element)
|
|
1372
|
+
if (!React.isValidElement(element)) {
|
|
1373
|
+
throw new TypeError(
|
|
1374
|
+
`Compiled component ${currentDefinition.displayName} must return one host element.`
|
|
1375
|
+
);
|
|
1376
|
+
}
|
|
1377
|
+
if (element.type === this.blockRuntime.KeyedRanges) {
|
|
1378
|
+
return React.cloneElement(element, {
|
|
1379
|
+
rootRef: this.captureRoot
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
if (typeof element.type !== "string") {
|
|
1157
1383
|
throw new TypeError(
|
|
1158
1384
|
`Compiled component ${currentDefinition.displayName} must return one host element.`
|
|
1159
1385
|
);
|