@reckona/mreact-compat 0.0.164 → 0.0.165
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/dist/dom-children.d.ts +1 -0
- package/dist/dom-children.d.ts.map +1 -1
- package/dist/dom-children.js +10 -1
- package/dist/dom-children.js.map +1 -1
- package/dist/dom-host-rules.d.ts +15 -1
- package/dist/dom-host-rules.d.ts.map +1 -1
- package/dist/dom-host-rules.js +17 -1
- package/dist/dom-host-rules.js.map +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +20 -2
- package/dist/events.js.map +1 -1
- package/dist/fiber-commit.d.ts.map +1 -1
- package/dist/fiber-commit.js +51 -31
- package/dist/fiber-commit.js.map +1 -1
- package/dist/hooks.d.ts +2 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +57 -15
- package/dist/hooks.js.map +1 -1
- package/dist/host-reconciler.d.ts +2 -2
- package/dist/host-reconciler.d.ts.map +1 -1
- package/dist/host-reconciler.js +193 -29
- package/dist/host-reconciler.js.map +1 -1
- package/dist/root.js +3 -2
- package/dist/root.js.map +1 -1
- package/package.json +3 -3
- package/src/dom-children.ts +16 -1
- package/src/dom-host-rules.ts +42 -3
- package/src/events.ts +27 -2
- package/src/fiber-commit.ts +61 -37
- package/src/hooks.ts +61 -16
- package/src/host-reconciler.ts +264 -35
- package/src/root.ts +3 -2
package/src/host-reconciler.ts
CHANGED
|
@@ -27,13 +27,15 @@ import {
|
|
|
27
27
|
import { applyPostChildFormProps, applyProps } from "./dom-props.js";
|
|
28
28
|
import { syncChildNodes, syncOwnedChildNodes, syncScopedChildNodes } from "./dom-children.js";
|
|
29
29
|
import { setLogicalEventParent } from "./host-event-binder.js";
|
|
30
|
-
import { NoFlags, Placement, Update } from "./fiber-flags.js";
|
|
30
|
+
import { ChildDeletion, NoFlags, Placement, Update } from "./fiber-flags.js";
|
|
31
31
|
import {
|
|
32
32
|
createHostElement,
|
|
33
33
|
hostElementMatches,
|
|
34
|
+
isDomHostElement,
|
|
34
35
|
isHostElement,
|
|
35
36
|
namespaceForHostChildren,
|
|
36
37
|
namespaceForHostElement,
|
|
38
|
+
type CustomHostDocument,
|
|
37
39
|
type HostNamespace,
|
|
38
40
|
} from "./dom-host-rules.js";
|
|
39
41
|
import { createFiber, createWorkInProgress, type Fiber, type FiberRoot } from "./fiber.js";
|
|
@@ -90,13 +92,14 @@ interface SuspenseFiberState {
|
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
const committedPortalContainers = new Set<Element>();
|
|
95
|
+
const pendingHostRefUpdates: { ref: unknown; node: unknown }[] = [];
|
|
93
96
|
|
|
94
97
|
interface FiberHydrationOptions extends RenderOptions {
|
|
95
98
|
previousNodes?: readonly Node[];
|
|
96
99
|
resumeId?: string;
|
|
97
100
|
consumeResumeMarkers?: boolean;
|
|
98
101
|
namespace?: HostNamespace;
|
|
99
|
-
documentRef?: Document;
|
|
102
|
+
documentRef?: Document | CustomHostDocument;
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
const SKIP_COMMIT_PATH = "\0";
|
|
@@ -107,6 +110,11 @@ interface FiberReconcileResult {
|
|
|
107
110
|
consumed: number;
|
|
108
111
|
}
|
|
109
112
|
|
|
113
|
+
interface AppendSuffixCommitHint {
|
|
114
|
+
fiber: Fiber;
|
|
115
|
+
index: number;
|
|
116
|
+
}
|
|
117
|
+
|
|
110
118
|
interface ReactSuspenseBoundary {
|
|
111
119
|
previousNodes?: Node[];
|
|
112
120
|
consumed: number;
|
|
@@ -241,11 +249,14 @@ export function commitHostFiberRoot(
|
|
|
241
249
|
options: RenderOptions = {},
|
|
242
250
|
): void {
|
|
243
251
|
runWithHostCommit(() => {
|
|
252
|
+
let committed = false;
|
|
244
253
|
try {
|
|
245
254
|
committedPortalContainers.clear();
|
|
255
|
+
pendingHostRefUpdates.length = 0;
|
|
246
256
|
const commitPath = getRootCommitPath(options);
|
|
247
257
|
if (!hasChildListMutation(finishedWork)) {
|
|
248
258
|
commitHostDirtyChildren(finishedWork.child, root.container, root.container, commitPath, options);
|
|
259
|
+
committed = true;
|
|
249
260
|
return;
|
|
250
261
|
}
|
|
251
262
|
|
|
@@ -254,12 +265,19 @@ export function commitHostFiberRoot(
|
|
|
254
265
|
finishedWork.subtreeChildListChanged &&
|
|
255
266
|
commitHostKeyedChildListMutation(finishedWork.child, root.container, root.container, commitPath, options)
|
|
256
267
|
) {
|
|
268
|
+
committed = true;
|
|
257
269
|
return;
|
|
258
270
|
}
|
|
259
271
|
|
|
260
272
|
const nodes = commitHostChildren(finishedWork.child, root.container, root.container, commitPath, options);
|
|
261
273
|
syncChildNodes(root.container, nodes);
|
|
274
|
+
committed = true;
|
|
262
275
|
} finally {
|
|
276
|
+
if (committed) {
|
|
277
|
+
flushPendingHostRefUpdates();
|
|
278
|
+
} else {
|
|
279
|
+
pendingHostRefUpdates.length = 0;
|
|
280
|
+
}
|
|
263
281
|
committedPortalContainers.clear();
|
|
264
282
|
}
|
|
265
283
|
});
|
|
@@ -272,12 +290,20 @@ export function commitHydratingHostFiberRoot(
|
|
|
272
290
|
options: FiberHydrationOptions = {},
|
|
273
291
|
): void {
|
|
274
292
|
runWithHostCommit(() => {
|
|
293
|
+
let committed = false;
|
|
275
294
|
try {
|
|
276
295
|
committedPortalContainers.clear();
|
|
296
|
+
pendingHostRefUpdates.length = 0;
|
|
277
297
|
const eventRoot = root.container;
|
|
278
298
|
const nodes = commitHostChildren(finishedWork.child, scope.parent, eventRoot, "", options);
|
|
279
299
|
syncScopedChildNodes(scope.parent, scope.before, scope.after, nodes);
|
|
300
|
+
committed = true;
|
|
280
301
|
} finally {
|
|
302
|
+
if (committed) {
|
|
303
|
+
flushPendingHostRefUpdates();
|
|
304
|
+
} else {
|
|
305
|
+
pendingHostRefUpdates.length = 0;
|
|
306
|
+
}
|
|
281
307
|
committedPortalContainers.clear();
|
|
282
308
|
}
|
|
283
309
|
});
|
|
@@ -303,6 +329,9 @@ function reconcileHostChild(
|
|
|
303
329
|
|
|
304
330
|
if (node === null || node === undefined || typeof node === "boolean") {
|
|
305
331
|
parent.childListChanged = currentFirstChild !== undefined;
|
|
332
|
+
if (currentFirstChild !== undefined) {
|
|
333
|
+
markOptimizedChildrenForDeletion(parent, currentFirstChild);
|
|
334
|
+
}
|
|
306
335
|
return { fiber: undefined, consumed: 0 };
|
|
307
336
|
}
|
|
308
337
|
|
|
@@ -324,6 +353,8 @@ function reconcileHostChild(
|
|
|
324
353
|
let previous: Fiber | undefined;
|
|
325
354
|
let consumed = 0;
|
|
326
355
|
let skipRemainingKeyedLookup = false;
|
|
356
|
+
const usedCurrentChildren =
|
|
357
|
+
currentFirstChild === undefined ? undefined : new Set<Fiber>();
|
|
327
358
|
|
|
328
359
|
for (let index = 0; index < childCount; index += 1) {
|
|
329
360
|
const child = children === undefined ? node : children[index];
|
|
@@ -376,9 +407,20 @@ function reconcileHostChild(
|
|
|
376
407
|
const fiber = result.fiber;
|
|
377
408
|
|
|
378
409
|
if (fiber === undefined) {
|
|
410
|
+
if (matchedCurrent !== undefined) {
|
|
411
|
+
usedCurrentChildren?.add(matchedCurrent);
|
|
412
|
+
markOptimizedChildForDeletion(parent, matchedCurrent);
|
|
413
|
+
}
|
|
379
414
|
continue;
|
|
380
415
|
}
|
|
381
416
|
|
|
417
|
+
if (matchedCurrent !== undefined) {
|
|
418
|
+
usedCurrentChildren?.add(matchedCurrent);
|
|
419
|
+
if (fiber !== matchedCurrent && fiber.alternate !== matchedCurrent) {
|
|
420
|
+
markOptimizedChildForDeletion(parent, matchedCurrent);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
382
424
|
if (key === undefined) {
|
|
383
425
|
currentUnkeyed = currentUnkeyed?.sibling;
|
|
384
426
|
}
|
|
@@ -407,6 +449,7 @@ function reconcileHostChild(
|
|
|
407
449
|
previous = fiber;
|
|
408
450
|
}
|
|
409
451
|
|
|
452
|
+
markUnusedCurrentChildrenForDeletion(parent, currentFirstChild, usedCurrentChildren);
|
|
410
453
|
parent.childListChanged = childFiberListShapeChanged(currentFirstChild, first);
|
|
411
454
|
|
|
412
455
|
return { fiber: first, consumed };
|
|
@@ -435,6 +478,7 @@ function reconcileKeyedRowHostChildren(
|
|
|
435
478
|
let subtreeFlags = NoFlags;
|
|
436
479
|
let subtreeChildListChanged = false;
|
|
437
480
|
let hasRefSubtree = false;
|
|
481
|
+
let appendSuffix: AppendSuffixCommitHint | undefined;
|
|
438
482
|
const canReuseUnchangedRows = hasSameKeyOrderPrefix(currentFirstChild, children);
|
|
439
483
|
const row = createKeyedRowHostElementScratch();
|
|
440
484
|
|
|
@@ -446,12 +490,14 @@ function reconcileKeyedRowHostChildren(
|
|
|
446
490
|
}
|
|
447
491
|
|
|
448
492
|
let matchedCurrent: Fiber | undefined;
|
|
493
|
+
let matchedByAppendSuffix = false;
|
|
449
494
|
|
|
450
495
|
if (skipRemainingKeyedLookup) {
|
|
451
496
|
matchedCurrent = undefined;
|
|
452
497
|
} else if (currentKeyed === undefined) {
|
|
453
498
|
listShapeChanged = true;
|
|
454
499
|
skipRemainingKeyedLookup = true;
|
|
500
|
+
matchedByAppendSuffix = true;
|
|
455
501
|
matchedCurrent = undefined;
|
|
456
502
|
} else if (currentKeyed?.key === row.key) {
|
|
457
503
|
matchedCurrent = currentKeyed;
|
|
@@ -460,11 +506,15 @@ function reconcileKeyedRowHostChildren(
|
|
|
460
506
|
currentKeyed?.sibling?.key === row.key &&
|
|
461
507
|
canSkipSingleDeletedKeyedFiber(children, index, currentKeyed.sibling)
|
|
462
508
|
) {
|
|
509
|
+
const deleted = currentKeyed;
|
|
510
|
+
const matched = currentKeyed.sibling;
|
|
463
511
|
listShapeChanged = true;
|
|
464
|
-
|
|
465
|
-
|
|
512
|
+
markOptimizedChildForDeletion(parent, deleted);
|
|
513
|
+
matchedCurrent = matched;
|
|
514
|
+
currentKeyed = matched.sibling;
|
|
466
515
|
} else if (canSkipRemainingKeyedLookup(currentKeyed, children, index)) {
|
|
467
516
|
listShapeChanged = true;
|
|
517
|
+
markOptimizedChildrenForDeletion(parent, currentKeyed);
|
|
468
518
|
skipRemainingKeyedLookup = true;
|
|
469
519
|
currentKeyed = undefined;
|
|
470
520
|
matchedCurrent = undefined;
|
|
@@ -478,6 +528,10 @@ function reconcileKeyedRowHostChildren(
|
|
|
478
528
|
: (canReuseUnchangedRows ? getReusableKeyedRowHostFiber(matchedCurrent, row) : undefined) ??
|
|
479
529
|
createKeyedRowHostFiber(parent, matchedCurrent, row, options);
|
|
480
530
|
|
|
531
|
+
if (matchedByAppendSuffix && appendSuffix === undefined) {
|
|
532
|
+
appendSuffix = { fiber, index };
|
|
533
|
+
}
|
|
534
|
+
|
|
481
535
|
if (first === undefined) {
|
|
482
536
|
first = fiber;
|
|
483
537
|
} else if (previous !== undefined) {
|
|
@@ -502,15 +556,55 @@ function reconcileKeyedRowHostChildren(
|
|
|
502
556
|
|
|
503
557
|
if (currentKeyed !== undefined) {
|
|
504
558
|
listShapeChanged = true;
|
|
559
|
+
markOptimizedChildrenForDeletion(parent, currentKeyed);
|
|
505
560
|
}
|
|
506
561
|
|
|
507
562
|
parent.hasRefSubtree = hasRefSubtree;
|
|
508
563
|
parent.subtreeFlags = subtreeFlags;
|
|
509
564
|
parent.subtreeChildListChanged = subtreeChildListChanged;
|
|
510
565
|
parent.childListChanged = listShapeChanged;
|
|
566
|
+
if (appendSuffix !== undefined && canStoreAppendSuffixCommitHint(parent)) {
|
|
567
|
+
parent.memoizedState = appendSuffix;
|
|
568
|
+
}
|
|
511
569
|
return { fiber: first, consumed: 0 };
|
|
512
570
|
}
|
|
513
571
|
|
|
572
|
+
function canStoreAppendSuffixCommitHint(parent: Fiber): boolean {
|
|
573
|
+
return (
|
|
574
|
+
parent.tag === "fragment" ||
|
|
575
|
+
parent.tag === "host-component" ||
|
|
576
|
+
parent.tag === "host-root"
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function markOptimizedChildForDeletion(parent: Fiber, _child: Fiber): void {
|
|
581
|
+
parent.flags |= ChildDeletion;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function markOptimizedChildrenForDeletion(parent: Fiber, _firstChild: Fiber): void {
|
|
585
|
+
parent.flags |= ChildDeletion;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function markUnusedCurrentChildrenForDeletion(
|
|
589
|
+
parent: Fiber,
|
|
590
|
+
firstChild: Fiber | undefined,
|
|
591
|
+
used: ReadonlySet<Fiber> | undefined,
|
|
592
|
+
): void {
|
|
593
|
+
if (firstChild === undefined || used === undefined) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
let cursor: Fiber | undefined = firstChild;
|
|
598
|
+
|
|
599
|
+
while (cursor !== undefined) {
|
|
600
|
+
if (!used.has(cursor)) {
|
|
601
|
+
parent.flags |= ChildDeletion;
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
cursor = cursor.sibling;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
514
608
|
function hasSameKeyOrderPrefix(
|
|
515
609
|
currentFirstChild: Fiber,
|
|
516
610
|
children: readonly ReactCompatNode[],
|
|
@@ -927,7 +1021,7 @@ function createHostFiberImpl(
|
|
|
927
1021
|
? existing
|
|
928
1022
|
: current?.tag === "host-text" && current.stateNode instanceof Text
|
|
929
1023
|
? current.stateNode
|
|
930
|
-
: getDocumentRef(options)
|
|
1024
|
+
: createHostTextNode(getDocumentRef(options));
|
|
931
1025
|
|
|
932
1026
|
if (existing instanceof Text && existing.data !== String(node)) {
|
|
933
1027
|
reportRecoverable(
|
|
@@ -1627,13 +1721,12 @@ function commitHostDirtyFiber(
|
|
|
1627
1721
|
fiber.hydrateExisting !== true &&
|
|
1628
1722
|
isRowTextOnlyUpdate(fiber.memoizedProps, props);
|
|
1629
1723
|
|
|
1630
|
-
if (!propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
|
|
1724
|
+
if (isDomHostElement(element) && !propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
|
|
1631
1725
|
applyProps(element, props, path, {
|
|
1632
1726
|
...options,
|
|
1633
1727
|
eventRoot,
|
|
1634
1728
|
preserveHydrationAttributes: fiber.hydrateExisting,
|
|
1635
1729
|
});
|
|
1636
|
-
applyChangedRef(previousProps?.ref, props.ref, element);
|
|
1637
1730
|
}
|
|
1638
1731
|
|
|
1639
1732
|
if (directTextChild !== undefined) {
|
|
@@ -1646,16 +1739,19 @@ function commitHostDirtyFiber(
|
|
|
1646
1739
|
) {
|
|
1647
1740
|
const childNodes = commitHostChildren(fiber.child, element, eventRoot, `${path}.c`, options);
|
|
1648
1741
|
if (
|
|
1649
|
-
!(childNodes.length === 0 && committedPortalContainers.has(element)) &&
|
|
1650
|
-
!shouldPreserveContentEditableChildren(element, props, childNodes)
|
|
1742
|
+
!(isDomHostElement(element) && childNodes.length === 0 && committedPortalContainers.has(element)) &&
|
|
1743
|
+
!(isDomHostElement(element) && shouldPreserveContentEditableChildren(element, props, childNodes))
|
|
1651
1744
|
) {
|
|
1652
|
-
syncChildNodes(element, childNodes);
|
|
1745
|
+
syncChildNodes(element as ParentNode, childNodes);
|
|
1653
1746
|
}
|
|
1654
1747
|
} else if (fiber.subtreeFlags !== NoFlags) {
|
|
1655
1748
|
commitHostDirtyChildren(fiber.child, element, eventRoot, `${path}.c`, options);
|
|
1656
1749
|
}
|
|
1657
1750
|
|
|
1658
|
-
|
|
1751
|
+
if (isDomHostElement(element)) {
|
|
1752
|
+
applyPostChildFormProps(element, props, previousProps);
|
|
1753
|
+
}
|
|
1754
|
+
applyChangedRef(previousProps?.ref, props.ref, element);
|
|
1659
1755
|
fiber.memoizedProps = props;
|
|
1660
1756
|
finishCommittedFiber(fiber);
|
|
1661
1757
|
return;
|
|
@@ -1664,9 +1760,42 @@ function commitHostDirtyFiber(
|
|
|
1664
1760
|
if (fiber.tag === "portal") {
|
|
1665
1761
|
const container = fiber.stateNode;
|
|
1666
1762
|
|
|
1667
|
-
if (container
|
|
1668
|
-
|
|
1669
|
-
|
|
1763
|
+
if (isPortalHostContainer(container)) {
|
|
1764
|
+
if (container instanceof Element) {
|
|
1765
|
+
setLogicalEventParent(container, parent);
|
|
1766
|
+
}
|
|
1767
|
+
const portalEventRoot =
|
|
1768
|
+
container instanceof Element && eventRoot !== container && eventRoot.contains(container)
|
|
1769
|
+
? eventRoot
|
|
1770
|
+
: container instanceof Element
|
|
1771
|
+
? container
|
|
1772
|
+
: eventRoot;
|
|
1773
|
+
const portalOptions = withPortalDocumentRef(options, container);
|
|
1774
|
+
|
|
1775
|
+
if (
|
|
1776
|
+
fiber.childListChanged ||
|
|
1777
|
+
fiber.subtreeChildListChanged ||
|
|
1778
|
+
(fiber.subtreeFlags & Placement) !== NoFlags
|
|
1779
|
+
) {
|
|
1780
|
+
const childNodes = commitHostChildren(
|
|
1781
|
+
fiber.child,
|
|
1782
|
+
container as ParentNode,
|
|
1783
|
+
portalEventRoot,
|
|
1784
|
+
`${path}.portal`,
|
|
1785
|
+
portalOptions,
|
|
1786
|
+
);
|
|
1787
|
+
const previousNodes = committedHostNodesFromState(fiber.alternate?.memoizedState);
|
|
1788
|
+
syncOwnedChildNodes(container as ParentNode, previousNodes, childNodes);
|
|
1789
|
+
fiber.memoizedState = childNodes;
|
|
1790
|
+
} else {
|
|
1791
|
+
commitHostDirtyChildren(
|
|
1792
|
+
fiber.child,
|
|
1793
|
+
container as ParentNode,
|
|
1794
|
+
portalEventRoot,
|
|
1795
|
+
`${path}.portal`,
|
|
1796
|
+
portalOptions,
|
|
1797
|
+
);
|
|
1798
|
+
}
|
|
1670
1799
|
}
|
|
1671
1800
|
fiber.memoizedProps = fiber.pendingProps;
|
|
1672
1801
|
finishCommittedFiber(fiber);
|
|
@@ -1737,6 +1866,11 @@ function commitHostKeyedChildListMutationFiber(
|
|
|
1737
1866
|
path: string,
|
|
1738
1867
|
options: RenderOptions = {},
|
|
1739
1868
|
): boolean {
|
|
1869
|
+
if (fiber.tag === "portal") {
|
|
1870
|
+
commitHostDirtyFiber(fiber, parent, eventRoot, path, options);
|
|
1871
|
+
return true;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1740
1874
|
if (fiber.childListChanged) {
|
|
1741
1875
|
const mutationParent =
|
|
1742
1876
|
fiber.tag === "host-component" && isHostElement(fiber.stateNode)
|
|
@@ -1793,17 +1927,28 @@ function commitHostAppendSuffix(
|
|
|
1793
1927
|
path: string,
|
|
1794
1928
|
options: RenderOptions,
|
|
1795
1929
|
): boolean {
|
|
1796
|
-
const
|
|
1930
|
+
const appendHint = readAppendSuffixCommitHint(fiber.memoizedState);
|
|
1931
|
+
const append = appendHint ?? getAppendSuffix(fiber.alternate?.child, fiber.child);
|
|
1797
1932
|
|
|
1798
1933
|
if (append === undefined) {
|
|
1799
1934
|
return false;
|
|
1800
1935
|
}
|
|
1801
1936
|
|
|
1937
|
+
if (appendHint !== undefined) {
|
|
1938
|
+
fiber.memoizedState = undefined;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1802
1941
|
let cursor: Fiber | undefined = append.fiber;
|
|
1803
1942
|
let index = append.index;
|
|
1804
1943
|
|
|
1805
1944
|
while (cursor !== undefined) {
|
|
1806
|
-
for (const node of commitHostFiber(
|
|
1945
|
+
for (const node of commitHostFiber(
|
|
1946
|
+
cursor,
|
|
1947
|
+
parent,
|
|
1948
|
+
eventRoot,
|
|
1949
|
+
joinCommitPath(path, String(index)),
|
|
1950
|
+
options,
|
|
1951
|
+
)) {
|
|
1807
1952
|
parent.appendChild(node);
|
|
1808
1953
|
}
|
|
1809
1954
|
cursor = cursor.sibling;
|
|
@@ -1813,6 +1958,17 @@ function commitHostAppendSuffix(
|
|
|
1813
1958
|
return true;
|
|
1814
1959
|
}
|
|
1815
1960
|
|
|
1961
|
+
function readAppendSuffixCommitHint(value: unknown): AppendSuffixCommitHint | undefined {
|
|
1962
|
+
if (typeof value !== "object" || value === null) {
|
|
1963
|
+
return undefined;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
const candidate = value as Partial<AppendSuffixCommitHint>;
|
|
1967
|
+
return candidate.fiber !== undefined && typeof candidate.index === "number"
|
|
1968
|
+
? { fiber: candidate.fiber, index: candidate.index }
|
|
1969
|
+
: undefined;
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1816
1972
|
function commitHostSingleRemoval(fiber: Fiber, parent: ParentNode): boolean {
|
|
1817
1973
|
const removed = getSingleRemovedFiber(fiber.alternate?.child, fiber.child);
|
|
1818
1974
|
|
|
@@ -2016,13 +2172,12 @@ function commitHostFiber(
|
|
|
2016
2172
|
fiber.hydrateExisting !== true &&
|
|
2017
2173
|
isRowTextOnlyUpdate(fiber.memoizedProps, props);
|
|
2018
2174
|
|
|
2019
|
-
if (!propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
|
|
2175
|
+
if (isDomHostElement(element) && !propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
|
|
2020
2176
|
applyProps(element, props, path, {
|
|
2021
2177
|
...options,
|
|
2022
2178
|
eventRoot,
|
|
2023
2179
|
preserveHydrationAttributes: fiber.hydrateExisting,
|
|
2024
2180
|
});
|
|
2025
|
-
applyChangedRef(previousProps?.ref, props.ref, element);
|
|
2026
2181
|
}
|
|
2027
2182
|
if (directTextChild !== undefined) {
|
|
2028
2183
|
const text = syncDirectHostTextChild(element, directTextChild);
|
|
@@ -2036,16 +2191,19 @@ function commitHostFiber(
|
|
|
2036
2191
|
) {
|
|
2037
2192
|
const childNodes = commitHostChildren(fiber.child, element, eventRoot, `${path}.c`, options);
|
|
2038
2193
|
if (
|
|
2039
|
-
!(childNodes.length === 0 && committedPortalContainers.has(element)) &&
|
|
2040
|
-
!shouldPreserveContentEditableChildren(element, props, childNodes)
|
|
2194
|
+
!(isDomHostElement(element) && childNodes.length === 0 && committedPortalContainers.has(element)) &&
|
|
2195
|
+
!(isDomHostElement(element) && shouldPreserveContentEditableChildren(element, props, childNodes))
|
|
2041
2196
|
) {
|
|
2042
|
-
syncChildNodes(element, childNodes);
|
|
2197
|
+
syncChildNodes(element as ParentNode, childNodes);
|
|
2043
2198
|
}
|
|
2044
2199
|
} else if (fiber.subtreeFlags !== NoFlags) {
|
|
2045
2200
|
commitHostChildren(fiber.child, element, eventRoot, `${path}.c`, options);
|
|
2046
2201
|
}
|
|
2047
2202
|
|
|
2048
|
-
|
|
2203
|
+
if (isDomHostElement(element)) {
|
|
2204
|
+
applyPostChildFormProps(element, props, previousProps);
|
|
2205
|
+
}
|
|
2206
|
+
applyChangedRef(previousProps?.ref, props.ref, element);
|
|
2049
2207
|
fiber.memoizedProps = props;
|
|
2050
2208
|
finishCommittedFiber(fiber);
|
|
2051
2209
|
return [element];
|
|
@@ -2138,25 +2296,30 @@ function commitHostFiber(
|
|
|
2138
2296
|
if (fiber.tag === "portal") {
|
|
2139
2297
|
const container = fiber.stateNode;
|
|
2140
2298
|
|
|
2141
|
-
if (!(container
|
|
2299
|
+
if (!isPortalHostContainer(container)) {
|
|
2142
2300
|
return [];
|
|
2143
2301
|
}
|
|
2144
2302
|
|
|
2145
|
-
|
|
2146
|
-
|
|
2303
|
+
if (container instanceof Element) {
|
|
2304
|
+
setLogicalEventParent(container, parent);
|
|
2305
|
+
committedPortalContainers.add(container);
|
|
2306
|
+
}
|
|
2147
2307
|
const portalEventRoot =
|
|
2148
|
-
eventRoot !== container && eventRoot.contains(container)
|
|
2308
|
+
container instanceof Element && eventRoot !== container && eventRoot.contains(container)
|
|
2309
|
+
? eventRoot
|
|
2310
|
+
: container instanceof Element
|
|
2311
|
+
? container
|
|
2312
|
+
: eventRoot;
|
|
2313
|
+
const portalOptions = withPortalDocumentRef(options, container);
|
|
2149
2314
|
const childNodes = commitHostChildren(
|
|
2150
2315
|
fiber.child,
|
|
2151
|
-
container,
|
|
2316
|
+
container as ParentNode,
|
|
2152
2317
|
portalEventRoot,
|
|
2153
2318
|
`${path}.portal`,
|
|
2154
|
-
|
|
2319
|
+
portalOptions,
|
|
2155
2320
|
);
|
|
2156
|
-
const previousNodes =
|
|
2157
|
-
|
|
2158
|
-
: [];
|
|
2159
|
-
syncOwnedChildNodes(container, previousNodes, childNodes);
|
|
2321
|
+
const previousNodes = committedHostNodesFromState(fiber.alternate?.memoizedState);
|
|
2322
|
+
syncOwnedChildNodes(container as ParentNode, previousNodes, childNodes);
|
|
2160
2323
|
fiber.memoizedState = childNodes;
|
|
2161
2324
|
fiber.memoizedProps = fiber.pendingProps;
|
|
2162
2325
|
finishCommittedFiber(fiber);
|
|
@@ -2968,10 +3131,18 @@ function normalizeChildren(node: ReactCompatNode): ReactCompatNode[] {
|
|
|
2968
3131
|
return Array.isArray(node) ? node : [node];
|
|
2969
3132
|
}
|
|
2970
3133
|
|
|
2971
|
-
function getDocumentRef(options: FiberHydrationOptions): Document {
|
|
3134
|
+
function getDocumentRef(options: FiberHydrationOptions): Document | CustomHostDocument {
|
|
2972
3135
|
return options.documentRef ?? document;
|
|
2973
3136
|
}
|
|
2974
3137
|
|
|
3138
|
+
function createHostTextNode(documentRef: Document | CustomHostDocument): Text {
|
|
3139
|
+
if ("createTextNode" in documentRef && typeof documentRef.createTextNode === "function") {
|
|
3140
|
+
return documentRef.createTextNode("");
|
|
3141
|
+
}
|
|
3142
|
+
|
|
3143
|
+
return document.createTextNode("");
|
|
3144
|
+
}
|
|
3145
|
+
|
|
2975
3146
|
function collectExistingKeyedFibers(
|
|
2976
3147
|
firstChild: Fiber | undefined,
|
|
2977
3148
|
): Map<string, Fiber> {
|
|
@@ -3184,6 +3355,64 @@ function applyChangedRef(previousRef: unknown, nextRef: unknown, node: unknown):
|
|
|
3184
3355
|
return;
|
|
3185
3356
|
}
|
|
3186
3357
|
|
|
3187
|
-
|
|
3188
|
-
|
|
3358
|
+
queueHostRefUpdate(previousRef, null);
|
|
3359
|
+
queueHostRefUpdate(nextRef, node);
|
|
3360
|
+
}
|
|
3361
|
+
|
|
3362
|
+
function queueHostRefUpdate(ref: unknown, node: unknown): void {
|
|
3363
|
+
if (ref === null || ref === undefined) {
|
|
3364
|
+
return;
|
|
3365
|
+
}
|
|
3366
|
+
|
|
3367
|
+
pendingHostRefUpdates.push({ ref, node });
|
|
3368
|
+
}
|
|
3369
|
+
|
|
3370
|
+
function flushPendingHostRefUpdates(): void {
|
|
3371
|
+
const pending = pendingHostRefUpdates.splice(0);
|
|
3372
|
+
for (const { ref, node } of pending) {
|
|
3373
|
+
applyRef(ref, node);
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
|
|
3377
|
+
function isPortalHostContainer(value: unknown): value is ParentNode {
|
|
3378
|
+
if (value instanceof Element) {
|
|
3379
|
+
return true;
|
|
3380
|
+
}
|
|
3381
|
+
|
|
3382
|
+
if (typeof value !== "object" || value === null) {
|
|
3383
|
+
return false;
|
|
3384
|
+
}
|
|
3385
|
+
|
|
3386
|
+
const candidate = value as Partial<ParentNode> & {
|
|
3387
|
+
ownerDocument?: { createElement?: unknown };
|
|
3388
|
+
};
|
|
3389
|
+
return (
|
|
3390
|
+
typeof candidate.appendChild === "function" &&
|
|
3391
|
+
typeof candidate.insertBefore === "function" &&
|
|
3392
|
+
typeof candidate.removeChild === "function" &&
|
|
3393
|
+
typeof candidate.ownerDocument?.createElement === "function"
|
|
3394
|
+
);
|
|
3395
|
+
}
|
|
3396
|
+
|
|
3397
|
+
function withPortalDocumentRef(
|
|
3398
|
+
options: RenderOptions,
|
|
3399
|
+
container: ParentNode,
|
|
3400
|
+
): RenderOptions & { documentRef?: Document | CustomHostDocument } {
|
|
3401
|
+
const ownerDocument = (container as { ownerDocument?: unknown }).ownerDocument;
|
|
3402
|
+
if (
|
|
3403
|
+
typeof ownerDocument === "object" &&
|
|
3404
|
+
ownerDocument !== null &&
|
|
3405
|
+
typeof (ownerDocument as { createElement?: unknown }).createElement === "function"
|
|
3406
|
+
) {
|
|
3407
|
+
return {
|
|
3408
|
+
...options,
|
|
3409
|
+
documentRef: ownerDocument as Document | CustomHostDocument,
|
|
3410
|
+
};
|
|
3411
|
+
}
|
|
3412
|
+
|
|
3413
|
+
return options;
|
|
3414
|
+
}
|
|
3415
|
+
|
|
3416
|
+
function committedHostNodesFromState(state: unknown): Node[] {
|
|
3417
|
+
return Array.isArray(state) ? state as Node[] : [];
|
|
3189
3418
|
}
|
package/src/root.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
type RenderPriority,
|
|
7
7
|
type RootRuntime,
|
|
8
8
|
} from "./hooks.js";
|
|
9
|
-
import { removeChildIfPresent } from "./dom-children.js";
|
|
9
|
+
import { collectOwnedChildNodes, removeChildIfPresent } from "./dom-children.js";
|
|
10
10
|
import { commitDevToolsRoot, unmountDevToolsRoot } from "./devtools.js";
|
|
11
11
|
import {
|
|
12
12
|
applyStreamingHydrationFragments,
|
|
@@ -532,7 +532,8 @@ function removeStalePortalNodes(
|
|
|
532
532
|
snapshot: PortalRenderSnapshot,
|
|
533
533
|
runtime: RootRuntime,
|
|
534
534
|
): void {
|
|
535
|
-
for (const
|
|
535
|
+
for (const container of snapshot.containers) {
|
|
536
|
+
const nodes = snapshot.nodes.get(container) ?? new Set(collectOwnedChildNodes(container));
|
|
536
537
|
const currentNodes = runtime.portalNodes.get(container);
|
|
537
538
|
|
|
538
539
|
for (const node of nodes) {
|