import type {ViewTransitionProps} from 'shared/ReactTypes';
import type {Fiber, FiberRoot} from './ReactInternalTypes';
import type {Instance, TextInstance, Props} from './ReactFiberConfig';
import type {OffscreenState} from './ReactFiberOffscreenComponent';
import type {ViewTransitionState} from './ReactFiberViewTransitionComponent';
import {
cloneMutableInstance,
cloneMutableTextInstance,
cloneRootViewTransitionContainer,
removeRootViewTransitionClone,
cancelRootViewTransitionName,
restoreRootViewTransitionName,
cancelViewTransitionName,
applyViewTransitionName,
appendChild,
commitUpdate,
commitTextUpdate,
resetTextContent,
supportsResources,
supportsSingletons,
unhideInstance,
unhideTextInstance,
} from './ReactFiberConfig';
import {
popMutationContext,
pushMutationContext,
viewTransitionMutationContext,
trackHostMutation,
} from './ReactFiberMutationTracking';
import {
MutationMask,
Update,
ContentReset,
NoFlags,
Visibility,
ViewTransitionNamedStatic,
ViewTransitionStatic,
AffectedParentLayout,
} from './ReactFiberFlags';
import {
HostComponent,
HostHoistable,
HostSingleton,
HostText,
HostPortal,
OffscreenComponent,
ViewTransitionComponent,
} from './ReactWorkTags';
import {
restoreEnterOrExitViewTransitions,
restoreNestedViewTransitions,
restoreUpdateViewTransitionForGesture,
appearingViewTransitions,
commitEnterViewTransitions,
measureNestedViewTransitions,
measureUpdateViewTransition,
viewTransitionCancelableChildren,
pushViewTransitionCancelableScope,
popViewTransitionCancelableScope,
} from './ReactFiberCommitViewTransitions';
import {
getViewTransitionName,
getViewTransitionClassName,
} from './ReactFiberViewTransitionComponent';
let didWarnForRootClone = false;
let viewTransitionContextChanged: boolean = false;
function detectMutationOrInsertClones(finishedWork: Fiber): boolean {
return true;
}
const CLONE_UPDATE = 0;
const CLONE_EXIT = 1;
const CLONE_UNHIDE = 2;
const CLONE_APPEARING_PAIR = 3;
const CLONE_UNCHANGED = 4;
const INSERT_EXIT = 5;
const INSERT_APPEND = 6;
const INSERT_APPEARING_PAIR = 7;
type VisitPhase = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;
function applyViewTransitionToClones(
name: string,
className: ?string,
clones: Array<Instance>,
): void {
for (let i = 0; i < clones.length; i++) {
applyViewTransitionName(
clones[i],
i === 0
? name
:
name + '_' + i,
className,
);
}
}
function trackDeletedPairViewTransitions(deletion: Fiber): void {
if (
appearingViewTransitions === null ||
appearingViewTransitions.size === 0
) {
return;
}
const pairs = appearingViewTransitions;
if ((deletion.subtreeFlags & ViewTransitionNamedStatic) === NoFlags) {
return;
}
let child = deletion.child;
while (child !== null) {
if (child.tag === OffscreenComponent && child.memoizedState === null) {
} else {
if (
child.tag === ViewTransitionComponent &&
(child.flags & ViewTransitionNamedStatic) !== NoFlags
) {
const props: ViewTransitionProps = child.memoizedProps;
const name = props.name;
if (name != null && name !== 'auto') {
const pair = pairs.get(name);
if (pair !== undefined) {
pairs.delete(name);
const className: ?string = getViewTransitionClassName(
props.default,
props.share,
);
if (className !== 'none') {
const oldInstance: ViewTransitionState = pair;
const newInstance: ViewTransitionState = child.stateNode;
oldInstance.paired = newInstance;
newInstance.paired = oldInstance;
const clones = oldInstance.clones;
if (clones !== null) {
applyViewTransitionToClones(name, className, clones);
}
}
if (pairs.size === 0) {
break;
}
}
}
}
trackDeletedPairViewTransitions(child);
}
child = child.sibling;
}
}
function trackEnterViewTransitions(deletion: Fiber): void {
if (deletion.tag === ViewTransitionComponent) {
const props: ViewTransitionProps = deletion.memoizedProps;
const name = getViewTransitionName(props, deletion.stateNode);
const pair =
appearingViewTransitions !== null
? appearingViewTransitions.get(name)
: undefined;
const className: ?string = getViewTransitionClassName(
props.default,
pair !== undefined ? props.share : props.enter,
);
if (className !== 'none') {
if (pair !== undefined) {
appearingViewTransitions.delete(name);
const oldInstance: ViewTransitionState = pair;
const newInstance: ViewTransitionState = deletion.stateNode;
oldInstance.paired = newInstance;
newInstance.paired = oldInstance;
const clones = oldInstance.clones;
if (clones !== null) {
applyViewTransitionToClones(name, className, clones);
}
}
}
trackDeletedPairViewTransitions(deletion);
} else if ((deletion.subtreeFlags & ViewTransitionStatic) !== NoFlags) {
let child = deletion.child;
while (child !== null) {
trackEnterViewTransitions(child);
child = child.sibling;
}
} else {
trackDeletedPairViewTransitions(deletion);
}
}
function applyAppearingPairViewTransition(child: Fiber): void {
if ((child.flags & ViewTransitionNamedStatic) !== NoFlags) {
const state: ViewTransitionState = child.stateNode;
if (state.paired) {
const props: ViewTransitionProps = child.memoizedProps;
if (props.name == null || props.name === 'auto') {
throw new Error(
'Found a pair with an auto name. This is a bug in React.',
);
}
const name = props.name;
const className: ?string = getViewTransitionClassName(
props.default,
props.share,
);
if (className !== 'none') {
const clones = state.clones;
if (clones !== null) {
applyViewTransitionToClones(name, className, clones);
}
}
}
}
}
function applyExitViewTransition(placement: Fiber): void {
const state: ViewTransitionState = placement.stateNode;
const props: ViewTransitionProps = placement.memoizedProps;
const name = getViewTransitionName(props, state);
const className: ?string = getViewTransitionClassName(
props.default,
state.paired ? props.share : props.exit,
);
if (className !== 'none') {
const clones = state.clones;
if (clones !== null) {
applyViewTransitionToClones(name, className, clones);
}
}
}
function applyNestedViewTransition(child: Fiber): void {
const state: ViewTransitionState = child.stateNode;
const props: ViewTransitionProps = child.memoizedProps;
const name = getViewTransitionName(props, state);
const className: ?string = getViewTransitionClassName(
props.default,
props.update,
);
if (className !== 'none') {
const clones = state.clones;
if (clones !== null) {
applyViewTransitionToClones(name, className, clones);
}
}
}
function applyUpdateViewTransition(current: Fiber, finishedWork: Fiber): void {
const state: ViewTransitionState = finishedWork.stateNode;
const newProps: ViewTransitionProps = current.memoizedProps;
const oldProps: ViewTransitionProps = finishedWork.memoizedProps;
const oldName = getViewTransitionName(oldProps, state);
const className: ?string = getViewTransitionClassName(
newProps.default,
newProps.update,
);
if (className === 'none') {
return;
}
const clones = state.clones;
if (clones !== null) {
applyViewTransitionToClones(oldName, className, clones);
}
}
function recursivelyInsertNew(
parentFiber: Fiber,
hostParentClone: Instance,
parentViewTransition: null | ViewTransitionState,
visitPhase: VisitPhase,
): void {
if (
visitPhase === INSERT_APPEARING_PAIR &&
parentViewTransition === null &&
(parentFiber.subtreeFlags & ViewTransitionNamedStatic) === NoFlags
) {
return;
}
let child = parentFiber.child;
while (child !== null) {
recursivelyInsertNewFiber(
child,
hostParentClone,
parentViewTransition,
visitPhase,
);
child = child.sibling;
}
}
function recursivelyInsertNewFiber(
finishedWork: Fiber,
hostParentClone: Instance,
parentViewTransition: null | ViewTransitionState,
visitPhase: VisitPhase,
): void {
switch (finishedWork.tag) {
case HostHoistable: {
if (supportsResources) {
recursivelyInsertNew(
finishedWork,
hostParentClone,
parentViewTransition,
visitPhase,
);
break;
}
}
case HostSingleton: {
if (supportsSingletons) {
recursivelyInsertNew(
finishedWork,
hostParentClone,
parentViewTransition,
visitPhase,
);
if (__DEV__) {
if (finishedWork.flags & Update) {
console.error(
'startGestureTransition() caused something to render a new <%s>. ' +
'This is not possible in the current implementation. ' +
"Make sure that the swipe doesn't mount any new <%s> elements.",
finishedWork.type,
finishedWork.type,
);
}
}
break;
}
}
case HostComponent: {
const instance: Instance = finishedWork.stateNode;
if (visitPhase !== INSERT_APPEARING_PAIR) {
appendChild(hostParentClone, instance);
trackHostMutation();
recursivelyInsertNew(
finishedWork,
instance,
null,
INSERT_APPEARING_PAIR,
);
} else {
recursivelyInsertNew(finishedWork, instance, null, visitPhase);
}
if (parentViewTransition !== null) {
if (parentViewTransition.clones === null) {
parentViewTransition.clones = [instance];
} else {
parentViewTransition.clones.push(instance);
}
}
break;
}
case HostText: {
const textInstance: TextInstance = finishedWork.stateNode;
if (textInstance === null) {
throw new Error(
'This should have a text node initialized. This error is likely ' +
'caused by a bug in React. Please file an issue.',
);
}
if (visitPhase !== INSERT_APPEARING_PAIR) {
appendChild(hostParentClone, textInstance);
trackHostMutation();
}
break;
}
case HostPortal: {
break;
}
case OffscreenComponent: {
const newState: OffscreenState | null = finishedWork.memoizedState;
const isHidden = newState !== null;
if (!isHidden) {
recursivelyInsertNew(
finishedWork,
hostParentClone,
parentViewTransition,
visitPhase,
);
}
break;
}
case ViewTransitionComponent:
const prevMutationContext = pushMutationContext();
const viewTransitionState: ViewTransitionState = finishedWork.stateNode;
viewTransitionState.clones = null;
let nextPhase;
if (visitPhase === INSERT_EXIT) {
nextPhase = INSERT_APPEND;
} else {
nextPhase = visitPhase;
}
recursivelyInsertNew(
finishedWork,
hostParentClone,
viewTransitionState,
nextPhase,
);
if (visitPhase === INSERT_EXIT) {
applyExitViewTransition(finishedWork);
} else if (
visitPhase === INSERT_APPEARING_PAIR ||
visitPhase === INSERT_APPEND
) {
applyAppearingPairViewTransition(finishedWork);
}
popMutationContext(prevMutationContext);
break;
default: {
recursivelyInsertNew(
finishedWork,
hostParentClone,
parentViewTransition,
visitPhase,
);
break;
}
}
}
function recursivelyInsertClonesFromExistingTree(
parentFiber: Fiber,
hostParentClone: Instance,
parentViewTransition: null | ViewTransitionState,
visitPhase: VisitPhase,
): void {
let child = parentFiber.child;
while (child !== null) {
switch (child.tag) {
case HostComponent: {
const instance: Instance = child.stateNode;
let nextPhase: VisitPhase;
switch (visitPhase) {
case CLONE_EXIT:
case CLONE_UNHIDE:
case CLONE_APPEARING_PAIR:
nextPhase =
(child.subtreeFlags & ViewTransitionNamedStatic) !== NoFlags
? CLONE_APPEARING_PAIR
: CLONE_UNCHANGED;
break;
default:
nextPhase = CLONE_UNCHANGED;
}
let clone: Instance;
if (nextPhase !== CLONE_UNCHANGED) {
clone = cloneMutableInstance(instance, false);
recursivelyInsertClonesFromExistingTree(
child,
clone,
null,
nextPhase,
);
} else {
clone = cloneMutableInstance(instance, true);
}
appendChild(hostParentClone, clone);
if (parentViewTransition !== null) {
if (parentViewTransition.clones === null) {
parentViewTransition.clones = [clone];
} else {
parentViewTransition.clones.push(clone);
}
}
if (visitPhase === CLONE_EXIT || visitPhase === CLONE_UNHIDE) {
unhideInstance(clone, child.memoizedProps);
trackHostMutation();
}
break;
}
case HostText: {
const textInstance: TextInstance = child.stateNode;
if (textInstance === null) {
throw new Error(
'This should have a text node initialized. This error is likely ' +
'caused by a bug in React. Please file an issue.',
);
}
const clone = cloneMutableTextInstance(textInstance);
appendChild(hostParentClone, clone);
if (visitPhase === CLONE_EXIT || visitPhase === CLONE_UNHIDE) {
unhideTextInstance(clone, child.memoizedProps);
trackHostMutation();
}
break;
}
case HostPortal: {
break;
}
case OffscreenComponent: {
const newState: OffscreenState | null = child.memoizedState;
const isHidden = newState !== null;
if (!isHidden) {
recursivelyInsertClonesFromExistingTree(
child,
hostParentClone,
parentViewTransition,
visitPhase,
);
}
break;
}
case ViewTransitionComponent:
const prevMutationContext = pushMutationContext();
const viewTransitionState: ViewTransitionState = child.stateNode;
viewTransitionState.clones = null;
child.flags &= ~Update;
let nextPhase;
if (visitPhase === CLONE_EXIT) {
nextPhase = CLONE_UNHIDE;
} else if (visitPhase === CLONE_UPDATE) {
nextPhase = CLONE_UNCHANGED;
} else {
nextPhase = visitPhase;
}
recursivelyInsertClonesFromExistingTree(
child,
hostParentClone,
viewTransitionState,
nextPhase,
);
if (visitPhase === CLONE_EXIT) {
applyExitViewTransition(child);
} else if (
visitPhase === CLONE_APPEARING_PAIR ||
visitPhase === CLONE_UNHIDE
) {
applyAppearingPairViewTransition(child);
} else if (visitPhase === CLONE_UPDATE) {
applyNestedViewTransition(child);
}
popMutationContext(prevMutationContext);
break;
default: {
recursivelyInsertClonesFromExistingTree(
child,
hostParentClone,
parentViewTransition,
visitPhase,
);
break;
}
}
child = child.sibling;
}
}
function recursivelyInsertClones(
parentFiber: Fiber,
hostParentClone: Instance,
parentViewTransition: null | ViewTransitionState,
visitPhase: VisitPhase,
) {
const deletions = parentFiber.deletions;
if (deletions !== null) {
for (let i = 0; i < deletions.length; i++) {
const childToDelete = deletions[i];
trackEnterViewTransitions(childToDelete);
trackHostMutation();
}
}
if (
parentFiber.alternate === null ||
(parentFiber.subtreeFlags & MutationMask) !== NoFlags
) {
let child = parentFiber.child;
while (child !== null) {
insertDestinationClonesOfFiber(
child,
hostParentClone,
parentViewTransition,
visitPhase,
);
child = child.sibling;
}
} else {
recursivelyInsertClonesFromExistingTree(
parentFiber,
hostParentClone,
parentViewTransition,
visitPhase,
);
}
}
function insertDestinationClonesOfFiber(
finishedWork: Fiber,
hostParentClone: Instance,
parentViewTransition: null | ViewTransitionState,
visitPhase: VisitPhase,
) {
const current = finishedWork.alternate;
if (current === null) {
recursivelyInsertNewFiber(
finishedWork,
hostParentClone,
parentViewTransition,
INSERT_EXIT,
);
return;
}
const flags = finishedWork.flags;
switch (finishedWork.tag) {
case HostHoistable: {
if (supportsResources) {
recursivelyInsertClones(
finishedWork,
hostParentClone,
parentViewTransition,
visitPhase,
);
break;
}
}
case HostSingleton: {
if (supportsSingletons) {
recursivelyInsertClones(
finishedWork,
hostParentClone,
parentViewTransition,
visitPhase,
);
if (__DEV__) {
if (flags & Update) {
const newProps = finishedWork.memoizedProps;
const oldProps = current.memoizedProps;
const instance = finishedWork.stateNode;
const type = finishedWork.type;
const prev = pushMutationContext();
try {
commitUpdate(instance, type, oldProps, newProps, finishedWork);
if (viewTransitionMutationContext) {
console.error(
'startGestureTransition() caused something to mutate <%s>. ' +
'This is not possible in the current implementation. ' +
"Make sure that the swipe doesn't update any state which " +
'causes <%s> to change.',
finishedWork.type,
finishedWork.type,
);
}
commitUpdate(instance, type, newProps, oldProps, finishedWork);
} finally {
popMutationContext(prev);
}
}
}
break;
}
}
case HostComponent: {
const instance: Instance = finishedWork.stateNode;
let clone: Instance;
if (finishedWork.child === null) {
clone = cloneMutableInstance(instance, true);
if (finishedWork.flags & ContentReset) {
resetTextContent(clone);
trackHostMutation();
}
} else {
clone = cloneMutableInstance(instance, false);
}
if (flags & Update) {
const newProps = finishedWork.memoizedProps;
const oldProps = current.memoizedProps;
const type = finishedWork.type;
commitUpdate(clone, type, oldProps, newProps, finishedWork);
}
if (visitPhase === CLONE_EXIT || visitPhase === CLONE_UNHIDE) {
appendChild(hostParentClone, clone);
unhideInstance(clone, finishedWork.memoizedProps);
recursivelyInsertClones(
finishedWork,
clone,
null,
CLONE_APPEARING_PAIR,
);
trackHostMutation();
} else {
appendChild(hostParentClone, clone);
recursivelyInsertClones(finishedWork, clone, null, visitPhase);
}
if (parentViewTransition !== null) {
if (parentViewTransition.clones === null) {
parentViewTransition.clones = [clone];
} else {
parentViewTransition.clones.push(clone);
}
}
break;
}
case HostText: {
const textInstance: TextInstance = finishedWork.stateNode;
if (textInstance === null) {
throw new Error(
'This should have a text node initialized. This error is likely ' +
'caused by a bug in React. Please file an issue.',
);
}
const clone = cloneMutableTextInstance(textInstance);
if (flags & Update) {
const newText: string = finishedWork.memoizedProps;
const oldText: string = current.memoizedProps;
commitTextUpdate(clone, newText, oldText);
trackHostMutation();
}
appendChild(hostParentClone, clone);
if (visitPhase === CLONE_EXIT || visitPhase === CLONE_UNHIDE) {
unhideTextInstance(clone, finishedWork.memoizedProps);
trackHostMutation();
}
break;
}
case HostPortal: {
break;
}
case OffscreenComponent: {
const newState: OffscreenState | null = finishedWork.memoizedState;
const isHidden = newState !== null;
if (!isHidden) {
let nextPhase;
if (visitPhase === CLONE_UPDATE && (flags & Visibility) !== NoFlags) {
nextPhase = CLONE_EXIT;
} else {
nextPhase = visitPhase;
}
recursivelyInsertClones(
finishedWork,
hostParentClone,
parentViewTransition,
nextPhase,
);
} else if (current !== null && current.memoizedState === null) {
trackEnterViewTransitions(current);
trackHostMutation();
}
break;
}
case ViewTransitionComponent:
const prevMutationContext = pushMutationContext();
const viewTransitionState: ViewTransitionState = finishedWork.stateNode;
viewTransitionState.clones = null;
let nextPhase;
if (visitPhase === CLONE_EXIT) {
nextPhase = CLONE_UNHIDE;
} else {
nextPhase = visitPhase;
}
recursivelyInsertClones(
finishedWork,
hostParentClone,
viewTransitionState,
nextPhase,
);
if (viewTransitionMutationContext) {
finishedWork.flags |= Update;
}
if (visitPhase === CLONE_EXIT) {
applyExitViewTransition(finishedWork);
} else if (
visitPhase === CLONE_APPEARING_PAIR ||
visitPhase === CLONE_UNHIDE
) {
applyAppearingPairViewTransition(finishedWork);
} else if (visitPhase === CLONE_UPDATE) {
applyUpdateViewTransition(current, finishedWork);
}
popMutationContext(prevMutationContext);
break;
default: {
recursivelyInsertClones(
finishedWork,
hostParentClone,
parentViewTransition,
visitPhase,
);
break;
}
}
}
export function insertDestinationClones(
root: FiberRoot,
finishedWork: Fiber,
): void {
const needsClone = detectMutationOrInsertClones(finishedWork);
if (needsClone) {
if (__DEV__) {
if (!didWarnForRootClone) {
didWarnForRootClone = true;
console.warn(
'startGestureTransition() caused something to mutate or relayout the root. ' +
'This currently requires a clone of the whole document. Make sure to ' +
'add a <ViewTransition> directly around an absolutely positioned DOM node ' +
'to minimize the impact of any changes caused by the Gesture Transition.',
);
}
}
const rootClone = cloneRootViewTransitionContainer(root.containerInfo);
root.gestureClone = rootClone;
recursivelyInsertClones(finishedWork, rootClone, null, CLONE_UPDATE);
} else {
root.gestureClone = null;
cancelRootViewTransitionName(root.containerInfo);
}
}
function measureExitViewTransitions(placement: Fiber): void {
if (placement.tag === ViewTransitionComponent) {
const props: ViewTransitionProps = placement.memoizedProps;
const name = props.name;
if (name != null && name !== 'auto') {
}
} else if ((placement.subtreeFlags & ViewTransitionStatic) !== NoFlags) {
let child = placement.child;
while (child !== null) {
measureExitViewTransitions(child);
child = child.sibling;
}
} else {
}
}
function recursivelyApplyViewTransitions(parentFiber: Fiber) {
const deletions = parentFiber.deletions;
if (deletions !== null) {
for (let i = 0; i < deletions.length; i++) {
const childToDelete = deletions[i];
commitEnterViewTransitions(childToDelete, true);
}
}
if (
parentFiber.alternate === null ||
(parentFiber.subtreeFlags & MutationMask) !== NoFlags
) {
let child = parentFiber.child;
while (child !== null) {
applyViewTransitionsOnFiber(child);
child = child.sibling;
}
} else {
measureNestedViewTransitions(parentFiber, true);
}
}
function applyViewTransitionsOnFiber(finishedWork: Fiber) {
const current = finishedWork.alternate;
if (current === null) {
measureExitViewTransitions(finishedWork);
return;
}
const flags = finishedWork.flags;
switch (finishedWork.tag) {
case HostPortal: {
break;
}
case OffscreenComponent: {
if (flags & Visibility) {
const newState: OffscreenState | null = finishedWork.memoizedState;
const isHidden = newState !== null;
if (!isHidden) {
measureExitViewTransitions(finishedWork);
} else if (current !== null && current.memoizedState === null) {
commitEnterViewTransitions(current, true);
}
}
break;
}
case ViewTransitionComponent: {
const prevContextChanged = viewTransitionContextChanged;
const prevCancelableChildren = pushViewTransitionCancelableScope();
viewTransitionContextChanged = false;
recursivelyApplyViewTransitions(finishedWork);
if (viewTransitionContextChanged) {
finishedWork.flags |= Update;
}
const inViewport = measureUpdateViewTransition(
current,
finishedWork,
true,
);
if ((finishedWork.flags & Update) === NoFlags || !inViewport) {
if (prevCancelableChildren === null) {
} else {
prevCancelableChildren.push.apply(
prevCancelableChildren,
viewTransitionCancelableChildren,
);
popViewTransitionCancelableScope(prevCancelableChildren);
}
} else {
popViewTransitionCancelableScope(prevCancelableChildren);
}
if ((finishedWork.flags & AffectedParentLayout) !== NoFlags) {
viewTransitionContextChanged = true;
} else {
viewTransitionContextChanged = prevContextChanged;
}
const viewTransitionState: ViewTransitionState = finishedWork.stateNode;
viewTransitionState.clones = null;
break;
}
default: {
recursivelyApplyViewTransitions(finishedWork);
break;
}
}
}
export function applyDepartureTransitions(
root: FiberRoot,
finishedWork: Fiber,
): void {
viewTransitionContextChanged = false;
pushViewTransitionCancelableScope();
recursivelyApplyViewTransitions(finishedWork);
const rootClone = root.gestureClone;
if (rootClone !== null) {
root.gestureClone = null;
removeRootViewTransitionClone(root.containerInfo, rootClone);
}
if (!viewTransitionContextChanged) {
const cancelableChildren = viewTransitionCancelableChildren;
if (cancelableChildren !== null) {
for (let i = 0; i < cancelableChildren.length; i += 3) {
cancelViewTransitionName(
((cancelableChildren[i]: any): Instance),
((cancelableChildren[i + 1]: any): string),
((cancelableChildren[i + 2]: any): Props),
);
}
}
restoreRootViewTransitionName(root.containerInfo);
cancelRootViewTransitionName(root.containerInfo);
}
popViewTransitionCancelableScope(null);
}
function recursivelyRestoreViewTransitions(parentFiber: Fiber) {
const deletions = parentFiber.deletions;
if (deletions !== null) {
for (let i = 0; i < deletions.length; i++) {
const childToDelete = deletions[i];
restoreEnterOrExitViewTransitions(childToDelete);
}
}
if (
parentFiber.alternate === null ||
(parentFiber.subtreeFlags & MutationMask) !== NoFlags
) {
let child = parentFiber.child;
while (child !== null) {
restoreViewTransitionsOnFiber(child);
child = child.sibling;
}
} else {
restoreNestedViewTransitions(parentFiber);
}
}
function restoreViewTransitionsOnFiber(finishedWork: Fiber) {
const current = finishedWork.alternate;
if (current === null) {
restoreEnterOrExitViewTransitions(finishedWork);
return;
}
const flags = finishedWork.flags;
switch (finishedWork.tag) {
case HostPortal: {
break;
}
case OffscreenComponent: {
if (flags & Visibility) {
const newState: OffscreenState | null = finishedWork.memoizedState;
const isHidden = newState !== null;
if (!isHidden) {
restoreEnterOrExitViewTransitions(finishedWork);
} else if (current !== null && current.memoizedState === null) {
restoreEnterOrExitViewTransitions(current);
}
}
break;
}
case ViewTransitionComponent:
restoreUpdateViewTransitionForGesture(current, finishedWork);
recursivelyRestoreViewTransitions(finishedWork);
break;
default: {
recursivelyRestoreViewTransitions(finishedWork);
break;
}
}
}
export function startGestureAnimations(
root: FiberRoot,
finishedWork: Fiber,
): void {
restoreViewTransitionsOnFiber(finishedWork);
restoreRootViewTransitionName(root.containerInfo);
}