9
10
import type {Fiber, FiberRoot} from './ReactInternalTypes';
11
12
-import type {Instance, TextInstance} from './ReactFiberConfig';
12
+import type {Instance, TextInstance, Props} from './ReactFiberConfig';
13
14
import type {OffscreenState} from './ReactFiberActivityComponent';
15
25
removeRootViewTransitionClone,
26
cancelRootViewTransitionName,
27
restoreRootViewTransitionName,
28
+ cancelViewTransitionName,
29
applyViewTransitionName,
30
appendChild,
31
commitUpdate,
40
popMutationContext,
41
pushMutationContext,
42
viewTransitionMutationContext,
43
+ trackHostMutation,
44
} from './ReactFiberMutationTracking';
45
import {
46
MutationMask,
50
Visibility,
51
ViewTransitionNamedStatic,
52
ViewTransitionStatic,
53
+ AffectedParentLayout,
54
} from './ReactFiberFlags';
55
import {
56
HostComponent,
64
import {
65
restoreEnterOrExitViewTransitions,
66
restoreNestedViewTransitions,
67
+ restoreUpdateViewTransitionForGesture,
68
appearingViewTransitions,
69
commitEnterViewTransitions,
70
measureNestedViewTransitions,
71
+ measureUpdateViewTransition,
72
+ viewTransitionCancelableChildren,
73
+ pushViewTransitionCancelableScope,
74
+ popViewTransitionCancelableScope,
75
} from './ReactFiberCommitViewTransitions';
76
import {
77
getViewTransitionName,
80
81
let didWarnForRootClone = false;
82
83
+// Used during the apply phase to track whether a parent ViewTransition component
84
+// might have been affected by any mutations / relayouts below.
85
+let viewTransitionContextChanged: boolean = false;
86
+
87
function detectMutationOrInsertClones(finishedWork: Fiber): boolean {
88
return true;
89
}
433
// For insertions we don't need to clone. It's already new state node.
434
if (visitPhase !== INSERT_APPEARING_PAIR) {
435
appendChild(hostParentClone, instance);
436
+ trackHostMutation();
437
recursivelyInsertNew(
438
finishedWork,
439
instance,
463
// For insertions we don't need to clone. It's already new state node.
464
if (visitPhase !== INSERT_APPEARING_PAIR) {
465
appendChild(hostParentClone, textInstance);
466
+ trackHostMutation();
467
}
468
break;
469
}
589
}
590
if (visitPhase === CLONE_EXIT || visitPhase === CLONE_UNHIDE) {
591
unhideInstance(clone, child.memoizedProps);
592
+ trackHostMutation();
593
}
594
break;
595
}
605
appendChild(hostParentClone, clone);
606
if (visitPhase === CLONE_EXIT || visitPhase === CLONE_UNHIDE) {
607
unhideTextInstance(clone, child.memoizedProps);
608
+ trackHostMutation();
609
}
610
break;
611
}
695
for (let i = 0; i < deletions.length; i++) {
696
const childToDelete = deletions[i];
697
trackEnterViewTransitions(childToDelete);
698
+ // Normally we would only mark something as triggering a mutation if there was
699
+ // actually a HostInstance below here. If this tree didn't contain a HostInstances
700
+ // we shouldn't trigger a mutation even though a virtual component was deleted.
701
+ trackHostMutation();
702
}
703
}
704
821
clone = cloneMutableInstance(instance, true);
822
if (finishedWork.flags & ContentReset) {
823
resetTextContent(clone);
824
+ trackHostMutation();
825
}
826
} else {
827
// If we have children we'll clone them as we walk the tree so we just
846
);
847
appendChild(hostParentClone, clone);
848
unhideInstance(clone, finishedWork.memoizedProps);
849
+ trackHostMutation();
850
} else {
851
recursivelyInsertClones(finishedWork, clone, null, visitPhase);
852
appendChild(hostParentClone, clone);
873
const newText: string = finishedWork.memoizedProps;
874
const oldText: string = current.memoizedProps;
875
commitTextUpdate(clone, newText, oldText);
876
+ trackHostMutation();
877
}
878
appendChild(hostParentClone, clone);
879
if (visitPhase === CLONE_EXIT || visitPhase === CLONE_UNHIDE) {
880
unhideTextInstance(clone, finishedWork.memoizedProps);
881
+ trackHostMutation();
882
}
883
break;
884
}
909
} else if (current !== null && current.memoizedState === null) {
910
// Was previously mounted as visible but is now hidden.
911
trackEnterViewTransitions(current);
912
+ // Normally we would only mark something as triggering a mutation if there was
913
+ // actually a HostInstance below here. If this tree didn't contain a HostInstances
914
+ // we shouldn't trigger a mutation even though a virtual component was hidden.
915
+ trackHostMutation();
916
}
917
break;
918
}
1019
}
1020
}
1021
994
-function measureUpdateViewTransition(
995
- current: Fiber,
996
- finishedWork: Fiber,
997
-): void {
998
- // TODO
999
-}
1000
-
1022
function recursivelyApplyViewTransitions(parentFiber: Fiber) {
1023
const deletions = parentFiber.deletions;
1024
if (deletions !== null) {
1058
// because the fiber tag is more specific. An exception is any flag related
1059
// to reconciliation, because those can be set on all fiber types.
1060
switch (finishedWork.tag) {
1040
- case HostComponent: {
1041
- // const instance: Instance = finishedWork.stateNode;
1042
- // TODO: Apply name and measure.
1043
- recursivelyApplyViewTransitions(finishedWork);
1044
- break;
1045
- }
1046
- case HostText: {
1047
- break;
1048
- }
1061
case HostPortal: {
1062
// TODO: Consider what should happen to Portals. For now we exclude them.
1063
break;
1075
}
1076
break;
1077
}
1066
- case ViewTransitionComponent:
1067
- measureUpdateViewTransition(current, finishedWork);
1078
+ case ViewTransitionComponent: {
1079
+ const prevContextChanged = viewTransitionContextChanged;
1080
+ const prevCancelableChildren = pushViewTransitionCancelableScope();
1081
+ viewTransitionContextChanged = false;
1082
+ recursivelyApplyViewTransitions(finishedWork);
1083
+
1084
+ if (viewTransitionContextChanged) {
1085
+ finishedWork.flags |= Update;
1086
+ }
1087
+
1088
+ const inViewport = measureUpdateViewTransition(
1089
+ current,
1090
+ finishedWork,
1091
+ true,
1092
+ );
1093
+
1094
+ if ((finishedWork.flags & Update) === NoFlags || !inViewport) {
1095
+ // If this boundary didn't update, then we may be able to cancel its children.
1096
+ // We bubble them up to the parent set to be determined later if we can cancel.
1097
+ // Similarly, if old and new state was outside the viewport, we can skip it
1098
+ // even if it did update.
1099
+ if (prevCancelableChildren === null) {
1100
+ // Bubbling up this whole set to the parent.
1101
+ } else {
1102
+ // Merge with parent set.
1103
+ // $FlowFixMe[method-unbinding]
1104
+ prevCancelableChildren.push.apply(
1105
+ prevCancelableChildren,
1106
+ viewTransitionCancelableChildren,
1107
+ );
1108
+ popViewTransitionCancelableScope(prevCancelableChildren);
1109
+ }
1110
+ // TODO: If this doesn't end up canceled, because a parent animates,
1111
+ // then we should probably issue an event since this instance is part of it.
1112
+ } else {
1113
+ // TODO: Schedule gesture events.
1114
+ // If this boundary did update, we cannot cancel its children so those are dropped.
1115
+ popViewTransitionCancelableScope(prevCancelableChildren);
1116
+ }
1117
+
1118
+ if ((finishedWork.flags & AffectedParentLayout) !== NoFlags) {
1119
+ // This boundary changed size in a way that may have caused its parent to
1120
+ // relayout. We need to bubble this information up to the parent.
1121
+ viewTransitionContextChanged = true;
1122
+ } else {
1123
+ // Otherwise, we restore it to whatever the parent had found so far.
1124
+ viewTransitionContextChanged = prevContextChanged;
1125
+ }
1126
+
1127
const viewTransitionState: ViewTransitionState = finishedWork.stateNode;
1128
viewTransitionState.clones = null; // Reset
1070
- recursivelyApplyViewTransitions(finishedWork);
1129
break;
1130
+ }
1131
default: {
1132
recursivelyApplyViewTransitions(finishedWork);
1133
break;
1141
finishedWork: Fiber,
1142
): void {
1143
// First measure and apply view-transition-names to the "new" states.
1144
+ viewTransitionContextChanged = false;
1145
+ pushViewTransitionCancelableScope();
1146
+
1147
recursivelyApplyViewTransitions(finishedWork);
1148
+
1149
// Then remove the clones.
1150
const rootClone = root.gestureClone;
1151
if (rootClone !== null) {
1152
root.gestureClone = null;
1153
removeRootViewTransitionClone(root.containerInfo, rootClone);
1154
}
1155
+
1156
+ if (!viewTransitionContextChanged) {
1157
+ // If we didn't leak any resizing out to the root, we don't have to transition
1158
+ // the root itself. This means that we can now safely cancel any cancellations
1159
+ // that bubbled all the way up.
1160
+ const cancelableChildren = viewTransitionCancelableChildren;
1161
+ if (cancelableChildren !== null) {
1162
+ for (let i = 0; i < cancelableChildren.length; i += 3) {
1163
+ cancelViewTransitionName(
1164
+ ((cancelableChildren[i]: any): Instance),
1165
+ ((cancelableChildren[i + 1]: any): string),
1166
+ ((cancelableChildren[i + 2]: any): Props),
1167
+ );
1168
+ }
1169
+ }
1170
+ // We also cancel the root itself. First we restore the name to the documentElement
1171
+ // and then we cancel it.
1172
+ restoreRootViewTransitionName(root.containerInfo);
1173
+ cancelRootViewTransitionName(root.containerInfo);
1174
+ }
1175
+ popViewTransitionCancelableScope(null);
1176
}
1177
1178
function recursivelyRestoreViewTransitions(parentFiber: Fiber) {
1214
// because the fiber tag is more specific. An exception is any flag related
1215
// to reconciliation, because those can be set on all fiber types.
1216
switch (finishedWork.tag) {
1133
- case HostComponent: {
1134
- // const instance: Instance = finishedWork.stateNode;
1135
- // TODO: Restore the name.
1136
- recursivelyRestoreViewTransitions(finishedWork);
1137
- break;
1138
- }
1139
- case HostText: {
1140
- break;
1141
- }
1217
case HostPortal: {
1218
// TODO: Consider what should happen to Portals. For now we exclude them.
1219
break;
1232
break;
1233
}
1234
case ViewTransitionComponent:
1160
- const viewTransitionState: ViewTransitionState = finishedWork.stateNode;
1161
- viewTransitionState.clones = null; // Reset
1235
+ restoreUpdateViewTransitionForGesture(current, finishedWork);
1236
recursivelyRestoreViewTransitions(finishedWork);
1237
break;
1238
default: {