@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.
- package/CHANGELOG.json +16 -1
- package/CHANGELOG.md +12 -4
- package/Libraries/Animated/useAnimatedProps.js +2 -14
- package/Libraries/Components/Keyboard/KeyboardAvoidingView.js +2 -0
- package/Libraries/Components/View/ViewPropTypes.js +0 -3
- package/Libraries/Components/View/ViewPropTypes.win32.js +0 -3
- package/Libraries/Core/ExceptionsManager.js +6 -0
- package/Libraries/Core/ReactNativeVersion.js +1 -1
- package/Libraries/Image/Image.android.js +0 -2
- package/Libraries/Image/ImageViewNativeComponent.js +3 -1
- package/Libraries/NewAppScreen/components/HermesBadge.js +1 -1
- package/Libraries/StyleSheet/processBackgroundImage.js +87 -110
- package/Libraries/TurboModule/TurboModuleRegistry.js +5 -5
- package/Libraries/Utilities/Appearance.js +3 -1
- package/jest/setup.js +5 -1
- package/overrides.json +3 -3
- package/package.json +11 -11
- package/src/private/featureflags/ReactNativeFeatureFlags.js +7 -18
- package/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +1 -2
- package/src/private/setup/setUpDOM.js +14 -6
- package/src/private/setup/setUpMutationObserver.js +5 -0
- package/src/private/webapis/intersectionobserver/IntersectionObserver.js +96 -11
- package/src/private/webapis/intersectionobserver/IntersectionObserverEntry.js +26 -0
- package/src/private/webapis/intersectionobserver/IntersectionObserverManager.js +1 -0
- package/src/private/webapis/intersectionobserver/specs/NativeIntersectionObserver.js +1 -0
- 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": "
|
|
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
|
|
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.
|
|
7
|
+
## 0.0.0-canary.276
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Wed, 18 Dec 2024 06:21:37 GMT
|
|
10
10
|
|
|
11
11
|
### Changes
|
|
12
12
|
|
|
13
|
-
- integrate RN
|
|
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
|
-
|
|
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
|
}
|
|
@@ -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
|
},
|
|
@@ -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
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (
|
|
96
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
|
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
|
|
133
|
-
if (
|
|
134
|
-
|
|
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 (
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
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
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
227
|
+
function getDirectionForKeyword(direction?: string): ?LinearGradientDirection {
|
|
228
|
+
if (direction == null) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
214
231
|
// Remove extra whitespace
|
|
215
|
-
const
|
|
232
|
+
const normalized = direction.replace(/\s+/g, ' ').toLowerCase();
|
|
216
233
|
|
|
217
|
-
switch (
|
|
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
|
|
230
|
-
case 'to
|
|
231
|
-
return {
|
|
232
|
-
|
|
233
|
-
|
|
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
|
|
265
|
-
|
|
266
|
-
|
|
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
|
|
274
|
+
return numericValue;
|
|
298
275
|
case 'grad':
|
|
299
|
-
return
|
|
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 *
|
|
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 (
|
|
31
|
-
|
|
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 = {
|
|
108
|
+
state.appearance = {
|
|
109
|
+
colorScheme: toColorScheme(NativeAppearance.getColorScheme()),
|
|
110
|
+
};
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
113
|
|
package/jest/setup.js
CHANGED
package/overrides.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"**/__snapshots__/**",
|
|
8
8
|
"src-win/rntypes/**"
|
|
9
9
|
],
|
|
10
|
-
"baseVersion": "0.77.0-nightly-
|
|
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": "
|
|
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": "
|
|
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.
|
|
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-
|
|
34
|
-
"@react-native/codegen": "0.77.0-nightly-
|
|
35
|
-
"@react-native/community-cli-plugin": "0.77.0-nightly-
|
|
36
|
-
"@react-native/gradle-plugin": "0.77.0-nightly-
|
|
37
|
-
"@react-native/js-polyfills": "0.77.0-nightly-
|
|
38
|
-
"@react-native/normalize-colors": "0.77.0-nightly-
|
|
39
|
-
"@react-native/virtualized-lists": "0.77.0-nightly-
|
|
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.
|
|
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-
|
|
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-
|
|
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<<
|
|
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<<
|
|
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
|
|
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
|
-
|
|
24
|
-
|
|
22
|
+
polyfillGlobal(
|
|
23
|
+
'DOMRect',
|
|
24
|
+
() => require('../webapis/dom/geometry/DOMRect').default,
|
|
25
|
+
);
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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
|
|
119
|
-
* crossed for that target.
|
|
120
|
-
*
|
|
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
|
|
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
|
|
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
|
-
|
|
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(
|
|
323
|
+
function normalizeThresholdValue(
|
|
324
|
+
threshold: mixed,
|
|
325
|
+
property: string,
|
|
326
|
+
): null | number {
|
|
242
327
|
if (threshold == null) {
|
|
243
|
-
return
|
|
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
|
-
|
|
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
|
*/
|
package/src/private/webapis/intersectionobserver/specs/__mocks__/NativeIntersectionObserver.js
CHANGED
|
@@ -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);
|