@office-iss/react-native-win32 0.0.0-canary.275 → 0.0.0-canary.276

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 (26) hide show
  1. package/CHANGELOG.json +16 -1
  2. package/CHANGELOG.md +12 -4
  3. package/Libraries/Animated/useAnimatedProps.js +2 -14
  4. package/Libraries/Components/Keyboard/KeyboardAvoidingView.js +2 -0
  5. package/Libraries/Components/View/ViewPropTypes.js +0 -3
  6. package/Libraries/Components/View/ViewPropTypes.win32.js +0 -3
  7. package/Libraries/Core/ExceptionsManager.js +6 -0
  8. package/Libraries/Core/ReactNativeVersion.js +1 -1
  9. package/Libraries/Image/Image.android.js +0 -2
  10. package/Libraries/Image/ImageViewNativeComponent.js +3 -1
  11. package/Libraries/NewAppScreen/components/HermesBadge.js +1 -1
  12. package/Libraries/StyleSheet/processBackgroundImage.js +87 -110
  13. package/Libraries/TurboModule/TurboModuleRegistry.js +5 -5
  14. package/Libraries/Utilities/Appearance.js +3 -1
  15. package/jest/setup.js +5 -1
  16. package/overrides.json +3 -3
  17. package/package.json +11 -11
  18. package/src/private/featureflags/ReactNativeFeatureFlags.js +7 -18
  19. package/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +1 -2
  20. package/src/private/setup/setUpDOM.js +14 -6
  21. package/src/private/setup/setUpMutationObserver.js +5 -0
  22. package/src/private/webapis/intersectionobserver/IntersectionObserver.js +96 -11
  23. package/src/private/webapis/intersectionobserver/IntersectionObserverEntry.js +26 -0
  24. package/src/private/webapis/intersectionobserver/IntersectionObserverManager.js +1 -0
  25. package/src/private/webapis/intersectionobserver/specs/NativeIntersectionObserver.js +1 -0
  26. package/src/private/webapis/intersectionobserver/specs/__mocks__/NativeIntersectionObserver.js +9 -0
package/CHANGELOG.json CHANGED
@@ -2,7 +2,22 @@
2
2
  "name": "@office-iss/react-native-win32",
3
3
  "entries": [
4
4
  {
5
- "date": "Sat, 14 Dec 2024 06:28:25 GMT",
5
+ "date": "Wed, 18 Dec 2024 06:21:37 GMT",
6
+ "version": "0.0.0-canary.276",
7
+ "tag": "@office-iss/react-native-win32_v0.0.0-canary.276",
8
+ "comments": {
9
+ "prerelease": [
10
+ {
11
+ "author": "email not defined",
12
+ "package": "@office-iss/react-native-win32",
13
+ "commit": "d669d78450c97decf9f9f5acc09e526ddf8dd911",
14
+ "comment": "integrate RN-Nightly 0.77.0-nightly-20241125-4cffff35e"
15
+ }
16
+ ]
17
+ }
18
+ },
19
+ {
20
+ "date": "Sat, 14 Dec 2024 06:29:12 GMT",
6
21
  "version": "0.0.0-canary.275",
7
22
  "tag": "@office-iss/react-native-win32_v0.0.0-canary.275",
8
23
  "comments": {
package/CHANGELOG.md CHANGED
@@ -1,17 +1,25 @@
1
1
  # Change Log - @office-iss/react-native-win32
2
2
 
3
- <!-- This log was last generated on Sat, 14 Dec 2024 06:28:25 GMT and should not be manually modified. -->
3
+ <!-- This log was last generated on Wed, 18 Dec 2024 06:21:37 GMT and should not be manually modified. -->
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
- ## 0.0.0-canary.275
7
+ ## 0.0.0-canary.276
8
8
 
9
- Sat, 14 Dec 2024 06:28:25 GMT
9
+ Wed, 18 Dec 2024 06:21:37 GMT
10
10
 
11
11
  ### Changes
12
12
 
13
- - integrate RN Nightly 0.77.0-nightly-20241118-3986eefed (tatianakapos@microsoft.com)
13
+ - integrate RN-Nightly 0.77.0-nightly-20241125-4cffff35e (email not defined)
14
14
 
15
+ ## 0.0.0-canary.275
16
+
17
+ Sat, 14 Dec 2024 06:29:12 GMT
18
+
19
+ ### Changes
20
+
21
+ - integrate RN Nightly 0.77.0-nightly-20241118-3986eefed (tatianakapos@microsoft.com)
22
+
15
23
  ## 0.0.0-canary.274
16
24
 
17
25
  Fri, 06 Dec 2024 06:22:27 GMT
@@ -73,8 +73,6 @@ export default function useAnimatedProps<TProps: {...}, TInstance>(
73
73
 
74
74
  const useNativePropsInFabric =
75
75
  ReactNativeFeatureFlags.shouldUseSetNativePropsInFabric();
76
- const useSetNativePropsInNativeAnimationsInFabric =
77
- ReactNativeFeatureFlags.shouldUseSetNativePropsInNativeAnimationsInFabric();
78
76
 
79
77
  const useAnimatedPropsLifecycle =
80
78
  ReactNativeFeatureFlags.useInsertionEffectsForAnimations()
@@ -119,12 +117,7 @@ export default function useAnimatedProps<TProps: {...}, TInstance>(
119
117
  if (isFabricNode) {
120
118
  // Call `scheduleUpdate` to synchronise Fiber and Shadow tree.
121
119
  // Must not be called in Paper.
122
- if (useSetNativePropsInNativeAnimationsInFabric) {
123
- // $FlowFixMe[incompatible-use]
124
- instance.setNativeProps(node.__getAnimatedValue());
125
- } else {
126
- scheduleUpdate();
127
- }
120
+ scheduleUpdate();
128
121
  }
129
122
  return;
130
123
  }
@@ -201,12 +194,7 @@ export default function useAnimatedProps<TProps: {...}, TInstance>(
201
194
  }
202
195
  };
203
196
  },
204
- [
205
- node,
206
- useNativePropsInFabric,
207
- useSetNativePropsInNativeAnimationsInFabric,
208
- props,
209
- ],
197
+ [node, useNativePropsInFabric, props],
210
198
  );
211
199
  const callbackRef = useRefEffect<TInstance>(refEffect);
212
200
 
@@ -116,6 +116,8 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
116
116
  };
117
117
 
118
118
  _onLayout = async (event: ViewLayoutEvent) => {
119
+ event.persist();
120
+
119
121
  const oldFrame = this._frame;
120
122
  this._frame = event.nativeEvent.layout;
121
123
  if (!this._initialFrameHeight) {
@@ -567,9 +567,6 @@ export type ViewProps = $ReadOnly<{|
567
567
  * optimization. Set this property to `false` to disable this optimization and
568
568
  * ensure that this `View` exists in the native view hierarchy.
569
569
  *
570
- * @platform android
571
- * In Fabric, this prop is used in ios as well.
572
- *
573
570
  * See https://reactnative.dev/docs/view#collapsable
574
571
  */
575
572
  collapsable?: ?boolean,
@@ -628,9 +628,6 @@ export type ViewProps = $ReadOnly<{|
628
628
  * optimization. Set this property to `false` to disable this optimization and
629
629
  * ensure that this `View` exists in the native view hierarchy.
630
630
  *
631
- * @platform android
632
- * In Fabric, this prop is used in ios as well.
633
- *
634
631
  * See https://reactnative.dev/docs/view#collapsable
635
632
  */
636
633
  collapsable?: ?boolean,
@@ -121,6 +121,12 @@ function reportException(
121
121
  const NativeExceptionsManager =
122
122
  require('./NativeExceptionsManager').default;
123
123
  if (NativeExceptionsManager) {
124
+ if (isFatal) {
125
+ if (global.RN$hasHandledFatalException?.()) {
126
+ return;
127
+ }
128
+ global.RN$notifyOfFatalException?.();
129
+ }
124
130
  NativeExceptionsManager.reportException(data);
125
131
  }
126
132
  }
@@ -17,7 +17,7 @@ const version: $ReadOnly<{
17
17
  major: 0,
18
18
  minor: 77,
19
19
  patch: 0,
20
- prerelease: 'nightly-20241118-3986eefed',
20
+ prerelease: 'nightly-20241125-4cffff35e',
21
21
  };
22
22
 
23
23
  module.exports = {version};
@@ -133,7 +133,6 @@ let BaseImage: AbstractImageAndroid = React.forwardRef(
133
133
  width: undefined,
134
134
  height: undefined,
135
135
  };
136
- const defaultSource = resolveAssetSource(props.defaultSource);
137
136
  const loadingIndicatorSource = resolveAssetSource(
138
137
  props.loadingIndicatorSource,
139
138
  );
@@ -179,7 +178,6 @@ let BaseImage: AbstractImageAndroid = React.forwardRef(
179
178
  /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found
180
179
  * when making Flow check .android.js files. */
181
180
  headers: (source?.[0]?.headers || source?.headers: ?{[string]: string}),
182
- defaultSrc: defaultSource ? defaultSource.uri : null,
183
181
  loadingIndicatorSrc: loadingIndicatorSource
184
182
  ? loadingIndicatorSource.uri
185
183
  : null,
@@ -82,6 +82,9 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig =
82
82
  },
83
83
  validAttributes: {
84
84
  blurRadius: true,
85
+ defaultSource: {
86
+ process: require('./resolveAssetSource'),
87
+ },
85
88
  internal_analyticTag: true,
86
89
  resizeMethod: true,
87
90
  resizeMode: true,
@@ -100,7 +103,6 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig =
100
103
  borderRadius: true,
101
104
  headers: true,
102
105
  shouldNotifyLoadEvents: true,
103
- defaultSrc: true,
104
106
  overlayColor: {
105
107
  process: require('../StyleSheet/processColor').default,
106
108
  },
@@ -40,8 +40,8 @@ const HermesBadge = (): Node => {
40
40
  const styles = StyleSheet.create({
41
41
  badge: {
42
42
  position: 'absolute',
43
- top: 8,
44
43
  right: 12,
44
+ bottom: 8,
45
45
  },
46
46
  badgeText: {
47
47
  fontSize: 14,
@@ -14,25 +14,28 @@ import type {ProcessedColorValue} from './processColor';
14
14
  import type {GradientValue} from './StyleSheetTypes';
15
15
 
16
16
  const processColor = require('./processColor').default;
17
- const DIRECTION_REGEX =
18
- /^to\s+(?:top|bottom|left|right)(?:\s+(?:top|bottom|left|right))?/;
17
+ const DIRECTION_KEYWORD_REGEX =
18
+ /^to\s+(?:top|bottom|left|right)(?:\s+(?:top|bottom|left|right))?/i;
19
19
  const ANGLE_UNIT_REGEX = /^([+-]?\d*\.?\d+)(deg|grad|rad|turn)$/i;
20
20
 
21
- const TO_BOTTOM_START_END_POINTS = {
22
- start: {x: 0.5, y: 0},
23
- end: {x: 0.5, y: 1},
24
- };
21
+ type LinearGradientDirection =
22
+ | {type: 'angle', value: number}
23
+ | {type: 'keyword', value: string};
25
24
 
26
25
  type ParsedGradientValue = {
27
26
  type: 'linearGradient',
28
- start: {x: number, y: number},
29
- end: {x: number, y: number},
27
+ direction: LinearGradientDirection,
30
28
  colorStops: $ReadOnlyArray<{
31
29
  color: ProcessedColorValue,
32
30
  position: number,
33
31
  }>,
34
32
  };
35
33
 
34
+ const DEFAULT_DIRECTION: LinearGradientDirection = {
35
+ type: 'angle',
36
+ value: 180,
37
+ };
38
+
36
39
  export default function processBackgroundImage(
37
40
  backgroundImage: ?($ReadOnlyArray<GradientValue> | string),
38
41
  ): $ReadOnlyArray<ParsedGradientValue> {
@@ -76,37 +79,43 @@ export default function processBackgroundImage(
76
79
  }
77
80
  }
78
81
 
79
- let points: {
80
- start: ParsedGradientValue['start'],
81
- end: ParsedGradientValue['end'],
82
- } | null = null;
83
-
84
- if (typeof bgImage.direction === 'undefined') {
85
- points = TO_BOTTOM_START_END_POINTS;
86
- } else if (ANGLE_UNIT_REGEX.test(bgImage.direction)) {
87
- const angle = parseAngle(bgImage.direction);
88
- if (angle != null) {
89
- points = calculateStartEndPointsFromAngle(angle);
90
- }
91
- } else if (DIRECTION_REGEX.test(bgImage.direction)) {
92
- const processedPoints = calculateStartEndPointsFromDirection(
93
- bgImage.direction,
94
- );
95
- if (processedPoints != null) {
96
- points = processedPoints;
82
+ let direction: LinearGradientDirection = DEFAULT_DIRECTION;
83
+ const bgDirection =
84
+ bgImage.direction != null ? bgImage.direction.toLowerCase() : null;
85
+
86
+ if (bgDirection != null) {
87
+ if (ANGLE_UNIT_REGEX.test(bgDirection)) {
88
+ const parsedAngle = getAngleInDegrees(bgDirection);
89
+ if (parsedAngle != null) {
90
+ direction = {
91
+ type: 'angle',
92
+ value: parsedAngle,
93
+ };
94
+ } else {
95
+ // If an angle is invalid, return an empty array and do not apply any gradient. Same as web.
96
+ return [];
97
+ }
98
+ } else if (DIRECTION_KEYWORD_REGEX.test(bgDirection)) {
99
+ const parsedDirection = getDirectionForKeyword(bgDirection);
100
+ if (parsedDirection != null) {
101
+ direction = parsedDirection;
102
+ } else {
103
+ // If a direction is invalid, return an empty array and do not apply any gradient. Same as web.
104
+ return [];
105
+ }
106
+ } else {
107
+ // If a direction is invalid, return an empty array and do not apply any gradient. Same as web.
108
+ return [];
97
109
  }
98
110
  }
99
111
 
100
112
  const fixedColorStops = getFixedColorStops(processedColorStops);
101
113
 
102
- if (points != null) {
103
- result = result.concat({
104
- type: 'linearGradient',
105
- start: points.start,
106
- end: points.end,
107
- colorStops: fixedColorStops,
108
- });
109
- }
114
+ result = result.concat({
115
+ type: 'linearGradient',
116
+ direction,
117
+ colorStops: fixedColorStops,
118
+ });
110
119
  }
111
120
  }
112
121
 
@@ -118,30 +127,39 @@ function parseCSSLinearGradient(
118
127
  ): $ReadOnlyArray<ParsedGradientValue> {
119
128
  const gradients = [];
120
129
  let match;
130
+
131
+ // matches one or more linear-gradient functions in CSS
121
132
  const linearGradientRegex = /linear-gradient\s*\(((?:\([^)]*\)|[^())])*)\)/gi;
122
133
 
123
134
  while ((match = linearGradientRegex.exec(cssString))) {
124
135
  const gradientContent = match[1];
125
136
  const parts = gradientContent.split(',');
126
- let points = TO_BOTTOM_START_END_POINTS;
137
+ let direction: LinearGradientDirection = DEFAULT_DIRECTION;
127
138
  const trimmedDirection = parts[0].trim().toLowerCase();
139
+
140
+ // matches individual color stops in a gradient function
141
+ // supports various color formats: named colors, hex colors, rgb(a), and hsl(a)
142
+ // e.g. "red 20%", "blue 50%", "rgba(0, 0, 0, 0.5) 30% 50%"
143
+ // TODO: does not support color hint syntax yet. It is WIP.
128
144
  const colorStopRegex =
129
145
  /\s*((?:(?:rgba?|hsla?)\s*\([^)]+\))|#[0-9a-fA-F]+|[a-zA-Z]+)(?:\s+(-?[0-9.]+%?)(?:\s+(-?[0-9.]+%?))?)?\s*/gi;
130
146
 
131
147
  if (ANGLE_UNIT_REGEX.test(trimmedDirection)) {
132
- const angle = parseAngle(trimmedDirection);
133
- if (angle != null) {
134
- points = calculateStartEndPointsFromAngle(angle);
148
+ const parsedAngle = getAngleInDegrees(trimmedDirection);
149
+ if (parsedAngle != null) {
150
+ direction = {
151
+ type: 'angle',
152
+ value: parsedAngle,
153
+ };
135
154
  parts.shift();
136
155
  } else {
137
156
  // If an angle is invalid, return an empty array and do not apply any gradient. Same as web.
138
157
  return [];
139
158
  }
140
- } else if (DIRECTION_REGEX.test(trimmedDirection)) {
141
- const parsedPoints =
142
- calculateStartEndPointsFromDirection(trimmedDirection);
143
- if (parsedPoints != null) {
144
- points = parsedPoints;
159
+ } else if (DIRECTION_KEYWORD_REGEX.test(trimmedDirection)) {
160
+ const parsedDirection = getDirectionForKeyword(trimmedDirection);
161
+ if (parsedDirection != null) {
162
+ direction = parsedDirection;
145
163
  parts.shift();
146
164
  } else {
147
165
  // If a direction is invalid, return an empty array and do not apply any gradient. Same as web.
@@ -198,8 +216,7 @@ function parseCSSLinearGradient(
198
216
 
199
217
  gradients.push({
200
218
  type: 'linearGradient',
201
- start: points.start,
202
- end: points.end,
219
+ direction,
203
220
  colorStops: fixedColorStops,
204
221
  });
205
222
  }
@@ -207,83 +224,43 @@ function parseCSSLinearGradient(
207
224
  return gradients;
208
225
  }
209
226
 
210
- function calculateStartEndPointsFromDirection(direction: string): ?{
211
- start: {x: number, y: number},
212
- end: {x: number, y: number},
213
- } {
227
+ function getDirectionForKeyword(direction?: string): ?LinearGradientDirection {
228
+ if (direction == null) {
229
+ return null;
230
+ }
214
231
  // Remove extra whitespace
215
- const normalizedDirection = direction.replace(/\s+/g, ' ');
232
+ const normalized = direction.replace(/\s+/g, ' ').toLowerCase();
216
233
 
217
- switch (normalizedDirection) {
234
+ switch (normalized) {
235
+ case 'to top':
236
+ return {type: 'angle', value: 0};
218
237
  case 'to right':
219
- return {
220
- start: {x: 0, y: 0.5},
221
- end: {x: 1, y: 0.5},
222
- };
223
- case 'to left':
224
- return {
225
- start: {x: 1, y: 0.5},
226
- end: {x: 0, y: 0.5},
227
- };
238
+ return {type: 'angle', value: 90};
228
239
  case 'to bottom':
229
- return TO_BOTTOM_START_END_POINTS;
230
- case 'to top':
231
- return {
232
- start: {x: 0.5, y: 1},
233
- end: {x: 0.5, y: 0},
234
- };
240
+ return {type: 'angle', value: 180};
241
+ case 'to left':
242
+ return {type: 'angle', value: 270};
243
+ case 'to top right':
244
+ case 'to right top':
245
+ return {type: 'keyword', value: 'to top right'};
235
246
  case 'to bottom right':
236
247
  case 'to right bottom':
237
- return {
238
- start: {x: 0, y: 0},
239
- end: {x: 1, y: 1},
240
- };
248
+ return {type: 'keyword', value: 'to bottom right'};
241
249
  case 'to top left':
242
250
  case 'to left top':
243
- return {
244
- start: {x: 1, y: 1},
245
- end: {x: 0, y: 0},
246
- };
251
+ return {type: 'keyword', value: 'to top left'};
247
252
  case 'to bottom left':
248
253
  case 'to left bottom':
249
- return {
250
- start: {x: 1, y: 0},
251
- end: {x: 0, y: 1},
252
- };
253
- case 'to top right':
254
- case 'to right top':
255
- return {
256
- start: {x: 0, y: 1},
257
- end: {x: 1, y: 0},
258
- };
254
+ return {type: 'keyword', value: 'to bottom left'};
259
255
  default:
260
256
  return null;
261
257
  }
262
258
  }
263
259
 
264
- function calculateStartEndPointsFromAngle(angleRadians: number): {
265
- start: {x: number, y: number},
266
- end: {x: number, y: number},
267
- } {
268
- // Normalize angle to be between 0 and 2π
269
- let angleRadiansNormalized = angleRadians % (2 * Math.PI);
270
- if (angleRadiansNormalized < 0) {
271
- angleRadiansNormalized += 2 * Math.PI;
260
+ function getAngleInDegrees(angle?: string): ?number {
261
+ if (angle == null) {
262
+ return null;
272
263
  }
273
-
274
- const endX = 0.5 + 0.5 * Math.sin(angleRadiansNormalized);
275
- const endY = 0.5 - 0.5 * Math.cos(angleRadiansNormalized);
276
-
277
- const startX = 1 - endX;
278
- const startY = 1 - endY;
279
-
280
- return {
281
- start: {x: startX, y: startY},
282
- end: {x: endX, y: endY},
283
- };
284
- }
285
-
286
- function parseAngle(angle: string): ?number {
287
264
  const match = angle.match(ANGLE_UNIT_REGEX);
288
265
  if (!match) {
289
266
  return null;
@@ -294,13 +271,13 @@ function parseAngle(angle: string): ?number {
294
271
  const numericValue = parseFloat(value);
295
272
  switch (unit) {
296
273
  case 'deg':
297
- return (numericValue * Math.PI) / 180;
274
+ return numericValue;
298
275
  case 'grad':
299
- return (numericValue * Math.PI) / 200;
276
+ return numericValue * 0.9; // 1 grad = 0.9 degrees
300
277
  case 'rad':
301
- return numericValue;
278
+ return (numericValue * 180) / Math.PI;
302
279
  case 'turn':
303
- return numericValue * 2 * Math.PI;
280
+ return numericValue * 360; // 1 turn = 360 degrees
304
281
  default:
305
282
  return null;
306
283
  }
@@ -16,9 +16,6 @@ const NativeModules = require('../BatchedBridge/NativeModules');
16
16
 
17
17
  const turboModuleProxy = global.__turboModuleProxy;
18
18
 
19
- const useLegacyNativeModuleInterop =
20
- global.RN$Bridgeless !== true || global.RN$TurboInterop === true;
21
-
22
19
  function requireModule<T: TurboModule>(name: string): ?T {
23
20
  if (turboModuleProxy != null) {
24
21
  const module: ?T = turboModuleProxy(name);
@@ -27,8 +24,11 @@ function requireModule<T: TurboModule>(name: string): ?T {
27
24
  }
28
25
  }
29
26
 
30
- if (useLegacyNativeModuleInterop) {
31
- // Backward compatibility layer during migration.
27
+ if (
28
+ global.RN$Bridgeless !== true ||
29
+ global.RN$TurboInterop === true ||
30
+ global.RN$UnifiedNativeModuleProxy === true
31
+ ) {
32
32
  const legacyModule: ?T = NativeModules[name];
33
33
  if (legacyModule != null) {
34
34
  return legacyModule;
@@ -105,7 +105,9 @@ export function setColorScheme(colorScheme: ?ColorSchemeName): void {
105
105
  const {NativeAppearance} = state;
106
106
  if (NativeAppearance != null) {
107
107
  NativeAppearance.setColorScheme(colorScheme ?? 'unspecified');
108
- state.appearance = {colorScheme};
108
+ state.appearance = {
109
+ colorScheme: toColorScheme(NativeAppearance.getColorScheme()),
110
+ };
109
111
  }
110
112
  }
111
113
 
package/jest/setup.js CHANGED
@@ -448,4 +448,8 @@ jest
448
448
  return jest.requireActual(
449
449
  '../Libraries/ReactNative/RendererImplementation',
450
450
  );
451
- });
451
+ })
452
+ .mock('../Libraries/Utilities/useColorScheme', () => ({
453
+ __esModule: true,
454
+ default: jest.fn().mockReturnValue('light'),
455
+ }));
package/overrides.json CHANGED
@@ -7,13 +7,13 @@
7
7
  "**/__snapshots__/**",
8
8
  "src-win/rntypes/**"
9
9
  ],
10
- "baseVersion": "0.77.0-nightly-20241118-3986eefed",
10
+ "baseVersion": "0.77.0-nightly-20241125-4cffff35e",
11
11
  "overrides": [
12
12
  {
13
13
  "type": "derived",
14
14
  "file": ".flowconfig",
15
15
  "baseFile": ".flowconfig",
16
- "baseHash": "afa414432bb8dae6ce606a148b74f85fcef8a59d"
16
+ "baseHash": "168a2b4bcf33aba4727eb608902b17b4eb74c95d"
17
17
  },
18
18
  {
19
19
  "type": "derived",
@@ -197,7 +197,7 @@
197
197
  "type": "patch",
198
198
  "file": "src-win/Libraries/Components/View/ViewPropTypes.win32.js",
199
199
  "baseFile": "packages/react-native/Libraries/Components/View/ViewPropTypes.js",
200
- "baseHash": "a742b26e4c96fdefb07779e40bc58cd1cc872675",
200
+ "baseHash": "994aead3d5ab49e6bd34a497094a4bc131a8bdb1",
201
201
  "issue": 6240
202
202
  },
203
203
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@office-iss/react-native-win32",
3
- "version": "0.0.0-canary.275",
3
+ "version": "0.0.0-canary.276",
4
4
  "description": "Implementation of react native on top of Office's Win32 platform.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,19 +30,19 @@
30
30
  "@react-native-community/cli-platform-android": "15.0.0-alpha.2",
31
31
  "@react-native-community/cli-platform-ios": "15.0.0-alpha.2",
32
32
  "@react-native/assets": "1.0.0",
33
- "@react-native/assets-registry": "0.77.0-nightly-20241118-3986eefed",
34
- "@react-native/codegen": "0.77.0-nightly-20241118-3986eefed",
35
- "@react-native/community-cli-plugin": "0.77.0-nightly-20241118-3986eefed",
36
- "@react-native/gradle-plugin": "0.77.0-nightly-20241118-3986eefed",
37
- "@react-native/js-polyfills": "0.77.0-nightly-20241118-3986eefed",
38
- "@react-native/normalize-colors": "0.77.0-nightly-20241118-3986eefed",
39
- "@react-native/virtualized-lists": "0.77.0-nightly-20241118-3986eefed",
33
+ "@react-native/assets-registry": "0.77.0-nightly-20241125-4cffff35e",
34
+ "@react-native/codegen": "0.77.0-nightly-20241125-4cffff35e",
35
+ "@react-native/community-cli-plugin": "0.77.0-nightly-20241125-4cffff35e",
36
+ "@react-native/gradle-plugin": "0.77.0-nightly-20241125-4cffff35e",
37
+ "@react-native/js-polyfills": "0.77.0-nightly-20241125-4cffff35e",
38
+ "@react-native/normalize-colors": "0.77.0-nightly-20241125-4cffff35e",
39
+ "@react-native/virtualized-lists": "0.77.0-nightly-20241125-4cffff35e",
40
40
  "abort-controller": "^3.0.0",
41
41
  "anser": "^1.4.9",
42
42
  "ansi-regex": "^5.0.0",
43
43
  "art": "^0.10.0",
44
44
  "babel-jest": "^29.7.0",
45
- "babel-plugin-syntax-hermes-parser": "0.24.0",
45
+ "babel-plugin-syntax-hermes-parser": "0.25.1",
46
46
  "base64-js": "^1.5.1",
47
47
  "chalk": "^4.0.0",
48
48
  "commander": "^12.0.0",
@@ -90,14 +90,14 @@
90
90
  "just-scripts": "^1.3.3",
91
91
  "prettier": "2.8.8",
92
92
  "react": "18.3.1",
93
- "react-native": "0.77.0-nightly-20241118-3986eefed",
93
+ "react-native": "0.77.0-nightly-20241125-4cffff35e",
94
94
  "react-native-platform-override": "^1.9.49",
95
95
  "typescript": "5.0.4"
96
96
  },
97
97
  "peerDependencies": {
98
98
  "@types/react": "^18.2.6",
99
99
  "react": "^18.2.0",
100
- "react-native": "0.77.0-nightly-20241118-3986eefed"
100
+ "react-native": "0.77.0-nightly-20241125-4cffff35e"
101
101
  },
102
102
  "beachball": {
103
103
  "defaultNpmTag": "canary",
@@ -4,7 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @generated SignedSource<<8334fdc6d34d4a090eb8be73f92ccd0f>>
7
+ * @generated SignedSource<<83b5798ee1c7a28fffbf110e19641d69>>
8
8
  * @flow strict
9
9
  */
10
10
 
@@ -31,17 +31,16 @@ export type ReactNativeFeatureFlagsJsOnly = {
31
31
  animatedShouldDebounceQueueFlush: Getter<boolean>,
32
32
  animatedShouldUseSingleOp: Getter<boolean>,
33
33
  disableInteractionManager: Getter<boolean>,
34
+ disableInteractionManagerInBatchinator: Getter<boolean>,
34
35
  enableAccessToHostTreeInFabric: Getter<boolean>,
35
36
  enableAnimatedAllowlist: Getter<boolean>,
36
37
  enableAnimatedClearImmediateFix: Getter<boolean>,
37
38
  enableAnimatedPropsMemo: Getter<boolean>,
38
- enableOptimisedVirtualizedCells: Getter<boolean>,
39
39
  isLayoutAnimationEnabled: Getter<boolean>,
40
40
  shouldSkipStateUpdatesForLoopingAnimations: Getter<boolean>,
41
41
  shouldUseAnimatedObjectForTransform: Getter<boolean>,
42
42
  shouldUseRemoveClippedSubviewsAsDefaultOnIOS: Getter<boolean>,
43
43
  shouldUseSetNativePropsInFabric: Getter<boolean>,
44
- shouldUseSetNativePropsInNativeAnimationsInFabric: Getter<boolean>,
45
44
  useInsertionEffectsForAnimations: Getter<boolean>,
46
45
  useRefsForTextInputState: Getter<boolean>,
47
46
  };
@@ -86,7 +85,6 @@ export type ReactNativeFeatureFlags = {
86
85
  initEagerTurboModulesOnNativeModulesQueueAndroid: Getter<boolean>,
87
86
  lazyAnimationCallbacks: Getter<boolean>,
88
87
  loadVectorDrawablesOnImages: Getter<boolean>,
89
- setAndroidLayoutDirection: Getter<boolean>,
90
88
  traceTurboModulePromiseRejectionsOnAndroid: Getter<boolean>,
91
89
  useAlwaysAvailableJSErrorHandling: Getter<boolean>,
92
90
  useFabricInterop: Getter<boolean>,
@@ -119,6 +117,11 @@ export const animatedShouldUseSingleOp: Getter<boolean> = createJavaScriptFlagGe
119
117
  */
120
118
  export const disableInteractionManager: Getter<boolean> = createJavaScriptFlagGetter('disableInteractionManager', false);
121
119
 
120
+ /**
121
+ * Skips InteractionManager in `Batchinator` and invokes callbacks synchronously.
122
+ */
123
+ export const disableInteractionManagerInBatchinator: Getter<boolean> = createJavaScriptFlagGetter('disableInteractionManagerInBatchinator', false);
124
+
122
125
  /**
123
126
  * Enables access to the host tree in Fabric using DOM-compatible APIs.
124
127
  */
@@ -139,11 +142,6 @@ export const enableAnimatedClearImmediateFix: Getter<boolean> = createJavaScript
139
142
  */
140
143
  export const enableAnimatedPropsMemo: Getter<boolean> = createJavaScriptFlagGetter('enableAnimatedPropsMemo', true);
141
144
 
142
- /**
143
- * Removing unnecessary rerenders Virtualized cells after any rerenders of Virualized list. Works with strict=true option
144
- */
145
- export const enableOptimisedVirtualizedCells: Getter<boolean> = createJavaScriptFlagGetter('enableOptimisedVirtualizedCells', false);
146
-
147
145
  /**
148
146
  * Function used to enable / disabled Layout Animations in React Native.
149
147
  */
@@ -169,11 +167,6 @@ export const shouldUseRemoveClippedSubviewsAsDefaultOnIOS: Getter<boolean> = cre
169
167
  */
170
168
  export const shouldUseSetNativePropsInFabric: Getter<boolean> = createJavaScriptFlagGetter('shouldUseSetNativePropsInFabric', true);
171
169
 
172
- /**
173
- * Enables use of setNativeProps in Native driven animations in Fabric.
174
- */
175
- export const shouldUseSetNativePropsInNativeAnimationsInFabric: Getter<boolean> = createJavaScriptFlagGetter('shouldUseSetNativePropsInNativeAnimationsInFabric', false);
176
-
177
170
  /**
178
171
  * Changes construction of the animation graph to `useInsertionEffect` instead of `useLayoutEffect`.
179
172
  */
@@ -328,10 +321,6 @@ export const lazyAnimationCallbacks: Getter<boolean> = createNativeFlagGetter('l
328
321
  * Adds support for loading vector drawable assets in the Image component (only on Android)
329
322
  */
330
323
  export const loadVectorDrawablesOnImages: Getter<boolean> = createNativeFlagGetter('loadVectorDrawablesOnImages', false);
331
- /**
332
- * Propagate layout direction to Android views.
333
- */
334
- export const setAndroidLayoutDirection: Getter<boolean> = createNativeFlagGetter('setAndroidLayoutDirection', true);
335
324
  /**
336
325
  * Enables storing js caller stack when creating promise in native module. This is useful in case of Promise rejection and tracing the cause.
337
326
  */
@@ -4,7 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @generated SignedSource<<ced421dd8531e9a074999a1cdb5e4a16>>
7
+ * @generated SignedSource<<4caaf5dbaa68614ce53c8aecbd512df8>>
8
8
  * @flow strict
9
9
  */
10
10
 
@@ -59,7 +59,6 @@ export interface Spec extends TurboModule {
59
59
  +initEagerTurboModulesOnNativeModulesQueueAndroid?: () => boolean;
60
60
  +lazyAnimationCallbacks?: () => boolean;
61
61
  +loadVectorDrawablesOnImages?: () => boolean;
62
- +setAndroidLayoutDirection?: () => boolean;
63
62
  +traceTurboModulePromiseRejectionsOnAndroid?: () => boolean;
64
63
  +useAlwaysAvailableJSErrorHandling?: () => boolean;
65
64
  +useFabricInterop?: () => boolean;
@@ -8,8 +8,7 @@
8
8
  * @format
9
9
  */
10
10
 
11
- import DOMRect from '../webapis/dom/geometry/DOMRect';
12
- import DOMRectReadOnly from '../webapis/dom/geometry/DOMRectReadOnly';
11
+ import {polyfillGlobal} from '../../../Libraries/Utilities/PolyfillFunctions';
13
12
 
14
13
  let initialized = false;
15
14
 
@@ -20,9 +19,18 @@ export default function setUpDOM() {
20
19
 
21
20
  initialized = true;
22
21
 
23
- // $FlowExpectedError[cannot-write] The global isn't writable anywhere but here, where we define it
24
- global.DOMRect = DOMRect;
22
+ polyfillGlobal(
23
+ 'DOMRect',
24
+ () => require('../webapis/dom/geometry/DOMRect').default,
25
+ );
25
26
 
26
- // $FlowExpectedError[cannot-write] The global isn't writable anywhere but here, where we define it
27
- global.DOMRectReadOnly = DOMRectReadOnly;
27
+ polyfillGlobal(
28
+ 'DOMRectReadOnly',
29
+ () => require('../webapis/dom/geometry/DOMRectReadOnly').default,
30
+ );
31
+
32
+ polyfillGlobal(
33
+ 'NodeList',
34
+ () => require('../webapis/dom/oldstylecollections/NodeList').default,
35
+ );
28
36
  }
@@ -23,4 +23,9 @@ export default function setUpMutationObserver() {
23
23
  'MutationObserver',
24
24
  () => require('../webapis/mutationobserver/MutationObserver').default,
25
25
  );
26
+
27
+ polyfillGlobal(
28
+ 'MutationRecord',
29
+ () => require('../webapis/mutationobserver/MutationRecord').default,
30
+ );
26
31
  }
@@ -25,6 +25,18 @@ type IntersectionObserverInit = {
25
25
  // root?: ReactNativeElement, // This option exists on the Web but it's not currently supported in React Native.
26
26
  // rootMargin?: string, // This option exists on the Web but it's not currently supported in React Native.
27
27
  threshold?: number | $ReadOnlyArray<number>,
28
+
29
+ /**
30
+ * This is a React Native specific option (not spec compliant) that specifies
31
+ * ratio threshold(s) of the intersection area to the total `root` area.
32
+ *
33
+ * If set, it will either be a singular ratio value between 0-1 (inclusive)
34
+ * or an array of such ratios.
35
+ *
36
+ * Note: If `rn_rootThreshold` is set, and `threshold` is not set,
37
+ * `threshold` will not default to [0] (as per spec)
38
+ */
39
+ rn_rootThreshold?: number | $ReadOnlyArray<number>,
28
40
  };
29
41
 
30
42
  /**
@@ -44,13 +56,16 @@ type IntersectionObserverInit = {
44
56
  * elements with the same observer.
45
57
  *
46
58
  * This implementation only supports the `threshold` option at the moment
47
- * (`root` and `rootMargin` are not supported).
59
+ * (`root` and `rootMargin` are not supported) and provides a React Native specific
60
+ * option `rn_rootThreshold`.
61
+ *
48
62
  */
49
63
  export default class IntersectionObserver {
50
64
  _callback: IntersectionObserverCallback;
51
65
  _thresholds: $ReadOnlyArray<number>;
52
66
  _observationTargets: Set<ReactNativeElement> = new Set();
53
67
  _intersectionObserverId: ?IntersectionObserverId;
68
+ _rootThresholds: $ReadOnlyArray<number> | null;
54
69
 
55
70
  constructor(
56
71
  callback: IntersectionObserverCallback,
@@ -83,7 +98,12 @@ export default class IntersectionObserver {
83
98
  }
84
99
 
85
100
  this._callback = callback;
86
- this._thresholds = normalizeThresholds(options?.threshold);
101
+
102
+ this._rootThresholds = normalizeRootThreshold(options?.rn_rootThreshold);
103
+ this._thresholds = normalizeThreshold(
104
+ options?.threshold,
105
+ this._rootThresholds != null, // only provide default if no rootThreshold
106
+ );
87
107
  }
88
108
 
89
109
  /**
@@ -115,14 +135,27 @@ export default class IntersectionObserver {
115
135
  * A list of thresholds, sorted in increasing numeric order, where each
116
136
  * threshold is a ratio of intersection area to bounding box area of an
117
137
  * observed target.
118
- * Notifications for a target are generated when any of the thresholds are
119
- * crossed for that target.
120
- * If no value was passed to the constructor, `0` is used.
138
+ * Notifications for a target are generated when any of the thresholds specified
139
+ * in `rn_rootThreshold` or `threshold` are crossed for that target.
140
+ *
141
+ * If no value was passed to the constructor, and no `rn_rootThreshold`
142
+ * is set, `0` is used.
121
143
  */
122
144
  get thresholds(): $ReadOnlyArray<number> {
123
145
  return this._thresholds;
124
146
  }
125
147
 
148
+ /**
149
+ * A list of root thresholds, sorted in increasing numeric order, where each
150
+ * threshold is a ratio of intersection area to bounding box area of the specified
151
+ * root view, which defaults to the viewport.
152
+ * Notifications for a target are generated when any of the thresholds specified
153
+ * in `rn_rootThreshold` or `threshold` are crossed for that target.
154
+ */
155
+ get rootThresholds(): $ReadOnlyArray<number> | null {
156
+ return this._rootThresholds;
157
+ }
158
+
126
159
  /**
127
160
  * Adds an element to the set of target elements being watched by the
128
161
  * `IntersectionObserver`.
@@ -221,32 +254,84 @@ export default class IntersectionObserver {
221
254
  * Converts the user defined `threshold` value into an array of sorted valid
222
255
  * threshold options for `IntersectionObserver` (double ∈ [0, 1]).
223
256
  *
257
+ * If `defaultEmpty` is true, then defaults to empty array, otherwise [0].
258
+ *
224
259
  * @example
225
260
  * normalizeThresholds(0.5); // → [0.5]
226
261
  * normalizeThresholds([1, 0.5, 0]); // → [0, 0.5, 1]
227
262
  * normalizeThresholds(['1', '0.5', '0']); // → [0, 0.5, 1]
263
+ * normalizeThresholds(null); // → [0]
264
+ * normalizeThresholds([null, null]); // → [0, 0]
265
+ *
266
+ * normalizeThresholds([null], true); // → [0]
267
+ * normalizeThresholds(null, true); // → []
268
+ * normalizeThresholds([], true); // → []
228
269
  */
229
- function normalizeThresholds(threshold: mixed): $ReadOnlyArray<number> {
270
+ function normalizeThreshold(
271
+ threshold: mixed,
272
+ defaultEmpty: boolean = false,
273
+ ): $ReadOnlyArray<number> {
230
274
  if (Array.isArray(threshold)) {
231
275
  if (threshold.length > 0) {
232
- return threshold.map(normalizeThresholdValue).sort();
276
+ return threshold
277
+ .map(t => normalizeThresholdValue(t, 'threshold'))
278
+ .map(t => t ?? 0)
279
+ .sort();
280
+ } else if (defaultEmpty) {
281
+ return [];
233
282
  } else {
234
283
  return [0];
235
284
  }
236
285
  }
237
286
 
238
- return [normalizeThresholdValue(threshold)];
287
+ const normalized = normalizeThresholdValue(threshold, 'threshold');
288
+ if (normalized == null) {
289
+ return defaultEmpty ? [] : [0];
290
+ }
291
+
292
+ return [normalized];
293
+ }
294
+
295
+ /**
296
+ * Converts the user defined `rn_rootThreshold` value into an array of sorted valid
297
+ * threshold options for `IntersectionObserver` (double ∈ [0, 1]).
298
+ *
299
+ * If invalid array or null, returns null.
300
+ *
301
+ * @example
302
+ * normalizeRootThreshold(0.5); // → [0.5]
303
+ * normalizeRootThresholds([1, 0.5, 0]); // → [0, 0.5, 1]
304
+ * normalizeRootThresholds([null, '0.5', '0']); // → [0, 0.5]
305
+ * normalizeRootThresholds(null); // → null
306
+ * normalizeRootThresholds([null, null]); // → null
307
+ */
308
+ function normalizeRootThreshold(
309
+ rootThreshold: mixed,
310
+ ): null | $ReadOnlyArray<number> {
311
+ if (Array.isArray(rootThreshold)) {
312
+ const normalizedArr = rootThreshold
313
+ .map(rt => normalizeThresholdValue(rt, 'rn_rootThreshold'))
314
+ .filter((rt): rt is number => rt != null)
315
+ .sort();
316
+ return normalizedArr.length === 0 ? null : normalizedArr;
317
+ }
318
+
319
+ const normalized = normalizeThresholdValue(rootThreshold, 'rn_rootThreshold');
320
+ return normalized == null ? null : [normalized];
239
321
  }
240
322
 
241
- function normalizeThresholdValue(threshold: mixed): number {
323
+ function normalizeThresholdValue(
324
+ threshold: mixed,
325
+ property: string,
326
+ ): null | number {
242
327
  if (threshold == null) {
243
- return 0;
328
+ return null;
244
329
  }
245
330
 
246
331
  const thresholdAsNumber = Number(threshold);
247
332
  if (!Number.isFinite(thresholdAsNumber)) {
248
333
  throw new TypeError(
249
- "Failed to read the 'threshold' property from 'IntersectionObserverInit': The provided double value is non-finite.",
334
+ `Failed to read the '${property}' property from 'IntersectionObserverInit': The provided double value is non-finite.`,
250
335
  );
251
336
  }
252
337
 
@@ -74,6 +74,32 @@ export default class IntersectionObserverEntry {
74
74
  return Math.min(ratio, 1);
75
75
  }
76
76
 
77
+ /**
78
+ * Returns the ratio of the `intersectionRect` to the `boundingRootRect`.
79
+ */
80
+ get rn_intersectionRootRatio(): number {
81
+ const intersectionRect = this.intersectionRect;
82
+
83
+ const rootRect = this._nativeEntry.rootRect;
84
+ const boundingRootRect = new DOMRectReadOnly(
85
+ rootRect[0],
86
+ rootRect[1],
87
+ rootRect[2],
88
+ rootRect[3],
89
+ );
90
+
91
+ if (boundingRootRect.width === 0 || boundingRootRect.height === 0) {
92
+ return 0;
93
+ }
94
+
95
+ const ratio =
96
+ (intersectionRect.width * intersectionRect.height) /
97
+ (boundingRootRect.width * boundingRootRect.height);
98
+
99
+ // Prevent rounding errors from making this value greater than 1.
100
+ return Math.min(ratio, 1);
101
+ }
102
+
77
103
  /**
78
104
  * Returns a `DOMRectReadOnly` representing the target's visible area.
79
105
  */
@@ -162,6 +162,7 @@ export function observe({
162
162
  intersectionObserverId,
163
163
  targetShadowNode,
164
164
  thresholds: registeredObserver.observer.thresholds,
165
+ rootThresholds: registeredObserver.observer.rootThresholds,
165
166
  });
166
167
 
167
168
  return true;
@@ -26,6 +26,7 @@ export type NativeIntersectionObserverObserveOptions = {
26
26
  intersectionObserverId: number,
27
27
  targetShadowNode: mixed,
28
28
  thresholds: $ReadOnlyArray<number>,
29
+ rootThresholds?: ?$ReadOnlyArray<number>,
29
30
  };
30
31
 
31
32
  export interface Spec extends TurboModule {
@@ -23,8 +23,10 @@ import nullthrows from 'nullthrows';
23
23
 
24
24
  type ObserverState = {
25
25
  thresholds: $ReadOnlyArray<number>,
26
+ rootThresholds?: ?$ReadOnlyArray<number>,
26
27
  intersecting: boolean,
27
28
  currentThreshold: ?number,
29
+ currentRootThreshold: ?number,
28
30
  };
29
31
 
30
32
  type Observation = {
@@ -74,8 +76,10 @@ const NativeIntersectionObserverMock = {
74
76
  ...options,
75
77
  state: {
76
78
  thresholds: options.thresholds,
79
+ rootThresholds: options.rootThresholds,
77
80
  intersecting: false,
78
81
  currentThreshold: null,
82
+ currentRootThreshold: null,
79
83
  },
80
84
  };
81
85
  observations.push(observation);
@@ -141,9 +145,14 @@ const NativeIntersectionObserverMock = {
141
145
  if (observation.state.intersecting) {
142
146
  observation.state.intersecting = false;
143
147
  observation.state.currentThreshold = null;
148
+ observation.state.currentRootThreshold = null;
144
149
  } else {
145
150
  observation.state.intersecting = true;
146
151
  observation.state.currentThreshold = observation.thresholds[0];
152
+ observation.state.currentRootThreshold =
153
+ observation.rootThresholds != null
154
+ ? observation.rootThresholds[0]
155
+ : null;
147
156
  }
148
157
  pendingRecords.push(createRecordFromObservation(observation));
149
158
  setImmediate(notifyIntersectionObservers);