main
js 268 lines 6.42 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10 import type {TransitionTypes} from 'react/src/ReactTransitionType';
11 import type {
12 Instance,
13 Props,
14 Container,
15 SuspendedState,
16 GestureTimeline,
17 } from './ReactFiberConfigFabric';
18
19 import {allocateTag} from './ReactFiberConfigFabric';
20
21 const {
22 applyViewTransitionName: fabricApplyViewTransitionName,
23 createViewTransitionInstance: fabricCreateViewTransitionInstance,
24 startViewTransition: fabricStartViewTransition,
25 startViewTransitionReadyFinished: fabricStartViewTransitionReadyFinished,
26 } = nativeFabricUIManager;
27
28 export type InstanceMeasurement = {
29 rect: {x: number, y: number, width: number, height: number},
30 abs: boolean,
31 clip: boolean,
32 view: boolean,
33 };
34
35 export type RunningViewTransition = {
36 finished: Promise<void>,
37 ready: Promise<void>,
38 ...
39 };
40
41 interface ViewTransitionPseudoElementType extends mixin$Animatable {
42 _pseudo: string;
43 _name: string;
44 }
45
46 function ViewTransitionPseudoElement(
47 this: ViewTransitionPseudoElementType,
48 pseudo: string,
49 name: string,
50 ) {
51 // TODO: Get the owner document from the root container.
52 this._pseudo = pseudo;
53 this._name = name;
54 }
55
56 export type ViewTransitionInstance = null | {
57 name: string,
58 old: mixin$Animatable,
59 new: mixin$Animatable,
60 ...
61 };
62
63 export function restoreViewTransitionName(
64 instance: Instance,
65 props: Props,
66 ): void {
67 if (__DEV__) {
68 console.warn('restoreViewTransitionName is not implemented');
69 }
70 }
71
72 // Cancel the old and new snapshots of viewTransitionName
73 export function cancelViewTransitionName(
74 instance: Instance,
75 oldName: string,
76 props: Props,
77 ): void {
78 if (__DEV__) {
79 console.warn('cancelViewTransitionName is not implemented');
80 }
81 }
82
83 export function cancelRootViewTransitionName(rootContainer: Container): void {
84 // No-op
85 }
86
87 export function restoreRootViewTransitionName(rootContainer: Container): void {
88 // No-op
89 }
90
91 export function cloneRootViewTransitionContainer(
92 rootContainer: Container,
93 ): Instance {
94 if (__DEV__) {
95 console.warn('cloneRootViewTransitionContainer is not implemented');
96 }
97 // $FlowFixMe[incompatible-type] Return empty stub
98 return null;
99 }
100
101 export function removeRootViewTransitionClone(
102 rootContainer: Container,
103 clone: Instance,
104 ): void {
105 if (__DEV__) {
106 console.warn('removeRootViewTransitionClone is not implemented');
107 }
108 }
109
110 export function measureInstance(instance: Instance): InstanceMeasurement {
111 if (__DEV__) {
112 console.warn('measureInstance is not implemented');
113 }
114 return {
115 rect: {
116 x: 0,
117 y: 0,
118 width: 0,
119 height: 0,
120 },
121 abs: false,
122 clip: false,
123 // TODO: properly calculate whether instance is in viewport
124 view: true,
125 };
126 }
127
128 export function measureClonedInstance(instance: Instance): InstanceMeasurement {
129 if (__DEV__) {
130 console.warn('measureClonedInstance is not implemented');
131 }
132 return {
133 rect: {x: 0, y: 0, width: 0, height: 0},
134 abs: false,
135 clip: false,
136 view: true,
137 };
138 }
139
140 export function wasInstanceInViewport(
141 measurement: InstanceMeasurement,
142 ): boolean {
143 return measurement.view;
144 }
145
146 export function hasInstanceChanged(
147 oldMeasurement: InstanceMeasurement,
148 newMeasurement: InstanceMeasurement,
149 ): boolean {
150 if (__DEV__) {
151 console.warn('hasInstanceChanged is not implemented');
152 }
153 return false;
154 }
155
156 export function hasInstanceAffectedParent(
157 oldMeasurement: InstanceMeasurement,
158 newMeasurement: InstanceMeasurement,
159 ): boolean {
160 if (__DEV__) {
161 console.warn('hasInstanceAffectedParent is not implemented');
162 }
163 return false;
164 }
165
166 export function startGestureTransition(
167 suspendedState: null | SuspendedState,
168 rootContainer: Container,
169 timeline: GestureTimeline,
170 rangeStart: number,
171 rangeEnd: number,
172 transitionTypes: null | TransitionTypes,
173 mutationCallback: () => void,
174 animateCallback: () => void,
175 errorCallback: (error: mixed) => void,
176 finishedAnimation: () => void,
177 ): RunningViewTransition {
178 if (__DEV__) {
179 console.warn('startGestureTransition is not implemented');
180 }
181 return {
182 finished: Promise.resolve(),
183 ready: Promise.resolve(),
184 };
185 }
186
187 export function stopViewTransition(transition: RunningViewTransition): void {
188 if (__DEV__) {
189 console.warn('stopViewTransition is not implemented');
190 }
191 }
192
193 export function addViewTransitionFinishedListener(
194 transition: RunningViewTransition,
195 callback: () => void,
196 ): void {
197 transition.finished.finally(callback);
198 }
199
200 export function createViewTransitionInstance(
201 name: string,
202 ): ViewTransitionInstance {
203 const tag = allocateTag();
204 fabricCreateViewTransitionInstance(name, tag);
205 return {
206 name,
207 old: new (ViewTransitionPseudoElement as any)('old', name),
208 new: new (ViewTransitionPseudoElement as any)('new', name),
209 };
210 }
211
212 export function applyViewTransitionName(
213 instance: Instance,
214 name: string,
215 className: ?string,
216 ): void {
217 // add view-transition-name to things that might animate for browser
218 fabricApplyViewTransitionName(instance.node, name, className);
219 }
220
221 export function startViewTransition(
222 suspendedState: null | SuspendedState,
223 rootContainer: Container,
224 transitionTypes: null | TransitionTypes,
225 mutationCallback: () => void,
226 layoutCallback: () => void,
227 afterMutationCallback: () => void,
228 spawnedWorkCallback: () => void,
229 passiveCallback: () => mixed,
230 errorCallback: (error: mixed) => void,
231 blockedCallback: (name: string) => void,
232 finishedAnimation: () => void,
233 ): null | RunningViewTransition {
234 const transition = fabricStartViewTransition(
235 // mutation
236 () => {
237 mutationCallback(); // completeRoot should run here
238 layoutCallback();
239 afterMutationCallback();
240 },
241 );
242
243 if (transition == null) {
244 if (__DEV__) {
245 console.warn(
246 "startViewTransition didn't kick off transition in Fabric, the ViewTransition ReactNativeFeatureFlag might not be enabled.",
247 );
248 }
249 // Flush remaining work synchronously.
250 mutationCallback();
251 layoutCallback();
252 // Skip afterMutationCallback(). We don't need it since we're not animating.
253 spawnedWorkCallback();
254 // Skip passiveCallback(). Spawned work will schedule a task.
255 return null;
256 }
257
258 transition.ready.then(() => {
259 spawnedWorkCallback();
260 fabricStartViewTransitionReadyFinished();
261 });
262
263 transition.finished.finally(() => {
264 passiveCallback();
265 });
266
267 return transition;
268 }