react-native-windows 0.77.0-preview.1 → 0.77.0-preview.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Libraries/Animated/NativeAnimatedAllowlist.js +4 -4
- package/Libraries/Animated/animations/Animation.js +1 -1
- package/Libraries/Animated/nodes/AnimatedProps.js +9 -1
- package/Libraries/Animated/nodes/AnimatedStyle.js +9 -1
- package/Libraries/Core/ReactNativeVersion.js +1 -1
- package/Libraries/Network/FormData.js +11 -3
- package/PropertySheets/Generated/PackageVersion.g.props +2 -2
- package/PropertySheets/React.Cpp.props +6 -0
- package/ReactCommon/TEMP_UntilReactCommonUpdate/react/runtime/ReactInstance.cpp +39 -35
- package/package.json +10 -10
- package/src/private/animated/useAnimatedPropsMemo.js +12 -4
- package/Libraries/Animated/NativeAnimatedAllowlist.windows.js +0 -122
- package/Libraries/Animated/nodes/AnimatedProps.windows.js +0 -281
- package/Libraries/Animated/nodes/AnimatedStyle.windows.js +0 -251
- package/src/private/animated/useAnimatedPropsMemo.windows.js +0 -356
|
@@ -106,17 +106,17 @@ export function allowTransformProp(prop: string): void {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
export function isSupportedColorStyleProp(prop: string): boolean {
|
|
109
|
-
return
|
|
109
|
+
return SUPPORTED_COLOR_STYLES.hasOwnProperty(prop);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
export function isSupportedInterpolationParam(param: string): boolean {
|
|
113
|
-
return
|
|
113
|
+
return SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(param);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
export function isSupportedStyleProp(prop: string): boolean {
|
|
117
|
-
return
|
|
117
|
+
return SUPPORTED_STYLES.hasOwnProperty(prop);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
export function isSupportedTransformProp(prop: string): boolean {
|
|
121
|
-
return
|
|
121
|
+
return SUPPORTED_TRANSFORMS.hasOwnProperty(prop);
|
|
122
122
|
}
|
|
@@ -37,7 +37,7 @@ function createAnimatedProps(
|
|
|
37
37
|
const key = keys[ii];
|
|
38
38
|
const value = inputProps[key];
|
|
39
39
|
|
|
40
|
-
if (allowlist == null ||
|
|
40
|
+
if (allowlist == null || hasOwn(allowlist, key)) {
|
|
41
41
|
let node;
|
|
42
42
|
if (key === 'style') {
|
|
43
43
|
node = AnimatedStyle.from(value, allowlist?.style);
|
|
@@ -271,3 +271,11 @@ export default class AnimatedProps extends AnimatedNode {
|
|
|
271
271
|
};
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
|
+
|
|
275
|
+
// Supported versions of JSC do not implement the newer Object.hasOwn. Remove
|
|
276
|
+
// this shim when they do.
|
|
277
|
+
// $FlowIgnore[method-unbinding]
|
|
278
|
+
const _hasOwnProp = Object.prototype.hasOwnProperty;
|
|
279
|
+
const hasOwn: (obj: $ReadOnly<{...}>, prop: string) => boolean =
|
|
280
|
+
// $FlowIgnore[method-unbinding]
|
|
281
|
+
Object.hasOwn ?? ((obj, prop) => _hasOwnProp.call(obj, prop));
|
|
@@ -35,7 +35,7 @@ function createAnimatedStyle(
|
|
|
35
35
|
const key = keys[ii];
|
|
36
36
|
const value = inputStyle[key];
|
|
37
37
|
|
|
38
|
-
if (allowlist == null ||
|
|
38
|
+
if (allowlist == null || hasOwn(allowlist, key)) {
|
|
39
39
|
let node;
|
|
40
40
|
if (value != null && key === 'transform') {
|
|
41
41
|
node = ReactNativeFeatureFlags.shouldUseAnimatedObjectForTransform()
|
|
@@ -241,3 +241,11 @@ export default class AnimatedStyle extends AnimatedWithChildren {
|
|
|
241
241
|
};
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
|
+
|
|
245
|
+
// Supported versions of JSC do not implement the newer Object.hasOwn. Remove
|
|
246
|
+
// this shim when they do.
|
|
247
|
+
// $FlowIgnore[method-unbinding]
|
|
248
|
+
const _hasOwnProp = Object.prototype.hasOwnProperty;
|
|
249
|
+
const hasOwn: (obj: $ReadOnly<{...}>, prop: string) => boolean =
|
|
250
|
+
// $FlowIgnore[method-unbinding]
|
|
251
|
+
Object.hasOwn ?? ((obj, prop) => _hasOwnProp.call(obj, prop));
|
|
@@ -28,6 +28,15 @@ type FormDataPart =
|
|
|
28
28
|
...
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Encode a FormData filename compliant with RFC 2183
|
|
33
|
+
*
|
|
34
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#directives
|
|
35
|
+
*/
|
|
36
|
+
function encodeFilename(filename: string): string {
|
|
37
|
+
return encodeURIComponent(filename.replace(/\//g, '_'));
|
|
38
|
+
}
|
|
39
|
+
|
|
31
40
|
/**
|
|
32
41
|
* Polyfill for XMLHttpRequest2 FormData API, allowing multipart POST requests
|
|
33
42
|
* with mixed data (string, native files) to be submitted via XMLHttpRequest.
|
|
@@ -82,9 +91,8 @@ class FormData {
|
|
|
82
91
|
// content type (cf. web Blob interface.)
|
|
83
92
|
if (typeof value === 'object' && !Array.isArray(value) && value) {
|
|
84
93
|
if (typeof value.name === 'string') {
|
|
85
|
-
headers['content-disposition'] +=
|
|
86
|
-
value.name
|
|
87
|
-
}"; filename*=utf-8''${encodeURI(value.name)}`;
|
|
94
|
+
headers['content-disposition'] +=
|
|
95
|
+
`; filename="${encodeFilename(value.name)}"`;
|
|
88
96
|
}
|
|
89
97
|
if (typeof value.type === 'string') {
|
|
90
98
|
headers['content-type'] = value.type;
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
-->
|
|
11
11
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
12
12
|
<PropertyGroup>
|
|
13
|
-
<ReactNativeWindowsVersion>0.77.0-preview.
|
|
13
|
+
<ReactNativeWindowsVersion>0.77.0-preview.2</ReactNativeWindowsVersion>
|
|
14
14
|
<ReactNativeWindowsMajor>0</ReactNativeWindowsMajor>
|
|
15
15
|
<ReactNativeWindowsMinor>77</ReactNativeWindowsMinor>
|
|
16
16
|
<ReactNativeWindowsPatch>0</ReactNativeWindowsPatch>
|
|
17
17
|
<ReactNativeWindowsCanary>false</ReactNativeWindowsCanary>
|
|
18
|
-
<ReactNativeWindowsCommitId>
|
|
18
|
+
<ReactNativeWindowsCommitId>e70d4987c0929e4eb4125342c53cf6566e19af00</ReactNativeWindowsCommitId>
|
|
19
19
|
</PropertyGroup>
|
|
20
20
|
</Project>
|
|
@@ -66,6 +66,12 @@
|
|
|
66
66
|
<PreprocessorDefinitions Condition="'$(UseV8)'=='true'">USE_V8;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
67
67
|
<PreprocessorDefinitions Condition="'$(UseFabric)'=='true'">USE_FABRIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
68
68
|
<PreprocessorDefinitions>JSI_VERSION=11;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
69
|
+
<!--
|
|
70
|
+
To address the crash on the first call to std::mutex::lock.
|
|
71
|
+
See: https://github.com/microsoft/STL/wiki/Changelog#vs-2022-1710
|
|
72
|
+
https://stackoverflow.com/questions/78598141/first-stdmutexlock-crashes-in-application-built-with-latest-visual-studio
|
|
73
|
+
-->
|
|
74
|
+
<PreprocessorDefinitions>_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
69
75
|
</ClCompile>
|
|
70
76
|
</ItemDefinitionGroup>
|
|
71
77
|
|
|
@@ -235,47 +235,51 @@ std::string simpleBasename(const std::string& path) {
|
|
|
235
235
|
*/
|
|
236
236
|
void ReactInstance::loadScript(
|
|
237
237
|
std::unique_ptr<const JSBigString> script,
|
|
238
|
-
const std::string& sourceURL
|
|
238
|
+
const std::string& sourceURL,
|
|
239
|
+
std::function<void(jsi::Runtime& runtime)>&& completion) {
|
|
239
240
|
auto buffer = std::make_shared<BigStringBuffer>(std::move(script));
|
|
240
241
|
std::string scriptName = simpleBasename(sourceURL);
|
|
241
242
|
|
|
242
|
-
runtimeScheduler_->scheduleWork(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
243
|
+
runtimeScheduler_->scheduleWork([this,
|
|
244
|
+
scriptName,
|
|
245
|
+
sourceURL,
|
|
246
|
+
buffer = std::move(buffer),
|
|
247
|
+
weakBufferedRuntimeExecuter =
|
|
248
|
+
std::weak_ptr<BufferedRuntimeExecutor>(
|
|
249
|
+
bufferedRuntimeExecutor_),
|
|
250
|
+
completion](jsi::Runtime& runtime) {
|
|
251
|
+
SystraceSection s("ReactInstance::loadScript");
|
|
252
|
+
bool hasLogger(ReactMarker::logTaggedMarkerBridgelessImpl);
|
|
253
|
+
if (hasLogger) {
|
|
254
|
+
ReactMarker::logTaggedMarkerBridgeless(
|
|
255
|
+
ReactMarker::RUN_JS_BUNDLE_START, scriptName.c_str());
|
|
256
|
+
}
|
|
255
257
|
|
|
256
|
-
|
|
258
|
+
runtime.evaluateJavaScript(buffer, sourceURL);
|
|
257
259
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
260
|
+
/**
|
|
261
|
+
* TODO(T183610671): We need a safe/reliable way to enable the js
|
|
262
|
+
* pipeline from javascript. Remove this after we figure that out, or
|
|
263
|
+
* after we just remove the js pipeline.
|
|
264
|
+
*/
|
|
265
|
+
if (!jsErrorHandler_->hasHandledFatalError()) {
|
|
266
|
+
jsErrorHandler_->setRuntimeReady();
|
|
267
|
+
}
|
|
266
268
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
269
|
+
if (hasLogger) {
|
|
270
|
+
ReactMarker::logTaggedMarkerBridgeless(
|
|
271
|
+
ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str());
|
|
272
|
+
ReactMarker::logMarkerBridgeless(ReactMarker::INIT_REACT_RUNTIME_STOP);
|
|
273
|
+
ReactMarker::logMarkerBridgeless(ReactMarker::APP_STARTUP_STOP);
|
|
274
|
+
}
|
|
275
|
+
if (auto strongBufferedRuntimeExecuter =
|
|
276
|
+
weakBufferedRuntimeExecuter.lock()) {
|
|
277
|
+
strongBufferedRuntimeExecuter->flush();
|
|
278
|
+
}
|
|
279
|
+
if (completion) {
|
|
280
|
+
completion(runtime);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
279
283
|
}
|
|
280
284
|
|
|
281
285
|
/*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-windows",
|
|
3
|
-
"version": "0.77.0-preview.
|
|
3
|
+
"version": "0.77.0-preview.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -28,13 +28,13 @@
|
|
|
28
28
|
"@react-native-community/cli-platform-ios": "15.0.0-alpha.2",
|
|
29
29
|
"@react-native-windows/cli": "0.77.0-preview.1",
|
|
30
30
|
"@react-native/assets": "1.0.0",
|
|
31
|
-
"@react-native/assets-registry": "0.77.0-rc.
|
|
32
|
-
"@react-native/codegen": "0.77.0-rc.
|
|
33
|
-
"@react-native/community-cli-plugin": "0.77.0-rc.
|
|
34
|
-
"@react-native/gradle-plugin": "0.77.0-rc.
|
|
35
|
-
"@react-native/js-polyfills": "0.77.0-rc.
|
|
36
|
-
"@react-native/normalize-colors": "0.77.0-rc.
|
|
37
|
-
"@react-native/virtualized-lists": "0.77.0-rc.
|
|
31
|
+
"@react-native/assets-registry": "0.77.0-rc.4",
|
|
32
|
+
"@react-native/codegen": "0.77.0-rc.4",
|
|
33
|
+
"@react-native/community-cli-plugin": "0.77.0-rc.4",
|
|
34
|
+
"@react-native/gradle-plugin": "0.77.0-rc.4",
|
|
35
|
+
"@react-native/js-polyfills": "0.77.0-rc.4",
|
|
36
|
+
"@react-native/normalize-colors": "0.77.0-rc.4",
|
|
37
|
+
"@react-native/virtualized-lists": "0.77.0-rc.4",
|
|
38
38
|
"abort-controller": "^3.0.0",
|
|
39
39
|
"anser": "^1.4.9",
|
|
40
40
|
"ansi-regex": "^5.0.0",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"just-scripts": "^1.3.3",
|
|
86
86
|
"prettier": "2.8.8",
|
|
87
87
|
"react": "18.3.1",
|
|
88
|
-
"react-native": "0.77.0-rc.
|
|
88
|
+
"react-native": "0.77.0-rc.4",
|
|
89
89
|
"react-native-platform-override": "^1.9.49",
|
|
90
90
|
"react-refresh": "^0.14.0",
|
|
91
91
|
"typescript": "5.0.4"
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"peerDependencies": {
|
|
94
94
|
"@types/react": "^18.2.6",
|
|
95
95
|
"react": "^18.2.0",
|
|
96
|
-
"react-native": "0.77.0-rc.
|
|
96
|
+
"react-native": "0.77.0-rc.4"
|
|
97
97
|
},
|
|
98
98
|
"beachball": {
|
|
99
99
|
"defaultNpmTag": "preview",
|
|
@@ -113,7 +113,7 @@ export function createCompositeKeyForProps(
|
|
|
113
113
|
const key = keys[ii];
|
|
114
114
|
const value = props[key];
|
|
115
115
|
|
|
116
|
-
if (allowlist == null ||
|
|
116
|
+
if (allowlist == null || hasOwn(allowlist, key)) {
|
|
117
117
|
let compositeKeyComponent;
|
|
118
118
|
if (key === 'style') {
|
|
119
119
|
// $FlowFixMe[incompatible-call] - `style` is a valid argument.
|
|
@@ -205,7 +205,7 @@ function createCompositeKeyForObject(
|
|
|
205
205
|
for (let ii = 0, length = keys.length; ii < length; ii++) {
|
|
206
206
|
const key = keys[ii];
|
|
207
207
|
|
|
208
|
-
if (allowlist == null ||
|
|
208
|
+
if (allowlist == null || hasOwn(allowlist, key)) {
|
|
209
209
|
const value = object[key];
|
|
210
210
|
|
|
211
211
|
let compositeKeyComponent;
|
|
@@ -250,7 +250,7 @@ export function areCompositeKeysEqual(
|
|
|
250
250
|
}
|
|
251
251
|
for (let ii = 0; ii < length; ii++) {
|
|
252
252
|
const key = keys[ii];
|
|
253
|
-
if (!
|
|
253
|
+
if (!hasOwn(next, key)) {
|
|
254
254
|
return false;
|
|
255
255
|
}
|
|
256
256
|
const prevComponent = prev[key];
|
|
@@ -336,7 +336,7 @@ function areCompositeKeyComponentsEqual(
|
|
|
336
336
|
for (let ii = 0; ii < length; ii++) {
|
|
337
337
|
const key = keys[ii];
|
|
338
338
|
if (
|
|
339
|
-
!
|
|
339
|
+
!hasOwn(nullthrows(next), key) ||
|
|
340
340
|
!areCompositeKeyComponentsEqual(prev[key], next[key])
|
|
341
341
|
) {
|
|
342
342
|
return false;
|
|
@@ -346,3 +346,11 @@ function areCompositeKeyComponentsEqual(
|
|
|
346
346
|
}
|
|
347
347
|
return false;
|
|
348
348
|
}
|
|
349
|
+
|
|
350
|
+
// Supported versions of JSC do not implement the newer Object.hasOwn. Remove
|
|
351
|
+
// this shim when they do.
|
|
352
|
+
// $FlowIgnore[method-unbinding]
|
|
353
|
+
const _hasOwnProp = Object.prototype.hasOwnProperty;
|
|
354
|
+
const hasOwn: (obj: $ReadOnly<{...}>, prop: string) => boolean =
|
|
355
|
+
// $FlowIgnore[method-unbinding]
|
|
356
|
+
Object.hasOwn ?? ((obj, prop) => _hasOwnProp.call(obj, prop));
|
|
@@ -1,122 +0,0 @@
|
|
|
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 strict-local
|
|
8
|
-
* @format
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type {AnimatedPropsAllowlist} from './nodes/AnimatedProps';
|
|
12
|
-
|
|
13
|
-
import * as ReactNativeFeatureFlags from '../../src/private/featureflags/ReactNativeFeatureFlags';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Styles allowed by the native animated implementation.
|
|
17
|
-
*
|
|
18
|
-
* In general native animated implementation should support any numeric or color property that
|
|
19
|
-
* doesn't need to be updated through the shadow view hierarchy (all non-layout properties).
|
|
20
|
-
*/
|
|
21
|
-
const SUPPORTED_COLOR_STYLES: {[string]: true} = {
|
|
22
|
-
backgroundColor: true,
|
|
23
|
-
borderBottomColor: true,
|
|
24
|
-
borderColor: true,
|
|
25
|
-
borderEndColor: true,
|
|
26
|
-
borderLeftColor: true,
|
|
27
|
-
borderRightColor: true,
|
|
28
|
-
borderStartColor: true,
|
|
29
|
-
borderTopColor: true,
|
|
30
|
-
color: true,
|
|
31
|
-
tintColor: true,
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const SUPPORTED_STYLES: {[string]: true} = {
|
|
35
|
-
...SUPPORTED_COLOR_STYLES,
|
|
36
|
-
borderBottomEndRadius: true,
|
|
37
|
-
borderBottomLeftRadius: true,
|
|
38
|
-
borderBottomRightRadius: true,
|
|
39
|
-
borderBottomStartRadius: true,
|
|
40
|
-
borderEndEndRadius: true,
|
|
41
|
-
borderEndStartRadius: true,
|
|
42
|
-
borderRadius: true,
|
|
43
|
-
borderTopEndRadius: true,
|
|
44
|
-
borderTopLeftRadius: true,
|
|
45
|
-
borderTopRightRadius: true,
|
|
46
|
-
borderTopStartRadius: true,
|
|
47
|
-
borderStartEndRadius: true,
|
|
48
|
-
borderStartStartRadius: true,
|
|
49
|
-
elevation: true,
|
|
50
|
-
opacity: true,
|
|
51
|
-
transform: true,
|
|
52
|
-
zIndex: true,
|
|
53
|
-
/* ios styles */
|
|
54
|
-
shadowOpacity: true,
|
|
55
|
-
shadowRadius: true,
|
|
56
|
-
/* legacy android transform properties */
|
|
57
|
-
scaleX: true,
|
|
58
|
-
scaleY: true,
|
|
59
|
-
translateX: true,
|
|
60
|
-
translateY: true,
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const SUPPORTED_TRANSFORMS: {[string]: true} = {
|
|
64
|
-
translateX: true,
|
|
65
|
-
translateY: true,
|
|
66
|
-
scale: true,
|
|
67
|
-
scaleX: true,
|
|
68
|
-
scaleY: true,
|
|
69
|
-
rotate: true,
|
|
70
|
-
rotateX: true,
|
|
71
|
-
rotateY: true,
|
|
72
|
-
rotateZ: true,
|
|
73
|
-
perspective: true,
|
|
74
|
-
skewX: true,
|
|
75
|
-
skewY: true,
|
|
76
|
-
...(ReactNativeFeatureFlags.shouldUseAnimatedObjectForTransform()
|
|
77
|
-
? {matrix: true}
|
|
78
|
-
: {}),
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const SUPPORTED_INTERPOLATION_PARAMS: {[string]: true} = {
|
|
82
|
-
inputRange: true,
|
|
83
|
-
outputRange: true,
|
|
84
|
-
extrapolate: true,
|
|
85
|
-
extrapolateRight: true,
|
|
86
|
-
extrapolateLeft: true,
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Default allowlist for component props that support native animated values.
|
|
91
|
-
*/
|
|
92
|
-
export default {
|
|
93
|
-
style: SUPPORTED_STYLES,
|
|
94
|
-
} as AnimatedPropsAllowlist;
|
|
95
|
-
|
|
96
|
-
export function allowInterpolationParam(param: string): void {
|
|
97
|
-
SUPPORTED_INTERPOLATION_PARAMS[param] = true;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function allowStyleProp(prop: string): void {
|
|
101
|
-
SUPPORTED_STYLES[prop] = true;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function allowTransformProp(prop: string): void {
|
|
105
|
-
SUPPORTED_TRANSFORMS[prop] = true;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export function isSupportedColorStyleProp(prop: string): boolean {
|
|
109
|
-
return SUPPORTED_COLOR_STYLES.hasOwnProperty(prop);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function isSupportedInterpolationParam(param: string): boolean {
|
|
113
|
-
return SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(param);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export function isSupportedStyleProp(prop: string): boolean {
|
|
117
|
-
return SUPPORTED_STYLES.hasOwnProperty(prop);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export function isSupportedTransformProp(prop: string): boolean {
|
|
121
|
-
return SUPPORTED_TRANSFORMS.hasOwnProperty(prop);
|
|
122
|
-
}
|
|
@@ -1,281 +0,0 @@
|
|
|
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
|
-
* @format
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type {PlatformConfig} from '../AnimatedPlatformConfig';
|
|
12
|
-
import type {AnimatedStyleAllowlist} from './AnimatedStyle';
|
|
13
|
-
|
|
14
|
-
import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
|
|
15
|
-
import {findNodeHandle} from '../../ReactNative/RendererProxy';
|
|
16
|
-
import {AnimatedEvent} from '../AnimatedEvent';
|
|
17
|
-
import AnimatedNode from './AnimatedNode';
|
|
18
|
-
import AnimatedObject from './AnimatedObject';
|
|
19
|
-
import AnimatedStyle from './AnimatedStyle';
|
|
20
|
-
import invariant from 'invariant';
|
|
21
|
-
|
|
22
|
-
export type AnimatedPropsAllowlist = $ReadOnly<{
|
|
23
|
-
style?: ?AnimatedStyleAllowlist,
|
|
24
|
-
[string]: true,
|
|
25
|
-
}>;
|
|
26
|
-
|
|
27
|
-
function createAnimatedProps(
|
|
28
|
-
inputProps: {[string]: mixed},
|
|
29
|
-
allowlist: ?AnimatedPropsAllowlist,
|
|
30
|
-
): [$ReadOnlyArray<string>, $ReadOnlyArray<AnimatedNode>, {[string]: mixed}] {
|
|
31
|
-
const nodeKeys: Array<string> = [];
|
|
32
|
-
const nodes: Array<AnimatedNode> = [];
|
|
33
|
-
const props: {[string]: mixed} = {};
|
|
34
|
-
|
|
35
|
-
const keys = Object.keys(inputProps);
|
|
36
|
-
for (let ii = 0, length = keys.length; ii < length; ii++) {
|
|
37
|
-
const key = keys[ii];
|
|
38
|
-
const value = inputProps[key];
|
|
39
|
-
|
|
40
|
-
if (allowlist == null || hasOwn(allowlist, key)) {
|
|
41
|
-
let node;
|
|
42
|
-
if (key === 'style') {
|
|
43
|
-
node = AnimatedStyle.from(value, allowlist?.style);
|
|
44
|
-
} else if (value instanceof AnimatedNode) {
|
|
45
|
-
node = value;
|
|
46
|
-
} else {
|
|
47
|
-
node = AnimatedObject.from(value);
|
|
48
|
-
}
|
|
49
|
-
if (node == null) {
|
|
50
|
-
props[key] = value;
|
|
51
|
-
} else {
|
|
52
|
-
nodeKeys.push(key);
|
|
53
|
-
nodes.push(node);
|
|
54
|
-
props[key] = node;
|
|
55
|
-
}
|
|
56
|
-
} else {
|
|
57
|
-
if (__DEV__) {
|
|
58
|
-
// WARNING: This is a potentially expensive check that we should only
|
|
59
|
-
// do in development. Without this check in development, it might be
|
|
60
|
-
// difficult to identify which props need to be allowlisted.
|
|
61
|
-
if (AnimatedObject.from(inputProps[key]) != null) {
|
|
62
|
-
console.error(
|
|
63
|
-
`AnimatedProps: ${key} is not allowlisted for animation, but it ` +
|
|
64
|
-
'contains AnimatedNode values; props allowing animation: ',
|
|
65
|
-
allowlist,
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
props[key] = value;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return [nodeKeys, nodes, props];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export default class AnimatedProps extends AnimatedNode {
|
|
77
|
-
#animatedView: any = null;
|
|
78
|
-
#callback: () => void;
|
|
79
|
-
#nodeKeys: $ReadOnlyArray<string>;
|
|
80
|
-
#nodes: $ReadOnlyArray<AnimatedNode>;
|
|
81
|
-
#props: {[string]: mixed};
|
|
82
|
-
|
|
83
|
-
constructor(
|
|
84
|
-
inputProps: {[string]: mixed},
|
|
85
|
-
callback: () => void,
|
|
86
|
-
allowlist?: ?AnimatedPropsAllowlist,
|
|
87
|
-
) {
|
|
88
|
-
super();
|
|
89
|
-
const [nodeKeys, nodes, props] = createAnimatedProps(inputProps, allowlist);
|
|
90
|
-
this.#nodeKeys = nodeKeys;
|
|
91
|
-
this.#nodes = nodes;
|
|
92
|
-
this.#props = props;
|
|
93
|
-
this.#callback = callback;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
__getValue(): Object {
|
|
97
|
-
const props: {[string]: mixed} = {};
|
|
98
|
-
|
|
99
|
-
const keys = Object.keys(this.#props);
|
|
100
|
-
for (let ii = 0, length = keys.length; ii < length; ii++) {
|
|
101
|
-
const key = keys[ii];
|
|
102
|
-
const value = this.#props[key];
|
|
103
|
-
|
|
104
|
-
if (value instanceof AnimatedNode) {
|
|
105
|
-
props[key] = value.__getValue();
|
|
106
|
-
} else if (value instanceof AnimatedEvent) {
|
|
107
|
-
props[key] = value.__getHandler();
|
|
108
|
-
} else {
|
|
109
|
-
props[key] = value;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return props;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Creates a new `props` object that contains the same props as the supplied
|
|
118
|
-
* `staticProps` object, except with animated nodes for any props that were
|
|
119
|
-
* created by this `AnimatedProps` instance.
|
|
120
|
-
*/
|
|
121
|
-
__getValueWithStaticProps(staticProps: Object): Object {
|
|
122
|
-
const props: {[string]: mixed} = {...staticProps};
|
|
123
|
-
|
|
124
|
-
const keys = Object.keys(staticProps);
|
|
125
|
-
for (let ii = 0, length = keys.length; ii < length; ii++) {
|
|
126
|
-
const key = keys[ii];
|
|
127
|
-
const maybeNode = this.#props[key];
|
|
128
|
-
|
|
129
|
-
if (key === 'style' && maybeNode instanceof AnimatedStyle) {
|
|
130
|
-
props[key] = maybeNode.__getValueWithStaticStyle(staticProps.style);
|
|
131
|
-
} else if (maybeNode instanceof AnimatedNode) {
|
|
132
|
-
props[key] = maybeNode.__getValue();
|
|
133
|
-
} else if (maybeNode instanceof AnimatedEvent) {
|
|
134
|
-
props[key] = maybeNode.__getHandler();
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return props;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
__getAnimatedValue(): Object {
|
|
142
|
-
const props: {[string]: mixed} = {};
|
|
143
|
-
|
|
144
|
-
const nodeKeys = this.#nodeKeys;
|
|
145
|
-
const nodes = this.#nodes;
|
|
146
|
-
for (let ii = 0, length = nodes.length; ii < length; ii++) {
|
|
147
|
-
const key = nodeKeys[ii];
|
|
148
|
-
const node = nodes[ii];
|
|
149
|
-
props[key] = node.__getAnimatedValue();
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return props;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
__attach(): void {
|
|
156
|
-
const nodes = this.#nodes;
|
|
157
|
-
for (let ii = 0, length = nodes.length; ii < length; ii++) {
|
|
158
|
-
const node = nodes[ii];
|
|
159
|
-
node.__addChild(this);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
__detach(): void {
|
|
164
|
-
if (this.__isNative && this.#animatedView) {
|
|
165
|
-
this.__disconnectAnimatedView();
|
|
166
|
-
}
|
|
167
|
-
this.#animatedView = null;
|
|
168
|
-
|
|
169
|
-
const nodes = this.#nodes;
|
|
170
|
-
for (let ii = 0, length = nodes.length; ii < length; ii++) {
|
|
171
|
-
const node = nodes[ii];
|
|
172
|
-
node.__removeChild(this);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
super.__detach();
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
update(): void {
|
|
179
|
-
this.#callback();
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
__makeNative(platformConfig: ?PlatformConfig): void {
|
|
183
|
-
const nodes = this.#nodes;
|
|
184
|
-
for (let ii = 0, length = nodes.length; ii < length; ii++) {
|
|
185
|
-
const node = nodes[ii];
|
|
186
|
-
node.__makeNative(platformConfig);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (!this.__isNative) {
|
|
190
|
-
this.__isNative = true;
|
|
191
|
-
|
|
192
|
-
// Since this does not call the super.__makeNative, we need to store the
|
|
193
|
-
// supplied platformConfig here, before calling __connectAnimatedView
|
|
194
|
-
// where it will be needed to traverse the graph of attached values.
|
|
195
|
-
super.__setPlatformConfig(platformConfig);
|
|
196
|
-
|
|
197
|
-
if (this.#animatedView) {
|
|
198
|
-
this.__connectAnimatedView();
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
setNativeView(animatedView: any): void {
|
|
204
|
-
if (this.#animatedView === animatedView) {
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
this.#animatedView = animatedView;
|
|
208
|
-
if (this.__isNative) {
|
|
209
|
-
this.__connectAnimatedView();
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
__connectAnimatedView(): void {
|
|
214
|
-
invariant(this.__isNative, 'Expected node to be marked as "native"');
|
|
215
|
-
let nativeViewTag: ?number = findNodeHandle(this.#animatedView);
|
|
216
|
-
if (nativeViewTag == null) {
|
|
217
|
-
if (process.env.NODE_ENV === 'test') {
|
|
218
|
-
nativeViewTag = -1;
|
|
219
|
-
} else {
|
|
220
|
-
throw new Error('Unable to locate attached view in the native tree');
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
NativeAnimatedHelper.API.connectAnimatedNodeToView(
|
|
224
|
-
this.__getNativeTag(),
|
|
225
|
-
nativeViewTag,
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
__disconnectAnimatedView(): void {
|
|
230
|
-
invariant(this.__isNative, 'Expected node to be marked as "native"');
|
|
231
|
-
let nativeViewTag: ?number = findNodeHandle(this.#animatedView);
|
|
232
|
-
if (nativeViewTag == null) {
|
|
233
|
-
if (process.env.NODE_ENV === 'test') {
|
|
234
|
-
nativeViewTag = -1;
|
|
235
|
-
} else {
|
|
236
|
-
throw new Error('Unable to locate attached view in the native tree');
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
NativeAnimatedHelper.API.disconnectAnimatedNodeFromView(
|
|
240
|
-
this.__getNativeTag(),
|
|
241
|
-
nativeViewTag,
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
__restoreDefaultValues(): void {
|
|
246
|
-
// When using the native driver, view properties need to be restored to
|
|
247
|
-
// their default values manually since react no longer tracks them. This
|
|
248
|
-
// is needed to handle cases where a prop driven by native animated is removed
|
|
249
|
-
// after having been changed natively by an animation.
|
|
250
|
-
if (this.__isNative) {
|
|
251
|
-
NativeAnimatedHelper.API.restoreDefaultValues(this.__getNativeTag());
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
__getNativeConfig(): Object {
|
|
256
|
-
const platformConfig = this.__getPlatformConfig();
|
|
257
|
-
const propsConfig: {[string]: number} = {};
|
|
258
|
-
|
|
259
|
-
const nodeKeys = this.#nodeKeys;
|
|
260
|
-
const nodes = this.#nodes;
|
|
261
|
-
for (let ii = 0, length = nodes.length; ii < length; ii++) {
|
|
262
|
-
const key = nodeKeys[ii];
|
|
263
|
-
const node = nodes[ii];
|
|
264
|
-
node.__makeNative(platformConfig);
|
|
265
|
-
propsConfig[key] = node.__getNativeTag();
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
return {
|
|
269
|
-
type: 'props',
|
|
270
|
-
props: propsConfig,
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Supported versions of JSC do not implement the newer Object.hasOwn. Remove
|
|
276
|
-
// this shim when they do.
|
|
277
|
-
// $FlowIgnore[method-unbinding]
|
|
278
|
-
const _hasOwnProp = Object.prototype.hasOwnProperty;
|
|
279
|
-
const hasOwn: (obj: $ReadOnly<{...}>, prop: string) => boolean =
|
|
280
|
-
// $FlowIgnore[method-unbinding]
|
|
281
|
-
Object.hasOwn ?? ((obj, prop) => _hasOwnProp.call(obj, prop));
|
|
@@ -1,251 +0,0 @@
|
|
|
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
|
-
* @format
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type {PlatformConfig} from '../AnimatedPlatformConfig';
|
|
12
|
-
|
|
13
|
-
import {validateStyles} from '../../../src/private/animated/NativeAnimatedValidation';
|
|
14
|
-
import * as ReactNativeFeatureFlags from '../../../src/private/featureflags/ReactNativeFeatureFlags';
|
|
15
|
-
import flattenStyle from '../../StyleSheet/flattenStyle';
|
|
16
|
-
import Platform from '../../Utilities/Platform';
|
|
17
|
-
import AnimatedNode from './AnimatedNode';
|
|
18
|
-
import AnimatedObject from './AnimatedObject';
|
|
19
|
-
import AnimatedTransform from './AnimatedTransform';
|
|
20
|
-
import AnimatedWithChildren from './AnimatedWithChildren';
|
|
21
|
-
|
|
22
|
-
export type AnimatedStyleAllowlist = $ReadOnly<{[string]: true}>;
|
|
23
|
-
|
|
24
|
-
function createAnimatedStyle(
|
|
25
|
-
inputStyle: {[string]: mixed},
|
|
26
|
-
allowlist: ?AnimatedStyleAllowlist,
|
|
27
|
-
keepUnanimatedValues: boolean,
|
|
28
|
-
): [$ReadOnlyArray<string>, $ReadOnlyArray<AnimatedNode>, {[string]: mixed}] {
|
|
29
|
-
const nodeKeys: Array<string> = [];
|
|
30
|
-
const nodes: Array<AnimatedNode> = [];
|
|
31
|
-
const style: {[string]: mixed} = {};
|
|
32
|
-
|
|
33
|
-
const keys = Object.keys(inputStyle);
|
|
34
|
-
for (let ii = 0, length = keys.length; ii < length; ii++) {
|
|
35
|
-
const key = keys[ii];
|
|
36
|
-
const value = inputStyle[key];
|
|
37
|
-
|
|
38
|
-
if (allowlist == null || hasOwn(allowlist, key)) {
|
|
39
|
-
let node;
|
|
40
|
-
if (value != null && key === 'transform') {
|
|
41
|
-
node = ReactNativeFeatureFlags.shouldUseAnimatedObjectForTransform()
|
|
42
|
-
? AnimatedObject.from(value)
|
|
43
|
-
: // $FlowFixMe[incompatible-call] - `value` is mixed.
|
|
44
|
-
AnimatedTransform.from(value);
|
|
45
|
-
} else if (value instanceof AnimatedNode) {
|
|
46
|
-
node = value;
|
|
47
|
-
} else {
|
|
48
|
-
node = AnimatedObject.from(value);
|
|
49
|
-
}
|
|
50
|
-
if (node == null) {
|
|
51
|
-
if (keepUnanimatedValues) {
|
|
52
|
-
style[key] = value;
|
|
53
|
-
}
|
|
54
|
-
} else {
|
|
55
|
-
nodeKeys.push(key);
|
|
56
|
-
nodes.push(node);
|
|
57
|
-
style[key] = node;
|
|
58
|
-
}
|
|
59
|
-
} else {
|
|
60
|
-
if (__DEV__) {
|
|
61
|
-
// WARNING: This is a potentially expensive check that we should only
|
|
62
|
-
// do in development. Without this check in development, it might be
|
|
63
|
-
// difficult to identify which styles need to be allowlisted.
|
|
64
|
-
if (AnimatedObject.from(inputStyle[key]) != null) {
|
|
65
|
-
console.error(
|
|
66
|
-
`AnimatedStyle: ${key} is not allowlisted for animation, but it ` +
|
|
67
|
-
'contains AnimatedNode values; styles allowing animation: ',
|
|
68
|
-
allowlist,
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
if (keepUnanimatedValues) {
|
|
73
|
-
style[key] = value;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return [nodeKeys, nodes, style];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export default class AnimatedStyle extends AnimatedWithChildren {
|
|
82
|
-
#inputStyle: any;
|
|
83
|
-
#nodeKeys: $ReadOnlyArray<string>;
|
|
84
|
-
#nodes: $ReadOnlyArray<AnimatedNode>;
|
|
85
|
-
#style: {[string]: mixed};
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Creates an `AnimatedStyle` if `value` contains `AnimatedNode` instances.
|
|
89
|
-
* Otherwise, returns `null`.
|
|
90
|
-
*/
|
|
91
|
-
static from(
|
|
92
|
-
inputStyle: any,
|
|
93
|
-
allowlist: ?AnimatedStyleAllowlist,
|
|
94
|
-
): ?AnimatedStyle {
|
|
95
|
-
const flatStyle = flattenStyle(inputStyle);
|
|
96
|
-
if (flatStyle == null) {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
const [nodeKeys, nodes, style] = createAnimatedStyle(
|
|
100
|
-
flatStyle,
|
|
101
|
-
allowlist,
|
|
102
|
-
Platform.OS !== 'web',
|
|
103
|
-
);
|
|
104
|
-
if (nodes.length === 0) {
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
return new AnimatedStyle(nodeKeys, nodes, style, inputStyle);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
constructor(
|
|
111
|
-
nodeKeys: $ReadOnlyArray<string>,
|
|
112
|
-
nodes: $ReadOnlyArray<AnimatedNode>,
|
|
113
|
-
style: {[string]: mixed},
|
|
114
|
-
inputStyle: any,
|
|
115
|
-
) {
|
|
116
|
-
super();
|
|
117
|
-
this.#nodeKeys = nodeKeys;
|
|
118
|
-
this.#nodes = nodes;
|
|
119
|
-
this.#style = style;
|
|
120
|
-
this.#inputStyle = inputStyle;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
__getValue(): Object | Array<Object> {
|
|
124
|
-
const style: {[string]: mixed} = {};
|
|
125
|
-
|
|
126
|
-
const keys = Object.keys(this.#style);
|
|
127
|
-
for (let ii = 0, length = keys.length; ii < length; ii++) {
|
|
128
|
-
const key = keys[ii];
|
|
129
|
-
const value = this.#style[key];
|
|
130
|
-
|
|
131
|
-
if (value instanceof AnimatedNode) {
|
|
132
|
-
style[key] = value.__getValue();
|
|
133
|
-
} else {
|
|
134
|
-
style[key] = value;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/* $FlowFixMe[incompatible-type] Error found due to incomplete typing of
|
|
139
|
-
* Platform.flow.js */
|
|
140
|
-
return Platform.OS === 'web' ? [this.#inputStyle, style] : style;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Creates a new `style` object that contains the same style properties as
|
|
145
|
-
* the supplied `staticStyle` object, except with animated nodes for any
|
|
146
|
-
* style properties that were created by this `AnimatedStyle` instance.
|
|
147
|
-
*/
|
|
148
|
-
__getValueWithStaticStyle(staticStyle: Object): Object | Array<Object> {
|
|
149
|
-
const flatStaticStyle = flattenStyle(staticStyle);
|
|
150
|
-
const style: {[string]: mixed} =
|
|
151
|
-
flatStaticStyle == null
|
|
152
|
-
? {}
|
|
153
|
-
: flatStaticStyle === staticStyle
|
|
154
|
-
? // Copy the input style, since we'll mutate it below.
|
|
155
|
-
{...flatStaticStyle}
|
|
156
|
-
: // Reuse `flatStaticStyle` if it is a newly created object.
|
|
157
|
-
flatStaticStyle;
|
|
158
|
-
|
|
159
|
-
const keys = Object.keys(style);
|
|
160
|
-
for (let ii = 0, length = keys.length; ii < length; ii++) {
|
|
161
|
-
const key = keys[ii];
|
|
162
|
-
const maybeNode = this.#style[key];
|
|
163
|
-
|
|
164
|
-
if (key === 'transform' && maybeNode instanceof AnimatedTransform) {
|
|
165
|
-
style[key] = maybeNode.__getValueWithStaticTransforms(
|
|
166
|
-
// NOTE: This check should not be necessary, but the types are not
|
|
167
|
-
// enforced as of this writing.
|
|
168
|
-
Array.isArray(style[key]) ? style[key] : [],
|
|
169
|
-
);
|
|
170
|
-
} else if (maybeNode instanceof AnimatedObject) {
|
|
171
|
-
style[key] = maybeNode.__getValueWithStaticObject(style[key]);
|
|
172
|
-
} else if (maybeNode instanceof AnimatedNode) {
|
|
173
|
-
style[key] = maybeNode.__getValue();
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/* $FlowFixMe[incompatible-type] Error found due to incomplete typing of
|
|
178
|
-
* Platform.flow.js */
|
|
179
|
-
return Platform.OS === 'web' ? [this.#inputStyle, style] : style;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
__getAnimatedValue(): Object {
|
|
183
|
-
const style: {[string]: mixed} = {};
|
|
184
|
-
|
|
185
|
-
const nodeKeys = this.#nodeKeys;
|
|
186
|
-
const nodes = this.#nodes;
|
|
187
|
-
for (let ii = 0, length = nodes.length; ii < length; ii++) {
|
|
188
|
-
const key = nodeKeys[ii];
|
|
189
|
-
const node = nodes[ii];
|
|
190
|
-
style[key] = node.__getAnimatedValue();
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return style;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
__attach(): void {
|
|
197
|
-
const nodes = this.#nodes;
|
|
198
|
-
for (let ii = 0, length = nodes.length; ii < length; ii++) {
|
|
199
|
-
const node = nodes[ii];
|
|
200
|
-
node.__addChild(this);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
__detach(): void {
|
|
205
|
-
const nodes = this.#nodes;
|
|
206
|
-
for (let ii = 0, length = nodes.length; ii < length; ii++) {
|
|
207
|
-
const node = nodes[ii];
|
|
208
|
-
node.__removeChild(this);
|
|
209
|
-
}
|
|
210
|
-
super.__detach();
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
__makeNative(platformConfig: ?PlatformConfig) {
|
|
214
|
-
const nodes = this.#nodes;
|
|
215
|
-
for (let ii = 0, length = nodes.length; ii < length; ii++) {
|
|
216
|
-
const node = nodes[ii];
|
|
217
|
-
node.__makeNative(platformConfig);
|
|
218
|
-
}
|
|
219
|
-
super.__makeNative(platformConfig);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
__getNativeConfig(): Object {
|
|
223
|
-
const platformConfig = this.__getPlatformConfig();
|
|
224
|
-
const styleConfig: {[string]: ?number} = {};
|
|
225
|
-
|
|
226
|
-
const nodeKeys = this.#nodeKeys;
|
|
227
|
-
const nodes = this.#nodes;
|
|
228
|
-
for (let ii = 0, length = nodes.length; ii < length; ii++) {
|
|
229
|
-
const key = nodeKeys[ii];
|
|
230
|
-
const node = nodes[ii];
|
|
231
|
-
node.__makeNative(platformConfig);
|
|
232
|
-
styleConfig[key] = node.__getNativeTag();
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (__DEV__) {
|
|
236
|
-
validateStyles(styleConfig);
|
|
237
|
-
}
|
|
238
|
-
return {
|
|
239
|
-
type: 'style',
|
|
240
|
-
style: styleConfig,
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Supported versions of JSC do not implement the newer Object.hasOwn. Remove
|
|
246
|
-
// this shim when they do.
|
|
247
|
-
// $FlowIgnore[method-unbinding]
|
|
248
|
-
const _hasOwnProp = Object.prototype.hasOwnProperty;
|
|
249
|
-
const hasOwn: (obj: $ReadOnly<{...}>, prop: string) => boolean =
|
|
250
|
-
// $FlowIgnore[method-unbinding]
|
|
251
|
-
Object.hasOwn ?? ((obj, prop) => _hasOwnProp.call(obj, prop));
|
|
@@ -1,356 +0,0 @@
|
|
|
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 strict-local
|
|
8
|
-
* @format
|
|
9
|
-
* @oncall react_native
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import type AnimatedProps from '../../../Libraries/Animated/nodes/AnimatedProps';
|
|
13
|
-
import type {AnimatedPropsAllowlist} from '../../../Libraries/Animated/nodes/AnimatedProps';
|
|
14
|
-
import type {AnimatedStyleAllowlist} from '../../../Libraries/Animated/nodes/AnimatedStyle';
|
|
15
|
-
|
|
16
|
-
import {AnimatedEvent} from '../../../Libraries/Animated/AnimatedEvent';
|
|
17
|
-
import AnimatedNode from '../../../Libraries/Animated/nodes/AnimatedNode';
|
|
18
|
-
import {isPlainObject} from '../../../Libraries/Animated/nodes/AnimatedObject';
|
|
19
|
-
import flattenStyle from '../../../Libraries/StyleSheet/flattenStyle';
|
|
20
|
-
import nullthrows from 'nullthrows';
|
|
21
|
-
import {useMemo, useState} from 'react';
|
|
22
|
-
|
|
23
|
-
type CompositeKey = {
|
|
24
|
-
style?: {[string]: CompositeKeyComponent},
|
|
25
|
-
[string]:
|
|
26
|
-
| CompositeKeyComponent
|
|
27
|
-
| AnimatedEvent
|
|
28
|
-
| $ReadOnlyArray<mixed>
|
|
29
|
-
| $ReadOnly<{[string]: mixed}>,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
type CompositeKeyComponent =
|
|
33
|
-
| AnimatedNode
|
|
34
|
-
| Array<CompositeKeyComponent | null>
|
|
35
|
-
| {[string]: CompositeKeyComponent};
|
|
36
|
-
|
|
37
|
-
type $ReadOnlyCompositeKey = $ReadOnly<{
|
|
38
|
-
style?: $ReadOnly<{[string]: CompositeKeyComponent}>,
|
|
39
|
-
[string]:
|
|
40
|
-
| $ReadOnlyCompositeKeyComponent
|
|
41
|
-
| AnimatedEvent
|
|
42
|
-
| $ReadOnlyArray<mixed>
|
|
43
|
-
| $ReadOnly<{[string]: mixed}>,
|
|
44
|
-
}>;
|
|
45
|
-
|
|
46
|
-
type $ReadOnlyCompositeKeyComponent =
|
|
47
|
-
| AnimatedNode
|
|
48
|
-
| $ReadOnlyArray<$ReadOnlyCompositeKeyComponent | null>
|
|
49
|
-
| $ReadOnly<{[string]: $ReadOnlyCompositeKeyComponent}>;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* A hook that returns an `AnimatedProps` object that is memoized based on the
|
|
53
|
-
* subset of `props` that are instances of `AnimatedNode` or `AnimatedEvent`.
|
|
54
|
-
*/
|
|
55
|
-
export function useAnimatedPropsMemo(
|
|
56
|
-
create: () => AnimatedProps,
|
|
57
|
-
// TODO: Make this two separate arguments after the experiment is over. This
|
|
58
|
-
// is only an array-like structure to make it easier to experiment with this
|
|
59
|
-
// and `useMemo`.
|
|
60
|
-
[allowlist, props]: [?AnimatedPropsAllowlist, {[string]: mixed}],
|
|
61
|
-
): AnimatedProps {
|
|
62
|
-
const compositeKey = useMemo(
|
|
63
|
-
() => createCompositeKeyForProps(props, allowlist),
|
|
64
|
-
[allowlist, props],
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
const [state, setState] = useState<{
|
|
68
|
-
allowlist: ?AnimatedPropsAllowlist,
|
|
69
|
-
compositeKey: $ReadOnlyCompositeKey | null,
|
|
70
|
-
value: AnimatedProps,
|
|
71
|
-
}>(() => ({
|
|
72
|
-
allowlist,
|
|
73
|
-
compositeKey,
|
|
74
|
-
value: create(),
|
|
75
|
-
}));
|
|
76
|
-
|
|
77
|
-
if (
|
|
78
|
-
state.allowlist !== allowlist ||
|
|
79
|
-
!areCompositeKeysEqual(state.compositeKey, compositeKey)
|
|
80
|
-
) {
|
|
81
|
-
setState({
|
|
82
|
-
allowlist,
|
|
83
|
-
compositeKey,
|
|
84
|
-
value: create(),
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
return state.value;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Creates a new composite key for a `props` object that can be used to detect
|
|
92
|
-
* whether a new `AnimatedProps` instance must be created.
|
|
93
|
-
*
|
|
94
|
-
* - With an allowlist, those props are searched for `AnimatedNode` instances.
|
|
95
|
-
* - Without an allowlist, `style` is searched for `AnimatedNode` instances,
|
|
96
|
-
* but all other objects and arrays are included (not searched). We do not
|
|
97
|
-
* search objects and arrays without an allowlist in case they are very large
|
|
98
|
-
* data structures. We safely traverse `style` becuase it is bounded.
|
|
99
|
-
*
|
|
100
|
-
* Any `AnimatedEvent` instances at the first depth are always included.
|
|
101
|
-
*
|
|
102
|
-
* If `props` contains no `AnimatedNode` or `AnimatedEvent` instances, this
|
|
103
|
-
* returns null.
|
|
104
|
-
*/
|
|
105
|
-
export function createCompositeKeyForProps(
|
|
106
|
-
props: $ReadOnly<{[string]: mixed}>,
|
|
107
|
-
allowlist: ?AnimatedPropsAllowlist,
|
|
108
|
-
): $ReadOnlyCompositeKey | null {
|
|
109
|
-
let compositeKey: CompositeKey | null = null;
|
|
110
|
-
|
|
111
|
-
const keys = Object.keys(props);
|
|
112
|
-
for (let ii = 0, length = keys.length; ii < length; ii++) {
|
|
113
|
-
const key = keys[ii];
|
|
114
|
-
const value = props[key];
|
|
115
|
-
|
|
116
|
-
if (allowlist == null || hasOwn(allowlist, key)) {
|
|
117
|
-
let compositeKeyComponent;
|
|
118
|
-
if (key === 'style') {
|
|
119
|
-
// $FlowFixMe[incompatible-call] - `style` is a valid argument.
|
|
120
|
-
// $FlowFixMe[incompatible-type] - `flattenStyle` returns an object.
|
|
121
|
-
const flatStyle: ?{[string]: mixed} = flattenStyle(value);
|
|
122
|
-
if (flatStyle != null) {
|
|
123
|
-
compositeKeyComponent = createCompositeKeyForObject(
|
|
124
|
-
flatStyle,
|
|
125
|
-
allowlist?.style,
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
} else if (
|
|
129
|
-
value instanceof AnimatedNode ||
|
|
130
|
-
value instanceof AnimatedEvent
|
|
131
|
-
) {
|
|
132
|
-
compositeKeyComponent = value;
|
|
133
|
-
} else if (Array.isArray(value)) {
|
|
134
|
-
compositeKeyComponent =
|
|
135
|
-
allowlist == null ? value : createCompositeKeyForArray(value);
|
|
136
|
-
} else if (isPlainObject(value)) {
|
|
137
|
-
compositeKeyComponent =
|
|
138
|
-
allowlist == null ? value : createCompositeKeyForObject(value);
|
|
139
|
-
}
|
|
140
|
-
if (compositeKeyComponent != null) {
|
|
141
|
-
if (compositeKey == null) {
|
|
142
|
-
compositeKey = {} as CompositeKey;
|
|
143
|
-
}
|
|
144
|
-
compositeKey[key] = compositeKeyComponent;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return compositeKey;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Creates a new composite key for an array that retains all values that are or
|
|
154
|
-
* contain `AnimatedNode` instances, and `null` for the rest.
|
|
155
|
-
*
|
|
156
|
-
* If `array` contains no `AnimatedNode` instances, this returns null.
|
|
157
|
-
*/
|
|
158
|
-
function createCompositeKeyForArray(
|
|
159
|
-
array: $ReadOnlyArray<mixed>,
|
|
160
|
-
): $ReadOnlyArray<$ReadOnlyCompositeKeyComponent | null> | null {
|
|
161
|
-
let compositeKey: Array<$ReadOnlyCompositeKeyComponent | null> | null = null;
|
|
162
|
-
|
|
163
|
-
for (let ii = 0, length = array.length; ii < length; ii++) {
|
|
164
|
-
const value = array[ii];
|
|
165
|
-
|
|
166
|
-
let compositeKeyComponent;
|
|
167
|
-
if (value instanceof AnimatedNode) {
|
|
168
|
-
compositeKeyComponent = value;
|
|
169
|
-
} else if (Array.isArray(value)) {
|
|
170
|
-
compositeKeyComponent = createCompositeKeyForArray(value);
|
|
171
|
-
} else if (isPlainObject(value)) {
|
|
172
|
-
compositeKeyComponent = createCompositeKeyForObject(value);
|
|
173
|
-
}
|
|
174
|
-
if (compositeKeyComponent != null) {
|
|
175
|
-
if (compositeKey == null) {
|
|
176
|
-
compositeKey = new Array<$ReadOnlyCompositeKeyComponent | null>(
|
|
177
|
-
array.length,
|
|
178
|
-
).fill(null);
|
|
179
|
-
}
|
|
180
|
-
compositeKey[ii] = compositeKeyComponent;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return compositeKey;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Creates a new composite key for an object that retains only properties that
|
|
189
|
-
* are or contain `AnimatedNode` instances.
|
|
190
|
-
*
|
|
191
|
-
* When used to create composite keys for `style` props:
|
|
192
|
-
*
|
|
193
|
-
* - With an allowlist, those properties are searched.
|
|
194
|
-
* - Without an allowlist, every property is searched.
|
|
195
|
-
*
|
|
196
|
-
* If `object` contains no `AnimatedNode` instances, this returns null.
|
|
197
|
-
*/
|
|
198
|
-
function createCompositeKeyForObject(
|
|
199
|
-
object: $ReadOnly<{[string]: mixed}>,
|
|
200
|
-
allowlist?: ?AnimatedStyleAllowlist,
|
|
201
|
-
): $ReadOnly<{[string]: $ReadOnlyCompositeKeyComponent}> | null {
|
|
202
|
-
let compositeKey: {[string]: $ReadOnlyCompositeKeyComponent} | null = null;
|
|
203
|
-
|
|
204
|
-
const keys = Object.keys(object);
|
|
205
|
-
for (let ii = 0, length = keys.length; ii < length; ii++) {
|
|
206
|
-
const key = keys[ii];
|
|
207
|
-
|
|
208
|
-
if (allowlist == null || hasOwn(allowlist, key)) {
|
|
209
|
-
const value = object[key];
|
|
210
|
-
|
|
211
|
-
let compositeKeyComponent;
|
|
212
|
-
if (value instanceof AnimatedNode) {
|
|
213
|
-
compositeKeyComponent = value;
|
|
214
|
-
} else if (Array.isArray(value)) {
|
|
215
|
-
compositeKeyComponent = createCompositeKeyForArray(value);
|
|
216
|
-
} else if (isPlainObject(value)) {
|
|
217
|
-
compositeKeyComponent = createCompositeKeyForObject(value);
|
|
218
|
-
}
|
|
219
|
-
if (compositeKeyComponent != null) {
|
|
220
|
-
if (compositeKey == null) {
|
|
221
|
-
compositeKey = {} as {[string]: $ReadOnlyCompositeKeyComponent};
|
|
222
|
-
}
|
|
223
|
-
compositeKey[key] = compositeKeyComponent;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
return compositeKey;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
export function areCompositeKeysEqual(
|
|
232
|
-
maybePrev: $ReadOnlyCompositeKey | null,
|
|
233
|
-
maybeNext: $ReadOnlyCompositeKey | null,
|
|
234
|
-
allowlist: ?AnimatedPropsAllowlist,
|
|
235
|
-
): boolean {
|
|
236
|
-
if (maybePrev === maybeNext) {
|
|
237
|
-
return true;
|
|
238
|
-
}
|
|
239
|
-
if (maybePrev === null || maybeNext === null) {
|
|
240
|
-
return false;
|
|
241
|
-
}
|
|
242
|
-
// Help Flow retain the type refinements of these.
|
|
243
|
-
const prev = maybePrev;
|
|
244
|
-
const next = maybeNext;
|
|
245
|
-
|
|
246
|
-
const keys = Object.keys(prev);
|
|
247
|
-
const length = keys.length;
|
|
248
|
-
if (length !== Object.keys(next).length) {
|
|
249
|
-
return false;
|
|
250
|
-
}
|
|
251
|
-
for (let ii = 0; ii < length; ii++) {
|
|
252
|
-
const key = keys[ii];
|
|
253
|
-
if (!hasOwn(next, key)) {
|
|
254
|
-
return false;
|
|
255
|
-
}
|
|
256
|
-
const prevComponent = prev[key];
|
|
257
|
-
const nextComponent = next[key];
|
|
258
|
-
|
|
259
|
-
if (key === 'style') {
|
|
260
|
-
// We know style components are objects with non-mixed values.
|
|
261
|
-
if (
|
|
262
|
-
!areCompositeKeyComponentsEqual(
|
|
263
|
-
// $FlowIgnore[incompatible-cast]
|
|
264
|
-
prevComponent as $ReadOnlyCompositeKeyComponent,
|
|
265
|
-
// $FlowIgnore[incompatible-cast]
|
|
266
|
-
nextComponent as $ReadOnlyCompositeKeyComponent,
|
|
267
|
-
)
|
|
268
|
-
) {
|
|
269
|
-
return false;
|
|
270
|
-
}
|
|
271
|
-
} else if (
|
|
272
|
-
prevComponent instanceof AnimatedNode ||
|
|
273
|
-
prevComponent instanceof AnimatedEvent
|
|
274
|
-
) {
|
|
275
|
-
if (prevComponent !== nextComponent) {
|
|
276
|
-
return false;
|
|
277
|
-
}
|
|
278
|
-
} else {
|
|
279
|
-
// When `allowlist` is null, the components must be the same. Otherwise,
|
|
280
|
-
// we created the components using deep traversal, so deep compare them.
|
|
281
|
-
if (allowlist == null) {
|
|
282
|
-
if (prevComponent !== nextComponent) {
|
|
283
|
-
return false;
|
|
284
|
-
}
|
|
285
|
-
} else {
|
|
286
|
-
if (
|
|
287
|
-
!areCompositeKeyComponentsEqual(
|
|
288
|
-
// $FlowIgnore[incompatible-cast]
|
|
289
|
-
prevComponent as $ReadOnlyCompositeKeyComponent,
|
|
290
|
-
// $FlowIgnore[incompatible-cast]
|
|
291
|
-
nextComponent as $ReadOnlyCompositeKeyComponent,
|
|
292
|
-
)
|
|
293
|
-
) {
|
|
294
|
-
return false;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
return true;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
function areCompositeKeyComponentsEqual(
|
|
303
|
-
prev: $ReadOnlyCompositeKeyComponent | null,
|
|
304
|
-
next: $ReadOnlyCompositeKeyComponent | null,
|
|
305
|
-
): boolean {
|
|
306
|
-
if (prev === next) {
|
|
307
|
-
return true;
|
|
308
|
-
}
|
|
309
|
-
if (prev instanceof AnimatedNode) {
|
|
310
|
-
return prev === next;
|
|
311
|
-
}
|
|
312
|
-
if (Array.isArray(prev)) {
|
|
313
|
-
if (!Array.isArray(next)) {
|
|
314
|
-
return false;
|
|
315
|
-
}
|
|
316
|
-
const length = prev.length;
|
|
317
|
-
if (length !== next.length) {
|
|
318
|
-
return false;
|
|
319
|
-
}
|
|
320
|
-
for (let ii = 0; ii < length; ii++) {
|
|
321
|
-
if (!areCompositeKeyComponentsEqual(prev[ii], next[ii])) {
|
|
322
|
-
return false;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
return true;
|
|
326
|
-
}
|
|
327
|
-
if (isPlainObject(prev)) {
|
|
328
|
-
if (!isPlainObject(next)) {
|
|
329
|
-
return false;
|
|
330
|
-
}
|
|
331
|
-
const keys = Object.keys(prev);
|
|
332
|
-
const length = keys.length;
|
|
333
|
-
if (length !== Object.keys(next).length) {
|
|
334
|
-
return false;
|
|
335
|
-
}
|
|
336
|
-
for (let ii = 0; ii < length; ii++) {
|
|
337
|
-
const key = keys[ii];
|
|
338
|
-
if (
|
|
339
|
-
!hasOwn(nullthrows(next), key) ||
|
|
340
|
-
!areCompositeKeyComponentsEqual(prev[key], next[key])
|
|
341
|
-
) {
|
|
342
|
-
return false;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
return true;
|
|
346
|
-
}
|
|
347
|
-
return false;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// Supported versions of JSC do not implement the newer Object.hasOwn. Remove
|
|
351
|
-
// this shim when they do.
|
|
352
|
-
// $FlowIgnore[method-unbinding]
|
|
353
|
-
const _hasOwnProp = Object.prototype.hasOwnProperty;
|
|
354
|
-
const hasOwn: (obj: $ReadOnly<{...}>, prop: string) => boolean =
|
|
355
|
-
// $FlowIgnore[method-unbinding]
|
|
356
|
-
Object.hasOwn ?? ((obj, prop) => _hasOwnProp.call(obj, prop));
|