diff --git a/.eslintrc.js b/.eslintrc.js index ccd162593f5ca..280f57221ce76 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -589,6 +589,11 @@ module.exports = { WheelEventHandler: 'readonly', FinalizationRegistry: 'readonly', Omit: 'readonly', + Keyframe: 'readonly', + PropertyIndexedKeyframes: 'readonly', + KeyframeAnimationOptions: 'readonly', + GetAnimationsOptions: 'readonly', + Animatable: 'readonly', spyOnDev: 'readonly', spyOnDevAndProd: 'readonly', diff --git a/fixtures/view-transition/src/components/Page.js b/fixtures/view-transition/src/components/Page.js index 4e2e806055615..8a3638b01f4b3 100644 --- a/fixtures/view-transition/src/components/Page.js +++ b/fixtures/view-transition/src/components/Page.js @@ -1,6 +1,8 @@ import React, { unstable_ViewTransition as ViewTransition, unstable_Activity as Activity, + useRef, + useLayoutEffect, } from 'react'; import './Page.css'; @@ -35,7 +37,19 @@ function Component() { } export default function Page({url, navigate}) { + const ref = useRef(); const show = url === '/?b'; + useLayoutEffect(() => { + const viewTransition = ref.current; + requestAnimationFrame(() => { + const keyframes = [ + {rotate: '0deg', transformOrigin: '30px 8px'}, + {rotate: '360deg', transformOrigin: '30px 8px'}, + ]; + viewTransition.old.animate(keyframes, 300); + viewTransition.new.animate(keyframes, 300); + }); + }, [show]); const exclamation = ( ! @@ -62,7 +76,7 @@ export default function Page({url, navigate}) { {a} )} - + {show ?
hello{exclamation}
:
Loading
}

scroll me

diff --git a/packages/react-art/src/ReactFiberConfigART.js b/packages/react-art/src/ReactFiberConfigART.js index 7ad2fe0e2bb38..95acc9e541e99 100644 --- a/packages/react-art/src/ReactFiberConfigART.js +++ b/packages/react-art/src/ReactFiberConfigART.js @@ -500,6 +500,14 @@ export function startViewTransition() { return false; } +export type ViewTransitionInstance = null | {name: string, ...}; + +export function createViewTransitionInstance( + name: string, +): ViewTransitionInstance { + return null; +} + export function clearContainer(container) { // TODO Implement this } diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 9bb20a56df67e..af93760ccfcc9 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -187,6 +187,14 @@ export type RendererInspectionConfig = $ReadOnly<{}>; export type TransitionStatus = FormStatus; +export type ViewTransitionInstance = { + name: string, + group: Animatable, + imagePair: Animatable, + old: Animatable, + new: Animatable, +}; + type SelectionInformation = { focusedElem: null | HTMLElement, selectionRange: mixed, @@ -1323,6 +1331,75 @@ export function startViewTransition( } } +interface ViewTransitionPseudoElementType extends Animatable { + _scope: HTMLElement; + _selector: string; +} + +function ViewTransitionPseudoElement( + this: ViewTransitionPseudoElementType, + pseudo: string, + name: string, +) { + // TODO: Get the owner document from the root container. + this._scope = (document.documentElement: any); + this._selector = '::view-transition-' + pseudo + '(' + name + ')'; +} +// $FlowFixMe[prop-missing] +ViewTransitionPseudoElement.prototype.animate = function ( + this: ViewTransitionPseudoElementType, + keyframes: Keyframe[] | PropertyIndexedKeyframes | null, + options?: number | KeyframeAnimationOptions, +): Animation { + const opts: any = + typeof options === 'number' + ? { + duration: options, + } + : Object.assign(({}: KeyframeAnimationOptions), options); + opts.pseudoElement = this._selector; + // TODO: Handle multiple child instances. + return this._scope.animate(keyframes, opts); +}; +// $FlowFixMe[prop-missing] +ViewTransitionPseudoElement.prototype.getAnimations = function ( + this: ViewTransitionPseudoElementType, + options?: GetAnimationsOptions, +): Animation[] { + const scope = this._scope; + const selector = this._selector; + const animations = scope.getAnimations({subtree: true}); + const result = []; + for (let i = 0; i < animations.length; i++) { + const effect: null | { + target?: Element, + pseudoElement?: string, + ... + } = (animations[i].effect: any); + // TODO: Handle multiple child instances. + if ( + effect !== null && + effect.target === scope && + effect.pseudoElement === selector + ) { + result.push(animations[i]); + } + } + return result; +}; + +export function createViewTransitionInstance( + name: string, +): ViewTransitionInstance { + return { + name: name, + group: new (ViewTransitionPseudoElement: any)('group', name), + imagePair: new (ViewTransitionPseudoElement: any)('image-pair', name), + old: new (ViewTransitionPseudoElement: any)('old', name), + new: new (ViewTransitionPseudoElement: any)('new', name), + }; +} + export function clearContainer(container: Container): void { const nodeType = container.nodeType; if (nodeType === DOCUMENT_NODE) { diff --git a/packages/react-native-renderer/src/ReactFiberConfigNative.js b/packages/react-native-renderer/src/ReactFiberConfigNative.js index 55699f0973b76..9bbe8b962a65a 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigNative.js +++ b/packages/react-native-renderer/src/ReactFiberConfigNative.js @@ -591,6 +591,14 @@ export function startViewTransition( return false; } +export type ViewTransitionInstance = null | {name: string, ...}; + +export function createViewTransitionInstance( + name: string, +): ViewTransitionInstance { + return null; +} + export function clearContainer(container: Container): void { // TODO Implement this for React Native // UIManager does not expose a "remove all" type method. diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index 6536821c1e773..b1b4a3c9405fb 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -92,6 +92,8 @@ export type TransitionStatus = mixed; export type FormInstance = Instance; +export type ViewTransitionInstance = null | {name: string, ...}; + const NO_CONTEXT = {}; const UPPERCASE_CONTEXT = {}; if (__DEV__) { @@ -786,6 +788,10 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { return false; }, + createViewTransitionInstance(name: string): ViewTransitionInstance { + return null; + }, + resetTextContent(instance: Instance): void { instance.text = null; }, diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js index 0a8b88829d24c..1ebe08b76963e 100644 --- a/packages/react-reconciler/src/ReactFiber.js +++ b/packages/react-reconciler/src/ReactFiber.js @@ -21,7 +21,7 @@ import type { } from './ReactFiberActivityComponent'; import type { ViewTransitionProps, - ViewTransitionInstance, + ViewTransitionState, } from './ReactFiberViewTransitionComponent'; import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent'; @@ -884,9 +884,10 @@ export function createFiberFromViewTransition( const fiber = createFiber(ViewTransitionComponent, pendingProps, key, mode); fiber.elementType = REACT_VIEW_TRANSITION_TYPE; fiber.lanes = lanes; - const instance: ViewTransitionInstance = { + const instance: ViewTransitionState = { autoName: null, paired: null, + ref: null, }; fiber.stateNode = instance; return fiber; diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index 970be0b3ac0e8..a7bb6e6df5acd 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -30,7 +30,7 @@ import type { } from './ReactFiberActivityComponent'; import type { ViewTransitionProps, - ViewTransitionInstance, + ViewTransitionState, } from './ReactFiberViewTransitionComponent'; import {assignViewTransitionAutoName} from './ReactFiberViewTransitionComponent'; import {OffscreenDetached} from './ReactFiberActivityComponent'; @@ -3246,7 +3246,7 @@ function updateViewTransition( renderLanes: Lanes, ) { const pendingProps: ViewTransitionProps = workInProgress.pendingProps; - const instance: ViewTransitionInstance = workInProgress.stateNode; + const instance: ViewTransitionState = workInProgress.stateNode; if (pendingProps.name != null && pendingProps.name !== 'auto') { // Explicitly named boundary. We track it so that we can pair it up with another explicit // boundary if we get deleted. @@ -3264,6 +3264,12 @@ function updateViewTransition( // counter in the commit phase instead. assignViewTransitionAutoName(pendingProps, instance); } + if (current !== null && current.memoizedProps.name !== pendingProps.name) { + // If the name changes, we schedule a ref effect to create a new ref instance. + workInProgress.flags |= Ref | RefStatic; + } else { + markRef(current, workInProgress); + } const nextChildren = pendingProps.children; reconcileChildren(current, workInProgress, nextChildren, renderLanes); return workInProgress.child; diff --git a/packages/react-reconciler/src/ReactFiberCommitEffects.js b/packages/react-reconciler/src/ReactFiberCommitEffects.js index 0847f2603439a..6873bdf9e7a63 100644 --- a/packages/react-reconciler/src/ReactFiberCommitEffects.js +++ b/packages/react-reconciler/src/ReactFiberCommitEffects.js @@ -11,21 +11,26 @@ import type {Fiber} from './ReactInternalTypes'; import type {UpdateQueue} from './ReactFiberClassUpdateQueue'; import type {FunctionComponentUpdateQueue} from './ReactFiberHooks'; import type {HookFlags} from './ReactHookEffectTags'; +import { + getViewTransitionName, + type ViewTransitionState, + type ViewTransitionProps, +} from './ReactFiberViewTransitionComponent'; import { enableProfilerTimer, enableProfilerCommitHooks, enableProfilerNestedUpdatePhase, enableSchedulingProfiler, - enableScopeAPI, enableUseResourceEffectHook, + enableViewTransition, } from 'shared/ReactFeatureFlags'; import { ClassComponent, HostComponent, HostHoistable, HostSingleton, - ScopeComponent, + ViewTransitionComponent, } from './ReactWorkTags'; import {NoFlags} from './ReactFiberFlags'; import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber'; @@ -40,7 +45,10 @@ import { commitCallbacks, commitHiddenCallbacks, } from './ReactFiberClassUpdateQueue'; -import {getPublicInstance} from './ReactFiberConfig'; +import { + getPublicInstance, + createViewTransitionInstance, +} from './ReactFiberConfig'; import { captureCommitPhaseError, setIsRunningInsertionEffect, @@ -865,20 +873,27 @@ export function safelyCallComponentWillUnmount( function commitAttachRef(finishedWork: Fiber) { const ref = finishedWork.ref; if (ref !== null) { - const instance = finishedWork.stateNode; let instanceToUse; switch (finishedWork.tag) { case HostHoistable: case HostSingleton: case HostComponent: - instanceToUse = getPublicInstance(instance); + instanceToUse = getPublicInstance(finishedWork.stateNode); break; + case ViewTransitionComponent: + if (enableViewTransition) { + const instance: ViewTransitionState = finishedWork.stateNode; + const props: ViewTransitionProps = finishedWork.memoizedProps; + const name = getViewTransitionName(props, instance); + if (instance.ref === null || instance.ref.name !== name) { + instance.ref = createViewTransitionInstance(name); + } + instanceToUse = instance.ref; + break; + } + // Fallthrough default: - instanceToUse = instance; - } - // Moved outside to ensure DCE works with this flag - if (enableScopeAPI && finishedWork.tag === ScopeComponent) { - instanceToUse = instance; + instanceToUse = finishedWork.stateNode; } if (typeof ref === 'function') { if (shouldProfile(finishedWork)) { diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index 73877c52738d6..8b4baead09b89 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -43,7 +43,7 @@ import type { } from './ReactFiberTracingMarkerComponent'; import type { ViewTransitionProps, - ViewTransitionInstance, + ViewTransitionState, } from './ReactFiberViewTransitionComponent'; import { @@ -283,7 +283,7 @@ export function commitBeforeMutationEffects( root: FiberRoot, firstChild: Fiber, committedLanes: Lanes, - appearingViewTransitions: Map | null, + appearingViewTransitions: Map | null, ): void { focusedInstanceHandle = prepareForCommit(root.containerInfo); shouldFireAfterActiveInstanceBlur = false; @@ -305,7 +305,7 @@ export function commitBeforeMutationEffects( function commitBeforeMutationEffects_begin( isViewTransitionEligible: boolean, - appearingViewTransitions: Map | null, + appearingViewTransitions: Map | null, ) { // If this commit is eligible for a View Transition we look into all mutated subtrees. // TODO: We could optimize this by marking these with the Snapshot subtree flag in the render phase. @@ -523,7 +523,7 @@ function commitBeforeMutationEffectsOnFiber( function commitBeforeMutationEffectsDeletion( deletion: Fiber, isViewTransitionEligible: boolean, - appearingViewTransitions: Map | null, + appearingViewTransitions: Map | null, ) { if (enableCreateEventHandleAPI) { // TODO (effects) It would be nice to avoid calling doesFiberContain() @@ -653,7 +653,7 @@ function commitAppearingPairViewTransitions(placement: Fiber): void { child.tag === ViewTransitionComponent && (child.flags & ViewTransitionNamedStatic) !== NoFlags ) { - const instance: ViewTransitionInstance = child.stateNode; + const instance: ViewTransitionState = child.stateNode; if (instance.paired) { const props: ViewTransitionProps = child.memoizedProps; if (props.name == null || props.name === 'auto') { @@ -721,7 +721,7 @@ function commitEnterViewTransitions(placement: Fiber): void { function commitDeletedPairViewTransitions( deletion: Fiber, - appearingViewTransitions: Map, + appearingViewTransitions: Map, ): void { if (appearingViewTransitions.size === 0) { // We've found all. @@ -761,8 +761,8 @@ function commitDeletedPairViewTransitions( restoreViewTransitionOnHostInstances(child.child, false); } else { // We'll transition between them. - const oldinstance: ViewTransitionInstance = child.stateNode; - const newInstance: ViewTransitionInstance = pair; + const oldinstance: ViewTransitionState = child.stateNode; + const newInstance: ViewTransitionState = pair; newInstance.paired = oldinstance; } // Delete the entry so that we know when we've found all of them @@ -782,7 +782,7 @@ function commitDeletedPairViewTransitions( function commitExitViewTransitions( deletion: Fiber, - appearingViewTransitions: Map | null, + appearingViewTransitions: Map | null, ): void { if (deletion.tag === ViewTransitionComponent) { const props: ViewTransitionProps = deletion.memoizedProps; @@ -805,8 +805,8 @@ function commitExitViewTransitions( if (pair !== undefined) { // We found a new appearing view transition with the same name as this deletion. // We'll transition between them instead of running the normal exit. - const oldinstance: ViewTransitionInstance = deletion.stateNode; - const newInstance: ViewTransitionInstance = pair; + const oldinstance: ViewTransitionState = deletion.stateNode; + const newInstance: ViewTransitionState = pair; newInstance.paired = oldinstance; // Delete the entry so that we know when we've found all of them // and can stop searching (size reaches zero). @@ -894,7 +894,7 @@ function restorePairedViewTransitions(parent: Fiber): void { child.tag === ViewTransitionComponent && (child.flags & ViewTransitionNamedStatic) !== NoFlags ) { - const instance: ViewTransitionInstance = child.stateNode; + const instance: ViewTransitionState = child.stateNode; if (instance.paired !== null) { instance.paired = null; restoreViewTransitionOnHostInstances(child.child, false); @@ -908,7 +908,7 @@ function restorePairedViewTransitions(parent: Fiber): void { function restoreEnterViewTransitions(placement: Fiber): void { if (placement.tag === ViewTransitionComponent) { - const instance: ViewTransitionInstance = placement.stateNode; + const instance: ViewTransitionState = placement.stateNode; instance.paired = null; restoreViewTransitionOnHostInstances(placement.child, false); restorePairedViewTransitions(placement); @@ -925,7 +925,7 @@ function restoreEnterViewTransitions(placement: Fiber): void { function restoreExitViewTransitions(deletion: Fiber): void { if (deletion.tag === ViewTransitionComponent) { - const instance: ViewTransitionInstance = deletion.stateNode; + const instance: ViewTransitionState = deletion.stateNode; instance.paired = null; restoreViewTransitionOnHostInstances(deletion.child, false); restorePairedViewTransitions(deletion); @@ -1345,6 +1345,20 @@ function commitLayoutEffectOnFiber( } break; } + case ViewTransitionComponent: { + if (enableViewTransition) { + recursivelyTraverseLayoutEffects( + finishedRoot, + finishedWork, + committedLanes, + ); + if (flags & Ref) { + safelyAttachRef(finishedWork, finishedWork.return); + } + break; + } + // Fallthrough + } default: { recursivelyTraverseLayoutEffects( finishedRoot, @@ -2830,6 +2844,11 @@ function commitMutationEffectsOnFiber( } case ViewTransitionComponent: if (enableViewTransition) { + if (flags & Ref) { + if (!offscreenSubtreeWasHidden && current !== null) { + safelyDetachRef(current, current.return); + } + } const prevMutationContext = pushMutationContext(); recursivelyTraverseMutationEffects(root, finishedWork, lanes); commitReconciliationEffects(finishedWork, lanes); @@ -3194,6 +3213,12 @@ export function disappearLayoutEffects(finishedWork: Fiber) { } break; } + case ViewTransitionComponent: { + if (enableViewTransition) { + safelyDetachRef(finishedWork, finishedWork.return); + } + // Fallthrough + } default: { recursivelyTraverseDisappearLayoutEffects(finishedWork); break; @@ -3368,6 +3393,18 @@ export function reappearLayoutEffects( safelyAttachRef(finishedWork, finishedWork.return); break; } + case ViewTransitionComponent: { + if (enableViewTransition) { + recursivelyTraverseReappearLayoutEffects( + finishedRoot, + finishedWork, + includeWorkInProgressEffects, + ); + safelyAttachRef(finishedWork, finishedWork.return); + break; + } + // Fallthrough + } default: { recursivelyTraverseReappearLayoutEffects( finishedRoot, diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.js b/packages/react-reconciler/src/ReactFiberCompleteWork.js index 2066a110a1357..ca6462d7e7d5a 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.js @@ -30,7 +30,7 @@ import type { } from './ReactFiberActivityComponent'; import type { ViewTransitionProps, - ViewTransitionInstance, + ViewTransitionState, } from './ReactFiberViewTransitionComponent'; import {isOffscreenManual} from './ReactFiberActivityComponent'; import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent'; @@ -965,7 +965,7 @@ function trackReappearingViewTransitions(workInProgress: Fiber): void { ) { const props: ViewTransitionProps = child.memoizedProps; if (props.name != null && props.name !== 'auto') { - const instance: ViewTransitionInstance = child.stateNode; + const instance: ViewTransitionState = child.stateNode; trackAppearingViewTransition(instance, props.name); } } diff --git a/packages/react-reconciler/src/ReactFiberConfigWithNoMutation.js b/packages/react-reconciler/src/ReactFiberConfigWithNoMutation.js index 57a7ea793a9c5..5e54757807cd8 100644 --- a/packages/react-reconciler/src/ReactFiberConfigWithNoMutation.js +++ b/packages/react-reconciler/src/ReactFiberConfigWithNoMutation.js @@ -46,3 +46,5 @@ export const wasInstanceInViewport = shim; export const hasInstanceChanged = shim; export const hasInstanceAffectedParent = shim; export const startViewTransition = shim; +export type ViewTransitionInstance = null | {name: string, ...}; +export const createViewTransitionInstance = shim; diff --git a/packages/react-reconciler/src/ReactFiberViewTransitionComponent.js b/packages/react-reconciler/src/ReactFiberViewTransitionComponent.js index 0fd74b5bb6c99..008a6992f275d 100644 --- a/packages/react-reconciler/src/ReactFiberViewTransitionComponent.js +++ b/packages/react-reconciler/src/ReactFiberViewTransitionComponent.js @@ -9,6 +9,7 @@ import type {ReactNodeList} from 'shared/ReactTypes'; import type {FiberRoot} from './ReactInternalTypes'; +import type {ViewTransitionInstance} from './ReactFiberConfig'; import {getWorkInProgressRoot} from './ReactFiberWorkLoop'; @@ -22,16 +23,17 @@ export type ViewTransitionProps = { children?: ReactNodeList, }; -export type ViewTransitionInstance = { +export type ViewTransitionState = { autoName: null | string, // the view-transition-name to use when an explicit one is not specified - paired: null | ViewTransitionInstance, // a temporary state during the commit phase if we have paired this with another instance + paired: null | ViewTransitionState, // a temporary state during the commit phase if we have paired this with another instance + ref: null | ViewTransitionInstance, // the current ref instance. This can change through the lifetime of the instance. }; let globalClientIdCounter: number = 0; export function assignViewTransitionAutoName( props: ViewTransitionProps, - instance: ViewTransitionInstance, + instance: ViewTransitionState, ): string { if (instance.autoName !== null) { return instance.autoName; @@ -61,7 +63,7 @@ export function assignViewTransitionAutoName( export function getViewTransitionName( props: ViewTransitionProps, - instance: ViewTransitionInstance, + instance: ViewTransitionState, ): string { if (props.name != null && props.name !== 'auto') { return props.name; diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index c79d6d541c271..f3ba210bf9704 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -23,7 +23,7 @@ import type { import type {OffscreenInstance} from './ReactFiberActivityComponent'; import type {Resource} from './ReactFiberConfig'; import type {RootState} from './ReactFiberRoot'; -import type {ViewTransitionInstance} from './ReactFiberViewTransitionComponent'; +import type {ViewTransitionState} from './ReactFiberViewTransitionComponent'; import { enableCreateEventHandleAPI, @@ -431,7 +431,7 @@ let workInProgressRootRecoverableErrors: Array> | null = // pairs in the snapshot phase. let workInProgressAppearingViewTransitions: Map< string, - ViewTransitionInstance, + ViewTransitionState, > | null = null; // Tracks when an update occurs during the render phase. @@ -1377,7 +1377,7 @@ function commitRootWhenReady( finishedWork: Fiber, recoverableErrors: Array> | null, transitions: Array | null, - appearingViewTransitions: Map | null, + appearingViewTransitions: Map | null, didIncludeRenderPhaseUpdate: boolean, lanes: Lanes, spawnedLane: Lane, @@ -2270,7 +2270,7 @@ export function renderHasNotSuspendedYet(): boolean { } export function trackAppearingViewTransition( - instance: ViewTransitionInstance, + instance: ViewTransitionState, name: string, ): void { if (workInProgressAppearingViewTransitions === null) { @@ -3197,7 +3197,7 @@ function commitRoot( lanes: Lanes, recoverableErrors: null | Array>, transitions: Array | null, - appearingViewTransitions: Map | null, + appearingViewTransitions: Map | null, didIncludeRenderPhaseUpdate: boolean, spawnedLane: Lane, updatedLanes: Lanes, diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js index 10e419f2c08ef..5d6ab3224ee51 100644 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js +++ b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js @@ -40,6 +40,7 @@ export opaque type NoTimeout = mixed; export opaque type RendererInspectionConfig = mixed; export opaque type TransitionStatus = mixed; export opaque type FormInstance = mixed; +export type ViewTransitionInstance = null | {name: string, ...}; export opaque type InstanceMeasurement = mixed; export type EventResponder = any; @@ -143,6 +144,8 @@ export const wasInstanceInViewport = $$$config.wasInstanceInViewport; export const hasInstanceChanged = $$$config.hasInstanceChanged; export const hasInstanceAffectedParent = $$$config.hasInstanceAffectedParent; export const startViewTransition = $$$config.startViewTransition; +export const createViewTransitionInstance = + $$$config.createViewTransitionInstance; export const clearContainer = $$$config.clearContainer; // ------------------- diff --git a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js index c05f59af160f0..a33590a32b2f0 100644 --- a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js +++ b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js @@ -373,6 +373,14 @@ export function startViewTransition( return false; } +export type ViewTransitionInstance = null | {name: string, ...}; + +export function createViewTransitionInstance( + name: string, +): ViewTransitionInstance { + return null; +} + export function getInstanceFromNode(mockNode: Object): Object | null { const instance = nodeToInstanceMap.get(mockNode); if (instance !== undefined) {