react-native-tvos 0.82.0-0rc5 → 0.82.1-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.
@@ -104,10 +104,12 @@ function TVFocusGuideView({
104
104
  const mergedRef = useMergeRefs(setLocalRef, ref);
105
105
 
106
106
  React.useEffect(() => {
107
- if (destinationsProp !== null && destinationsProp !== undefined) {
107
+ if (focusable === false) {
108
+ setDestinations([]);
109
+ } else if (destinationsProp !== null && destinationsProp !== undefined) {
108
110
  setDestinations(destinationsProp); // $FlowFixMe[incompatible-call]
109
111
  }
110
- }, [setDestinations, destinationsProp]);
112
+ }, [setDestinations, destinationsProp, focusable]);
111
113
 
112
114
  const enabledStyle = {display: enabled ? 'flex' : 'none'};
113
115
  const style = [styles.container, props.style, enabledStyle];
@@ -124,7 +126,7 @@ function TVFocusGuideView({
124
126
  style={style}
125
127
  ref={mergedRef}
126
128
  collapsable={false}
127
- autoFocus={autoFocus}
129
+ autoFocus={focusable === false ? true : autoFocus}
128
130
  // tvOS only prop
129
131
  isTVSelectable={tvOSSelectable}
130
132
  // Android TV only prop
@@ -28,8 +28,8 @@
28
28
  export default class ReactNativeVersion {
29
29
  static major: number = 0;
30
30
  static minor: number = 82;
31
- static patch: number = 0;
32
- static prerelease: string | null = '0rc5';
31
+ static patch: number = 1;
32
+ static prerelease: string | null = '0';
33
33
 
34
34
  static getVersionString(): string {
35
35
  return `${this.major}.${this.minor}.${this.patch}${this.prerelease != null ? `-${this.prerelease}` : ''}`;
@@ -23,8 +23,8 @@ NSDictionary* RCTGetReactNativeVersion(void)
23
23
  __rnVersion = @{
24
24
  RCTVersionMajor: @(0),
25
25
  RCTVersionMinor: @(82),
26
- RCTVersionPatch: @(0),
27
- RCTVersionPrerelease: @"0rc5",
26
+ RCTVersionPatch: @(1),
27
+ RCTVersionPrerelease: @"0",
28
28
  };
29
29
  });
30
30
  return __rnVersion;
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.82.0-0rc5
1
+ VERSION_NAME=0.82.1-0
2
2
  react.internal.publishingGroup=io.github.react-native-tvos
3
3
  react.internal.hermesPublishingGroup=com.facebook.hermes
4
4
 
@@ -33,26 +33,6 @@ internal class ReactAndroidHWInputDeviceHelper {
33
33
  }
34
34
  }
35
35
 
36
- /** Called from [ReactRootView] when focused view changes. */
37
- fun onFocusChanged(newFocusedView: View, context: ReactContext) {
38
- if (lastFocusedViewId == newFocusedView.id) {
39
- return
40
- }
41
- if (lastFocusedViewId != View.NO_ID) {
42
- dispatchEvent(context, "blur", lastFocusedViewId)
43
- }
44
- lastFocusedViewId = newFocusedView.id
45
- dispatchEvent(context, "focus", newFocusedView.id)
46
- }
47
-
48
- /** Called from [ReactRootView] when the whole view hierarchy looses focus. */
49
- fun clearFocus(context: ReactContext) {
50
- if (lastFocusedViewId != View.NO_ID) {
51
- dispatchEvent(context, "blur", lastFocusedViewId)
52
- }
53
- lastFocusedViewId = View.NO_ID
54
- }
55
-
56
36
  private fun dispatchEvent(
57
37
  context: ReactContext,
58
38
  eventType: String?,
@@ -341,39 +341,6 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
341
341
  return super.dispatchKeyEvent(ev);
342
342
  }
343
343
 
344
- @Override
345
- protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
346
- if (!hasActiveReactContext() || !isViewAttachedToReactInstance()) {
347
- FLog.w(
348
- TAG,
349
- "Unable to handle focus changed event as the catalyst instance has not been attached");
350
- super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
351
- return;
352
- }
353
- ReactContext context = getCurrentReactContext();
354
- if (context != null) {
355
- mAndroidHWInputDeviceHelper.clearFocus(context);
356
- }
357
- super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
358
- }
359
-
360
- @Override
361
- public void requestChildFocus(View child, View focused) {
362
- if (!hasActiveReactContext() || !isViewAttachedToReactInstance()) {
363
- FLog.w(
364
- TAG,
365
- "Unable to handle child focus changed event as the catalyst instance has not been"
366
- + " attached");
367
- super.requestChildFocus(child, focused);
368
- return;
369
- }
370
- ReactContext context = getCurrentReactContext();
371
- if (context != null) {
372
- mAndroidHWInputDeviceHelper.onFocusChanged(focused, context);
373
- }
374
- super.requestChildFocus(child, focused);
375
- }
376
-
377
344
  protected void dispatchJSPointerEvent(MotionEvent event, boolean isCapture) {
378
345
  if (!hasActiveReactContext() || !isViewAttachedToReactInstance()) {
379
346
  FLog.w(TAG, "Unable to dispatch touch to JS as the catalyst instance has not been attached");
@@ -192,26 +192,6 @@ public class ReactAndroidHWInputDeviceHelper {
192
192
  );
193
193
  }
194
194
 
195
- /** Called from {@link com.facebook.react.ReactRootView} when focused view changes. */
196
- public void onFocusChanged(View newFocusedView, ReactContext context) {
197
- if (mLastFocusedViewId == newFocusedView.getId()) {
198
- return;
199
- }
200
- if (mLastFocusedViewId != View.NO_ID) {
201
- dispatchEvent("blur", mLastFocusedViewId, context);
202
- }
203
- mLastFocusedViewId = newFocusedView.getId();
204
- dispatchEvent("focus", newFocusedView.getId(), context);
205
- }
206
-
207
- /** Called from {@link com.facebook.react.ReactRootView} when the whole view hierarchy looses focus. */
208
- public void clearFocus(ReactContext context) {
209
- if (mLastFocusedViewId != View.NO_ID) {
210
- dispatchEvent("blur", mLastFocusedViewId, context);
211
- }
212
- mLastFocusedViewId = View.NO_ID;
213
- }
214
-
215
195
  private void dispatchEvent(String eventType, int targetViewId, ReactContext context) {
216
196
  dispatchEvent(eventType, targetViewId, -1, context);
217
197
  }
@@ -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 82,
17
- "patch" to 0,
18
- "prerelease" to "0rc5"
17
+ "patch" to 1,
18
+ "prerelease" to "0"
19
19
  )
20
20
  }
@@ -560,15 +560,6 @@ public class ReactModalHostView(context: ThemedReactContext) :
560
560
  updateState(viewWidth, viewHeight)
561
561
  }
562
562
 
563
- protected override fun onFocusChanged(
564
- gainFocus: Boolean,
565
- direction: Int,
566
- previouslyFocusedRect: Rect?
567
- ) {
568
- androidHWInputDeviceHelper.clearFocus(reactContext)
569
- super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
570
- }
571
-
572
563
  /*
573
564
  public override fun requestChildFocus(child: View?, focused: View?) {
574
565
  androidHWInputDeviceHelper.onFocusChanged(focused, reactContext)
@@ -425,7 +425,7 @@ public open class ReactEditText public constructor(context: Context) : AppCompat
425
425
  // that. This method will eventually replace requestFocusInternal()
426
426
  private fun requestFocusProgrammatically(): Boolean {
427
427
  val focused = super.requestFocus(FOCUS_DOWN, null)
428
- if (isInTouchMode && showSoftInputOnFocus) {
428
+ if (showSoftInputOnFocus) {
429
429
  showSoftKeyboard()
430
430
  } else {
431
431
  if (isKeyboardOpened) {
@@ -70,9 +70,7 @@ import com.facebook.react.uimanager.UIManagerHelper
70
70
  import com.facebook.react.uimanager.ViewGroupDrawingOrderHelper
71
71
  import com.facebook.react.uimanager.common.UIManagerType
72
72
  import com.facebook.react.uimanager.common.ViewUtil.getUIManagerType
73
- import com.facebook.react.uimanager.events.BlurEvent
74
73
  import com.facebook.react.uimanager.events.EventDispatcher
75
- import com.facebook.react.uimanager.events.FocusEvent
76
74
  import com.facebook.react.uimanager.events.PressInEvent
77
75
  import com.facebook.react.uimanager.events.PressOutEvent
78
76
  import com.facebook.react.uimanager.style.BorderRadiusProp
@@ -747,6 +745,18 @@ public open class ReactViewGroup public constructor(context: Context?) :
747
745
  }
748
746
  super.onViewAdded(child)
749
747
  }
748
+
749
+ override fun removeView(view: View?) {
750
+ if (view != null) {
751
+ recoverFocus(view);
752
+ }
753
+ super.removeView(view);
754
+ }
755
+
756
+ override fun removeViewAt(index: Int) {
757
+ recoverFocus(getChildAt(index));
758
+ super.removeViewAt(index);
759
+ }
750
760
 
751
761
  override fun onViewRemoved(child: View) {
752
762
  assertOnUiThread()
@@ -869,7 +879,7 @@ public open class ReactViewGroup public constructor(context: Context?) :
869
879
 
870
880
  internal fun removeViewWithSubviewClippingEnabled(view: View) {
871
881
  assertOnUiThread()
872
-
882
+ recoverFocus(view)
873
883
  check(_removeClippedSubviews)
874
884
  val allChildren = checkNotNull(allChildren)
875
885
  view.removeOnLayoutChangeListener(childrenLayoutChangeListener)
@@ -1360,31 +1370,7 @@ public open class ReactViewGroup public constructor(context: Context?) :
1360
1370
  }
1361
1371
 
1362
1372
  override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
1363
- // Calling the super method causes duplicate events
1364
- // super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
1365
-
1366
- val mEventDispatcher: EventDispatcher? =
1367
- UIManagerHelper.getEventDispatcherForReactTag(
1368
- this.context as ReactContext, this.id
1369
- )
1370
-
1371
- if (mEventDispatcher == null) {
1372
- return
1373
- }
1374
-
1375
- if (gainFocus) {
1376
- mEventDispatcher.dispatchEvent(
1377
- FocusEvent(
1378
- UIManagerHelper.getSurfaceId(this.context), this.id
1379
- )
1380
- )
1381
- } else {
1382
- mEventDispatcher.dispatchEvent(
1383
- BlurEvent(
1384
- UIManagerHelper.getSurfaceId(this.context), this.id
1385
- )
1386
- )
1387
- }
1373
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
1388
1374
  }
1389
1375
 
1390
1376
  override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
@@ -108,7 +108,7 @@ public open class ReactViewManager : ReactClippingViewManager<ReactViewGroup>()
108
108
  view.isFocusable = false
109
109
  view.descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS
110
110
  } else {
111
- view.descendantFocusability = ViewGroup.FOCUS_BEFORE_DESCENDANTS
111
+ view.descendantFocusability = ViewGroup.FOCUS_AFTER_DESCENDANTS
112
112
  }
113
113
  }
114
114
 
@@ -407,8 +407,7 @@ public open class ReactViewManager : ReactClippingViewManager<ReactViewGroup>()
407
407
  } else {
408
408
  view.setOnClickListener(null)
409
409
  view.isClickable = false
410
- // Don't set view.setFocusable(false) because we might still want it to be focusable for
411
- // accessibility reasons
410
+ view.isFocusable = false
412
411
  }
413
412
  // This is required to handle Android TV/ Fire TV Devices that are Touch Enabled as well as LeanBack
414
413
  // https://developer.android.com/reference/android/view/View#requestFocus(int,%20android.graphics.Rect)
@@ -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 82
17
- #define REACT_NATIVE_VERSION_PATCH 0
17
+ #define REACT_NATIVE_VERSION_PATCH 1
18
18
 
19
19
  namespace facebook::react {
20
20
 
21
21
  constexpr struct {
22
22
  int32_t Major = 0;
23
23
  int32_t Minor = 82;
24
- int32_t Patch = 0;
25
- std::string_view Prerelease = "0rc5";
24
+ int32_t Patch = 1;
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.82.0-0rc5",
3
+ "version": "0.82.1-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.82.0-rc.5",
170
- "@react-native/codegen": "0.82.0-rc.5",
171
- "@react-native/community-cli-plugin": "0.82.0-rc.5",
172
- "@react-native/gradle-plugin": "0.82.0-rc.5",
173
- "@react-native/js-polyfills": "0.82.0-rc.5",
174
- "@react-native/normalize-colors": "0.82.0-rc.5",
169
+ "@react-native/assets-registry": "0.82.1",
170
+ "@react-native/codegen": "0.82.1",
171
+ "@react-native/community-cli-plugin": "0.82.1",
172
+ "@react-native/gradle-plugin": "0.82.1",
173
+ "@react-native/js-polyfills": "0.82.1",
174
+ "@react-native/normalize-colors": "0.82.1",
175
175
  "abort-controller": "^3.0.0",
176
176
  "anser": "^1.4.9",
177
177
  "ansi-regex": "^5.0.0",
@@ -199,7 +199,7 @@
199
199
  "whatwg-fetch": "^3.0.0",
200
200
  "ws": "^6.2.3",
201
201
  "yargs": "^17.6.2",
202
- "@react-native-tvos/virtualized-lists": "0.82.0-0rc5"
202
+ "@react-native-tvos/virtualized-lists": "0.82.1-0"
203
203
  },
204
204
  "codegenConfig": {
205
205
  "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 {} +