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