@office-iss/react-native-win32 0.0.0-canary.262 → 0.0.0-canary.264

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.
Files changed (45) hide show
  1. package/.eslintrc.js +11 -0
  2. package/.flowconfig +1 -1
  3. package/CHANGELOG.json +40 -1
  4. package/CHANGELOG.md +17 -5
  5. package/Libraries/Animated/AnimatedEvent.js +1 -1
  6. package/Libraries/Animated/NativeAnimatedAllowlist.js +111 -0
  7. package/Libraries/Animated/animations/Animation.js +1 -1
  8. package/Libraries/Animated/animations/DecayAnimation.js +1 -1
  9. package/Libraries/Animated/animations/SpringAnimation.js +1 -1
  10. package/Libraries/Animated/animations/TimingAnimation.js +1 -1
  11. package/Libraries/Animated/components/AnimatedScrollView.js +1 -0
  12. package/Libraries/Animated/createAnimatedComponent.js +9 -8
  13. package/Libraries/Animated/nodes/AnimatedColor.js +1 -1
  14. package/Libraries/Animated/nodes/AnimatedInterpolation.js +3 -2
  15. package/Libraries/Animated/nodes/AnimatedNode.js +42 -33
  16. package/Libraries/Animated/nodes/AnimatedObject.js +54 -45
  17. package/Libraries/Animated/nodes/AnimatedProps.js +75 -40
  18. package/Libraries/Animated/nodes/AnimatedStyle.js +103 -59
  19. package/Libraries/Animated/nodes/AnimatedTracking.js +1 -1
  20. package/Libraries/Animated/nodes/AnimatedTransform.js +102 -67
  21. package/Libraries/Animated/nodes/AnimatedValue.js +1 -1
  22. package/Libraries/Animated/nodes/AnimatedWithChildren.js +21 -22
  23. package/Libraries/Animated/useAnimatedProps.js +18 -15
  24. package/Libraries/Core/ReactNativeVersion.js +1 -1
  25. package/Libraries/Image/Image.win32.js +1 -3
  26. package/Libraries/Inspector/Inspector.js +3 -2
  27. package/Libraries/Inspector/Inspector.win32.js +3 -2
  28. package/Libraries/Inspector/InspectorPanel.js +16 -10
  29. package/Libraries/LogBox/LogBoxNotificationContainer.js +3 -2
  30. package/Libraries/LogBox/UI/LogBoxInspectorHeader.js +1 -12
  31. package/Libraries/LogBox/UI/LogBoxInspectorHeader.win32.js +1 -12
  32. package/Libraries/StyleSheet/StyleSheetTypes.d.ts +17 -15
  33. package/Libraries/Text/TextNativeComponent.win32.js +0 -1
  34. package/Libraries/vendor/emitter/EventEmitter.js +5 -5
  35. package/overrides.json +11 -11
  36. package/package.json +19 -18
  37. package/src/private/animated/NativeAnimatedHelper.js +438 -0
  38. package/src/private/animated/NativeAnimatedHelper.win32.js +440 -0
  39. package/src/private/animated/NativeAnimatedValidation.js +64 -0
  40. package/src/private/components/SafeAreaView_INTERNAL_DO_NOT_USE.js +27 -0
  41. package/src/private/featureflags/ReactNativeFeatureFlags.js +1 -7
  42. package/Libraries/Animated/NativeAnimatedHelper.js +0 -615
  43. package/Libraries/Animated/NativeAnimatedHelper.win32.js +0 -617
  44. package/src/private/hooks/DebouncedEffectImplementation.js +0 -148
  45. package/src/private/hooks/useDebouncedEffect.js +0 -23
package/.eslintrc.js CHANGED
@@ -6,6 +6,17 @@ module.exports = {
6
6
  "react-hooks/rules-of-hooks": "warn",
7
7
  },
8
8
  overrides: [
9
+ {
10
+ files: ['*.js', '*.js.flow', '*.jsx'],
11
+ parser: 'hermes-eslint',
12
+ rules: {
13
+ // These rules are not required with hermes-eslint
14
+ 'ft-flow/define-flow-type': 0,
15
+ 'ft-flow/use-flow-type': 0,
16
+ // flow handles this check for us, so it's not required
17
+ 'no-undef': 0,
18
+ },
19
+ },
9
20
  {
10
21
  files: ['*.ts', '*.tsx'],
11
22
  excludedFiles: ['*.d.ts'],
package/.flowconfig CHANGED
@@ -160,4 +160,4 @@ untyped-import
160
160
  untyped-type-import
161
161
 
162
162
  [version]
163
- ^0.245.0
163
+ ^0.245.2
package/CHANGELOG.json CHANGED
@@ -2,7 +2,46 @@
2
2
  "name": "@office-iss/react-native-win32",
3
3
  "entries": [
4
4
  {
5
- "date": "Sat, 07 Sep 2024 05:13:20 GMT",
5
+ "date": "Thu, 26 Sep 2024 16:04:45 GMT",
6
+ "version": "0.0.0-canary.264",
7
+ "tag": "@office-iss/react-native-win32_v0.0.0-canary.264",
8
+ "comments": {
9
+ "prerelease": [
10
+ {
11
+ "author": "1422161+marlenecota@users.noreply.github.com",
12
+ "package": "@office-iss/react-native-win32",
13
+ "commit": "ca03cc6d24f6863ef5f94aab8755e8fb61e34c8f",
14
+ "comment": "RN Integration 9/9"
15
+ },
16
+ {
17
+ "author": "beachball",
18
+ "package": "@office-iss/react-native-win32",
19
+ "comment": "Bump @rnw-scripts/eslint-config to v1.2.28",
20
+ "commit": "not available"
21
+ },
22
+ {
23
+ "author": "beachball",
24
+ "package": "@office-iss/react-native-win32",
25
+ "comment": "Bump @rnw-scripts/jest-out-of-tree-snapshot-resolver to v1.1.32",
26
+ "commit": "not available"
27
+ },
28
+ {
29
+ "author": "beachball",
30
+ "package": "@office-iss/react-native-win32",
31
+ "comment": "Bump @rnw-scripts/just-task to v2.3.45",
32
+ "commit": "not available"
33
+ },
34
+ {
35
+ "author": "beachball",
36
+ "package": "@office-iss/react-native-win32",
37
+ "comment": "Bump react-native-platform-override to v1.9.47",
38
+ "commit": "not available"
39
+ }
40
+ ]
41
+ }
42
+ },
43
+ {
44
+ "date": "Sat, 07 Sep 2024 05:14:10 GMT",
6
45
  "version": "0.0.0-canary.262",
7
46
  "tag": "@office-iss/react-native-win32_v0.0.0-canary.262",
8
47
  "comments": {
package/CHANGELOG.md CHANGED
@@ -1,18 +1,30 @@
1
1
  # Change Log - @office-iss/react-native-win32
2
2
 
3
- <!-- This log was last generated on Sat, 07 Sep 2024 05:13:20 GMT and should not be manually modified. -->
3
+ <!-- This log was last generated on Thu, 26 Sep 2024 16:04:45 GMT and should not be manually modified. -->
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
- ## 0.0.0-canary.262
7
+ ## 0.0.0-canary.264
8
8
 
9
- Sat, 07 Sep 2024 05:13:20 GMT
9
+ Thu, 26 Sep 2024 16:04:45 GMT
10
10
 
11
11
  ### Changes
12
12
 
13
- - switch to optimized text (tatianakapos@microsoft.com)
14
- - Integrate 8/31 (34109996+chiaramooney@users.noreply.github.com)
13
+ - RN Integration 9/9 (1422161+marlenecota@users.noreply.github.com)
14
+ - Bump @rnw-scripts/eslint-config to v1.2.28
15
+ - Bump @rnw-scripts/jest-out-of-tree-snapshot-resolver to v1.1.32
16
+ - Bump @rnw-scripts/just-task to v2.3.45
17
+ - Bump react-native-platform-override to v1.9.47
15
18
 
19
+ ## 0.0.0-canary.262
20
+
21
+ Sat, 07 Sep 2024 05:14:10 GMT
22
+
23
+ ### Changes
24
+
25
+ - switch to optimized text (tatianakapos@microsoft.com)
26
+ - Integrate 8/31 (34109996+chiaramooney@users.noreply.github.com)
27
+
16
28
  ## 0.0.0-canary.261
17
29
 
18
30
  Thu, 05 Sep 2024 05:13:45 GMT
@@ -13,7 +13,7 @@
13
13
  import type {PlatformConfig} from './AnimatedPlatformConfig';
14
14
 
15
15
  import {findNodeHandle} from '../ReactNative/RendererProxy';
16
- import NativeAnimatedHelper from './NativeAnimatedHelper';
16
+ import NativeAnimatedHelper from '../../src/private/animated/NativeAnimatedHelper';
17
17
  import AnimatedValue from './nodes/AnimatedValue';
18
18
  import AnimatedValueXY from './nodes/AnimatedValueXY';
19
19
  import invariant from 'invariant';
@@ -0,0 +1,111 @@
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 * as ReactNativeFeatureFlags from '../../src/private/featureflags/ReactNativeFeatureFlags';
12
+
13
+ /**
14
+ * Styles allowed by the native animated implementation.
15
+ *
16
+ * In general native animated implementation should support any numeric or color property that
17
+ * doesn't need to be updated through the shadow view hierarchy (all non-layout properties).
18
+ */
19
+ const SUPPORTED_COLOR_STYLES: {[string]: boolean} = {
20
+ backgroundColor: true,
21
+ borderBottomColor: true,
22
+ borderColor: true,
23
+ borderEndColor: true,
24
+ borderLeftColor: true,
25
+ borderRightColor: true,
26
+ borderStartColor: true,
27
+ borderTopColor: true,
28
+ color: true,
29
+ tintColor: true,
30
+ };
31
+
32
+ const SUPPORTED_STYLES: {[string]: boolean} = {
33
+ ...SUPPORTED_COLOR_STYLES,
34
+ borderBottomEndRadius: true,
35
+ borderBottomLeftRadius: true,
36
+ borderBottomRightRadius: true,
37
+ borderBottomStartRadius: true,
38
+ borderEndEndRadius: true,
39
+ borderEndStartRadius: true,
40
+ borderRadius: true,
41
+ borderTopEndRadius: true,
42
+ borderTopLeftRadius: true,
43
+ borderTopRightRadius: true,
44
+ borderTopStartRadius: true,
45
+ borderStartEndRadius: true,
46
+ borderStartStartRadius: true,
47
+ elevation: true,
48
+ opacity: true,
49
+ transform: true,
50
+ zIndex: true,
51
+ /* ios styles */
52
+ shadowOpacity: true,
53
+ shadowRadius: true,
54
+ /* legacy android transform properties */
55
+ scaleX: true,
56
+ scaleY: true,
57
+ translateX: true,
58
+ translateY: true,
59
+ };
60
+
61
+ const SUPPORTED_TRANSFORMS: {[string]: boolean} = {
62
+ translateX: true,
63
+ translateY: true,
64
+ scale: true,
65
+ scaleX: true,
66
+ scaleY: true,
67
+ rotate: true,
68
+ rotateX: true,
69
+ rotateY: true,
70
+ rotateZ: true,
71
+ perspective: true,
72
+ skewX: true,
73
+ skewY: true,
74
+ matrix: ReactNativeFeatureFlags.shouldUseAnimatedObjectForTransform(),
75
+ };
76
+
77
+ const SUPPORTED_INTERPOLATION_PARAMS: {[string]: boolean} = {
78
+ inputRange: true,
79
+ outputRange: true,
80
+ extrapolate: true,
81
+ extrapolateRight: true,
82
+ extrapolateLeft: true,
83
+ };
84
+
85
+ export function allowInterpolationParam(param: string): void {
86
+ SUPPORTED_INTERPOLATION_PARAMS[param] = true;
87
+ }
88
+
89
+ export function allowStyleProp(prop: string): void {
90
+ SUPPORTED_STYLES[prop] = true;
91
+ }
92
+
93
+ export function allowTransformProp(prop: string): void {
94
+ SUPPORTED_TRANSFORMS[prop] = true;
95
+ }
96
+
97
+ export function isSupportedColorStyleProp(prop: string): boolean {
98
+ return SUPPORTED_COLOR_STYLES[prop] === true;
99
+ }
100
+
101
+ export function isSupportedInterpolationParam(param: string): boolean {
102
+ return SUPPORTED_INTERPOLATION_PARAMS[param] === true;
103
+ }
104
+
105
+ export function isSupportedStyleProp(prop: string): boolean {
106
+ return SUPPORTED_STYLES[prop] === true;
107
+ }
108
+
109
+ export function isSupportedTransformProp(prop: string): boolean {
110
+ return SUPPORTED_TRANSFORMS[prop] === true;
111
+ }
@@ -15,7 +15,7 @@ import type AnimatedNode from '../nodes/AnimatedNode';
15
15
  import type AnimatedValue from '../nodes/AnimatedValue';
16
16
 
17
17
  import * as ReactNativeFeatureFlags from '../../../src/private/featureflags/ReactNativeFeatureFlags';
18
- import NativeAnimatedHelper from '../NativeAnimatedHelper';
18
+ import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
19
19
  import AnimatedProps from '../nodes/AnimatedProps';
20
20
 
21
21
  export type EndResult = {finished: boolean, value?: number, ...};
@@ -14,7 +14,7 @@ import type {PlatformConfig} from '../AnimatedPlatformConfig';
14
14
  import type AnimatedValue from '../nodes/AnimatedValue';
15
15
  import type {AnimationConfig, EndCallback} from './Animation';
16
16
 
17
- import NativeAnimatedHelper from '../NativeAnimatedHelper';
17
+ import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
18
18
  import Animation from './Animation';
19
19
 
20
20
  export type DecayAnimationConfig = {
@@ -16,7 +16,7 @@ import type AnimatedValue from '../nodes/AnimatedValue';
16
16
  import type AnimatedValueXY from '../nodes/AnimatedValueXY';
17
17
  import type {AnimationConfig, EndCallback} from './Animation';
18
18
 
19
- import NativeAnimatedHelper from '../NativeAnimatedHelper';
19
+ import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
20
20
  import AnimatedColor from '../nodes/AnimatedColor';
21
21
  import * as SpringConfig from '../SpringConfig';
22
22
  import Animation from './Animation';
@@ -17,7 +17,7 @@ import type AnimatedValue from '../nodes/AnimatedValue';
17
17
  import type AnimatedValueXY from '../nodes/AnimatedValueXY';
18
18
  import type {AnimationConfig, EndCallback} from './Animation';
19
19
 
20
- import NativeAnimatedHelper from '../NativeAnimatedHelper';
20
+ import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
21
21
  import AnimatedColor from '../nodes/AnimatedColor';
22
22
  import Animation from './Animation';
23
23
 
@@ -46,6 +46,7 @@ const AnimatedScrollView: AnimatedComponentType<Props, Instance> =
46
46
  props.style != null
47
47
  ) {
48
48
  return (
49
+ // $FlowFixMe[prop-missing]
49
50
  <AnimatedScrollViewWithInvertedRefreshControl
50
51
  scrollEventThrottle={0.0001}
51
52
  {...props}
@@ -15,14 +15,15 @@ import useAnimatedProps from './useAnimatedProps';
15
15
  import * as React from 'react';
16
16
  import {useMemo} from 'react';
17
17
 
18
- // $FlowFixMe[deprecated-type]
19
- export type AnimatedProps<Props: {...}> = $ObjMap<
20
- Props &
21
- $ReadOnly<{
22
- passthroughAnimatedPropExplicitValues?: React.ElementConfig<typeof View>,
23
- }>,
24
- () => any,
25
- >;
18
+ export type AnimatedProps<Props: {...}> = {
19
+ // eslint-disable-next-line no-unused-vars
20
+ +[_K in keyof (Props &
21
+ $ReadOnly<{
22
+ passthroughAnimatedPropExplicitValues?: React.ElementConfig<
23
+ typeof View,
24
+ >,
25
+ }>)]: any,
26
+ };
26
27
 
27
28
  export type AnimatedComponentType<
28
29
  Props: {...},
@@ -17,7 +17,7 @@ import type {PlatformConfig} from '../AnimatedPlatformConfig';
17
17
 
18
18
  import normalizeColor from '../../StyleSheet/normalizeColor';
19
19
  import {processColorObject} from '../../StyleSheet/PlatformColorValueTypes';
20
- import NativeAnimatedHelper from '../NativeAnimatedHelper';
20
+ import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
21
21
  import AnimatedValue, {flushValue} from './AnimatedValue';
22
22
  import AnimatedWithChildren from './AnimatedWithChildren';
23
23
 
@@ -15,10 +15,11 @@
15
15
  import type {PlatformConfig} from '../AnimatedPlatformConfig';
16
16
  import type AnimatedNode from './AnimatedNode';
17
17
 
18
+ import {validateInterpolation} from '../../../src/private/animated/NativeAnimatedValidation';
18
19
  import normalizeColor from '../../StyleSheet/normalizeColor';
19
20
  import processColor from '../../StyleSheet/processColor';
20
21
  import Easing from '../Easing';
21
- import NativeAnimatedHelper from '../NativeAnimatedHelper';
22
+ import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
22
23
  import AnimatedWithChildren from './AnimatedWithChildren';
23
24
  import invariant from 'invariant';
24
25
 
@@ -382,7 +383,7 @@ export default class AnimatedInterpolation<
382
383
 
383
384
  __getNativeConfig(): any {
384
385
  if (__DEV__) {
385
- NativeAnimatedHelper.validateInterpolation(this._config);
386
+ validateInterpolation(this._config);
386
387
  }
387
388
 
388
389
  // Only the `outputRange` can contain strings so we don't need to transform `inputRange` here
@@ -10,23 +10,31 @@
10
10
 
11
11
  'use strict';
12
12
 
13
+ import type {EventSubscription} from '../../vendor/emitter/EventEmitter';
13
14
  import type {PlatformConfig} from '../AnimatedPlatformConfig';
14
15
 
15
- import NativeAnimatedHelper from '../NativeAnimatedHelper';
16
+ import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
16
17
  import invariant from 'invariant';
17
18
 
18
- const NativeAnimatedAPI = NativeAnimatedHelper.API;
19
+ const {startListeningToAnimatedNodeValue, stopListeningToAnimatedNodeValue} =
20
+ NativeAnimatedHelper.API;
19
21
 
20
22
  type ValueListenerCallback = (state: {value: number, ...}) => mixed;
21
23
 
22
24
  let _uniqueId = 1;
25
+ let _assertNativeAnimatedModule: ?() => void = () => {
26
+ NativeAnimatedHelper.assertNativeAnimatedModule();
27
+ // We only have to assert that the module exists once. After we've asserted
28
+ // this, clear out the function so we know to skip it in the future.
29
+ _assertNativeAnimatedModule = null;
30
+ };
23
31
 
24
32
  // Note(vjeux): this would be better as an interface but flow doesn't
25
33
  // support them yet
26
34
  export default class AnimatedNode {
27
- _listeners: {[key: string]: ValueListenerCallback, ...};
28
- _platformConfig: ?PlatformConfig;
29
- __nativeAnimatedValueListener: ?any;
35
+ #listeners: Map<string, ValueListenerCallback> = new Map();
36
+ _platformConfig: ?PlatformConfig = undefined;
37
+ __nativeAnimatedValueListener: ?EventSubscription = null;
30
38
  __attach(): void {}
31
39
  __detach(): void {
32
40
  this.removeAllListeners();
@@ -46,13 +54,9 @@ export default class AnimatedNode {
46
54
  }
47
55
 
48
56
  /* Methods and props used by native Animated impl */
49
- __isNative: boolean;
50
- __nativeTag: ?number;
51
- __shouldUpdateListenersForNewNativeTag: boolean;
52
-
53
- constructor() {
54
- this._listeners = {};
55
- }
57
+ __isNative: boolean = false;
58
+ __nativeTag: ?number = undefined;
59
+ __shouldUpdateListenersForNewNativeTag: boolean = false;
56
60
 
57
61
  __makeNative(platformConfig: ?PlatformConfig): void {
58
62
  if (!this.__isNative) {
@@ -60,7 +64,7 @@ export default class AnimatedNode {
60
64
  }
61
65
 
62
66
  this._platformConfig = platformConfig;
63
- if (this.hasListeners()) {
67
+ if (this.#listeners.size > 0) {
64
68
  this._startListeningToNativeValueUpdates();
65
69
  }
66
70
  }
@@ -74,7 +78,7 @@ export default class AnimatedNode {
74
78
  */
75
79
  addListener(callback: (value: any) => mixed): string {
76
80
  const id = String(_uniqueId++);
77
- this._listeners[id] = callback;
81
+ this.#listeners.set(id, callback);
78
82
  if (this.__isNative) {
79
83
  this._startListeningToNativeValueUpdates();
80
84
  }
@@ -88,8 +92,8 @@ export default class AnimatedNode {
88
92
  * See https://reactnative.dev/docs/animatedvalue#removelistener
89
93
  */
90
94
  removeListener(id: string): void {
91
- delete this._listeners[id];
92
- if (this.__isNative && !this.hasListeners()) {
95
+ this.#listeners.delete(id);
96
+ if (this.__isNative && this.#listeners.size === 0) {
93
97
  this._stopListeningForNativeValueUpdates();
94
98
  }
95
99
  }
@@ -100,14 +104,14 @@ export default class AnimatedNode {
100
104
  * See https://reactnative.dev/docs/animatedvalue#removealllisteners
101
105
  */
102
106
  removeAllListeners(): void {
103
- this._listeners = {};
107
+ this.#listeners.clear();
104
108
  if (this.__isNative) {
105
109
  this._stopListeningForNativeValueUpdates();
106
110
  }
107
111
  }
108
112
 
109
113
  hasListeners(): boolean {
110
- return !!Object.keys(this._listeners).length;
114
+ return this.#listeners.size > 0;
111
115
  }
112
116
 
113
117
  _startListeningToNativeValueUpdates() {
@@ -123,7 +127,7 @@ export default class AnimatedNode {
123
127
  this._stopListeningForNativeValueUpdates();
124
128
  }
125
129
 
126
- NativeAnimatedAPI.startListeningToAnimatedNodeValue(this.__getNativeTag());
130
+ startListeningToAnimatedNodeValue(this.__getNativeTag());
127
131
  this.__nativeAnimatedValueListener =
128
132
  NativeAnimatedHelper.nativeEventEmitter.addListener(
129
133
  'onAnimatedValueUpdate',
@@ -141,9 +145,10 @@ export default class AnimatedNode {
141
145
  }
142
146
 
143
147
  __callListeners(value: number): void {
144
- for (const key in this._listeners) {
145
- this._listeners[key]({value});
146
- }
148
+ const event = {value};
149
+ this.#listeners.forEach(listener => {
150
+ listener(event);
151
+ });
147
152
  }
148
153
 
149
154
  _stopListeningForNativeValueUpdates() {
@@ -153,21 +158,24 @@ export default class AnimatedNode {
153
158
 
154
159
  this.__nativeAnimatedValueListener.remove();
155
160
  this.__nativeAnimatedValueListener = null;
156
- NativeAnimatedAPI.stopListeningToAnimatedNodeValue(this.__getNativeTag());
161
+ stopListeningToAnimatedNodeValue(this.__getNativeTag());
157
162
  }
158
163
 
159
164
  __getNativeTag(): number {
160
- NativeAnimatedHelper.assertNativeAnimatedModule();
161
- invariant(
162
- this.__isNative,
163
- 'Attempt to get native tag from node not marked as "native"',
164
- );
165
-
166
- const nativeTag =
167
- this.__nativeTag ?? NativeAnimatedHelper.generateNewNodeTag();
165
+ let nativeTag = this.__nativeTag;
166
+ if (nativeTag == null) {
167
+ _assertNativeAnimatedModule?.();
168
+
169
+ // `__isNative` is initialized as false and only ever set to true. So we
170
+ // only need to check it once here when initializing `__nativeTag`.
171
+ invariant(
172
+ this.__isNative,
173
+ 'Attempt to get native tag from node not marked as "native"',
174
+ );
168
175
 
169
- if (this.__nativeTag == null) {
176
+ nativeTag = NativeAnimatedHelper.generateNewNodeTag();
170
177
  this.__nativeTag = nativeTag;
178
+
171
179
  const config = this.__getNativeConfig();
172
180
  if (this._platformConfig) {
173
181
  config.platformConfig = this._platformConfig;
@@ -175,9 +183,9 @@ export default class AnimatedNode {
175
183
  NativeAnimatedHelper.API.createAnimatedNode(nativeTag, config);
176
184
  this.__shouldUpdateListenersForNewNativeTag = true;
177
185
  }
178
-
179
186
  return nativeTag;
180
187
  }
188
+
181
189
  __getNativeConfig(): Object {
182
190
  throw new Error(
183
191
  'This JS animated node type cannot be used as native animated node',
@@ -191,6 +199,7 @@ export default class AnimatedNode {
191
199
  __getPlatformConfig(): ?PlatformConfig {
192
200
  return this._platformConfig;
193
201
  }
202
+
194
203
  __setPlatformConfig(platformConfig: ?PlatformConfig) {
195
204
  this._platformConfig = platformConfig;
196
205
  }
@@ -19,7 +19,9 @@ import * as React from 'react';
19
19
 
20
20
  const MAX_DEPTH = 5;
21
21
 
22
- function isPlainObject(value: any): boolean {
22
+ /* $FlowIssue[incompatible-type-guard] - Flow does not know that the prototype
23
+ and ReactElement checks preserve the type refinement of `value`. */
24
+ function isPlainObject(value: mixed): value is $ReadOnly<{[string]: mixed}> {
23
25
  return (
24
26
  value !== null &&
25
27
  typeof value === 'object' &&
@@ -28,23 +30,29 @@ function isPlainObject(value: any): boolean {
28
30
  );
29
31
  }
30
32
 
31
- // Recurse through values, executing fn for any AnimatedNodes
32
- function visit(value: any, fn: any => void, depth: number = 0): void {
33
+ function flatAnimatedNodes(
34
+ value: mixed,
35
+ nodes: Array<AnimatedNode> = [],
36
+ depth: number = 0,
37
+ ): Array<AnimatedNode> {
33
38
  if (depth >= MAX_DEPTH) {
34
- return;
39
+ return nodes;
35
40
  }
36
-
37
41
  if (value instanceof AnimatedNode) {
38
- fn(value);
42
+ nodes.push(value);
39
43
  } else if (Array.isArray(value)) {
40
- value.forEach(element => {
41
- visit(element, fn, depth + 1);
42
- });
44
+ for (let ii = 0, length = value.length; ii < length; ii++) {
45
+ const element = value[ii];
46
+ flatAnimatedNodes(element, nodes, depth + 1);
47
+ }
43
48
  } else if (isPlainObject(value)) {
44
- Object.values(value).forEach(element => {
45
- visit(element, fn, depth + 1);
46
- });
49
+ const keys = Object.keys(value);
50
+ for (let ii = 0, length = keys.length; ii < length; ii++) {
51
+ const key = keys[ii];
52
+ flatAnimatedNodes(value[key], nodes, depth + 1);
53
+ }
47
54
  }
55
+ return nodes;
48
56
  }
49
57
 
50
58
  // Returns a copy of value with a transformation fn applied to any AnimatedNodes
@@ -59,7 +67,9 @@ function mapAnimatedNodes(value: any, fn: any => any, depth: number = 0): any {
59
67
  return value.map(element => mapAnimatedNodes(element, fn, depth + 1));
60
68
  } else if (isPlainObject(value)) {
61
69
  const result: {[string]: any} = {};
62
- for (const key in value) {
70
+ const keys = Object.keys(value);
71
+ for (let ii = 0, length = keys.length; ii < length; ii++) {
72
+ const key = keys[ii];
63
73
  result[key] = mapAnimatedNodes(value[key], fn, depth + 1);
64
74
  }
65
75
  return result;
@@ -68,34 +78,28 @@ function mapAnimatedNodes(value: any, fn: any => any, depth: number = 0): any {
68
78
  }
69
79
  }
70
80
 
71
- export function hasAnimatedNode(value: any, depth: number = 0): boolean {
72
- if (depth >= MAX_DEPTH) {
73
- return false;
74
- }
75
-
76
- if (value instanceof AnimatedNode) {
77
- return true;
78
- } else if (Array.isArray(value)) {
79
- for (const element of value) {
80
- if (hasAnimatedNode(element, depth + 1)) {
81
- return true;
82
- }
83
- }
84
- } else if (isPlainObject(value)) {
85
- for (const key in value) {
86
- if (hasAnimatedNode(value[key], depth + 1)) {
87
- return true;
88
- }
81
+ export default class AnimatedObject extends AnimatedWithChildren {
82
+ #nodes: $ReadOnlyArray<AnimatedNode>;
83
+ _value: mixed;
84
+
85
+ /**
86
+ * Creates an `AnimatedObject` if `value` contains `AnimatedNode` instances.
87
+ * Otherwise, returns `null`.
88
+ */
89
+ static from(value: mixed): ?AnimatedObject {
90
+ const nodes = flatAnimatedNodes(value);
91
+ if (nodes.length === 0) {
92
+ return null;
89
93
  }
94
+ return new AnimatedObject(nodes, value);
90
95
  }
91
- return false;
92
- }
93
96
 
94
- export default class AnimatedObject extends AnimatedWithChildren {
95
- _value: any;
96
-
97
- constructor(value: any) {
97
+ /**
98
+ * Should only be called by `AnimatedObject.from`.
99
+ */
100
+ constructor(nodes: $ReadOnlyArray<AnimatedNode>, value: mixed) {
98
101
  super();
102
+ this.#nodes = nodes;
99
103
  this._value = value;
100
104
  }
101
105
 
@@ -112,23 +116,28 @@ export default class AnimatedObject extends AnimatedWithChildren {
112
116
  }
113
117
 
114
118
  __attach(): void {
115
- super.__attach();
116
- visit(this._value, node => {
119
+ const nodes = this.#nodes;
120
+ for (let ii = 0, length = nodes.length; ii < length; ii++) {
121
+ const node = nodes[ii];
117
122
  node.__addChild(this);
118
- });
123
+ }
119
124
  }
120
125
 
121
126
  __detach(): void {
122
- visit(this._value, node => {
127
+ const nodes = this.#nodes;
128
+ for (let ii = 0, length = nodes.length; ii < length; ii++) {
129
+ const node = nodes[ii];
123
130
  node.__removeChild(this);
124
- });
131
+ }
125
132
  super.__detach();
126
133
  }
127
134
 
128
135
  __makeNative(platformConfig: ?PlatformConfig): void {
129
- visit(this._value, value => {
130
- value.__makeNative(platformConfig);
131
- });
136
+ const nodes = this.#nodes;
137
+ for (let ii = 0, length = nodes.length; ii < length; ii++) {
138
+ const node = nodes[ii];
139
+ node.__makeNative(platformConfig);
140
+ }
132
141
  super.__makeNative(platformConfig);
133
142
  }
134
143