@farm.js/react 0.1.0-beta.0 → 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 +277 -11
- package/dist/compiler-runtime.cjs +1290 -17
- package/dist/compiler-runtime.cjs.map +1 -1
- package/dist/compiler-runtime.d.mts +132 -5
- package/dist/compiler-runtime.d.ts +132 -5
- package/dist/compiler-runtime.mjs +1290 -17
- package/dist/compiler-runtime.mjs.map +1 -1
- package/dist/index.cjs +24 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.mjs +24 -2
- package/dist/index.mjs.map +1 -1
- package/dist/list.cjs +27 -0
- package/dist/list.cjs.map +1 -0
- package/dist/list.d.mts +20 -0
- package/dist/list.d.ts +20 -0
- package/dist/list.mjs +21 -0
- package/dist/list.mjs.map +1 -0
- package/dist/vite.cjs +2332 -140
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.mjs +2332 -140
- package/dist/vite.mjs.map +1 -1
- package/package.json +10 -4
|
@@ -1,30 +1,156 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { flushSync } from 'react-dom';
|
|
2
3
|
|
|
3
4
|
// src/compiler-runtime.tsx
|
|
5
|
+
var COMPILER_HMR_REGISTRY = /* @__PURE__ */ Symbol.for("@farm.js/react/compiler-hmr-registry");
|
|
6
|
+
function getCompilerHmrRegistry() {
|
|
7
|
+
const globalTarget = globalThis;
|
|
8
|
+
const existing = globalTarget[COMPILER_HMR_REGISTRY];
|
|
9
|
+
if (existing instanceof Map) return existing;
|
|
10
|
+
const registry = /* @__PURE__ */ new Map();
|
|
11
|
+
globalTarget[COMPILER_HMR_REGISTRY] = registry;
|
|
12
|
+
return registry;
|
|
13
|
+
}
|
|
4
14
|
function renderTextValue(value) {
|
|
5
15
|
if (Array.isArray(value)) return value.map(renderTextValue).join("");
|
|
6
16
|
if (value === null || value === void 0 || typeof value === "boolean") return "";
|
|
7
17
|
return String(value);
|
|
8
18
|
}
|
|
9
|
-
function findBindingTarget(root, path) {
|
|
19
|
+
function findBindingTarget(root, path, blockRoots) {
|
|
10
20
|
let current = root;
|
|
11
21
|
for (const index of path) {
|
|
12
|
-
|
|
22
|
+
if (!current) return null;
|
|
23
|
+
current = [...current.children].filter((child) => !blockRoots.has(child))[index] || null;
|
|
13
24
|
}
|
|
14
25
|
return current;
|
|
15
26
|
}
|
|
27
|
+
var UNITLESS_STYLE_PROPERTIES = /* @__PURE__ */ new Set([
|
|
28
|
+
"animationIterationCount",
|
|
29
|
+
"aspectRatio",
|
|
30
|
+
"borderImageOutset",
|
|
31
|
+
"borderImageSlice",
|
|
32
|
+
"borderImageWidth",
|
|
33
|
+
"boxFlex",
|
|
34
|
+
"boxFlexGroup",
|
|
35
|
+
"boxOrdinalGroup",
|
|
36
|
+
"columnCount",
|
|
37
|
+
"columns",
|
|
38
|
+
"flex",
|
|
39
|
+
"flexGrow",
|
|
40
|
+
"flexNegative",
|
|
41
|
+
"flexOrder",
|
|
42
|
+
"flexPositive",
|
|
43
|
+
"flexShrink",
|
|
44
|
+
"fontWeight",
|
|
45
|
+
"gridArea",
|
|
46
|
+
"gridColumn",
|
|
47
|
+
"gridColumnEnd",
|
|
48
|
+
"gridColumnSpan",
|
|
49
|
+
"gridColumnStart",
|
|
50
|
+
"gridRow",
|
|
51
|
+
"gridRowEnd",
|
|
52
|
+
"gridRowSpan",
|
|
53
|
+
"gridRowStart",
|
|
54
|
+
"lineClamp",
|
|
55
|
+
"lineHeight",
|
|
56
|
+
"opacity",
|
|
57
|
+
"order",
|
|
58
|
+
"orphans",
|
|
59
|
+
"scale",
|
|
60
|
+
"tabSize",
|
|
61
|
+
"widows",
|
|
62
|
+
"zIndex",
|
|
63
|
+
"zoom",
|
|
64
|
+
"fillOpacity",
|
|
65
|
+
"floodOpacity",
|
|
66
|
+
"stopOpacity",
|
|
67
|
+
"strokeDasharray",
|
|
68
|
+
"strokeDashoffset",
|
|
69
|
+
"strokeMiterlimit",
|
|
70
|
+
"strokeOpacity",
|
|
71
|
+
"strokeWidth"
|
|
72
|
+
]);
|
|
73
|
+
function unprefixedStyleName(name) {
|
|
74
|
+
const unprefixed = name.replace(/^(?:Webkit|Moz|ms|O)(?=[A-Z])/, "");
|
|
75
|
+
return unprefixed ? unprefixed[0].toLowerCase() + unprefixed.slice(1) : name;
|
|
76
|
+
}
|
|
77
|
+
function isHtmlOrSvgElement(element) {
|
|
78
|
+
const view = element.ownerDocument.defaultView;
|
|
79
|
+
return Boolean(
|
|
80
|
+
view && (element instanceof view.HTMLElement || element instanceof view.SVGElement)
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
function isInputElement(element) {
|
|
84
|
+
const view = element.ownerDocument.defaultView;
|
|
85
|
+
return Boolean(view && element instanceof view.HTMLInputElement);
|
|
86
|
+
}
|
|
87
|
+
function isTextAreaElement(element) {
|
|
88
|
+
const view = element.ownerDocument.defaultView;
|
|
89
|
+
return Boolean(view && element instanceof view.HTMLTextAreaElement);
|
|
90
|
+
}
|
|
91
|
+
function isSelectElement(element) {
|
|
92
|
+
const view = element.ownerDocument.defaultView;
|
|
93
|
+
return Boolean(view && element instanceof view.HTMLSelectElement);
|
|
94
|
+
}
|
|
95
|
+
function isOptionElement(element) {
|
|
96
|
+
const view = element.ownerDocument.defaultView;
|
|
97
|
+
return Boolean(view && element instanceof view.HTMLOptionElement);
|
|
98
|
+
}
|
|
99
|
+
function isSelectableTextControl(element) {
|
|
100
|
+
return Boolean(element && (isInputElement(element) || isTextAreaElement(element)));
|
|
101
|
+
}
|
|
102
|
+
function updateStyle(element, name, value) {
|
|
103
|
+
if (!isHtmlOrSvgElement(element)) return;
|
|
104
|
+
const customProperty = name.startsWith("--");
|
|
105
|
+
let nextValue = "";
|
|
106
|
+
if (value !== null && value !== void 0 && typeof value !== "boolean" && value !== "") {
|
|
107
|
+
nextValue = typeof value === "number" && value !== 0 && !customProperty && !UNITLESS_STYLE_PROPERTIES.has(unprefixedStyleName(name)) ? `${value}px` : String(value).trim();
|
|
108
|
+
}
|
|
109
|
+
if (customProperty || name.includes("-")) {
|
|
110
|
+
element.style.setProperty(name, nextValue);
|
|
111
|
+
} else {
|
|
112
|
+
element.style[name] = nextValue;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function updateSelectValue(element, value) {
|
|
116
|
+
const options = [...element.options];
|
|
117
|
+
if (element.multiple) {
|
|
118
|
+
const selected = new Set(
|
|
119
|
+
(Array.isArray(value) ? value : value === null || value === void 0 ? [] : [value]).map(
|
|
120
|
+
String
|
|
121
|
+
)
|
|
122
|
+
);
|
|
123
|
+
for (const option of options) option.selected = selected.has(option.value);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const selectedValue = value === null || value === void 0 ? "" : String(value);
|
|
127
|
+
let fallback;
|
|
128
|
+
for (const option of options) {
|
|
129
|
+
if (!option.disabled && !fallback) fallback = option;
|
|
130
|
+
if (option.value === selectedValue) {
|
|
131
|
+
option.selected = true;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (fallback) fallback.selected = true;
|
|
136
|
+
}
|
|
16
137
|
function updateAttribute(element, name, value) {
|
|
17
138
|
const attributeName = name === "className" ? "class" : name === "htmlFor" ? "for" : name;
|
|
18
139
|
const stringifiesBoolean = attributeName.startsWith("data-") || attributeName.startsWith("aria-");
|
|
19
|
-
if (name === "value" && element
|
|
20
|
-
|
|
140
|
+
if (name === "value" && (isInputElement(element) || isTextAreaElement(element))) {
|
|
141
|
+
const nextValue = value === null || value === void 0 ? "" : String(value);
|
|
142
|
+
if (element.value !== nextValue) element.value = nextValue;
|
|
21
143
|
return;
|
|
22
144
|
}
|
|
23
|
-
if (name === "
|
|
145
|
+
if (name === "value" && isSelectElement(element)) {
|
|
146
|
+
updateSelectValue(element, value);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (name === "checked" && isInputElement(element)) {
|
|
24
150
|
element.checked = Boolean(value);
|
|
25
151
|
return;
|
|
26
152
|
}
|
|
27
|
-
if (name === "selected" && element
|
|
153
|
+
if (name === "selected" && isOptionElement(element)) {
|
|
28
154
|
element.selected = Boolean(value);
|
|
29
155
|
return;
|
|
30
156
|
}
|
|
@@ -40,18 +166,1017 @@ function updateAttribute(element, name, value) {
|
|
|
40
166
|
element.setAttribute(attributeName, String(value));
|
|
41
167
|
}
|
|
42
168
|
}
|
|
169
|
+
function createConditionalBlockComponent(owner) {
|
|
170
|
+
class FarmConditionalBlock extends React.Component {
|
|
171
|
+
constructor() {
|
|
172
|
+
super(...arguments);
|
|
173
|
+
this.captureRoot = (root) => {
|
|
174
|
+
owner.setRoot(this.props.id, root);
|
|
175
|
+
};
|
|
176
|
+
this.refresh = (afterCommit) => {
|
|
177
|
+
this.forceUpdate(afterCommit);
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
subscribe() {
|
|
181
|
+
this.unsubscribe = owner.subscribe(this.props.id, this.refresh);
|
|
182
|
+
}
|
|
183
|
+
componentDidMount() {
|
|
184
|
+
this.subscribe();
|
|
185
|
+
}
|
|
186
|
+
componentDidUpdate(previous) {
|
|
187
|
+
if (previous.id === this.props.id) return;
|
|
188
|
+
this.unsubscribe?.();
|
|
189
|
+
owner.setRoot(previous.id, null);
|
|
190
|
+
this.subscribe();
|
|
191
|
+
}
|
|
192
|
+
componentWillUnmount() {
|
|
193
|
+
this.unsubscribe?.();
|
|
194
|
+
owner.setRoot(this.props.id, null);
|
|
195
|
+
}
|
|
196
|
+
render() {
|
|
197
|
+
const node = this.props.render();
|
|
198
|
+
if (!React.isValidElement(node)) {
|
|
199
|
+
if (node === null || node === void 0 || typeof node === "boolean" || typeof node === "string" || typeof node === "number" || typeof node === "bigint") {
|
|
200
|
+
return node;
|
|
201
|
+
}
|
|
202
|
+
throw new TypeError(
|
|
203
|
+
`Compiled conditional block ${this.props.id} must return one host element or an empty value.`
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
if (typeof node.type !== "string") {
|
|
207
|
+
throw new TypeError(
|
|
208
|
+
`Compiled conditional block ${this.props.id} must return one host element.`
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
return React.cloneElement(node, {
|
|
212
|
+
ref: this.captureRoot
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
FarmConditionalBlock.displayName = "FarmCompiledConditionalBlock";
|
|
217
|
+
return FarmConditionalBlock;
|
|
218
|
+
}
|
|
219
|
+
function createKeyedListBlockComponent(owner) {
|
|
220
|
+
class FarmKeyedListBlock extends React.Component {
|
|
221
|
+
constructor() {
|
|
222
|
+
super(...arguments);
|
|
223
|
+
this.refresh = (afterCommit) => {
|
|
224
|
+
this.forceUpdate(afterCommit);
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
subscribe() {
|
|
228
|
+
this.unsubscribe = owner.subscribe(this.props.id, this.refresh);
|
|
229
|
+
}
|
|
230
|
+
componentDidMount() {
|
|
231
|
+
this.subscribe();
|
|
232
|
+
}
|
|
233
|
+
componentDidUpdate(previous) {
|
|
234
|
+
if (previous.id === this.props.id) return;
|
|
235
|
+
this.unsubscribe?.();
|
|
236
|
+
this.subscribe();
|
|
237
|
+
}
|
|
238
|
+
componentWillUnmount() {
|
|
239
|
+
this.unsubscribe?.();
|
|
240
|
+
}
|
|
241
|
+
render() {
|
|
242
|
+
return this.props.render();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
FarmKeyedListBlock.displayName = "FarmCompiledKeyedListBlock";
|
|
246
|
+
return FarmKeyedListBlock;
|
|
247
|
+
}
|
|
248
|
+
function isCompilerHostElement(value) {
|
|
249
|
+
return typeof value === "object" && value !== null && value.kind === "element";
|
|
250
|
+
}
|
|
251
|
+
function createCompilerHostElement(document, descriptor) {
|
|
252
|
+
const element = document.createElement(descriptor.tag);
|
|
253
|
+
for (const attribute of descriptor.attributes) {
|
|
254
|
+
updateAttribute(element, attribute.name, attribute.value);
|
|
255
|
+
}
|
|
256
|
+
for (const style of descriptor.styles) updateStyle(element, style.name, style.value);
|
|
257
|
+
for (const child of descriptor.children) {
|
|
258
|
+
if (isCompilerHostElement(child)) {
|
|
259
|
+
element.append(createCompilerHostElement(document, child));
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
const text = renderTextValue(child);
|
|
263
|
+
if (text) element.append(document.createTextNode(text));
|
|
264
|
+
}
|
|
265
|
+
for (const attribute of descriptor.attributes) {
|
|
266
|
+
if (attribute.name === "value" && (isSelectElement(element) || isTextAreaElement(element))) {
|
|
267
|
+
updateAttribute(element, attribute.name, attribute.value);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return element;
|
|
271
|
+
}
|
|
272
|
+
function matchesCompilerHostElement(element, descriptor, opaquePaths = /* @__PURE__ */ new Set(), path = []) {
|
|
273
|
+
if (element.tagName.toLowerCase() !== descriptor.tag.toLowerCase()) return false;
|
|
274
|
+
if (opaquePaths.has(path.join("."))) return true;
|
|
275
|
+
const expectedChildren = descriptor.children.filter(isCompilerHostElement);
|
|
276
|
+
if (element.children.length !== expectedChildren.length) return false;
|
|
277
|
+
return expectedChildren.every(
|
|
278
|
+
(child, index) => matchesCompilerHostElement(element.children[index], child, opaquePaths, [...path, index])
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
function findCompilerHostTarget(root, path) {
|
|
282
|
+
let current = root;
|
|
283
|
+
for (const index of path) current = current?.children[index] || null;
|
|
284
|
+
return current;
|
|
285
|
+
}
|
|
286
|
+
var UNSET_KEYED_ROW_BINDING = /* @__PURE__ */ Symbol("unset interactive keyed-row binding");
|
|
287
|
+
function keyedRowIdentity(key) {
|
|
288
|
+
return String(key);
|
|
289
|
+
}
|
|
290
|
+
function normalizedKeyedRowBindingValue(binding, value) {
|
|
291
|
+
return binding.kind === "text" ? renderTextValue(value) : value;
|
|
292
|
+
}
|
|
293
|
+
function readKeyedRowBindingValues(props, item, index) {
|
|
294
|
+
return props.bindings.map(
|
|
295
|
+
(binding) => normalizedKeyedRowBindingValue(binding, binding.read(item, index))
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
function applyKeyedRowBindings(props, instance, item, index) {
|
|
299
|
+
for (let bindingIndex = 0; bindingIndex < props.bindings.length; bindingIndex += 1) {
|
|
300
|
+
const binding = props.bindings[bindingIndex];
|
|
301
|
+
const rawValue = binding.read(item, index);
|
|
302
|
+
const value = normalizedKeyedRowBindingValue(binding, rawValue);
|
|
303
|
+
if (Object.is(instance.values[bindingIndex], value)) continue;
|
|
304
|
+
instance.values[bindingIndex] = value;
|
|
305
|
+
const target = findCompilerHostTarget(instance.element, binding.path);
|
|
306
|
+
if (!target) continue;
|
|
307
|
+
if (binding.kind === "text") {
|
|
308
|
+
target.textContent = value;
|
|
309
|
+
} else if (binding.kind === "style" && binding.name) {
|
|
310
|
+
updateStyle(target, binding.name, rawValue);
|
|
311
|
+
} else if (binding.kind === "attribute" && binding.name) {
|
|
312
|
+
updateAttribute(target, binding.name, rawValue);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function keyedRowConditionalSnapshot(conditional, item, index) {
|
|
317
|
+
const test = conditional.test(item, index);
|
|
318
|
+
if (conditional.logical && !test) {
|
|
319
|
+
return typeof test === "number" || typeof test === "bigint" ? ["primitive", test] : ["empty"];
|
|
320
|
+
}
|
|
321
|
+
const key = test ? "truthy" : "falsy";
|
|
322
|
+
const branch = key === "truthy" ? conditional.truthy : conditional.falsy;
|
|
323
|
+
if (!branch) return ["empty"];
|
|
324
|
+
return [
|
|
325
|
+
key,
|
|
326
|
+
...branch.bindings.map(
|
|
327
|
+
(binding) => normalizedKeyedRowBindingValue(binding, binding.read(item, index))
|
|
328
|
+
)
|
|
329
|
+
];
|
|
330
|
+
}
|
|
331
|
+
function readKeyedRowConditionalValues(props, item, index) {
|
|
332
|
+
return new Map(
|
|
333
|
+
(props.conditionals || []).map((conditional) => [
|
|
334
|
+
conditional.id,
|
|
335
|
+
keyedRowConditionalSnapshot(conditional, item, index)
|
|
336
|
+
])
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
function keyedRowConditionalChanged(previous, next) {
|
|
340
|
+
return !previous || previous.length !== next.length || next.some((value, index) => !Object.is(value, previous[index]));
|
|
341
|
+
}
|
|
342
|
+
function hostConditionalSelection(props) {
|
|
343
|
+
const test = props.test();
|
|
344
|
+
if (props.logical && !test) {
|
|
345
|
+
if (typeof test === "number" || typeof test === "bigint") return { kind: "fallback" };
|
|
346
|
+
return { kind: "empty" };
|
|
347
|
+
}
|
|
348
|
+
const key = test ? "truthy" : "falsy";
|
|
349
|
+
const branch = key === "truthy" ? props.truthy : props.falsy;
|
|
350
|
+
return branch ? { kind: "branch", key, branch } : { kind: "empty" };
|
|
351
|
+
}
|
|
352
|
+
function normalizedHostConditionalBindingValue(binding, value) {
|
|
353
|
+
return binding.kind === "text" ? renderTextValue(value) : value;
|
|
354
|
+
}
|
|
355
|
+
function readHostConditionalBindingValues(branch) {
|
|
356
|
+
return branch.bindings.map(
|
|
357
|
+
(binding) => normalizedHostConditionalBindingValue(binding, binding.read())
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
function applyHostConditionalBindings(branch, instance) {
|
|
361
|
+
for (let bindingIndex = 0; bindingIndex < branch.bindings.length; bindingIndex += 1) {
|
|
362
|
+
const binding = branch.bindings[bindingIndex];
|
|
363
|
+
const rawValue = binding.read();
|
|
364
|
+
const value = normalizedHostConditionalBindingValue(binding, rawValue);
|
|
365
|
+
if (Object.is(instance.values[bindingIndex], value)) continue;
|
|
366
|
+
instance.values[bindingIndex] = value;
|
|
367
|
+
const target = findCompilerHostTarget(instance.element, binding.path);
|
|
368
|
+
if (!target) continue;
|
|
369
|
+
if (binding.kind === "text") {
|
|
370
|
+
target.textContent = value;
|
|
371
|
+
} else if (binding.kind === "style" && binding.name) {
|
|
372
|
+
updateStyle(target, binding.name, rawValue);
|
|
373
|
+
} else if (binding.kind === "attribute" && binding.name) {
|
|
374
|
+
updateAttribute(target, binding.name, rawValue);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
function createHostConditionalBlockComponent(owner) {
|
|
379
|
+
class FarmHostConditionalBlock extends React.Component {
|
|
380
|
+
constructor() {
|
|
381
|
+
super(...arguments);
|
|
382
|
+
this.state = { fallback: false };
|
|
383
|
+
this.root = null;
|
|
384
|
+
this.mounted = false;
|
|
385
|
+
this.fallbackRequested = false;
|
|
386
|
+
this.fallbackVersion = 0;
|
|
387
|
+
this.propSyncQueued = false;
|
|
388
|
+
this.currentProps = this.props;
|
|
389
|
+
this.activeBranch = null;
|
|
390
|
+
this.instance = null;
|
|
391
|
+
this.captureRoot = (root) => {
|
|
392
|
+
this.root = root;
|
|
393
|
+
};
|
|
394
|
+
this.refresh = (afterCommit) => {
|
|
395
|
+
this.reconcile(afterCommit);
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
adopt() {
|
|
399
|
+
if (!this.root) return false;
|
|
400
|
+
const selection = hostConditionalSelection(this.currentProps);
|
|
401
|
+
if (selection.kind === "fallback") return false;
|
|
402
|
+
if (selection.kind === "empty") {
|
|
403
|
+
if (this.root.childNodes.length !== 0) return false;
|
|
404
|
+
this.activeBranch = null;
|
|
405
|
+
this.instance = null;
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
408
|
+
if (this.root.childNodes.length !== 1 || this.root.firstElementChild === null || this.root.firstElementChild !== this.root.firstChild) {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
const descriptor = selection.branch.create();
|
|
412
|
+
const element = this.root.firstElementChild;
|
|
413
|
+
if (!matchesCompilerHostElement(element, descriptor)) return false;
|
|
414
|
+
this.activeBranch = selection.key;
|
|
415
|
+
this.instance = {
|
|
416
|
+
element,
|
|
417
|
+
values: readHostConditionalBindingValues(selection.branch)
|
|
418
|
+
};
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
421
|
+
activateFallback(afterCommit) {
|
|
422
|
+
if (!this.mounted || this.state.fallback || this.fallbackRequested) {
|
|
423
|
+
afterCommit?.();
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
this.fallbackRequested = true;
|
|
427
|
+
this.fallbackVersion += 1;
|
|
428
|
+
this.setState({ fallback: true }, afterCommit);
|
|
429
|
+
}
|
|
430
|
+
reconcile(afterCommit) {
|
|
431
|
+
if (!this.mounted || !this.root) {
|
|
432
|
+
afterCommit?.();
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (this.state.fallback) {
|
|
436
|
+
this.forceUpdate(afterCommit);
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
const selection = hostConditionalSelection(this.currentProps);
|
|
440
|
+
if (selection.kind === "fallback") {
|
|
441
|
+
this.activateFallback(afterCommit);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
if (selection.kind === "empty") {
|
|
445
|
+
if (this.instance) this.instance.element.remove();
|
|
446
|
+
this.activeBranch = null;
|
|
447
|
+
this.instance = null;
|
|
448
|
+
afterCommit?.();
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (this.activeBranch === selection.key && this.instance) {
|
|
452
|
+
applyHostConditionalBindings(selection.branch, this.instance);
|
|
453
|
+
afterCommit?.();
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
const element = createCompilerHostElement(this.root.ownerDocument, selection.branch.create());
|
|
457
|
+
this.root.replaceChildren(element);
|
|
458
|
+
this.activeBranch = selection.key;
|
|
459
|
+
this.instance = {
|
|
460
|
+
element,
|
|
461
|
+
values: readHostConditionalBindingValues(selection.branch)
|
|
462
|
+
};
|
|
463
|
+
afterCommit?.();
|
|
464
|
+
}
|
|
465
|
+
schedulePropSync() {
|
|
466
|
+
if (this.propSyncQueued) return;
|
|
467
|
+
this.propSyncQueued = true;
|
|
468
|
+
queueMicrotask(() => {
|
|
469
|
+
this.propSyncQueued = false;
|
|
470
|
+
if (this.mounted && !this.state.fallback) this.reconcile();
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
shouldComponentUpdate(nextProps, nextState) {
|
|
474
|
+
this.currentProps = nextProps;
|
|
475
|
+
if (nextState.fallback || this.state.fallback) return true;
|
|
476
|
+
this.schedulePropSync();
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
componentDidMount() {
|
|
480
|
+
this.mounted = true;
|
|
481
|
+
this.unsubscribe = owner.subscribe(this.props.id, this.refresh);
|
|
482
|
+
if (!this.adopt()) this.activateFallback();
|
|
483
|
+
}
|
|
484
|
+
componentWillUnmount() {
|
|
485
|
+
this.mounted = false;
|
|
486
|
+
this.unsubscribe?.();
|
|
487
|
+
this.root = null;
|
|
488
|
+
this.activeBranch = null;
|
|
489
|
+
this.instance = null;
|
|
490
|
+
}
|
|
491
|
+
render() {
|
|
492
|
+
const container = this.currentProps.render();
|
|
493
|
+
if (!React.isValidElement(container) || typeof container.type !== "string") {
|
|
494
|
+
throw new TypeError(
|
|
495
|
+
`Compiled host conditional ${this.currentProps.id} must own one host container.`
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
return React.cloneElement(container, {
|
|
499
|
+
key: this.state.fallback ? `react-${this.fallbackVersion}` : "compiled",
|
|
500
|
+
ref: this.captureRoot
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
FarmHostConditionalBlock.displayName = "FarmCompiledHostConditional";
|
|
505
|
+
return FarmHostConditionalBlock;
|
|
506
|
+
}
|
|
507
|
+
function longestIncreasingSubsequencePositions(sequence) {
|
|
508
|
+
const tails = [];
|
|
509
|
+
const previous = Array.from({ length: sequence.length }, () => -1);
|
|
510
|
+
for (let index = 0; index < sequence.length; index += 1) {
|
|
511
|
+
if (sequence[index] < 0) continue;
|
|
512
|
+
let low = 0;
|
|
513
|
+
let high = tails.length;
|
|
514
|
+
while (low < high) {
|
|
515
|
+
const middle = low + high >> 1;
|
|
516
|
+
if (sequence[tails[middle]] < sequence[index]) low = middle + 1;
|
|
517
|
+
else high = middle;
|
|
518
|
+
}
|
|
519
|
+
if (low > 0) previous[index] = tails[low - 1];
|
|
520
|
+
tails[low] = index;
|
|
521
|
+
}
|
|
522
|
+
const positions = /* @__PURE__ */ new Set();
|
|
523
|
+
let cursor = tails.at(-1) ?? -1;
|
|
524
|
+
while (cursor >= 0) {
|
|
525
|
+
positions.add(cursor);
|
|
526
|
+
cursor = previous[cursor];
|
|
527
|
+
}
|
|
528
|
+
return positions;
|
|
529
|
+
}
|
|
530
|
+
function createKeyedRowsBlockComponent(owner) {
|
|
531
|
+
class FarmKeyedRowConditional extends React.Component {
|
|
532
|
+
constructor() {
|
|
533
|
+
super(...arguments);
|
|
534
|
+
this.state = {
|
|
535
|
+
renderVersion: this.props.renderVersion,
|
|
536
|
+
item: this.props.item,
|
|
537
|
+
index: this.props.index
|
|
538
|
+
};
|
|
539
|
+
this.refresh = (item, index, afterCommit) => {
|
|
540
|
+
this.setState({ item, index }, afterCommit);
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
static getDerivedStateFromProps(props, state) {
|
|
544
|
+
if (props.renderVersion === state.renderVersion) return null;
|
|
545
|
+
return {
|
|
546
|
+
renderVersion: props.renderVersion,
|
|
547
|
+
item: props.item,
|
|
548
|
+
index: props.index
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
subscribe() {
|
|
552
|
+
this.unsubscribe = this.props.owner.subscribe(this.props.rowKey, this.props.id, this.refresh);
|
|
553
|
+
}
|
|
554
|
+
componentDidMount() {
|
|
555
|
+
this.subscribe();
|
|
556
|
+
}
|
|
557
|
+
componentDidUpdate(previous) {
|
|
558
|
+
if (previous.owner === this.props.owner && previous.rowKey === this.props.rowKey && previous.id === this.props.id) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
this.unsubscribe?.();
|
|
562
|
+
this.subscribe();
|
|
563
|
+
}
|
|
564
|
+
componentWillUnmount() {
|
|
565
|
+
this.unsubscribe?.();
|
|
566
|
+
}
|
|
567
|
+
render() {
|
|
568
|
+
return this.props.render(this.state.item, this.state.index);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
FarmKeyedRowConditional.displayName = "FarmCompiledKeyedRowConditional";
|
|
572
|
+
class FarmKeyedRowsBlock extends React.Component {
|
|
573
|
+
constructor() {
|
|
574
|
+
super(...arguments);
|
|
575
|
+
this.state = { fallback: false };
|
|
576
|
+
this.root = null;
|
|
577
|
+
this.mounted = false;
|
|
578
|
+
this.fallbackRequested = false;
|
|
579
|
+
this.fallbackVersion = 0;
|
|
580
|
+
this.fallbackKeysWereUnsafe = false;
|
|
581
|
+
this.propSyncQueued = false;
|
|
582
|
+
this.currentProps = this.props;
|
|
583
|
+
this.instances = /* @__PURE__ */ new Map();
|
|
584
|
+
this.renderVersion = 0;
|
|
585
|
+
this.eventHandlers = /* @__PURE__ */ new Map();
|
|
586
|
+
this.conditionalListeners = /* @__PURE__ */ new Map();
|
|
587
|
+
this.conditionalOwner = {
|
|
588
|
+
subscribe: (key, id, refresh) => this.subscribeToRowConditional(key, id, refresh)
|
|
589
|
+
};
|
|
590
|
+
this.captureRoot = (root) => {
|
|
591
|
+
this.root = root;
|
|
592
|
+
};
|
|
593
|
+
this.rowEvent = (item, index, eventId) => {
|
|
594
|
+
if (this.state.fallback) {
|
|
595
|
+
return (event) => this.currentProps.events?.[eventId]?.invoke(item, index, event);
|
|
596
|
+
}
|
|
597
|
+
const key = keyedRowIdentity(this.currentProps.rowKey(item, index));
|
|
598
|
+
let handlers = this.eventHandlers.get(key);
|
|
599
|
+
if (!handlers) {
|
|
600
|
+
handlers = /* @__PURE__ */ new Map();
|
|
601
|
+
this.eventHandlers.set(key, handlers);
|
|
602
|
+
}
|
|
603
|
+
const existing = handlers.get(eventId);
|
|
604
|
+
if (existing) return existing;
|
|
605
|
+
const handler = (event) => {
|
|
606
|
+
const instance = this.instances.get(key);
|
|
607
|
+
const descriptor = this.currentProps.events?.[eventId];
|
|
608
|
+
if (!instance || !descriptor) return;
|
|
609
|
+
return descriptor.invoke(instance.item, instance.index, event);
|
|
610
|
+
};
|
|
611
|
+
handlers.set(eventId, handler);
|
|
612
|
+
return handler;
|
|
613
|
+
};
|
|
614
|
+
this.rowConditional = (item, index, conditionalId, render) => {
|
|
615
|
+
if (this.state.fallback) return render(item, index);
|
|
616
|
+
const key = keyedRowIdentity(this.currentProps.rowKey(item, index));
|
|
617
|
+
return React.createElement(FarmKeyedRowConditional, {
|
|
618
|
+
key: `${key}:${conditionalId}`,
|
|
619
|
+
owner: this.conditionalOwner,
|
|
620
|
+
rowKey: key,
|
|
621
|
+
id: conditionalId,
|
|
622
|
+
renderVersion: this.renderVersion,
|
|
623
|
+
item,
|
|
624
|
+
index,
|
|
625
|
+
render
|
|
626
|
+
});
|
|
627
|
+
};
|
|
628
|
+
this.refresh = (afterCommit) => {
|
|
629
|
+
this.reconcile(afterCommit);
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
readRows(props) {
|
|
633
|
+
const source = props.items();
|
|
634
|
+
const items = source ? Array.from(source) : [];
|
|
635
|
+
const keys = items.map((item, index) => keyedRowIdentity(props.rowKey(item, index)));
|
|
636
|
+
if (new Set(keys).size !== keys.length) return null;
|
|
637
|
+
return { items, keys };
|
|
638
|
+
}
|
|
639
|
+
subscribeToRowConditional(key, id, refresh) {
|
|
640
|
+
let listeners = this.conditionalListeners.get(key);
|
|
641
|
+
if (!listeners) {
|
|
642
|
+
listeners = /* @__PURE__ */ new Map();
|
|
643
|
+
this.conditionalListeners.set(key, listeners);
|
|
644
|
+
}
|
|
645
|
+
listeners.set(id, refresh);
|
|
646
|
+
return () => {
|
|
647
|
+
const current = this.conditionalListeners.get(key);
|
|
648
|
+
if (current?.get(id) !== refresh) return;
|
|
649
|
+
current.delete(id);
|
|
650
|
+
if (current.size === 0) this.conditionalListeners.delete(key);
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
hasReactOwnedRows(props = this.currentProps) {
|
|
654
|
+
return Boolean(props.events?.length || props.conditionals?.length);
|
|
655
|
+
}
|
|
656
|
+
pruneEventHandlers(keys) {
|
|
657
|
+
const active = new Set(keys);
|
|
658
|
+
for (const key of this.eventHandlers.keys()) {
|
|
659
|
+
if (!active.has(key)) this.eventHandlers.delete(key);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
pruneConditionalListeners(keys) {
|
|
663
|
+
const active = new Set(keys);
|
|
664
|
+
for (const key of this.conditionalListeners.keys()) {
|
|
665
|
+
if (!active.has(key)) this.conditionalListeners.delete(key);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
adopt() {
|
|
669
|
+
if (!this.root) return false;
|
|
670
|
+
const rows = this.readRows(this.currentProps);
|
|
671
|
+
const elements = [...this.root.children];
|
|
672
|
+
if (!rows || rows.items.length !== elements.length) return false;
|
|
673
|
+
const opaqueConditionalPaths = new Set(
|
|
674
|
+
(this.currentProps.conditionals || []).map((conditional) => conditional.path.join("."))
|
|
675
|
+
);
|
|
676
|
+
const instances = /* @__PURE__ */ new Map();
|
|
677
|
+
for (let index = 0; index < rows.items.length; index += 1) {
|
|
678
|
+
if (this.hasReactOwnedRows() && !matchesCompilerHostElement(
|
|
679
|
+
elements[index],
|
|
680
|
+
this.currentProps.create(rows.items[index], index),
|
|
681
|
+
opaqueConditionalPaths
|
|
682
|
+
)) {
|
|
683
|
+
return false;
|
|
684
|
+
}
|
|
685
|
+
const instance = {
|
|
686
|
+
key: rows.keys[index],
|
|
687
|
+
element: elements[index],
|
|
688
|
+
values: this.hasReactOwnedRows() ? this.currentProps.bindings.map(() => UNSET_KEYED_ROW_BINDING) : readKeyedRowBindingValues(this.currentProps, rows.items[index], index),
|
|
689
|
+
item: rows.items[index],
|
|
690
|
+
index,
|
|
691
|
+
conditionalValues: readKeyedRowConditionalValues(
|
|
692
|
+
this.currentProps,
|
|
693
|
+
rows.items[index],
|
|
694
|
+
index
|
|
695
|
+
)
|
|
696
|
+
};
|
|
697
|
+
if (this.hasReactOwnedRows()) {
|
|
698
|
+
applyKeyedRowBindings(this.currentProps, instance, rows.items[index], index);
|
|
699
|
+
}
|
|
700
|
+
instances.set(rows.keys[index], instance);
|
|
701
|
+
}
|
|
702
|
+
this.instances = instances;
|
|
703
|
+
this.pruneEventHandlers(rows.keys);
|
|
704
|
+
this.pruneConditionalListeners(rows.keys);
|
|
705
|
+
return true;
|
|
706
|
+
}
|
|
707
|
+
hasUnsafeFallbackKeys() {
|
|
708
|
+
try {
|
|
709
|
+
return this.readRows(this.currentProps) === null;
|
|
710
|
+
} catch {
|
|
711
|
+
return true;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
activateFallback(afterCommit) {
|
|
715
|
+
if (!this.mounted || this.state.fallback || this.fallbackRequested) {
|
|
716
|
+
afterCommit?.();
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
this.fallbackRequested = true;
|
|
720
|
+
this.fallbackKeysWereUnsafe = this.hasUnsafeFallbackKeys();
|
|
721
|
+
this.fallbackVersion += 1;
|
|
722
|
+
this.setState({ fallback: true }, afterCommit);
|
|
723
|
+
}
|
|
724
|
+
synchronizeReactOwnedRows(afterCommit) {
|
|
725
|
+
this.forceUpdate(() => {
|
|
726
|
+
if (!this.mounted) {
|
|
727
|
+
afterCommit?.();
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
if (!this.adopt()) {
|
|
731
|
+
this.activateFallback(afterCommit);
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
afterCommit?.();
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
notifyConditionalChanges(changes, afterCommit) {
|
|
738
|
+
if (changes.length === 0) {
|
|
739
|
+
afterCommit?.();
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
let pending = changes.length;
|
|
743
|
+
const committed = () => {
|
|
744
|
+
pending -= 1;
|
|
745
|
+
if (pending === 0) afterCommit?.();
|
|
746
|
+
};
|
|
747
|
+
flushSync(() => {
|
|
748
|
+
for (const change of changes) {
|
|
749
|
+
const refresh = this.conditionalListeners.get(change.key)?.get(change.id);
|
|
750
|
+
if (refresh) refresh(change.item, change.index, committed);
|
|
751
|
+
else committed();
|
|
752
|
+
}
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
reconcile(afterCommit) {
|
|
756
|
+
if (!this.mounted || !this.root) {
|
|
757
|
+
afterCommit?.();
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
if (this.state.fallback) {
|
|
761
|
+
const unsafeKeys = this.hasUnsafeFallbackKeys();
|
|
762
|
+
if (unsafeKeys || this.fallbackKeysWereUnsafe) {
|
|
763
|
+
this.fallbackVersion += 1;
|
|
764
|
+
}
|
|
765
|
+
this.fallbackKeysWereUnsafe = unsafeKeys;
|
|
766
|
+
this.forceUpdate(afterCommit);
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
const rows = this.readRows(this.currentProps);
|
|
770
|
+
if (!rows) {
|
|
771
|
+
this.activateFallback(afterCommit);
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
const previousKeys = [...this.instances.keys()];
|
|
775
|
+
if (this.hasReactOwnedRows() && (rows.keys.length !== previousKeys.length || rows.keys.some((key, index) => key !== previousKeys[index]))) {
|
|
776
|
+
this.synchronizeReactOwnedRows(afterCommit);
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
const oldIndices = /* @__PURE__ */ new Map();
|
|
780
|
+
const activeElement = this.root.ownerDocument.activeElement;
|
|
781
|
+
const restoreFocus = Boolean(activeElement && this.root.contains(activeElement));
|
|
782
|
+
[...this.instances].forEach(([key], index) => oldIndices.set(key, index));
|
|
783
|
+
const nextKeys = new Set(rows.keys);
|
|
784
|
+
for (const [key, instance] of this.instances) {
|
|
785
|
+
if (!nextKeys.has(key)) instance.element.remove();
|
|
786
|
+
}
|
|
787
|
+
const sequence = rows.keys.map((key) => oldIndices.get(key) ?? -1);
|
|
788
|
+
const stablePositions = longestIncreasingSubsequencePositions(sequence);
|
|
789
|
+
const nextInstances = [];
|
|
790
|
+
const conditionalChanges = [];
|
|
791
|
+
for (let index = 0; index < rows.items.length; index += 1) {
|
|
792
|
+
const key = rows.keys[index];
|
|
793
|
+
const existing = this.instances.get(key);
|
|
794
|
+
if (existing) {
|
|
795
|
+
applyKeyedRowBindings(this.currentProps, existing, rows.items[index], index);
|
|
796
|
+
const conditionalValues = readKeyedRowConditionalValues(
|
|
797
|
+
this.currentProps,
|
|
798
|
+
rows.items[index],
|
|
799
|
+
index
|
|
800
|
+
);
|
|
801
|
+
for (const [id, values] of conditionalValues) {
|
|
802
|
+
if (keyedRowConditionalChanged(existing.conditionalValues.get(id), values)) {
|
|
803
|
+
conditionalChanges.push({ key, id, item: rows.items[index], index });
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
existing.conditionalValues = conditionalValues;
|
|
807
|
+
existing.item = rows.items[index];
|
|
808
|
+
existing.index = index;
|
|
809
|
+
nextInstances.push(existing);
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
const element = createCompilerHostElement(
|
|
813
|
+
this.root.ownerDocument,
|
|
814
|
+
this.currentProps.create(rows.items[index], index)
|
|
815
|
+
);
|
|
816
|
+
nextInstances.push({
|
|
817
|
+
key,
|
|
818
|
+
element,
|
|
819
|
+
values: readKeyedRowBindingValues(this.currentProps, rows.items[index], index),
|
|
820
|
+
item: rows.items[index],
|
|
821
|
+
index,
|
|
822
|
+
conditionalValues: readKeyedRowConditionalValues(
|
|
823
|
+
this.currentProps,
|
|
824
|
+
rows.items[index],
|
|
825
|
+
index
|
|
826
|
+
)
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
let anchor = null;
|
|
830
|
+
for (let index = nextInstances.length - 1; index >= 0; index -= 1) {
|
|
831
|
+
const instance = nextInstances[index];
|
|
832
|
+
if (sequence[index] < 0) {
|
|
833
|
+
this.root.insertBefore(instance.element, anchor);
|
|
834
|
+
} else if (!stablePositions.has(index) && instance.element.nextSibling !== anchor) {
|
|
835
|
+
this.root.insertBefore(instance.element, anchor);
|
|
836
|
+
}
|
|
837
|
+
anchor = instance.element;
|
|
838
|
+
}
|
|
839
|
+
this.instances = new Map(nextInstances.map((instance) => [instance.key, instance]));
|
|
840
|
+
this.pruneEventHandlers(rows.keys);
|
|
841
|
+
this.pruneConditionalListeners(rows.keys);
|
|
842
|
+
if (restoreFocus && activeElement?.isConnected && activeElement.ownerDocument.activeElement !== activeElement && "focus" in activeElement) {
|
|
843
|
+
activeElement.focus({ preventScroll: true });
|
|
844
|
+
}
|
|
845
|
+
this.notifyConditionalChanges(conditionalChanges, afterCommit);
|
|
846
|
+
}
|
|
847
|
+
schedulePropSync() {
|
|
848
|
+
if (this.propSyncQueued) return;
|
|
849
|
+
this.propSyncQueued = true;
|
|
850
|
+
queueMicrotask(() => {
|
|
851
|
+
this.propSyncQueued = false;
|
|
852
|
+
if (this.mounted && !this.state.fallback) this.reconcile();
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
shouldComponentUpdate(nextProps, nextState) {
|
|
856
|
+
const wasReactOwned = this.hasReactOwnedRows(this.currentProps);
|
|
857
|
+
const willBeReactOwned = this.hasReactOwnedRows(nextProps);
|
|
858
|
+
this.currentProps = nextProps;
|
|
859
|
+
if (nextState.fallback || this.state.fallback) return true;
|
|
860
|
+
if (wasReactOwned || willBeReactOwned) {
|
|
861
|
+
this.eventHandlers.clear();
|
|
862
|
+
return true;
|
|
863
|
+
}
|
|
864
|
+
this.schedulePropSync();
|
|
865
|
+
return false;
|
|
866
|
+
}
|
|
867
|
+
componentDidUpdate(previousProps) {
|
|
868
|
+
if (this.state.fallback || previousProps === this.props || !this.hasReactOwnedRows(previousProps) && !this.hasReactOwnedRows(this.props) || this.adopt()) {
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
this.activateFallback();
|
|
872
|
+
}
|
|
873
|
+
componentDidMount() {
|
|
874
|
+
this.mounted = true;
|
|
875
|
+
this.unsubscribe = owner.subscribe(this.props.id, this.refresh);
|
|
876
|
+
if (!this.adopt()) this.activateFallback();
|
|
877
|
+
}
|
|
878
|
+
componentWillUnmount() {
|
|
879
|
+
this.mounted = false;
|
|
880
|
+
this.unsubscribe?.();
|
|
881
|
+
this.root = null;
|
|
882
|
+
this.instances.clear();
|
|
883
|
+
this.eventHandlers.clear();
|
|
884
|
+
this.conditionalListeners.clear();
|
|
885
|
+
}
|
|
886
|
+
render() {
|
|
887
|
+
this.renderVersion += 1;
|
|
888
|
+
const container = this.currentProps.render(this.rowEvent, this.rowConditional);
|
|
889
|
+
if (!React.isValidElement(container) || typeof container.type !== "string") {
|
|
890
|
+
throw new TypeError(
|
|
891
|
+
`Compiled keyed rows ${this.currentProps.id} must own one host container.`
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
return React.cloneElement(container, {
|
|
895
|
+
key: this.state.fallback ? `react-${this.fallbackVersion}` : "compiled",
|
|
896
|
+
ref: this.captureRoot
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
FarmKeyedRowsBlock.displayName = "FarmCompiledKeyedRows";
|
|
901
|
+
return FarmKeyedRowsBlock;
|
|
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
|
+
}
|
|
1116
|
+
function createComponentBlockComponent(owner) {
|
|
1117
|
+
class FarmComponentBlock extends React.Component {
|
|
1118
|
+
constructor() {
|
|
1119
|
+
super(...arguments);
|
|
1120
|
+
this.refresh = (afterCommit) => {
|
|
1121
|
+
this.forceUpdate(afterCommit);
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
subscribe() {
|
|
1125
|
+
this.unsubscribe = owner.subscribe(this.props.id, this.refresh);
|
|
1126
|
+
}
|
|
1127
|
+
componentDidMount() {
|
|
1128
|
+
this.subscribe();
|
|
1129
|
+
}
|
|
1130
|
+
componentDidUpdate(previous) {
|
|
1131
|
+
if (previous.id === this.props.id) return;
|
|
1132
|
+
this.unsubscribe?.();
|
|
1133
|
+
this.subscribe();
|
|
1134
|
+
}
|
|
1135
|
+
componentWillUnmount() {
|
|
1136
|
+
this.unsubscribe?.();
|
|
1137
|
+
}
|
|
1138
|
+
render() {
|
|
1139
|
+
return this.props.render();
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
FarmComponentBlock.displayName = "FarmCompiledComponentIsland";
|
|
1143
|
+
return FarmComponentBlock;
|
|
1144
|
+
}
|
|
43
1145
|
function createCompiledComponent(definition) {
|
|
1146
|
+
const stateSignature = definition.stateSignature || String(definition.initialize.length);
|
|
1147
|
+
if (definition.hmrId) {
|
|
1148
|
+
const registry = getCompilerHmrRegistry();
|
|
1149
|
+
const existing = registry.get(definition.hmrId);
|
|
1150
|
+
if (existing?.stateSignature === stateSignature) {
|
|
1151
|
+
existing.definition.current = definition;
|
|
1152
|
+
existing.component.displayName = `FarmCompiled(${definition.displayName})`;
|
|
1153
|
+
for (const refresh of existing.refreshListeners) refresh();
|
|
1154
|
+
return existing.component;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
const definitionReference = { current: definition };
|
|
1158
|
+
const refreshListeners = /* @__PURE__ */ new Set();
|
|
44
1159
|
class FarmCompiledComponent extends React.Component {
|
|
45
1160
|
constructor(props) {
|
|
46
1161
|
super(props);
|
|
47
1162
|
this.root = null;
|
|
48
1163
|
this.mounted = false;
|
|
49
1164
|
this.flushQueued = false;
|
|
1165
|
+
this.hasBindingError = false;
|
|
1166
|
+
this.inputSelection = null;
|
|
50
1167
|
this.dirtyState = /* @__PURE__ */ new Set();
|
|
1168
|
+
this.blockRefreshListeners = /* @__PURE__ */ new Map();
|
|
1169
|
+
this.blockRoots = /* @__PURE__ */ new Map();
|
|
1170
|
+
this.blockRootElements = /* @__PURE__ */ new Set();
|
|
1171
|
+
this.bindingTargets = /* @__PURE__ */ new Map();
|
|
1172
|
+
this.bindingTargetRefs = /* @__PURE__ */ new Map();
|
|
51
1173
|
this.captureRoot = (root) => {
|
|
52
1174
|
this.root = root;
|
|
53
1175
|
};
|
|
54
|
-
this.
|
|
1176
|
+
this.refreshDefinition = () => {
|
|
1177
|
+
if (this.mounted) this.forceUpdate();
|
|
1178
|
+
};
|
|
1179
|
+
this.cells = definitionReference.current.initialize(props).map((initialValue, index) => {
|
|
55
1180
|
let value = initialValue;
|
|
56
1181
|
const pending = [];
|
|
57
1182
|
return {
|
|
@@ -70,13 +1195,87 @@ function createCompiledComponent(definition) {
|
|
|
70
1195
|
}
|
|
71
1196
|
};
|
|
72
1197
|
});
|
|
1198
|
+
this.blockRuntime = {
|
|
1199
|
+
Conditional: createConditionalBlockComponent({
|
|
1200
|
+
setRoot: (id, root) => this.setBlockRoot(id, root),
|
|
1201
|
+
subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
|
|
1202
|
+
}),
|
|
1203
|
+
HostConditional: createHostConditionalBlockComponent({
|
|
1204
|
+
subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
|
|
1205
|
+
}),
|
|
1206
|
+
KeyedList: createKeyedListBlockComponent({
|
|
1207
|
+
subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
|
|
1208
|
+
}),
|
|
1209
|
+
KeyedRows: createKeyedRowsBlockComponent({
|
|
1210
|
+
subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
|
|
1211
|
+
}),
|
|
1212
|
+
KeyedRanges: createKeyedRangesBlockComponent({
|
|
1213
|
+
subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
|
|
1214
|
+
}),
|
|
1215
|
+
Component: createComponentBlockComponent({
|
|
1216
|
+
subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
|
|
1217
|
+
}),
|
|
1218
|
+
target: (id) => this.bindingTarget(id)
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
bindingTarget(id) {
|
|
1222
|
+
const existing = this.bindingTargetRefs.get(id);
|
|
1223
|
+
if (existing) return existing;
|
|
1224
|
+
const capture = (element) => {
|
|
1225
|
+
if (element) {
|
|
1226
|
+
this.bindingTargets.set(id, element);
|
|
1227
|
+
} else {
|
|
1228
|
+
this.bindingTargets.delete(id);
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
1231
|
+
this.bindingTargetRefs.set(id, capture);
|
|
1232
|
+
return capture;
|
|
1233
|
+
}
|
|
1234
|
+
captureInputSelection() {
|
|
1235
|
+
const active = this.root?.ownerDocument.activeElement;
|
|
1236
|
+
if (!isSelectableTextControl(active) || !this.root?.contains(active) || active.selectionStart === null || active.selectionEnd === null) {
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
this.inputSelection = {
|
|
1240
|
+
element: active,
|
|
1241
|
+
start: active.selectionStart,
|
|
1242
|
+
end: active.selectionEnd,
|
|
1243
|
+
direction: active.selectionDirection || "none"
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
restoreInputSelection(snapshot) {
|
|
1247
|
+
if (!snapshot || !snapshot.element.isConnected || snapshot.element.ownerDocument.activeElement !== snapshot.element) {
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1250
|
+
snapshot.element.setSelectionRange(snapshot.start, snapshot.end, snapshot.direction);
|
|
1251
|
+
}
|
|
1252
|
+
setBlockRoot(id, root) {
|
|
1253
|
+
const previous = this.blockRoots.get(id);
|
|
1254
|
+
if (previous) this.blockRootElements.delete(previous);
|
|
1255
|
+
if (root) {
|
|
1256
|
+
this.blockRoots.set(id, root);
|
|
1257
|
+
this.blockRootElements.add(root);
|
|
1258
|
+
} else {
|
|
1259
|
+
this.blockRoots.delete(id);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
subscribeToBlock(id, refresh) {
|
|
1263
|
+
this.blockRefreshListeners.set(id, refresh);
|
|
1264
|
+
return () => {
|
|
1265
|
+
if (this.blockRefreshListeners.get(id) === refresh) {
|
|
1266
|
+
this.blockRefreshListeners.delete(id);
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
73
1269
|
}
|
|
74
1270
|
scheduleBindingFlush(index) {
|
|
75
1271
|
this.dirtyState.add(index);
|
|
1272
|
+
this.captureInputSelection();
|
|
76
1273
|
if (!this.mounted || this.flushQueued) return;
|
|
77
1274
|
this.flushQueued = true;
|
|
78
1275
|
queueMicrotask(() => {
|
|
79
1276
|
this.flushQueued = false;
|
|
1277
|
+
const inputSelection = this.inputSelection;
|
|
1278
|
+
this.inputSelection = null;
|
|
80
1279
|
if (!this.mounted) return;
|
|
81
1280
|
const dirty = /* @__PURE__ */ new Set();
|
|
82
1281
|
for (const index2 of this.dirtyState) {
|
|
@@ -84,40 +1283,105 @@ function createCompiledComponent(definition) {
|
|
|
84
1283
|
}
|
|
85
1284
|
this.dirtyState.clear();
|
|
86
1285
|
if (dirty.size === 0) return;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
1286
|
+
try {
|
|
1287
|
+
const bindings = definitionReference.current.bindings;
|
|
1288
|
+
const blockBindings = /* @__PURE__ */ new Map();
|
|
1289
|
+
const affectedBlockIds = /* @__PURE__ */ new Set();
|
|
1290
|
+
for (const binding of bindings) {
|
|
1291
|
+
if (binding.kind !== "block") continue;
|
|
1292
|
+
blockBindings.set(binding.id, binding);
|
|
1293
|
+
if (binding.dependencies.some((dependency) => dirty.has(dependency))) {
|
|
1294
|
+
affectedBlockIds.add(binding.id);
|
|
1295
|
+
}
|
|
90
1296
|
}
|
|
1297
|
+
const hasAffectedMountedAncestor = (binding) => {
|
|
1298
|
+
let parent = binding.parent;
|
|
1299
|
+
while (parent !== void 0) {
|
|
1300
|
+
if (affectedBlockIds.has(parent) && this.blockRefreshListeners.has(parent)) {
|
|
1301
|
+
return true;
|
|
1302
|
+
}
|
|
1303
|
+
parent = blockBindings.get(parent)?.parent;
|
|
1304
|
+
}
|
|
1305
|
+
return false;
|
|
1306
|
+
};
|
|
1307
|
+
const blockRefreshes = [...affectedBlockIds].map((id) => {
|
|
1308
|
+
const binding = blockBindings.get(id);
|
|
1309
|
+
const refresh = this.blockRefreshListeners.get(id);
|
|
1310
|
+
return binding && refresh && !hasAffectedMountedAncestor(binding) ? refresh : void 0;
|
|
1311
|
+
}).filter(
|
|
1312
|
+
(refresh) => refresh !== void 0
|
|
1313
|
+
);
|
|
1314
|
+
for (const binding of bindings) {
|
|
1315
|
+
if (binding.kind !== "block" && binding.dependencies.some((dependency) => dirty.has(dependency))) {
|
|
1316
|
+
this.applyBinding(binding);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
let pendingBlockCommits = blockRefreshes.length;
|
|
1320
|
+
for (const refresh of blockRefreshes) {
|
|
1321
|
+
refresh(() => {
|
|
1322
|
+
pendingBlockCommits -= 1;
|
|
1323
|
+
if (pendingBlockCommits === 0) this.restoreInputSelection(inputSelection);
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
if (blockRefreshes.length === 0) this.restoreInputSelection(inputSelection);
|
|
1327
|
+
} catch (error) {
|
|
1328
|
+
this.bindingError = error;
|
|
1329
|
+
this.hasBindingError = true;
|
|
1330
|
+
this.forceUpdate();
|
|
91
1331
|
}
|
|
92
1332
|
});
|
|
93
1333
|
}
|
|
94
1334
|
applyBinding(binding) {
|
|
95
|
-
if (!this.root) return;
|
|
96
|
-
const target = findBindingTarget(this.root, binding.path);
|
|
1335
|
+
if (!this.root || binding.kind === "block") return;
|
|
1336
|
+
const target = binding.target === void 0 ? findBindingTarget(this.root, binding.path, this.blockRootElements) : binding.path.length === 0 ? this.root : this.bindingTargets.get(binding.target) || null;
|
|
97
1337
|
if (!target) return;
|
|
98
1338
|
const value = binding.read(this.props, this.cells);
|
|
99
1339
|
if (binding.kind === "text") {
|
|
100
1340
|
target.textContent = renderTextValue(value);
|
|
1341
|
+
} else if (binding.kind === "style") {
|
|
1342
|
+
updateStyle(target, binding.name, value);
|
|
101
1343
|
} else {
|
|
102
1344
|
updateAttribute(target, binding.name, value);
|
|
103
1345
|
}
|
|
104
1346
|
}
|
|
105
1347
|
componentDidMount() {
|
|
106
1348
|
this.mounted = true;
|
|
1349
|
+
refreshListeners.add(this.refreshDefinition);
|
|
107
1350
|
}
|
|
108
1351
|
componentDidUpdate() {
|
|
109
|
-
for (const binding of
|
|
1352
|
+
for (const binding of definitionReference.current.bindings) {
|
|
1353
|
+
if (binding.kind !== "block") this.applyBinding(binding);
|
|
1354
|
+
}
|
|
110
1355
|
}
|
|
111
1356
|
componentWillUnmount() {
|
|
112
1357
|
this.mounted = false;
|
|
113
1358
|
this.root = null;
|
|
114
1359
|
this.dirtyState.clear();
|
|
1360
|
+
this.inputSelection = null;
|
|
1361
|
+
this.blockRefreshListeners.clear();
|
|
1362
|
+
this.blockRoots.clear();
|
|
1363
|
+
this.blockRootElements.clear();
|
|
1364
|
+
this.bindingTargets.clear();
|
|
1365
|
+
this.bindingTargetRefs.clear();
|
|
1366
|
+
refreshListeners.delete(this.refreshDefinition);
|
|
115
1367
|
}
|
|
116
1368
|
render() {
|
|
117
|
-
|
|
118
|
-
|
|
1369
|
+
if (this.hasBindingError) throw this.bindingError;
|
|
1370
|
+
const currentDefinition = definitionReference.current;
|
|
1371
|
+
const element = currentDefinition.render(this.props, this.cells, this.blockRuntime);
|
|
1372
|
+
if (!React.isValidElement(element)) {
|
|
119
1373
|
throw new TypeError(
|
|
120
|
-
`Compiled component ${
|
|
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") {
|
|
1383
|
+
throw new TypeError(
|
|
1384
|
+
`Compiled component ${currentDefinition.displayName} must return one host element.`
|
|
121
1385
|
);
|
|
122
1386
|
}
|
|
123
1387
|
return React.cloneElement(element, {
|
|
@@ -126,7 +1390,16 @@ function createCompiledComponent(definition) {
|
|
|
126
1390
|
}
|
|
127
1391
|
}
|
|
128
1392
|
FarmCompiledComponent.displayName = `FarmCompiled(${definition.displayName})`;
|
|
129
|
-
|
|
1393
|
+
const component = FarmCompiledComponent;
|
|
1394
|
+
if (definition.hmrId) {
|
|
1395
|
+
getCompilerHmrRegistry().set(definition.hmrId, {
|
|
1396
|
+
component,
|
|
1397
|
+
definition: definitionReference,
|
|
1398
|
+
refreshListeners,
|
|
1399
|
+
stateSignature
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
return component;
|
|
130
1403
|
}
|
|
131
1404
|
|
|
132
1405
|
export { createCompiledComponent };
|