react-native-tvos 0.81.4-1 → 0.81.5-0

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.
@@ -17,6 +17,6 @@ export const version: $ReadOnly<{
17
17
  }> = {
18
18
  major: 0,
19
19
  minor: 81,
20
- patch: 4,
21
- prerelease: '1',
20
+ patch: 5,
21
+ prerelease: '0',
22
22
  };
@@ -445,7 +445,11 @@ CGSize RCTSwitchSize(void)
445
445
  #if TARGET_OS_TV
446
446
  rctSwitchSize = CGSizeMake(0.0, 0.0); // no switch on tvOS
447
447
  #else
448
- rctSwitchSize = [UISwitch new].intrinsicContentSize;
448
+ CGSize switchSize = [UISwitch new].intrinsicContentSize;
449
+ // Apple does not take into account the thumb border when returning the
450
+ // width of the UISwitch component, so we are adding 2 pixels for the border
451
+ // which is not customizable and it is the same for legacy and liquid glass.
452
+ rctSwitchSize = CGSizeMake(switchSize.width + 2, switchSize.height);
449
453
  #endif
450
454
  });
451
455
  });
@@ -23,8 +23,8 @@ NSDictionary* RCTGetReactNativeVersion(void)
23
23
  __rnVersion = @{
24
24
  RCTVersionMajor: @(0),
25
25
  RCTVersionMinor: @(81),
26
- RCTVersionPatch: @(4),
27
- RCTVersionPrerelease: @"1",
26
+ RCTVersionPatch: @(5),
27
+ RCTVersionPrerelease: @"0",
28
28
  };
29
29
  });
30
30
  return __rnVersion;
@@ -246,11 +246,10 @@ static NSDictionary *RCTExportedDimensions(CGFloat fontScale)
246
246
  - (void)interfaceOrientationDidChange
247
247
  {
248
248
  #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
249
- UIApplication *application = RCTSharedApplication();
250
- UIInterfaceOrientation nextOrientation = RCTKeyWindow().windowScene.interfaceOrientation;
249
+ UIWindow *window = RCTKeyWindow();
250
+ UIInterfaceOrientation nextOrientation = window.windowScene.interfaceOrientation;
251
251
 
252
- BOOL isRunningInFullScreen =
253
- CGRectEqualToRect(application.delegate.window.frame, application.delegate.window.screen.bounds);
252
+ BOOL isRunningInFullScreen = window ? CGRectEqualToRect(window.frame, window.screen.bounds) : YES;
254
253
  // We are catching here two situations for multitasking view:
255
254
  // a) The app is in Split View and the container gets resized -> !isRunningInFullScreen
256
255
  // b) The app changes to/from fullscreen example: App runs in slide over mode and goes into fullscreen->
@@ -9,10 +9,29 @@
9
9
  #import "RCTSwitchManager.h"
10
10
 
11
11
  #import <React/RCTUIManager.h>
12
+ #import <React/RCTUtils.h>
12
13
  #import "RCTBridge.h"
14
+ #import "RCTShadowView.h"
13
15
  #import "RCTSwitch.h"
14
16
  #import "UIView+React.h"
15
17
 
18
+ @interface RCTSwitchShadowView : RCTShadowView
19
+
20
+ @end
21
+
22
+ @implementation RCTSwitchShadowView
23
+
24
+ - (instancetype)init
25
+ {
26
+ if (self = [super init]) {
27
+ self.intrinsicContentSize = RCTSwitchSize();
28
+ }
29
+
30
+ return self;
31
+ }
32
+
33
+ @end
34
+
16
35
  @implementation RCTSwitchManager
17
36
 
18
37
  RCT_EXPORT_MODULE()
@@ -34,6 +53,11 @@ RCT_EXPORT_MODULE()
34
53
  }
35
54
  }
36
55
 
56
+ - (RCTShadowView *)shadowView
57
+ {
58
+ return [RCTSwitchShadowView new];
59
+ }
60
+
37
61
  RCT_EXPORT_METHOD(setValue : (nonnull NSNumber *)viewTag toValue : (BOOL)value)
38
62
  {
39
63
  [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.81.4-1
1
+ VERSION_NAME=0.81.5-0
2
2
  react.internal.publishingGroup=io.github.react-native-tvos
3
3
 
4
4
  android.useAndroidX=true
@@ -21,6 +21,7 @@ import com.facebook.infer.annotation.Assertions;
21
21
  import com.facebook.infer.annotation.Nullsafe;
22
22
  import com.facebook.react.bridge.Callback;
23
23
  import com.facebook.react.bridge.ReactContext;
24
+ import com.facebook.react.common.LifecycleState;
24
25
  import com.facebook.react.interfaces.fabric.ReactSurface;
25
26
  import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags;
26
27
  import com.facebook.react.modules.core.PermissionListener;
@@ -255,7 +256,7 @@ public class ReactActivityDelegate {
255
256
 
256
257
  public void onRequestPermissionsResult(
257
258
  final int requestCode, final String[] permissions, final int[] grantResults) {
258
- mPermissionsCallback =
259
+ Callback permissionsCallback =
259
260
  args -> {
260
261
  if (mPermissionListener != null
261
262
  && mPermissionListener.onRequestPermissionsResult(
@@ -263,6 +264,29 @@ public class ReactActivityDelegate {
263
264
  mPermissionListener = null;
264
265
  }
265
266
  };
267
+
268
+ LifecycleState lifecycle;
269
+ if (isFabricEnabled()) {
270
+ ReactHost reactHost = getReactHost();
271
+ lifecycle = reactHost != null ? reactHost.getLifecycleState() : LifecycleState.BEFORE_CREATE;
272
+ } else {
273
+ ReactNativeHost reactNativeHost = getReactNativeHost();
274
+ if (!reactNativeHost.hasInstance()) {
275
+ lifecycle = LifecycleState.BEFORE_CREATE;
276
+ } else {
277
+ lifecycle = reactNativeHost.getReactInstanceManager().getLifecycleState();
278
+ }
279
+ }
280
+
281
+ // If the permission request didn't show a dialog to the user, we can call the callback
282
+ // immediately.
283
+ // Otherwise, we need to wait until onResume to call it.
284
+ if (lifecycle == LifecycleState.RESUMED) {
285
+ permissionsCallback.invoke();
286
+ return;
287
+ }
288
+
289
+ mPermissionsCallback = permissionsCallback;
266
290
  }
267
291
 
268
292
  protected Context getContext() {
@@ -14,7 +14,7 @@ public object ReactNativeVersion {
14
14
  public val VERSION: Map<String, Any?> = mapOf(
15
15
  "major" to 0,
16
16
  "minor" to 81,
17
- "patch" to 4,
18
- "prerelease" to "1"
17
+ "patch" to 5,
18
+ "prerelease" to "0"
19
19
  )
20
20
  }
@@ -51,7 +51,7 @@ import kotlin.math.min
51
51
  * constructed in superclass.
52
52
  */
53
53
  @LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
54
- public class ReactTextShadowNode
54
+ public open class ReactTextShadowNode
55
55
  @JvmOverloads
56
56
  public constructor(reactTextViewManagerCallback: ReactTextViewManagerCallback? = null) :
57
57
  ReactBaseTextShadowNode(reactTextViewManagerCallback) {
@@ -29,7 +29,7 @@ import java.util.HashMap
29
29
  */
30
30
  @ReactModule(name = ReactTextViewManager.REACT_CLASS)
31
31
  @OptIn(UnstableReactNativeAPI::class)
32
- public class ReactTextViewManager
32
+ public open class ReactTextViewManager
33
33
  @JvmOverloads
34
34
  public constructor(
35
35
  protected var reactTextViewManagerCallback: ReactTextViewManagerCallback? = null
@@ -92,7 +92,7 @@ public constructor(
92
92
  override fun createShadowNodeInstance(): ReactTextShadowNode =
93
93
  ReactTextShadowNode(reactTextViewManagerCallback)
94
94
 
95
- public fun createShadowNodeInstance(
95
+ public open fun createShadowNodeInstance(
96
96
  reactTextViewManagerCallback: ReactTextViewManagerCallback?
97
97
  ): ReactTextShadowNode = ReactTextShadowNode(reactTextViewManagerCallback)
98
98
 
@@ -736,6 +736,18 @@ public open class ReactViewGroup public constructor(context: Context?) :
736
736
  }
737
737
  super.onViewAdded(child)
738
738
  }
739
+
740
+ override fun removeView(view: View?) {
741
+ if (view != null) {
742
+ recoverFocus(view);
743
+ }
744
+ super.removeView(view);
745
+ }
746
+
747
+ override fun removeViewAt(index: Int) {
748
+ recoverFocus(getChildAt(index));
749
+ super.removeViewAt(index);
750
+ }
739
751
 
740
752
  override fun onViewRemoved(child: View) {
741
753
  assertOnUiThread()
@@ -854,7 +866,7 @@ public open class ReactViewGroup public constructor(context: Context?) :
854
866
 
855
867
  internal fun removeViewWithSubviewClippingEnabled(view: View) {
856
868
  assertOnUiThread()
857
-
869
+ recoverFocus(view)
858
870
  check(_removeClippedSubviews)
859
871
  val allChildren = checkNotNull(allChildren)
860
872
  view.removeOnLayoutChangeListener(childrenLayoutChangeListener)
@@ -39,7 +39,9 @@ void processTransform(
39
39
  }
40
40
 
41
41
  auto result = BaseViewProps::resolveTransform(
42
- Size(viewWidth, viewHeight), transform, transformOrigin);
42
+ Size{.width = viewWidth, .height = viewHeight},
43
+ transform,
44
+ transformOrigin);
43
45
 
44
46
  // Convert from matrix of floats to double matrix
45
47
  constexpr size_t MatrixSize = std::tuple_size_v<decltype(result.matrix)>;
@@ -14,15 +14,15 @@
14
14
 
15
15
  #define REACT_NATIVE_VERSION_MAJOR 0
16
16
  #define REACT_NATIVE_VERSION_MINOR 81
17
- #define REACT_NATIVE_VERSION_PATCH 4
17
+ #define REACT_NATIVE_VERSION_PATCH 5
18
18
 
19
19
  namespace facebook::react {
20
20
 
21
21
  constexpr struct {
22
22
  int32_t Major = 0;
23
23
  int32_t Minor = 81;
24
- int32_t Patch = 4;
25
- std::string_view Prerelease = "1";
24
+ int32_t Patch = 5;
25
+ std::string_view Prerelease = "0";
26
26
  } ReactNativeVersion;
27
27
 
28
28
  } // namespace facebook::react
@@ -646,9 +646,6 @@ Transform BaseViewProps::resolveTransform(
646
646
  const Transform& transform,
647
647
  const TransformOrigin& transformOrigin) {
648
648
  auto transformMatrix = Transform{};
649
- if (frameSize.width == 0 && frameSize.height == 0) {
650
- return transformMatrix;
651
- }
652
649
 
653
650
  // transform is matrix
654
651
  if (transform.operations.size() == 1 &&
@@ -0,0 +1,377 @@
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
+
8
+ #include <gtest/gtest.h>
9
+
10
+ #include <react/renderer/components/view/BaseViewProps.h>
11
+
12
+ namespace facebook::react {
13
+
14
+ namespace {
15
+
16
+ // For transforms involving rotations, use this helper to fix floating point
17
+ // accuracies
18
+ void expectTransformsEqual(const Transform& t1, const Transform& t2) {
19
+ for (int i = 0; i < 16; i++) {
20
+ EXPECT_NEAR(t1.matrix[i], t2.matrix[i], 0.0001);
21
+ }
22
+ }
23
+
24
+ } // namespace
25
+
26
+ class ResolveTransformTest : public ::testing::Test {
27
+ protected:
28
+ TransformOrigin createTransformOriginPoints(float x, float y, float z = 0) {
29
+ TransformOrigin origin;
30
+ origin.xy[0] = ValueUnit(x, UnitType::Point);
31
+ origin.xy[1] = ValueUnit(y, UnitType::Point);
32
+ origin.z = z;
33
+ return origin;
34
+ }
35
+
36
+ TransformOrigin createTransformOriginPercent(float x, float y, float z = 0) {
37
+ TransformOrigin origin;
38
+ origin.xy[0] = ValueUnit(x, UnitType::Percent);
39
+ origin.xy[1] = ValueUnit(y, UnitType::Percent);
40
+ origin.z = z;
41
+ return origin;
42
+ }
43
+ };
44
+
45
+ TEST_F(ResolveTransformTest, EmptyFrameNoTransformOrigin) {
46
+ Size frameSize{.width = 0, .height = 0};
47
+ Transform transform = Transform::Translate(10.0, 20.0, 0.0);
48
+ TransformOrigin transformOrigin; // Default (not set)
49
+
50
+ auto result =
51
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
52
+
53
+ // With empty frame size and no transform origin, should just apply the
54
+ // transform directly
55
+ EXPECT_EQ(result.matrix, transform.matrix);
56
+ }
57
+
58
+ TEST_F(ResolveTransformTest, EmptyFrameTransformOriginPoints) {
59
+ Size frameSize{.width = 0, .height = 0};
60
+ Transform transform = Transform::Translate(10.0, 20.0, 0.0);
61
+ TransformOrigin transformOrigin = createTransformOriginPoints(5, 8);
62
+
63
+ auto result =
64
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
65
+
66
+ // Should handle transform origin even with empty frame size
67
+ EXPECT_EQ(result.matrix, Transform::Translate(10.0, 20.0, 0.0).matrix);
68
+ }
69
+
70
+ TEST_F(ResolveTransformTest, EmptyFrameTransformOriginPercent) {
71
+ Size frameSize{.width = 0, .height = 0};
72
+ Transform transform = Transform::Translate(10.0, 20.0, 0.0);
73
+ TransformOrigin transformOrigin = createTransformOriginPercent(50, 50);
74
+
75
+ auto result =
76
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
77
+
78
+ // Transform origin does not affect translate transform
79
+ EXPECT_EQ(result.matrix, Transform::Translate(10.0, 20.0, 0.0).matrix);
80
+ }
81
+
82
+ TEST_F(ResolveTransformTest, NonEmptyFrameNoTransformOrigin) {
83
+ Size frameSize{.width = 100, .height = 200};
84
+ Transform transform = Transform::Translate(10.0, 20.0, 0.0);
85
+ TransformOrigin transformOrigin; // Default (not set)
86
+
87
+ auto result =
88
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
89
+
90
+ // Transform origin does not affect translate transform
91
+ EXPECT_EQ(result.matrix, Transform::Translate(10.0, 20.0, 0.0).matrix);
92
+ }
93
+
94
+ TEST_F(ResolveTransformTest, NonEmptyFrameTransformOriginPoints) {
95
+ Size frameSize{.width = 100, .height = 200};
96
+ Transform transform = Transform::Scale(2.0, 1.5, 0.);
97
+ TransformOrigin transformOrigin = createTransformOriginPoints(25, 50);
98
+
99
+ auto result =
100
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
101
+
102
+ auto expected = Transform::Translate(25.0, 25.0, 0.0) * transform;
103
+ EXPECT_EQ(result.matrix, expected.matrix);
104
+ }
105
+
106
+ TEST_F(ResolveTransformTest, NonEmptyFrameTransformOriginPercent) {
107
+ Size frameSize{.width = 100, .height = 200};
108
+ Transform transform = Transform::Scale(2.0, 1.5, 0.);
109
+ TransformOrigin transformOrigin =
110
+ createTransformOriginPercent(25, 75); // 25% width, 75% height
111
+
112
+ auto result =
113
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
114
+
115
+ // Should resolve percentages: 25% of 100 = 25, 75% of 200 = 150
116
+ auto expected = Transform::Translate(25.0, -25.0, 0.0) * transform;
117
+ EXPECT_EQ(result.matrix, expected.matrix);
118
+ }
119
+
120
+ TEST_F(ResolveTransformTest, IdentityTransformWithOrigin) {
121
+ Size frameSize{.width = 100, .height = 200};
122
+ Transform transform = Transform::Identity();
123
+ TransformOrigin transformOrigin = createTransformOriginPoints(25, 50);
124
+
125
+ auto result =
126
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
127
+
128
+ // Even with identity transform, transform origin should still apply
129
+ // translations but they should cancel out, resulting in identity
130
+ EXPECT_EQ(result.matrix, transform.matrix);
131
+ }
132
+
133
+ TEST_F(ResolveTransformTest, MultipleTransformOperations) {
134
+ Size frameSize{.width = 100, .height = 200};
135
+
136
+ Transform transform = Transform::Identity();
137
+ transform = transform * Transform::Translate(10.0, 20.0, 0.0);
138
+ transform = transform * Transform::Scale(2.0, 1.5, 0.0);
139
+
140
+ TransformOrigin transformOrigin = createTransformOriginPercent(50, 50);
141
+
142
+ auto result =
143
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
144
+
145
+ EXPECT_EQ(result.matrix, transform.matrix);
146
+ }
147
+
148
+ TEST_F(ResolveTransformTest, VariousTransformOriginPositions) {
149
+ Size frameSize{.width = 100, .height = 200};
150
+ Transform transform = Transform::Scale(2.0, 2.0, 0.);
151
+
152
+ // Test origin at top-left (0, 0)
153
+ TransformOrigin topLeft = createTransformOriginPoints(0, 0);
154
+ auto resultTopLeft =
155
+ BaseViewProps::resolveTransform(frameSize, transform, topLeft);
156
+ auto expected = Transform::Translate(50.0, 100.0, 0.0) * transform;
157
+ EXPECT_EQ(resultTopLeft.matrix, expected.matrix);
158
+
159
+ // Test origin at center (50%, 50%)
160
+ TransformOrigin center = createTransformOriginPercent(50, 50);
161
+ auto resultCenter =
162
+ BaseViewProps::resolveTransform(frameSize, transform, center);
163
+ EXPECT_EQ(resultCenter.matrix, transform.matrix);
164
+
165
+ // Test origin at bottom-right (100%, 100%)
166
+ TransformOrigin bottomRight = createTransformOriginPercent(100, 100);
167
+ auto resultBottomRight =
168
+ BaseViewProps::resolveTransform(frameSize, transform, bottomRight);
169
+ expected = Transform::Translate(-50.0, -100.0, 0.0) * transform;
170
+ EXPECT_EQ(resultBottomRight.matrix, expected.matrix);
171
+ }
172
+
173
+ // Test with z-component in transform origin
174
+ TEST_F(ResolveTransformTest, TransformOriginWithZComponent) {
175
+ Size frameSize{.width = 100, .height = 200};
176
+ Transform transform = Transform::Scale(1.5, 1.5, 0.);
177
+
178
+ TransformOrigin transformOrigin;
179
+ transformOrigin.xy[0] = ValueUnit(50, UnitType::Point);
180
+ transformOrigin.xy[1] = ValueUnit(100, UnitType::Point);
181
+ transformOrigin.z = 10.0f;
182
+
183
+ auto result =
184
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
185
+ auto expected = Transform::Translate(0.0, 0.0, 10.0) * transform;
186
+ EXPECT_EQ(result.matrix, expected.matrix);
187
+ }
188
+
189
+ TEST_F(ResolveTransformTest, ArbitraryTransformMatrix) {
190
+ Size frameSize{.width = 100, .height = 200};
191
+
192
+ Transform transform;
193
+ transform.operations.push_back({
194
+ .type = TransformOperationType::Arbitrary,
195
+ .x = ValueUnit(0, UnitType::Point),
196
+ .y = ValueUnit(0, UnitType::Point),
197
+ .z = ValueUnit(0, UnitType::Point),
198
+ });
199
+ // Set custom matrix
200
+ transform.matrix = {{2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 10, 20, 0, 1}};
201
+
202
+ TransformOrigin transformOrigin = createTransformOriginPoints(25, 50);
203
+
204
+ auto result =
205
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
206
+
207
+ auto expected = Transform::Translate(25.0, 50.0, 0.0) * transform;
208
+ EXPECT_EQ(result.matrix, expected.matrix);
209
+ }
210
+
211
+ // Test rotation with empty frame size and no transform origin
212
+ TEST_F(ResolveTransformTest, RotationEmptyFrameNoTransformOrigin) {
213
+ Size frameSize{.width = 0, .height = 0};
214
+ Transform transform = Transform::RotateZ(M_PI / 4.0); // 45 degrees
215
+ TransformOrigin transformOrigin; // Default (not set)
216
+
217
+ auto result =
218
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
219
+
220
+ // With empty frame size and no transform origin, should just apply the
221
+ // rotation directly
222
+ expectTransformsEqual(result, transform);
223
+ }
224
+
225
+ // Test rotation with empty frame size and transform origin in points
226
+ TEST_F(ResolveTransformTest, RotationEmptyFrameTransformOriginPoints) {
227
+ Size frameSize{.width = 0, .height = 0};
228
+ Transform transform = Transform::RotateZ(M_PI / 4.0); // 45 degrees
229
+ TransformOrigin transformOrigin = createTransformOriginPoints(10, 20);
230
+
231
+ auto result =
232
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
233
+
234
+ // With empty frame size, center is (0, 0), so origin offset is (10, 20)
235
+ auto expected = Transform::Translate(10.0, 20.0, 0.0) * transform *
236
+ Transform::Translate(-10.0, -20.0, 0.0);
237
+ expectTransformsEqual(result, expected);
238
+ }
239
+
240
+ // Test rotation with empty frame size and transform origin in percentages
241
+ TEST_F(ResolveTransformTest, RotationEmptyFrameTransformOriginPercent) {
242
+ Size frameSize{.width = 0, .height = 0};
243
+ Transform transform = Transform::RotateZ(M_PI / 6.0); // 30 degrees
244
+ TransformOrigin transformOrigin = createTransformOriginPercent(50, 50);
245
+
246
+ auto result =
247
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
248
+
249
+ // With 0 frame size, percentages resolve to 0, so no origin offset
250
+ expectTransformsEqual(result, transform);
251
+ }
252
+
253
+ // Test rotation with non-empty frame size and no transform origin
254
+ TEST_F(ResolveTransformTest, RotationNonEmptyFrameNoTransformOrigin) {
255
+ Size frameSize{.width = 100, .height = 200};
256
+ Transform transform = Transform::RotateZ(M_PI / 3.0); // 60 degrees
257
+ TransformOrigin transformOrigin; // Default (not set)
258
+
259
+ auto result =
260
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
261
+
262
+ // Without transform origin, rotation should happen around default center
263
+ expectTransformsEqual(result, transform);
264
+ }
265
+
266
+ // Test rotation with non-empty frame size and transform origin in points
267
+ TEST_F(ResolveTransformTest, RotationNonEmptyFrameTransformOriginPoints) {
268
+ Size frameSize{.width = 100, .height = 200};
269
+ Transform transform = Transform::RotateZ(M_PI / 4.0); // 45 degrees
270
+ TransformOrigin transformOrigin = createTransformOriginPoints(25, 50);
271
+
272
+ auto result =
273
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
274
+
275
+ // Center of 100x200 frame is (50, 100), origin at (25, 50) means offset of
276
+ // (-25, -50)
277
+ auto expected = Transform::Translate(-25.0, -50.0, 0.0) * transform *
278
+ Transform::Translate(25.0, 50.0, 0.0);
279
+ expectTransformsEqual(result, expected);
280
+ }
281
+
282
+ // Test rotation with non-empty frame size and transform origin in percentages
283
+ TEST_F(ResolveTransformTest, RotationNonEmptyFrameTransformOriginPercent) {
284
+ Size frameSize{.width = 100, .height = 200};
285
+ Transform transform = Transform::RotateZ(M_PI / 2.0); // 90 degrees
286
+ TransformOrigin transformOrigin =
287
+ createTransformOriginPercent(25, 75); // 25% width, 75% height
288
+
289
+ auto result =
290
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
291
+
292
+ // Should resolve percentages: 25% of 100 = 25, 75% of 200 = 150
293
+ // Center is (50, 100), so origin offset is (25-50, 150-100) = (-25, 50)
294
+ auto expected = Transform::Translate(-25.0, 50.0, 0.0) * transform *
295
+ Transform::Translate(25.0, -50.0, 0.0);
296
+ expectTransformsEqual(result, expected);
297
+ }
298
+
299
+ // Test rotation with mixed transform origin units
300
+ TEST_F(ResolveTransformTest, RotationMixedTransformOriginUnits) {
301
+ Size frameSize{.width = 100, .height = 200};
302
+ Transform transform = Transform::RotateZ(M_PI); // 180 degrees
303
+
304
+ TransformOrigin transformOrigin;
305
+ transformOrigin.xy[0] = ValueUnit(30, UnitType::Point); // 30 points
306
+ transformOrigin.xy[1] = ValueUnit(25, UnitType::Percent); // 25% of 200 = 50
307
+ transformOrigin.z = 0;
308
+
309
+ auto result =
310
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
311
+
312
+ // Center is (50, 100), origin is (30, 50), so offset is (-20, -50)
313
+ auto expected = Transform::Translate(-20.0, -50.0, 0.0) * transform *
314
+ Transform::Translate(20.0, 50.0, 0.0);
315
+ expectTransformsEqual(result, expected);
316
+ }
317
+
318
+ // Test multiple rotations (RotateX, RotateY, RotateZ)
319
+ TEST_F(ResolveTransformTest, MultipleRotationsWithTransformOrigin) {
320
+ Size frameSize{.width = 100, .height = 100};
321
+
322
+ Transform transform = Transform::Rotate(M_PI / 6.0, M_PI / 4.0, M_PI / 3.0);
323
+ TransformOrigin transformOrigin = createTransformOriginPercent(50, 50);
324
+
325
+ auto result =
326
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
327
+ expectTransformsEqual(result, transform);
328
+ }
329
+
330
+ // Test rotation with z-component in transform origin
331
+ TEST_F(ResolveTransformTest, RotationWithZTransformOrigin) {
332
+ Size frameSize{.width = 100, .height = 200};
333
+ Transform transform = Transform::RotateZ(M_PI / 4.0); // 45 degrees
334
+
335
+ TransformOrigin transformOrigin;
336
+ transformOrigin.xy[0] = ValueUnit(50, UnitType::Point);
337
+ transformOrigin.xy[1] = ValueUnit(100, UnitType::Point);
338
+ transformOrigin.z = 15.0f;
339
+
340
+ auto result =
341
+ BaseViewProps::resolveTransform(frameSize, transform, transformOrigin);
342
+
343
+ // Center is (50, 100), origin is (50, 100, 15), so offset is (0, 0, 15)
344
+ auto expected = Transform::Translate(0.0, 0.0, 15.0) * transform *
345
+ Transform::Translate(0.0, 0.0, -15.0);
346
+ expectTransformsEqual(result, expected);
347
+ }
348
+
349
+ // Test rotation at different origin positions (corners vs center)
350
+ TEST_F(ResolveTransformTest, RotationDifferentOriginPositions) {
351
+ Size frameSize{.width = 100, .height = 100};
352
+ Transform transform = Transform::RotateZ(M_PI / 2.0); // 90 degrees
353
+
354
+ // Test rotation around top-left corner (0, 0)
355
+ TransformOrigin topLeft = createTransformOriginPoints(0, 0);
356
+ auto resultTopLeft =
357
+ BaseViewProps::resolveTransform(frameSize, transform, topLeft);
358
+ auto expectedTopLeft = Transform::Translate(-50.0, -50.0, 0.0) * transform *
359
+ Transform::Translate(50.0, 50.0, 0.0);
360
+ expectTransformsEqual(resultTopLeft, expectedTopLeft);
361
+
362
+ // Test rotation around center (50%, 50%)
363
+ TransformOrigin center = createTransformOriginPercent(50, 50);
364
+ auto resultCenter =
365
+ BaseViewProps::resolveTransform(frameSize, transform, center);
366
+ expectTransformsEqual(resultCenter, transform);
367
+
368
+ // Test rotation around bottom-right corner (100%, 100%)
369
+ TransformOrigin bottomRight = createTransformOriginPercent(100, 100);
370
+ auto resultBottomRight =
371
+ BaseViewProps::resolveTransform(frameSize, transform, bottomRight);
372
+ auto expectedBottomRight = Transform::Translate(50.0, 50.0, 0.0) * transform *
373
+ Transform::Translate(-50.0, -50.0, 0.0);
374
+ expectTransformsEqual(resultBottomRight, expectedBottomRight);
375
+ }
376
+
377
+ } // namespace facebook::react
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-tvos",
3
- "version": "0.81.4-1",
3
+ "version": "0.81.5-0",
4
4
  "description": "A framework for building native apps using React",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -166,12 +166,12 @@
166
166
  },
167
167
  "dependencies": {
168
168
  "@jest/create-cache-key-function": "^29.7.0",
169
- "@react-native/assets-registry": "0.81.4",
170
- "@react-native/codegen": "0.81.4",
171
- "@react-native/community-cli-plugin": "0.81.4",
172
- "@react-native/gradle-plugin": "0.81.4",
173
- "@react-native/js-polyfills": "0.81.4",
174
- "@react-native/normalize-colors": "0.81.4",
169
+ "@react-native/assets-registry": "0.81.5",
170
+ "@react-native/codegen": "0.81.5",
171
+ "@react-native/community-cli-plugin": "0.81.5",
172
+ "@react-native/gradle-plugin": "0.81.5",
173
+ "@react-native/js-polyfills": "0.81.5",
174
+ "@react-native/normalize-colors": "0.81.5",
175
175
  "abort-controller": "^3.0.0",
176
176
  "anser": "^1.4.9",
177
177
  "ansi-regex": "^5.0.0",
@@ -198,7 +198,7 @@
198
198
  "whatwg-fetch": "^3.0.0",
199
199
  "ws": "^6.2.3",
200
200
  "yargs": "^17.6.2",
201
- "@react-native-tvos/virtualized-lists": "0.81.4-1"
201
+ "@react-native-tvos/virtualized-lists": "0.81.5-0"
202
202
  },
203
203
  "codegenConfig": {
204
204
  "libraries": [
Binary file
Binary file
Binary file
@@ -64,7 +64,7 @@ Pod::Spec.new do |spec|
64
64
  exit 0
65
65
  fi
66
66
 
67
- cp -R "$HEADERS_PATH/" Headers
67
+ cp -R "$HEADERS_PATH/." Headers
68
68
  mkdir -p framework/packages/react-native
69
69
  cp -R "$XCFRAMEWORK_PATH/../." framework/packages/react-native/
70
70
  find "$XCFRAMEWORK_PATH/.." -type f -exec rm {} +