react-native-tvos 0.76.6-0 → 0.76.7-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.
Files changed (32) hide show
  1. package/Libraries/AppDelegate/RCTAppDelegate.mm +0 -5
  2. package/Libraries/AppDelegate/RCTAppSetupUtils.mm +3 -1
  3. package/Libraries/AppDelegate/RCTRootViewFactory.mm +3 -3
  4. package/Libraries/Components/TV/TVFocusGuideView.js +0 -1
  5. package/Libraries/Components/View/ViewPropTypes.d.ts +4 -2
  6. package/Libraries/Core/ReactNativeVersion.js +1 -1
  7. package/Libraries/Image/RCTImageLoader.mm +9 -1
  8. package/Libraries/Utilities/Appearance.js +3 -1
  9. package/React/Base/RCTVersion.m +1 -1
  10. package/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingProxyRootView.mm +2 -5
  11. package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +85 -31
  12. package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +5 -2
  13. package/React/Views/RCTTVView.m +5 -2
  14. package/React/Views/ScrollView/RCTScrollView.m +63 -26
  15. package/ReactAndroid/api/ReactAndroid.api +2 -0
  16. package/ReactAndroid/gradle.properties +2 -2
  17. package/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java +15 -8
  18. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +1 -1
  19. package/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java +16 -2
  20. package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
  21. package/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTInteropTurboModule.mm +9 -0
  22. package/ReactCommon/react/renderer/attributedstring/TextAttributes.cpp +5 -0
  23. package/ReactCommon/react/renderer/attributedstring/TextAttributes.h +2 -0
  24. package/ReactCommon/react/renderer/attributedstring/conversions.h +5 -0
  25. package/ReactCommon/react/renderer/components/text/BaseTextProps.cpp +12 -0
  26. package/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm +5 -3
  27. package/gradle/libs.versions.toml +1 -1
  28. package/package.json +8 -8
  29. package/sdks/hermesc/linux64-bin/hermesc +0 -0
  30. package/sdks/hermesc/osx-bin/hermes +0 -0
  31. package/sdks/hermesc/osx-bin/hermesc +0 -0
  32. package/sdks/hermesc/win64-bin/hermesc.exe +0 -0
@@ -89,11 +89,6 @@
89
89
  [_window makeKeyAndVisible];
90
90
  }
91
91
 
92
- - (void)applicationDidEnterBackground:(UIApplication *)application
93
- {
94
- // Noop
95
- }
96
-
97
92
  - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
98
93
  {
99
94
  [NSException raise:@"RCTBridgeDelegate::sourceURLForBridge not implemented"
@@ -53,7 +53,9 @@ RCTAppSetupDefaultRootView(RCTBridge *bridge, NSString *moduleName, NSDictionary
53
53
  id<RCTSurfaceProtocol> surface = [[RCTFabricSurface alloc] initWithBridge:bridge
54
54
  moduleName:moduleName
55
55
  initialProperties:initialProperties];
56
- return [[RCTSurfaceHostingProxyRootView alloc] initWithSurface:surface];
56
+ UIView *rootView = [[RCTSurfaceHostingProxyRootView alloc] initWithSurface:surface];
57
+ [surface start];
58
+ return rootView;
57
59
  }
58
60
  return [[RCTRootView alloc] initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties];
59
61
  }
@@ -159,9 +159,9 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
159
159
 
160
160
  RCTFabricSurface *surface = [self.reactHost createSurfaceWithModuleName:moduleName initialProperties:initProps];
161
161
 
162
- RCTSurfaceHostingProxyRootView *surfaceHostingProxyRootView = [[RCTSurfaceHostingProxyRootView alloc]
163
- initWithSurface:surface
164
- sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact];
162
+ RCTSurfaceHostingProxyRootView *surfaceHostingProxyRootView =
163
+ [[RCTSurfaceHostingProxyRootView alloc] initWithSurface:surface];
164
+
165
165
  #if !TARGET_OS_TV
166
166
  surfaceHostingProxyRootView.backgroundColor = [UIColor systemBackgroundColor];
167
167
  #endif
@@ -8,7 +8,6 @@
8
8
  * @format
9
9
  */
10
10
 
11
- import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes';
12
11
  import type {ViewProps} from '../View/ViewPropTypes';
13
12
  import type {ComponentOrHandleType} from './tagForComponentOrHandle';
14
13
 
@@ -17,6 +17,8 @@ import {
17
17
  PointerEvents,
18
18
  FocusEvents,
19
19
  PressEvents,
20
+ NativeFocusEvent,
21
+ NativeBlurEvent,
20
22
  } from '../../Types/CoreEventTypes';
21
23
  import {Touchable} from '../Touchable/Touchable';
22
24
  import {AccessibilityProps} from './ViewAccessibility';
@@ -220,6 +222,6 @@ export interface ViewProps
220
222
  */
221
223
  nativeID?: string | undefined;
222
224
 
223
- readonly onFocus?: BubblingEventHandler<Event> | undefined;
224
- readonly onBlur?: BubblingEventHandler<Event> | undefined;
225
+ readonly onFocus?: BubblingEventHandler<NativeFocusEvent> | undefined;
226
+ readonly onBlur?: BubblingEventHandler<NativeBlurEvent> | undefined;
225
227
  }
@@ -16,7 +16,7 @@ const version: $ReadOnly<{
16
16
  }> = {
17
17
  major: 0,
18
18
  minor: 76,
19
- patch: 6,
19
+ patch: 7,
20
20
  prerelease: '0',
21
21
  };
22
22
 
@@ -477,7 +477,15 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, CGSize size, CGFloat scal
477
477
 
478
478
  // Add missing png extension
479
479
  if (request.URL.fileURL && request.URL.pathExtension.length == 0) {
480
- mutableRequest.URL = [request.URL URLByAppendingPathExtension:@"png"];
480
+ // Check if there exists a file with that url on disk already
481
+ // This should fix issue https://github.com/facebook/react-native/issues/46870
482
+ if ([[NSFileManager defaultManager] fileExistsAtPath:request.URL.path]) {
483
+ mutableRequest.URL = request.URL;
484
+ } else {
485
+ // This is the default behavior in case there is no file on disk with no extension.
486
+ // We assume that the extension is `png`.
487
+ mutableRequest.URL = [request.URL URLByAppendingPathExtension:@"png"];
488
+ }
481
489
  }
482
490
  if (_redirectDelegate != nil) {
483
491
  mutableRequest.URL = [_redirectDelegate redirectAssetsURL:mutableRequest.URL];
@@ -105,7 +105,9 @@ export function setColorScheme(colorScheme: ?ColorSchemeName): void {
105
105
  const {NativeAppearance} = state;
106
106
  if (NativeAppearance != null) {
107
107
  NativeAppearance.setColorScheme(colorScheme ?? 'unspecified');
108
- state.appearance = {colorScheme};
108
+ state.appearance = {
109
+ colorScheme: toColorScheme(NativeAppearance.getColorScheme()),
110
+ };
109
111
  }
110
112
  }
111
113
 
@@ -23,7 +23,7 @@ NSDictionary* RCTGetReactNativeVersion(void)
23
23
  __rnVersion = @{
24
24
  RCTVersionMajor: @(0),
25
25
  RCTVersionMinor: @(76),
26
- RCTVersionPatch: @(6),
26
+ RCTVersionPatch: @(7),
27
27
  RCTVersionPrerelease: @"0",
28
28
  };
29
29
  });
@@ -53,11 +53,8 @@ static RCTRootViewSizeFlexibility convertToRootViewSizeFlexibility(RCTSurfaceSiz
53
53
 
54
54
  - (instancetype)initWithSurface:(id<RCTSurfaceProtocol>)surface
55
55
  {
56
- if (self = [super initWithSurface:surface
57
- sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact]) {
58
- [surface start];
59
- }
60
- return self;
56
+ return [super initWithSurface:surface
57
+ sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact];
61
58
  }
62
59
 
63
60
  RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame)
@@ -1030,18 +1030,23 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
1030
1030
  [self sendBlurNotification];
1031
1031
  [self removeSwipeGestureRecognizers];
1032
1032
  [self resignFirstResponder];
1033
- // if we leave the scroll view and go up, then scroll to top; if going down,
1034
- // scroll to bottom
1035
- // Similarly for left and right
1033
+ // If scrolling is enabled:
1034
+ // - Scroll to the top when moving up and to the bottom when moving down.
1035
+ // - Similarly, scroll towards leading edge when moving towards leading edge and to the trailing edge when moving towards the trailing edge.
1036
+ BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1037
+ BOOL isMovingTowardsLeadingEdge = (isRTL ? context.focusHeading == UIFocusHeadingRight : context.focusHeading == UIFocusHeadingLeft);
1038
+ BOOL isMovingTowardsTrailingEdge = (isRTL ? context.focusHeading == UIFocusHeadingLeft : context.focusHeading == UIFocusHeadingRight);
1036
1039
  RCTEnhancedScrollView *scrollView = (RCTEnhancedScrollView *)_scrollView;
1037
- if (context.focusHeading == UIFocusHeadingUp && scrollView.snapToStart) {
1038
- [self swipeVerticalScrollToOffset:0.0];
1039
- } else if(context.focusHeading == UIFocusHeadingDown && scrollView.snapToEnd) {
1040
- [self swipeVerticalScrollToOffset:scrollView.contentSize.height];
1041
- } else if(context.focusHeading == UIFocusHeadingLeft && scrollView.snapToStart) {
1042
- [self swipeHorizontalScrollToOffset:0.0];
1043
- } else if(context.focusHeading == UIFocusHeadingRight && scrollView.snapToEnd) {
1044
- [self swipeHorizontalScrollToOffset:scrollView.contentSize.width];
1040
+ if (scrollView.isScrollEnabled) {
1041
+ if (context.focusHeading == UIFocusHeadingUp && scrollView.snapToStart) {
1042
+ [self scrollToVerticalOffset:0.0];
1043
+ } else if(context.focusHeading == UIFocusHeadingDown && scrollView.snapToEnd) {
1044
+ [self scrollToVerticalOffset:scrollView.contentSize.height];
1045
+ } else if(isMovingTowardsLeadingEdge && scrollView.snapToStart) {
1046
+ [self scrollToHorizontalOffset:0.0];
1047
+ } else if(isMovingTowardsTrailingEdge && scrollView.snapToEnd) {
1048
+ [self scrollToHorizontalOffset:scrollView.contentSize.width];
1049
+ }
1045
1050
  }
1046
1051
  }
1047
1052
  }
@@ -1100,23 +1105,32 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
1100
1105
 
1101
1106
  - (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
1102
1107
  {
1108
+ // If the previously focused item is this view and scrolling is disabled, defer to the superclass
1109
+ if (context.previouslyFocusedItem == self && !self.scrollView.isScrollEnabled) {
1110
+ return [super shouldUpdateFocusInContext:context];
1111
+ }
1112
+
1103
1113
  // Determine if the layout is Right-to-Left
1104
1114
  BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1105
1115
  BOOL isHorizontal = _scrollView.contentSize.width > self.frame.size.width;
1106
1116
  // Adjust for horizontal scrolling with RTL support
1107
1117
  if (isHorizontal) {
1108
- BOOL isNavigatingToEnd = (isRTL ? context.focusHeading == UIFocusHeadingLeft : context.focusHeading == UIFocusHeadingRight);
1109
- BOOL isNavigatingToStart = (isRTL ? context.focusHeading == UIFocusHeadingRight : context.focusHeading == UIFocusHeadingLeft);
1118
+ BOOL isMovingTowardsLeadingEdge = (isRTL ? context.focusHeading == UIFocusHeadingRight : context.focusHeading == UIFocusHeadingLeft);
1119
+ BOOL isMovingTowardsTrailingEdge = (isRTL ? context.focusHeading == UIFocusHeadingLeft : context.focusHeading == UIFocusHeadingRight);
1110
1120
 
1111
- if ((isNavigatingToEnd && self.scrollView.contentOffset.x < self.scrollView.contentSize.width - self.scrollView.visibleSize.width) ||
1112
- (isNavigatingToStart && self.scrollView.contentOffset.x > 0)) {
1113
- return [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem];
1121
+ BOOL isScrollingToLeading = (isMovingTowardsLeadingEdge && self.scrollView.contentOffset.x > 0);
1122
+ BOOL isScrollingToTrailing = (isMovingTowardsTrailingEdge && self.scrollView.contentOffset.x < self.scrollView.contentSize.width - MAX(self.scrollView.visibleSize.width, 1));
1123
+
1124
+ if (isScrollingToLeading || isScrollingToTrailing) {
1125
+ return (context.nextFocusedItem && [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem]);
1114
1126
  }
1115
1127
  } else {
1116
1128
  // Handle vertical scrolling as before
1117
- if ((context.focusHeading == UIFocusHeadingUp && self.scrollView.contentOffset.y > 0) ||
1118
- (context.focusHeading == UIFocusHeadingDown && self.scrollView.contentOffset.y < self.scrollView.contentSize.height - self.scrollView.visibleSize.height)) {
1119
- return [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem];
1129
+ BOOL isMovingUp = (context.focusHeading == UIFocusHeadingUp && self.scrollView.contentOffset.y > 0);
1130
+ BOOL isMovingDown = (context.focusHeading == UIFocusHeadingDown && self.scrollView.contentOffset.y < self.scrollView.contentSize.height - MAX(self.scrollView.visibleSize.height, 1));
1131
+
1132
+ if (isMovingUp || isMovingDown) {
1133
+ return (context.nextFocusedItem && [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem]);
1120
1134
  }
1121
1135
  }
1122
1136
  return [super shouldUpdateFocusInContext:context];
@@ -1160,60 +1174,100 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
1160
1174
  return duration;
1161
1175
  }
1162
1176
 
1163
- - (void)swipeVerticalScrollToOffset:(CGFloat)yOffset
1177
+ - (void)scrollToVerticalOffset:(CGFloat)yOffset
1164
1178
  {
1165
1179
  _blockFirstTouch = NO;
1166
1180
  dispatch_async(dispatch_get_main_queue(), ^{
1167
1181
  CGFloat limitedOffset = yOffset;
1182
+
1183
+ // Ensure content size and visible size are non-negative
1184
+ CGFloat contentHeight = MAX(self.scrollView.contentSize.height, 0.0);
1185
+ CGFloat visibleHeight = MAX(self.scrollView.visibleSize.height, 0.0);
1186
+
1187
+ // Compute the maximum offset, ensuring it's non-negative
1188
+ CGFloat maxOffset = MAX(contentHeight - visibleHeight, 0.0);
1189
+
1190
+ // Clamp the offset within valid bounds
1168
1191
  limitedOffset = MAX(limitedOffset, 0.0);
1169
- limitedOffset = MIN(limitedOffset, self.scrollView.contentSize.height - self.scrollView.visibleSize.height);
1192
+ limitedOffset = MIN(limitedOffset, maxOffset);
1193
+
1170
1194
  [UIView animateWithDuration:[self swipeDuration] animations:^{
1171
1195
  self.scrollView.contentOffset =
1172
- CGPointMake(self.scrollView.contentOffset.x, limitedOffset);
1196
+ CGPointMake(self.scrollView.contentOffset.x, limitedOffset);
1173
1197
  }];
1174
1198
  });
1175
1199
  }
1176
1200
 
1177
- - (void)swipeHorizontalScrollToOffset:(CGFloat)xOffset
1201
+ - (void)scrollToHorizontalOffset:(CGFloat)xOffset
1178
1202
  {
1179
1203
  _blockFirstTouch = NO;
1180
1204
  dispatch_async(dispatch_get_main_queue(), ^{
1181
1205
  CGFloat limitedOffset = xOffset;
1206
+
1207
+ // Ensure content size and visible size are non-negative
1208
+ CGFloat contentWidth = MAX(self.scrollView.contentSize.width, 0.0);
1209
+ CGFloat visibleWidth = MAX(self.scrollView.visibleSize.width, 0.0);
1210
+
1211
+ // Compute the maximum offset, ensuring it's non-negative
1212
+ CGFloat maxOffset = MAX(contentWidth - visibleWidth, 0.0);
1213
+
1214
+ // Clamp the offset within valid bounds
1182
1215
  limitedOffset = MAX(limitedOffset, 0.0);
1183
- limitedOffset = MIN(limitedOffset, self.scrollView.contentSize.width - self.scrollView.visibleSize.width);
1216
+ limitedOffset = MIN(limitedOffset, maxOffset);
1217
+
1184
1218
  [UIView animateWithDuration:[self swipeDuration] animations:^{
1185
1219
  self.scrollView.contentOffset =
1186
- CGPointMake(limitedOffset, self.scrollView.contentOffset.y);
1220
+ CGPointMake(limitedOffset, self.scrollView.contentOffset.y);
1187
1221
  }];
1188
1222
  });
1189
1223
  }
1190
1224
 
1191
1225
  - (void)swipedUp
1192
1226
  {
1227
+ if (!self.scrollView.scrollEnabled) {
1228
+ return;
1229
+ }
1230
+
1193
1231
  CGFloat newOffset = self.scrollView.contentOffset.y - [self swipeVerticalInterval];
1194
1232
  // NSLog(@"Swiped up to %f", newOffset);
1195
- [self swipeVerticalScrollToOffset:newOffset];
1233
+ [self scrollToVerticalOffset:newOffset];
1196
1234
  }
1197
1235
 
1198
1236
  - (void)swipedDown
1199
1237
  {
1238
+ if (!self.scrollView.scrollEnabled) {
1239
+ return;
1240
+ }
1241
+
1200
1242
  CGFloat newOffset = self.scrollView.contentOffset.y + [self swipeVerticalInterval];
1201
1243
  // NSLog(@"Swiped down to %f", newOffset);
1202
- [self swipeVerticalScrollToOffset:newOffset];
1244
+ [self scrollToVerticalOffset:newOffset];
1203
1245
  }
1204
1246
 
1205
1247
  - (void)swipedLeft
1206
1248
  {
1207
- CGFloat newOffset = self.scrollView.contentOffset.x - [self swipeHorizontalInterval];
1249
+ if (!self.scrollView.scrollEnabled) {
1250
+ return;
1251
+ }
1252
+
1253
+ BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1254
+ NSInteger horizontalInterval = [self swipeHorizontalInterval];
1255
+ CGFloat newOffset = self.scrollView.contentOffset.x + (isRTL ? horizontalInterval : -horizontalInterval);
1208
1256
  // NSLog(@"Swiped left to %f", newOffset);
1209
- [self swipeHorizontalScrollToOffset:newOffset];
1257
+ [self scrollToHorizontalOffset:newOffset];
1210
1258
  }
1211
1259
 
1212
1260
  - (void)swipedRight
1213
1261
  {
1214
- CGFloat newOffset = self.scrollView.contentOffset.x + [self swipeHorizontalInterval];
1262
+ if (!self.scrollView.scrollEnabled) {
1263
+ return;
1264
+ }
1265
+
1266
+ BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1267
+ NSInteger horizontalInterval = [self swipeHorizontalInterval];
1268
+ CGFloat newOffset = self.scrollView.contentOffset.x + (isRTL ? -horizontalInterval : horizontalInterval);
1215
1269
  // NSLog(@"Swiped right to %f", newOffset);
1216
- [self swipeHorizontalScrollToOffset:newOffset];
1270
+ [self scrollToHorizontalOffset:newOffset];
1217
1271
  }
1218
1272
 
1219
1273
  - (void)addSwipeGestureRecognizers
@@ -582,7 +582,7 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
582
582
  [self handleFocusGuide];
583
583
  }
584
584
 
585
- if (context.nextFocusedView == self && self.isUserInteractionEnabled && ![self isTVFocusGuide]) {
585
+ if (context.nextFocusedView == self) {
586
586
  if(_eventEmitter) _eventEmitter->onFocus();
587
587
 
588
588
  [self becomeFirstResponder];
@@ -591,7 +591,10 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
591
591
  [self addParallaxMotionEffects];
592
592
  [self sendFocusNotification:context];
593
593
  } completion:^(void){}];
594
- } else {
594
+ // Without this check, onBlur would also trigger when `TVFocusGuideView` transfers focus to its children.
595
+ // [self isTVFocusGuide] is false when autofocus and destinations are not used, so we cannot use that.
596
+ // Generally speaking, it would happen for any non-collapsable `View`.
597
+ } else if (context.previouslyFocusedView == self) {
595
598
  if (_eventEmitter) _eventEmitter->onBlur();
596
599
 
597
600
  [self disableDirectionalFocusGuides];
@@ -303,7 +303,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
303
303
  [self handleFocusGuide];
304
304
  }
305
305
 
306
- if (context.nextFocusedView == self && ![self isTVFocusGuide] && self.isTVSelectable ) {
306
+ if (context.nextFocusedView == self) {
307
307
  if (self.onFocus) self.onFocus(nil);
308
308
  [self becomeFirstResponder];
309
309
  [self enableDirectionalFocusGuides];
@@ -311,7 +311,10 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
311
311
  [self addParallaxMotionEffects];
312
312
  [self sendFocusNotification:context];
313
313
  } completion:^(void){}];
314
- } else {
314
+ // Without this check, onBlur would also trigger when `TVFocusGuideView` transfers focus to its children.
315
+ // [self isTVFocusGuide] is false when autofocus and destinations are not used, so we cannot use that.
316
+ // Generally speaking, it would happen for any non-collapsable `View`.
317
+ } else if (context.previouslyFocusedView == self ) {
315
318
  if (self.onBlur) self.onBlur(nil);
316
319
  [self disableDirectionalFocusGuides];
317
320
  [coordinator addCoordinatedAnimations:^(void){
@@ -1025,19 +1025,23 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1025
1025
  [self sendBlurNotification];
1026
1026
  [self removeSwipeGestureRecognizers];
1027
1027
  [self resignFirstResponder];
1028
- // if we leave the scroll view and go up, then scroll to top; if going down,
1029
- // scroll to bottom
1030
- // Similarly for left and right
1031
- if (context.focusHeading == UIFocusHeadingUp && self.snapToStart) {
1032
- [self scrollToVerticalOffset:0.0];
1033
- } else if(context.focusHeading == UIFocusHeadingDown && self.snapToEnd) {
1034
- [self scrollToVerticalOffset:self.scrollView.contentSize.height];
1035
- } else if(context.focusHeading == UIFocusHeadingLeft && self.snapToStart) {
1036
- [self scrollToHorizontalOffset:0.0];
1037
- } else if(context.focusHeading == UIFocusHeadingRight && self.snapToEnd) {
1038
- [self scrollToHorizontalOffset:self.scrollView.contentSize.width];
1028
+ // If scrolling is enabled:
1029
+ // - Scroll to the top when moving up and to the bottom when moving down.
1030
+ // - Similarly, scroll towards leading edge when moving towards leading edge and to the trailing edge when moving towards the trailing edge.
1031
+ BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1032
+ BOOL isMovingTowardsLeadingEdge = (isRTL ? context.focusHeading == UIFocusHeadingRight : context.focusHeading == UIFocusHeadingLeft);
1033
+ BOOL isMovingTowardsTrailingEdge = (isRTL ? context.focusHeading == UIFocusHeadingLeft : context.focusHeading == UIFocusHeadingRight);
1034
+ if (self.scrollView.isScrollEnabled) {
1035
+ if (context.focusHeading == UIFocusHeadingUp && self.snapToStart) {
1036
+ [self scrollToVerticalOffset:0.0];
1037
+ } else if(context.focusHeading == UIFocusHeadingDown && self.snapToEnd) {
1038
+ [self scrollToVerticalOffset:self.scrollView.contentSize.height];
1039
+ } else if(isMovingTowardsLeadingEdge && self.snapToStart) {
1040
+ [self scrollToHorizontalOffset:0.0];
1041
+ } else if(isMovingTowardsLeadingEdge && self.snapToEnd) {
1042
+ [self scrollToHorizontalOffset:self.scrollView.contentSize.width];
1043
+ }
1039
1044
  }
1040
-
1041
1045
  }
1042
1046
  }
1043
1047
 
@@ -1093,22 +1097,31 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1093
1097
 
1094
1098
  - (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
1095
1099
  {
1100
+ // If the previously focused item is this view and scrolling is disabled, defer to the superclass
1101
+ if (context.previouslyFocusedItem == self && !self.scrollView.isScrollEnabled) {
1102
+ return [super shouldUpdateFocusInContext:context];
1103
+ }
1104
+
1096
1105
  // Determine if the layout is Right-to-Left
1097
1106
  BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1098
1107
  // Adjust for horizontal scrolling with RTL support
1099
1108
  if ([self isHorizontal:self.scrollView]) {
1100
- BOOL isNavigatingToEnd = (isRTL ? context.focusHeading == UIFocusHeadingLeft : context.focusHeading == UIFocusHeadingRight);
1101
- BOOL isNavigatingToStart = (isRTL ? context.focusHeading == UIFocusHeadingRight : context.focusHeading == UIFocusHeadingLeft);
1109
+ BOOL isMovingTowardsLeadingEdge = (isRTL ? context.focusHeading == UIFocusHeadingRight : context.focusHeading == UIFocusHeadingLeft);
1110
+ BOOL isMovingTowardsTrailingEdge = (isRTL ? context.focusHeading == UIFocusHeadingLeft : context.focusHeading == UIFocusHeadingRight);
1111
+
1112
+ BOOL isScrollingToLeading = (isMovingTowardsLeadingEdge && self.scrollView.contentOffset.x > 0);
1113
+ BOOL isScrollingToTrailing = (isMovingTowardsTrailingEdge && self.scrollView.contentOffset.x < self.scrollView.contentSize.width - MAX(self.scrollView.visibleSize.width, 1));
1102
1114
 
1103
- if ((isNavigatingToEnd && self.scrollView.contentOffset.x < self.scrollView.contentSize.width - self.scrollView.visibleSize.width) ||
1104
- (isNavigatingToStart && self.scrollView.contentOffset.x > 0)) {
1105
- return [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem];
1115
+ if (isScrollingToLeading || isScrollingToTrailing) {
1116
+ return (context.nextFocusedItem && [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem]);
1106
1117
  }
1107
1118
  } else {
1108
1119
  // Handle vertical scrolling as before
1109
- if ((context.focusHeading == UIFocusHeadingUp && self.scrollView.contentOffset.y > 0) ||
1110
- (context.focusHeading == UIFocusHeadingDown && self.scrollView.contentOffset.y < self.scrollView.contentSize.height - self.scrollView.visibleSize.height)) {
1111
- return [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem];
1120
+ BOOL isMovingUp = (context.focusHeading == UIFocusHeadingUp && self.scrollView.contentOffset.y > 0);
1121
+ BOOL isMovingDown = (context.focusHeading == UIFocusHeadingDown && self.scrollView.contentOffset.y < self.scrollView.contentSize.height - MAX(self.scrollView.visibleSize.height, 1));
1122
+
1123
+ if (isMovingUp || isMovingDown) {
1124
+ return (context.nextFocusedItem && [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem]);
1112
1125
  }
1113
1126
  }
1114
1127
  return [super shouldUpdateFocusInContext:context];
@@ -1154,11 +1167,21 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1154
1167
  _blockFirstTouch = NO;
1155
1168
  dispatch_async(dispatch_get_main_queue(), ^{
1156
1169
  CGFloat limitedOffset = yOffset;
1170
+
1171
+ // Ensure content size and visible size are non-negative
1172
+ CGFloat contentHeight = MAX(self.scrollView.contentSize.height, 0.0);
1173
+ CGFloat visibleHeight = MAX(self.scrollView.visibleSize.height, 0.0);
1174
+
1175
+ // Compute the maximum offset, ensuring it's non-negative
1176
+ CGFloat maxOffset = MAX(contentHeight - visibleHeight, 0.0);
1177
+
1178
+ // Clamp the offset within valid bounds
1157
1179
  limitedOffset = MAX(limitedOffset, 0.0);
1158
- limitedOffset = MIN(limitedOffset, self.scrollView.contentSize.height - self.scrollView.visibleSize.height);
1180
+ limitedOffset = MIN(limitedOffset, maxOffset);
1181
+
1159
1182
  [UIView animateWithDuration:[self swipeDuration] animations:^{
1160
1183
  self.scrollView.contentOffset =
1161
- CGPointMake(self.scrollView.contentOffset.x, limitedOffset);
1184
+ CGPointMake(self.scrollView.contentOffset.x, limitedOffset);
1162
1185
  }];
1163
1186
  });
1164
1187
  }
@@ -1168,11 +1191,21 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1168
1191
  _blockFirstTouch = NO;
1169
1192
  dispatch_async(dispatch_get_main_queue(), ^{
1170
1193
  CGFloat limitedOffset = xOffset;
1194
+
1195
+ // Ensure content size and visible size are non-negative
1196
+ CGFloat contentWidth = MAX(self.scrollView.contentSize.width, 0.0);
1197
+ CGFloat visibleWidth = MAX(self.scrollView.visibleSize.width, 0.0);
1198
+
1199
+ // Compute the maximum offset, ensuring it's non-negative
1200
+ CGFloat maxOffset = MAX(contentWidth - visibleWidth, 0.0);
1201
+
1202
+ // Clamp the offset within valid bounds
1171
1203
  limitedOffset = MAX(limitedOffset, 0.0);
1172
- limitedOffset = MIN(limitedOffset, self.scrollView.contentSize.width - self.scrollView.visibleSize.width);
1204
+ limitedOffset = MIN(limitedOffset, maxOffset);
1205
+
1173
1206
  [UIView animateWithDuration:[self swipeDuration] animations:^{
1174
1207
  self.scrollView.contentOffset =
1175
- CGPointMake(limitedOffset, self.scrollView.contentOffset.y);
1208
+ CGPointMake(limitedOffset, self.scrollView.contentOffset.y);
1176
1209
  }];
1177
1210
  });
1178
1211
  }
@@ -1205,7 +1238,9 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1205
1238
  return;
1206
1239
  }
1207
1240
 
1208
- CGFloat newOffset = self.scrollView.contentOffset.x - [self swipeHorizontalInterval];
1241
+ BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1242
+ NSInteger horizontalInterval = [self swipeHorizontalInterval];
1243
+ CGFloat newOffset = self.scrollView.contentOffset.x + (isRTL ? horizontalInterval : -horizontalInterval);
1209
1244
  // NSLog(@"Swiped left to %f", newOffset);
1210
1245
  [self scrollToHorizontalOffset:newOffset];
1211
1246
  }
@@ -1216,7 +1251,9 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1216
1251
  return;
1217
1252
  }
1218
1253
 
1219
- CGFloat newOffset = self.scrollView.contentOffset.x + [self swipeHorizontalInterval];
1254
+ BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1255
+ NSInteger horizontalInterval = [self swipeHorizontalInterval];
1256
+ CGFloat newOffset = self.scrollView.contentOffset.x + (isRTL ? -horizontalInterval : horizontalInterval);
1220
1257
  // NSLog(@"Swiped right to %f", newOffset);
1221
1258
  [self scrollToHorizontalOffset:newOffset];
1222
1259
  }
@@ -7859,6 +7859,7 @@ public class com/facebook/react/views/text/TextAttributeProps {
7859
7859
  public static final field TA_KEY_LETTER_SPACING S
7860
7860
  public static final field TA_KEY_LINE_BREAK_STRATEGY S
7861
7861
  public static final field TA_KEY_LINE_HEIGHT S
7862
+ public static final field TA_KEY_MAX_FONT_SIZE_MULTIPLIER S
7862
7863
  public static final field TA_KEY_OPACITY S
7863
7864
  public static final field TA_KEY_ROLE S
7864
7865
  public static final field TA_KEY_TEXT_DECORATION_COLOR S
@@ -7891,6 +7892,7 @@ public class com/facebook/react/views/text/TextAttributeProps {
7891
7892
  protected field mLetterSpacingInput F
7892
7893
  protected field mLineHeight F
7893
7894
  protected field mLineHeightInput F
7895
+ protected field mMaxFontSizeMultiplier F
7894
7896
  protected field mNumberOfLines I
7895
7897
  protected field mOpacity F
7896
7898
  protected field mRole Lcom/facebook/react/uimanager/ReactAccessibilityDelegate$Role;
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.76.6-0
1
+ VERSION_NAME=0.76.7-0
2
2
  react.internal.publishingGroup=io.github.react-native-tvos
3
3
 
4
4
  android.useAndroidX=true
@@ -25,4 +25,4 @@ binaryCompatibilityValidator.ignoredPackages=com.facebook.debug,\
25
25
  binaryCompatibilityValidator.nonPublicMarkers=com.facebook.react.common.annotations.VisibleForTesting,\
26
26
  com.facebook.react.common.annotations.UnstableReactNativeAPI
27
27
  binaryCompatibilityValidator.validationDisabled=true
28
- binaryCompatibilityValidator.outputApiFileName=ReactAndroid
28
+ binaryCompatibilityValidator.outputApiFileName=ReactAndroid
@@ -330,14 +330,11 @@ public class MountingManager {
330
330
  @AnyThread
331
331
  @ThreadConfined(ANY)
332
332
  public @Nullable EventEmitterWrapper getEventEmitter(int surfaceId, int reactTag) {
333
- SurfaceMountingManager surfaceMountingManager =
334
- (surfaceId == ViewUtil.NO_SURFACE_ID
335
- ? getSurfaceManagerForView(reactTag)
336
- : getSurfaceManager(surfaceId));
337
- if (surfaceMountingManager == null) {
333
+ SurfaceMountingManager smm = getSurfaceMountingManager(surfaceId, reactTag);
334
+ if (smm == null) {
338
335
  return null;
339
336
  }
340
- return surfaceMountingManager.getEventEmitter(reactTag);
337
+ return smm.getEventEmitter(reactTag);
341
338
  }
342
339
 
343
340
  /**
@@ -434,11 +431,21 @@ public class MountingManager {
434
431
  boolean canCoalesceEvent,
435
432
  @Nullable WritableMap params,
436
433
  @EventCategoryDef int eventCategory) {
437
- @Nullable SurfaceMountingManager smm = getSurfaceManager(surfaceId);
434
+ SurfaceMountingManager smm = getSurfaceMountingManager(surfaceId, reactTag);
438
435
  if (smm == null) {
439
- // Cannot queue event without valid surface mountng manager. Do nothing here.
436
+ FLog.d(
437
+ TAG,
438
+ "Cannot queue event without valid surface mounting manager for tag: %d, surfaceId: %d",
439
+ reactTag,
440
+ surfaceId);
440
441
  return;
441
442
  }
442
443
  smm.enqueuePendingEvent(reactTag, eventName, canCoalesceEvent, params, eventCategory);
443
444
  }
445
+
446
+ private @Nullable SurfaceMountingManager getSurfaceMountingManager(int surfaceId, int reactTag) {
447
+ return (surfaceId == ViewUtil.NO_SURFACE_ID
448
+ ? getSurfaceManagerForView(reactTag)
449
+ : getSurfaceManager(surfaceId));
450
+ }
444
451
  }
@@ -17,6 +17,6 @@ public class ReactNativeVersion {
17
17
  public static final Map<String, Object> VERSION = MapBuilder.<String, Object>of(
18
18
  "major", 0,
19
19
  "minor", 76,
20
- "patch", 6,
20
+ "patch", 7,
21
21
  "prerelease", "0");
22
22
  }
@@ -61,6 +61,7 @@ public class TextAttributeProps {
61
61
  public static final short TA_KEY_LINE_BREAK_STRATEGY = 25;
62
62
  public static final short TA_KEY_ROLE = 26;
63
63
  public static final short TA_KEY_TEXT_TRANSFORM = 27;
64
+ public static final short TA_KEY_MAX_FONT_SIZE_MULTIPLIER = 29;
64
65
 
65
66
  public static final int UNSET = -1;
66
67
 
@@ -81,6 +82,7 @@ public class TextAttributeProps {
81
82
  protected float mLineHeight = Float.NaN;
82
83
  protected boolean mIsColorSet = false;
83
84
  protected boolean mAllowFontScaling = true;
85
+ protected float mMaxFontSizeMultiplier = Float.NaN;
84
86
  protected int mColor;
85
87
  protected boolean mIsBackgroundColorSet = false;
86
88
  protected int mBackgroundColor;
@@ -227,6 +229,9 @@ public class TextAttributeProps {
227
229
  case TA_KEY_TEXT_TRANSFORM:
228
230
  result.setTextTransform(entry.getStringValue());
229
231
  break;
232
+ case TA_KEY_MAX_FONT_SIZE_MULTIPLIER:
233
+ result.setMaxFontSizeMultiplier((float) entry.getDoubleValue());
234
+ break;
230
235
  }
231
236
  }
232
237
 
@@ -243,6 +248,8 @@ public class TextAttributeProps {
243
248
  result.setLineHeight(getFloatProp(props, ViewProps.LINE_HEIGHT, ReactConstants.UNSET));
244
249
  result.setLetterSpacing(getFloatProp(props, ViewProps.LETTER_SPACING, Float.NaN));
245
250
  result.setAllowFontScaling(getBooleanProp(props, ViewProps.ALLOW_FONT_SCALING, true));
251
+ result.setMaxFontSizeMultiplier(
252
+ getFloatProp(props, ViewProps.MAX_FONT_SIZE_MULTIPLIER, Float.NaN));
246
253
  result.setFontSize(getFloatProp(props, ViewProps.FONT_SIZE, ReactConstants.UNSET));
247
254
  result.setColor(props.hasKey(ViewProps.COLOR) ? props.getInt(ViewProps.COLOR, 0) : null);
248
255
  result.setColor(
@@ -411,7 +418,14 @@ public class TextAttributeProps {
411
418
  mAllowFontScaling = allowFontScaling;
412
419
  setFontSize(mFontSizeInput);
413
420
  setLineHeight(mLineHeightInput);
414
- setLetterSpacing(mLetterSpacingInput);
421
+ }
422
+ }
423
+
424
+ private void setMaxFontSizeMultiplier(float maxFontSizeMultiplier) {
425
+ if (maxFontSizeMultiplier != mMaxFontSizeMultiplier) {
426
+ mMaxFontSizeMultiplier = maxFontSizeMultiplier;
427
+ setFontSize(mFontSizeInput);
428
+ setLineHeight(mLineHeightInput);
415
429
  }
416
430
  }
417
431
 
@@ -420,7 +434,7 @@ public class TextAttributeProps {
420
434
  if (fontSize != ReactConstants.UNSET) {
421
435
  fontSize =
422
436
  mAllowFontScaling
423
- ? (float) Math.ceil(PixelUtil.toPixelFromSP(fontSize))
437
+ ? (float) Math.ceil(PixelUtil.toPixelFromSP(fontSize, mMaxFontSizeMultiplier))
424
438
  : (float) Math.ceil(PixelUtil.toPixelFromDIP(fontSize));
425
439
  }
426
440
  mFontSize = (int) fontSize;
@@ -17,7 +17,7 @@ namespace facebook::react {
17
17
  constexpr struct {
18
18
  int32_t Major = 0;
19
19
  int32_t Minor = 76;
20
- int32_t Patch = 6;
20
+ int32_t Patch = 7;
21
21
  std::string_view Prerelease = "0";
22
22
  } ReactNativeVersion;
23
23
 
@@ -123,6 +123,15 @@ std::vector<ExportedMethod> parseExportedMethods(std::string moduleName, Class m
123
123
  NSArray<RCTMethodArgument *> *arguments;
124
124
  SEL objCMethodSelector = NSSelectorFromString(RCTParseMethodSignature(methodInfo->objcName, &arguments));
125
125
  NSMethodSignature *objCMethodSignature = [moduleClass instanceMethodSignatureForSelector:objCMethodSelector];
126
+ if (objCMethodSignature == nullptr) {
127
+ RCTLogWarn(
128
+ @"The objective-c `%s` method signature for the JS method `%@` can not be found in the ObjecitveC definition of the %s module.\nThe `%@` JS method will not be available.",
129
+ methodInfo->objcName,
130
+ jsMethodName,
131
+ moduleName.c_str(),
132
+ jsMethodName);
133
+ continue;
134
+ }
126
135
  std::string objCMethodReturnType = [objCMethodSignature methodReturnType];
127
136
 
128
137
  if (objCMethodSignature.numberOfArguments - 2 != [arguments count]) {
@@ -46,6 +46,9 @@ void TextAttributes::apply(TextAttributes textAttributes) {
46
46
  allowFontScaling = textAttributes.allowFontScaling.has_value()
47
47
  ? textAttributes.allowFontScaling
48
48
  : allowFontScaling;
49
+ maxFontSizeMultiplier = !std::isnan(textAttributes.maxFontSizeMultiplier)
50
+ ? textAttributes.maxFontSizeMultiplier
51
+ : maxFontSizeMultiplier;
49
52
  dynamicTypeRamp = textAttributes.dynamicTypeRamp.has_value()
50
53
  ? textAttributes.dynamicTypeRamp
51
54
  : dynamicTypeRamp;
@@ -168,6 +171,7 @@ bool TextAttributes::operator==(const TextAttributes& rhs) const {
168
171
  rhs.accessibilityRole,
169
172
  rhs.role,
170
173
  rhs.textTransform) &&
174
+ floatEquality(maxFontSizeMultiplier, rhs.maxFontSizeMultiplier) &&
171
175
  floatEquality(opacity, rhs.opacity) &&
172
176
  floatEquality(fontSize, rhs.fontSize) &&
173
177
  floatEquality(fontSizeMultiplier, rhs.fontSizeMultiplier) &&
@@ -213,6 +217,7 @@ SharedDebugStringConvertibleList TextAttributes::getDebugProps() const {
213
217
  debugStringConvertibleItem("allowFontScaling", allowFontScaling),
214
218
  debugStringConvertibleItem("dynamicTypeRamp", dynamicTypeRamp),
215
219
  debugStringConvertibleItem("letterSpacing", letterSpacing),
220
+ debugStringConvertibleItem("maxFontSizeMultiplier", maxFontSizeMultiplier),
216
221
 
217
222
  // Paragraph Styles
218
223
  debugStringConvertibleItem("lineHeight", lineHeight),
@@ -51,6 +51,7 @@ class TextAttributes : public DebugStringConvertible {
51
51
  std::optional<FontStyle> fontStyle{};
52
52
  std::optional<FontVariant> fontVariant{};
53
53
  std::optional<bool> allowFontScaling{};
54
+ Float maxFontSizeMultiplier{std::numeric_limits<Float>::quiet_NaN()};
54
55
  std::optional<DynamicTypeRamp> dynamicTypeRamp{};
55
56
  Float letterSpacing{std::numeric_limits<Float>::quiet_NaN()};
56
57
  std::optional<TextTransform> textTransform{};
@@ -117,6 +118,7 @@ struct hash<facebook::react::TextAttributes> {
117
118
  textAttributes.opacity,
118
119
  textAttributes.fontFamily,
119
120
  textAttributes.fontSize,
121
+ textAttributes.maxFontSizeMultiplier,
120
122
  textAttributes.fontSizeMultiplier,
121
123
  textAttributes.fontWeight,
122
124
  textAttributes.fontStyle,
@@ -909,6 +909,7 @@ constexpr static MapBuffer::Key TA_KEY_LINE_BREAK_STRATEGY = 25;
909
909
  constexpr static MapBuffer::Key TA_KEY_ROLE = 26;
910
910
  constexpr static MapBuffer::Key TA_KEY_TEXT_TRANSFORM = 27;
911
911
  constexpr static MapBuffer::Key TA_KEY_ALIGNMENT_VERTICAL = 28;
912
+ constexpr static MapBuffer::Key TA_KEY_MAX_FONT_SIZE_MULTIPLIER = 29;
912
913
 
913
914
  // constants for ParagraphAttributes serialization
914
915
  constexpr static MapBuffer::Key PA_KEY_MAX_NUMBER_OF_LINES = 0;
@@ -1003,6 +1004,10 @@ inline MapBuffer toMapBuffer(const TextAttributes& textAttributes) {
1003
1004
  builder.putBool(
1004
1005
  TA_KEY_ALLOW_FONT_SCALING, *textAttributes.allowFontScaling);
1005
1006
  }
1007
+ if (!std::isnan(textAttributes.maxFontSizeMultiplier)) {
1008
+ builder.putDouble(
1009
+ TA_KEY_MAX_FONT_SIZE_MULTIPLIER, textAttributes.maxFontSizeMultiplier);
1010
+ }
1006
1011
  if (!std::isnan(textAttributes.letterSpacing)) {
1007
1012
  builder.putDouble(TA_KEY_LETTER_SPACING, textAttributes.letterSpacing);
1008
1013
  }
@@ -73,6 +73,12 @@ static TextAttributes convertRawProp(
73
73
  "allowFontScaling",
74
74
  sourceTextAttributes.allowFontScaling,
75
75
  defaultTextAttributes.allowFontScaling);
76
+ textAttributes.maxFontSizeMultiplier = convertRawProp(
77
+ context,
78
+ rawProps,
79
+ "maxFontSizeMultiplier",
80
+ sourceTextAttributes.maxFontSizeMultiplier,
81
+ defaultTextAttributes.maxFontSizeMultiplier);
76
82
  textAttributes.dynamicTypeRamp = convertRawProp(
77
83
  context,
78
84
  rawProps,
@@ -266,6 +272,12 @@ void BaseTextProps::setProp(
266
272
  defaults, value, textAttributes, fontVariant, "fontVariant");
267
273
  REBUILD_FIELD_SWITCH_CASE(
268
274
  defaults, value, textAttributes, allowFontScaling, "allowFontScaling");
275
+ REBUILD_FIELD_SWITCH_CASE(
276
+ defaults,
277
+ value,
278
+ textAttributes,
279
+ maxFontSizeMultiplier,
280
+ "maxFontSizeMultiplier");
269
281
  REBUILD_FIELD_SWITCH_CASE(
270
282
  defaults, value, textAttributes, letterSpacing, "letterSpacing");
271
283
  REBUILD_FIELD_SWITCH_CASE(
@@ -139,6 +139,7 @@ inline static CGFloat RCTBaseSizeForDynamicTypeRamp(const DynamicTypeRamp &dynam
139
139
  inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const TextAttributes &textAttributes)
140
140
  {
141
141
  if (textAttributes.allowFontScaling.value_or(true)) {
142
+ CGFloat fontSizeMultiplier = !isnan(textAttributes.fontSizeMultiplier) ? textAttributes.fontSizeMultiplier : 1.0;
142
143
  if (textAttributes.dynamicTypeRamp.has_value()) {
143
144
  DynamicTypeRamp dynamicTypeRamp = textAttributes.dynamicTypeRamp.value();
144
145
  UIFontMetrics *fontMetrics =
@@ -146,10 +147,11 @@ inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const Tex
146
147
  // Using a specific font size reduces rounding errors from -scaledValueForValue:
147
148
  CGFloat requestedSize =
148
149
  isnan(textAttributes.fontSize) ? RCTBaseSizeForDynamicTypeRamp(dynamicTypeRamp) : textAttributes.fontSize;
149
- return [fontMetrics scaledValueForValue:requestedSize] / requestedSize;
150
- } else {
151
- return textAttributes.fontSizeMultiplier;
150
+ fontSizeMultiplier = [fontMetrics scaledValueForValue:requestedSize] / requestedSize;
152
151
  }
152
+ CGFloat maxFontSizeMultiplier =
153
+ !isnan(textAttributes.maxFontSizeMultiplier) ? textAttributes.maxFontSizeMultiplier : 0.0;
154
+ return maxFontSizeMultiplier >= 1.0 ? fminf(maxFontSizeMultiplier, fontSizeMultiplier) : fontSizeMultiplier;
153
155
  } else {
154
156
  return 1.0;
155
157
  }
@@ -23,7 +23,7 @@ javax-annotation-api = "1.3.2"
23
23
  javax-inject = "1"
24
24
  jsr305 = "3.0.2"
25
25
  junit = "4.13.2"
26
- kotlin = "1.9.24"
26
+ kotlin = "1.9.25"
27
27
  mockito = "3.12.4"
28
28
  nexus-publish = "1.3.0"
29
29
  okhttp = "4.9.2"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-tvos",
3
- "version": "0.76.6-0",
3
+ "version": "0.76.7-0",
4
4
  "description": "A framework for building native apps using React",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -110,13 +110,13 @@
110
110
  },
111
111
  "dependencies": {
112
112
  "@jest/create-cache-key-function": "^29.6.3",
113
- "@react-native/assets-registry": "0.76.6",
114
- "@react-native/codegen": "0.76.6",
115
- "@react-native/community-cli-plugin": "0.76.6",
116
- "@react-native/gradle-plugin": "0.76.6",
117
- "@react-native/js-polyfills": "0.76.6",
118
- "@react-native/normalize-colors": "0.76.6",
119
- "@react-native-tvos/virtualized-lists": "0.76.6-0",
113
+ "@react-native/assets-registry": "0.76.7",
114
+ "@react-native/codegen": "0.76.7",
115
+ "@react-native/community-cli-plugin": "0.76.7",
116
+ "@react-native/gradle-plugin": "0.76.7",
117
+ "@react-native/js-polyfills": "0.76.7",
118
+ "@react-native/normalize-colors": "0.76.7",
119
+ "@react-native-tvos/virtualized-lists": "0.76.7-0",
120
120
  "abort-controller": "^3.0.0",
121
121
  "anser": "^1.4.9",
122
122
  "ansi-regex": "^5.0.0",
Binary file
Binary file
Binary file
Binary file