Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions fixtures/view-transition/src/components/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ export default function Page({url, navigate}) {
<div>
<button
onClick={() => {
navigate(show ? '/?a' : '/?b');
navigate(url === '/?b' ? '/?a' : '/?b');
}}>
{show ? 'A' : 'B'}
{url === '/?b' ? 'A' : 'B'}
</button>
<ViewTransition className="none">
<div>
Expand Down
4 changes: 4 additions & 0 deletions packages/react-art/src/ReactFiberConfigART.js
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,10 @@ export function measureInstance(instance) {
return null;
}

export function measureClonedInstance(instance) {
return null;
}

export function wasInstanceInViewport(measurement): boolean {
return true;
}
Expand Down
30 changes: 26 additions & 4 deletions packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -1477,10 +1477,12 @@ export type InstanceMeasurement = {
view: boolean, // is in viewport bounds
};

export function measureInstance(instance: Instance): InstanceMeasurement {
const ownerWindow = instance.ownerDocument.defaultView;
const rect = instance.getBoundingClientRect();
const computedStyle = getComputedStyle(instance);
function createMeasurement(
rect: ClientRect | DOMRect,
computedStyle: CSSStyleDeclaration,
element: Element,
): InstanceMeasurement {
const ownerWindow = element.ownerDocument.defaultView;
return {
rect: rect,
abs:
Expand Down Expand Up @@ -1508,6 +1510,26 @@ export function measureInstance(instance: Instance): InstanceMeasurement {
};
}

export function measureInstance(instance: Instance): InstanceMeasurement {
const rect = instance.getBoundingClientRect();
const computedStyle = getComputedStyle(instance);
return createMeasurement(rect, computedStyle, instance);
}

export function measureClonedInstance(instance: Instance): InstanceMeasurement {
const measuredRect = instance.getBoundingClientRect();
// Adjust the DOMRect based on the translate that put it outside the viewport.
// TODO: This might not be completely correct if the parent also has a transform.
const rect = new DOMRect(
measuredRect.x + 20000,
measuredRect.y + 20000,
measuredRect.width,
measuredRect.height,
);
const computedStyle = getComputedStyle(instance);
return createMeasurement(rect, computedStyle, instance);
}

export function wasInstanceInViewport(
measurement: InstanceMeasurement,
): boolean {
Expand Down
4 changes: 4 additions & 0 deletions packages/react-native-renderer/src/ReactFiberConfigNative.js
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,10 @@ export function measureInstance(instance: Instance): InstanceMeasurement {
return null;
}

export function measureClonedInstance(instance: Instance): InstanceMeasurement {
return null;
}

export function wasInstanceInViewport(
measurement: InstanceMeasurement,
): boolean {
Expand Down
4 changes: 4 additions & 0 deletions packages/react-noop-renderer/src/createReactNoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,10 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
return null;
},

measureClonedInstance(instance: Instance): InstanceMeasurement {
return null;
},

wasInstanceInViewport(measurement: InstanceMeasurement): boolean {
return true;
},
Expand Down
136 changes: 105 additions & 31 deletions packages/react-reconciler/src/ReactFiberApplyGesture.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import type {Fiber, FiberRoot} from './ReactInternalTypes';

import type {Instance, TextInstance} from './ReactFiberConfig';
import type {Instance, TextInstance, Props} from './ReactFiberConfig';

import type {OffscreenState} from './ReactFiberActivityComponent';

Expand All @@ -25,6 +25,7 @@ import {
removeRootViewTransitionClone,
cancelRootViewTransitionName,
restoreRootViewTransitionName,
cancelViewTransitionName,
applyViewTransitionName,
appendChild,
commitUpdate,
Expand All @@ -39,6 +40,7 @@ import {
popMutationContext,
pushMutationContext,
viewTransitionMutationContext,
trackHostMutation,
} from './ReactFiberMutationTracking';
import {
MutationMask,
Expand All @@ -48,6 +50,7 @@ import {
Visibility,
ViewTransitionNamedStatic,
ViewTransitionStatic,
AffectedParentLayout,
} from './ReactFiberFlags';
import {
HostComponent,
Expand All @@ -61,9 +64,14 @@ import {
import {
restoreEnterOrExitViewTransitions,
restoreNestedViewTransitions,
restoreUpdateViewTransitionForGesture,
appearingViewTransitions,
commitEnterViewTransitions,
measureNestedViewTransitions,
measureUpdateViewTransition,
viewTransitionCancelableChildren,
pushViewTransitionCancelableScope,
popViewTransitionCancelableScope,
} from './ReactFiberCommitViewTransitions';
import {
getViewTransitionName,
Expand All @@ -72,6 +80,10 @@ import {

let didWarnForRootClone = false;

// Used during the apply phase to track whether a parent ViewTransition component
// might have been affected by any mutations / relayouts below.
let viewTransitionContextChanged: boolean = false;

function detectMutationOrInsertClones(finishedWork: Fiber): boolean {
return true;
}
Expand Down Expand Up @@ -421,6 +433,7 @@ function recursivelyInsertNewFiber(
// For insertions we don't need to clone. It's already new state node.
if (visitPhase !== INSERT_APPEARING_PAIR) {
appendChild(hostParentClone, instance);
trackHostMutation();
recursivelyInsertNew(
finishedWork,
instance,
Expand Down Expand Up @@ -450,6 +463,7 @@ function recursivelyInsertNewFiber(
// For insertions we don't need to clone. It's already new state node.
if (visitPhase !== INSERT_APPEARING_PAIR) {
appendChild(hostParentClone, textInstance);
trackHostMutation();
}
break;
}
Expand Down Expand Up @@ -575,6 +589,7 @@ function recursivelyInsertClonesFromExistingTree(
}
if (visitPhase === CLONE_EXIT || visitPhase === CLONE_UNHIDE) {
unhideInstance(clone, child.memoizedProps);
trackHostMutation();
}
break;
}
Expand All @@ -590,6 +605,7 @@ function recursivelyInsertClonesFromExistingTree(
appendChild(hostParentClone, clone);
if (visitPhase === CLONE_EXIT || visitPhase === CLONE_UNHIDE) {
unhideTextInstance(clone, child.memoizedProps);
trackHostMutation();
}
break;
}
Expand Down Expand Up @@ -679,6 +695,10 @@ function recursivelyInsertClones(
for (let i = 0; i < deletions.length; i++) {
const childToDelete = deletions[i];
trackEnterViewTransitions(childToDelete);
// Normally we would only mark something as triggering a mutation if there was
// actually a HostInstance below here. If this tree didn't contain a HostInstances
// we shouldn't trigger a mutation even though a virtual component was deleted.
trackHostMutation();
}
}

Expand Down Expand Up @@ -801,6 +821,7 @@ function insertDestinationClonesOfFiber(
clone = cloneMutableInstance(instance, true);
if (finishedWork.flags & ContentReset) {
resetTextContent(clone);
trackHostMutation();
}
} else {
// If we have children we'll clone them as we walk the tree so we just
Expand All @@ -825,6 +846,7 @@ function insertDestinationClonesOfFiber(
);
appendChild(hostParentClone, clone);
unhideInstance(clone, finishedWork.memoizedProps);
trackHostMutation();
} else {
recursivelyInsertClones(finishedWork, clone, null, visitPhase);
appendChild(hostParentClone, clone);
Expand All @@ -851,10 +873,12 @@ function insertDestinationClonesOfFiber(
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;
}
Expand Down Expand Up @@ -885,6 +909,10 @@ function insertDestinationClonesOfFiber(
} else if (current !== null && current.memoizedState === null) {
// Was previously mounted as visible but is now hidden.
trackEnterViewTransitions(current);
// Normally we would only mark something as triggering a mutation if there was
// actually a HostInstance below here. If this tree didn't contain a HostInstances
// we shouldn't trigger a mutation even though a virtual component was hidden.
trackHostMutation();
}
break;
}
Expand Down Expand Up @@ -991,13 +1019,6 @@ function measureExitViewTransitions(placement: Fiber): void {
}
}

function measureUpdateViewTransition(
current: Fiber,
finishedWork: Fiber,
): void {
// TODO
}

function recursivelyApplyViewTransitions(parentFiber: Fiber) {
const deletions = parentFiber.deletions;
if (deletions !== null) {
Expand Down Expand Up @@ -1037,15 +1058,6 @@ function applyViewTransitionsOnFiber(finishedWork: Fiber) {
// because the fiber tag is more specific. An exception is any flag related
// to reconciliation, because those can be set on all fiber types.
switch (finishedWork.tag) {
case HostComponent: {
// const instance: Instance = finishedWork.stateNode;
// TODO: Apply name and measure.
recursivelyApplyViewTransitions(finishedWork);
break;
}
case HostText: {
break;
}
case HostPortal: {
// TODO: Consider what should happen to Portals. For now we exclude them.
break;
Expand All @@ -1063,12 +1075,59 @@ function applyViewTransitionsOnFiber(finishedWork: Fiber) {
}
break;
}
case ViewTransitionComponent:
measureUpdateViewTransition(current, finishedWork);
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 this boundary didn't update, then we may be able to cancel its children.
// We bubble them up to the parent set to be determined later if we can cancel.
// Similarly, if old and new state was outside the viewport, we can skip it
// even if it did update.
if (prevCancelableChildren === null) {
// Bubbling up this whole set to the parent.
} else {
// Merge with parent set.
// $FlowFixMe[method-unbinding]
prevCancelableChildren.push.apply(
prevCancelableChildren,
viewTransitionCancelableChildren,
);
popViewTransitionCancelableScope(prevCancelableChildren);
}
// TODO: If this doesn't end up canceled, because a parent animates,
// then we should probably issue an event since this instance is part of it.
} else {
// TODO: Schedule gesture events.
// If this boundary did update, we cannot cancel its children so those are dropped.
popViewTransitionCancelableScope(prevCancelableChildren);
}

if ((finishedWork.flags & AffectedParentLayout) !== NoFlags) {
// This boundary changed size in a way that may have caused its parent to
// relayout. We need to bubble this information up to the parent.
viewTransitionContextChanged = true;
} else {
// Otherwise, we restore it to whatever the parent had found so far.
viewTransitionContextChanged = prevContextChanged;
}

const viewTransitionState: ViewTransitionState = finishedWork.stateNode;
viewTransitionState.clones = null; // Reset
recursivelyApplyViewTransitions(finishedWork);
break;
}
default: {
recursivelyApplyViewTransitions(finishedWork);
break;
Expand All @@ -1082,13 +1141,38 @@ export function applyDepartureTransitions(
finishedWork: Fiber,
): void {
// First measure and apply view-transition-names to the "new" states.
viewTransitionContextChanged = false;
pushViewTransitionCancelableScope();

recursivelyApplyViewTransitions(finishedWork);

// Then remove the clones.
const rootClone = root.gestureClone;
if (rootClone !== null) {
root.gestureClone = null;
removeRootViewTransitionClone(root.containerInfo, rootClone);
}

if (!viewTransitionContextChanged) {
// If we didn't leak any resizing out to the root, we don't have to transition
// the root itself. This means that we can now safely cancel any cancellations
// that bubbled all the way up.
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),
);
}
}
// We also cancel the root itself. First we restore the name to the documentElement
// and then we cancel it.
restoreRootViewTransitionName(root.containerInfo);
cancelRootViewTransitionName(root.containerInfo);
}
popViewTransitionCancelableScope(null);
}

function recursivelyRestoreViewTransitions(parentFiber: Fiber) {
Expand Down Expand Up @@ -1130,15 +1214,6 @@ function restoreViewTransitionsOnFiber(finishedWork: Fiber) {
// because the fiber tag is more specific. An exception is any flag related
// to reconciliation, because those can be set on all fiber types.
switch (finishedWork.tag) {
case HostComponent: {
// const instance: Instance = finishedWork.stateNode;
// TODO: Restore the name.
recursivelyRestoreViewTransitions(finishedWork);
break;
}
case HostText: {
break;
}
case HostPortal: {
// TODO: Consider what should happen to Portals. For now we exclude them.
break;
Expand All @@ -1157,8 +1232,7 @@ function restoreViewTransitionsOnFiber(finishedWork: Fiber) {
break;
}
case ViewTransitionComponent:
const viewTransitionState: ViewTransitionState = finishedWork.stateNode;
viewTransitionState.clones = null; // Reset
restoreUpdateViewTransitionForGesture(current, finishedWork);
recursivelyRestoreViewTransitions(finishedWork);
break;
default: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ export function commitShowHideHostTextInstance(node: Fiber, isHidden: boolean) {
unhideTextInstance(instance, node.memoizedProps);
}
}
trackHostMutation();
} catch (error) {
captureCommitPhaseError(node, node.return, error);
}
Expand Down
Loading
Loading