react-native-tvos 0.76.5-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.
- package/Libraries/AppDelegate/RCTAppDelegate.mm +0 -5
- package/Libraries/AppDelegate/RCTAppSetupUtils.mm +3 -1
- package/Libraries/AppDelegate/RCTRootViewFactory.mm +3 -3
- package/Libraries/Components/Pressable/Pressable.d.ts +1 -1
- package/Libraries/Components/TV/TVFocusGuideView.js +0 -1
- package/Libraries/Components/View/ViewPropTypes.d.ts +4 -2
- package/Libraries/Core/ReactNativeVersion.js +1 -1
- package/Libraries/Image/RCTImageLoader.mm +9 -1
- package/Libraries/Pressability/Pressability.js +2 -2
- package/Libraries/Text/TextInput/RCTBaseTextInputView.mm +1 -1
- package/Libraries/Utilities/Appearance.js +3 -1
- package/React/Base/RCTConvert.mm +3 -1
- package/React/Base/RCTVersion.m +1 -1
- package/React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingProxyRootView.mm +2 -5
- package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +85 -31
- package/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +4 -0
- package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +17 -11
- package/React/Views/RCTTVView.m +5 -2
- package/React/Views/ScrollView/RCTScrollView.m +63 -26
- package/ReactAndroid/api/ReactAndroid.api +2 -0
- package/ReactAndroid/cmake-utils/ReactNative-application.cmake +18 -3
- package/ReactAndroid/gradle.properties +2 -2
- package/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java +15 -8
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +1 -7
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +1 -11
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +1 -3
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +1 -3
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +1 -12
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +1 -3
- package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java +16 -2
- package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java +1 -0
- package/ReactAndroid/src/main/jni/react/fabric/Binding.cpp +17 -19
- package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +1 -15
- package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +1 -4
- package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +1 -5
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +1 -6
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +47 -65
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +2 -4
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +1 -5
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +1 -2
- package/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTInteropTurboModule.mm +9 -0
- package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +1 -6
- package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +1 -3
- package/ReactCommon/react/renderer/attributedstring/TextAttributes.cpp +5 -0
- package/ReactCommon/react/renderer/attributedstring/TextAttributes.h +2 -0
- package/ReactCommon/react/renderer/attributedstring/conversions.h +5 -0
- package/ReactCommon/react/renderer/components/text/BaseTextProps.cpp +12 -0
- package/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h +2 -3
- package/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm +7 -3
- package/gradle/libs.versions.toml +1 -1
- package/package.json +8 -8
- package/scripts/cocoapods/utils.rb +6 -6
- package/scripts/codegen/generate-artifacts-executor.js +6 -6
- package/sdks/hermesc/linux64-bin/hermesc +0 -0
- package/sdks/hermesc/osx-bin/hermes +0 -0
- package/sdks/hermesc/osx-bin/hermesc +0 -0
- package/sdks/hermesc/win64-bin/hermesc.exe +0 -0
- package/src/private/featureflags/ReactNativeFeatureFlags.js +1 -6
- package/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +1 -2
- package/types/public/ReactNativeTVTypes.d.ts +9 -7
|
@@ -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
|
-
|
|
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 =
|
|
163
|
-
initWithSurface:surface
|
|
164
|
-
|
|
162
|
+
RCTSurfaceHostingProxyRootView *surfaceHostingProxyRootView =
|
|
163
|
+
[[RCTSurfaceHostingProxyRootView alloc] initWithSurface:surface];
|
|
164
|
+
|
|
165
165
|
#if !TARGET_OS_TV
|
|
166
166
|
surfaceHostingProxyRootView.backgroundColor = [UIColor systemBackgroundColor];
|
|
167
167
|
#endif
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import {View} from '../View/View';
|
|
21
21
|
import {AccessibilityProps} from '../View/ViewAccessibility';
|
|
22
22
|
import {ViewProps} from '../View/ViewPropTypes';
|
|
23
|
-
import {TVParallaxProperties} from '../../../types/
|
|
23
|
+
import {TVParallaxProperties} from '../../../types/index';
|
|
24
24
|
|
|
25
25
|
export interface PressableStateCallbackType {
|
|
26
26
|
readonly pressed: boolean;
|
|
@@ -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<
|
|
224
|
-
readonly onBlur?: BubblingEventHandler<
|
|
225
|
+
readonly onFocus?: BubblingEventHandler<NativeFocusEvent> | undefined;
|
|
226
|
+
readonly onBlur?: BubblingEventHandler<NativeBlurEvent> | undefined;
|
|
225
227
|
}
|
|
@@ -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
|
-
|
|
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];
|
|
@@ -440,7 +440,7 @@ export default class Pressability {
|
|
|
440
440
|
_createEventHandlers(): EventHandlers {
|
|
441
441
|
const tvPressEventHandlers = {
|
|
442
442
|
onPressIn: (evt: any): void => {
|
|
443
|
-
if (this._config.disabled ===
|
|
443
|
+
if (this._config.disabled === true) {
|
|
444
444
|
return;
|
|
445
445
|
}
|
|
446
446
|
|
|
@@ -461,7 +461,7 @@ export default class Pressability {
|
|
|
461
461
|
}, delayLongPress + delayPressIn);
|
|
462
462
|
},
|
|
463
463
|
onPressOut: (evt: any): void => {
|
|
464
|
-
if (this._config.disabled ===
|
|
464
|
+
if (this._config.disabled === true) {
|
|
465
465
|
return;
|
|
466
466
|
}
|
|
467
467
|
this._cancelLongPressDelayTimeout();
|
|
@@ -461,7 +461,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame)
|
|
|
461
461
|
_maxLength.integerValue - (NSInteger)backedTextInputView.attributedText.string.length + (NSInteger)range.length,
|
|
462
462
|
0);
|
|
463
463
|
|
|
464
|
-
if (text.length >
|
|
464
|
+
if (text.length > allowedLength) {
|
|
465
465
|
// If we typed/pasted more than one character, limit the text inputted.
|
|
466
466
|
if (text.length > 1) {
|
|
467
467
|
if (allowedLength > 0) {
|
|
@@ -105,7 +105,9 @@ export function setColorScheme(colorScheme: ?ColorSchemeName): void {
|
|
|
105
105
|
const {NativeAppearance} = state;
|
|
106
106
|
if (NativeAppearance != null) {
|
|
107
107
|
NativeAppearance.setColorScheme(colorScheme ?? 'unspecified');
|
|
108
|
-
state.appearance = {
|
|
108
|
+
state.appearance = {
|
|
109
|
+
colorScheme: toColorScheme(NativeAppearance.getColorScheme()),
|
|
110
|
+
};
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
113
|
|
package/React/Base/RCTConvert.mm
CHANGED
|
@@ -528,17 +528,19 @@ RCT_ENUM_CONVERTER(
|
|
|
528
528
|
}),
|
|
529
529
|
NSNotFound,
|
|
530
530
|
unsignedIntegerValue)
|
|
531
|
+
#endif
|
|
531
532
|
RCT_ENUM_CONVERTER(
|
|
532
533
|
UIModalPresentationStyle,
|
|
533
534
|
(@{
|
|
534
535
|
@"fullScreen" : @(UIModalPresentationFullScreen),
|
|
536
|
+
#if !TARGET_OS_TV
|
|
535
537
|
@"pageSheet" : @(UIModalPresentationPageSheet),
|
|
536
538
|
@"formSheet" : @(UIModalPresentationFormSheet),
|
|
539
|
+
#endif
|
|
537
540
|
@"overFullScreen" : @(UIModalPresentationOverFullScreen),
|
|
538
541
|
}),
|
|
539
542
|
UIModalPresentationFullScreen,
|
|
540
543
|
integerValue)
|
|
541
|
-
#endif
|
|
542
544
|
|
|
543
545
|
RCT_ENUM_CONVERTER(
|
|
544
546
|
UIViewContentMode,
|
package/React/Base/RCTVersion.m
CHANGED
|
@@ -53,11 +53,8 @@ static RCTRootViewSizeFlexibility convertToRootViewSizeFlexibility(RCTSurfaceSiz
|
|
|
53
53
|
|
|
54
54
|
- (instancetype)initWithSurface:(id<RCTSurfaceProtocol>)surface
|
|
55
55
|
{
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
//
|
|
1034
|
-
//
|
|
1035
|
-
// Similarly
|
|
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 (
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
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
|
|
1109
|
-
BOOL
|
|
1118
|
+
BOOL isMovingTowardsLeadingEdge = (isRTL ? context.focusHeading == UIFocusHeadingRight : context.focusHeading == UIFocusHeadingLeft);
|
|
1119
|
+
BOOL isMovingTowardsTrailingEdge = (isRTL ? context.focusHeading == UIFocusHeadingLeft : context.focusHeading == UIFocusHeadingRight);
|
|
1110
1120
|
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
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
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
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)
|
|
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,
|
|
1192
|
+
limitedOffset = MIN(limitedOffset, maxOffset);
|
|
1193
|
+
|
|
1170
1194
|
[UIView animateWithDuration:[self swipeDuration] animations:^{
|
|
1171
1195
|
self.scrollView.contentOffset =
|
|
1172
|
-
|
|
1196
|
+
CGPointMake(self.scrollView.contentOffset.x, limitedOffset);
|
|
1173
1197
|
}];
|
|
1174
1198
|
});
|
|
1175
1199
|
}
|
|
1176
1200
|
|
|
1177
|
-
- (void)
|
|
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,
|
|
1216
|
+
limitedOffset = MIN(limitedOffset, maxOffset);
|
|
1217
|
+
|
|
1184
1218
|
[UIView animateWithDuration:[self swipeDuration] animations:^{
|
|
1185
1219
|
self.scrollView.contentOffset =
|
|
1186
|
-
|
|
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
|
|
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
|
|
1244
|
+
[self scrollToVerticalOffset:newOffset];
|
|
1203
1245
|
}
|
|
1204
1246
|
|
|
1205
1247
|
- (void)swipedLeft
|
|
1206
1248
|
{
|
|
1207
|
-
|
|
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
|
|
1257
|
+
[self scrollToHorizontalOffset:newOffset];
|
|
1210
1258
|
}
|
|
1211
1259
|
|
|
1212
1260
|
- (void)swipedRight
|
|
1213
1261
|
{
|
|
1214
|
-
|
|
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
|
|
1270
|
+
[self scrollToHorizontalOffset:newOffset];
|
|
1217
1271
|
}
|
|
1218
1272
|
|
|
1219
1273
|
- (void)addSwipeGestureRecognizers
|
|
@@ -99,9 +99,11 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
|
|
|
99
99
|
NSMutableDictionary<NSAttributedStringKey, id> *defaultAttributes =
|
|
100
100
|
[_backedTextInputView.defaultTextAttributes mutableCopy];
|
|
101
101
|
|
|
102
|
+
#if !TARGET_OS_MACCATALYST
|
|
102
103
|
RCTWeakEventEmitterWrapper *eventEmitterWrapper = [RCTWeakEventEmitterWrapper new];
|
|
103
104
|
eventEmitterWrapper.eventEmitter = _eventEmitter;
|
|
104
105
|
defaultAttributes[RCTAttributedStringEventEmitterKey] = eventEmitterWrapper;
|
|
106
|
+
#endif
|
|
105
107
|
|
|
106
108
|
_backedTextInputView.defaultTextAttributes = defaultAttributes;
|
|
107
109
|
}
|
|
@@ -261,8 +263,10 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
|
|
|
261
263
|
if (newTextInputProps.textAttributes != oldTextInputProps.textAttributes) {
|
|
262
264
|
NSMutableDictionary<NSAttributedStringKey, id> *defaultAttributes =
|
|
263
265
|
RCTNSTextAttributesFromTextAttributes(newTextInputProps.getEffectiveTextAttributes(RCTFontSizeMultiplier()));
|
|
266
|
+
#if !TARGET_OS_MACCATALYST
|
|
264
267
|
defaultAttributes[RCTAttributedStringEventEmitterKey] =
|
|
265
268
|
_backedTextInputView.defaultTextAttributes[RCTAttributedStringEventEmitterKey];
|
|
269
|
+
#endif
|
|
266
270
|
_backedTextInputView.defaultTextAttributes = defaultAttributes;
|
|
267
271
|
}
|
|
268
272
|
|
|
@@ -582,7 +582,7 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
|
|
|
582
582
|
[self handleFocusGuide];
|
|
583
583
|
}
|
|
584
584
|
|
|
585
|
-
if (context.nextFocusedView == self
|
|
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
|
-
|
|
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];
|
|
@@ -1656,15 +1659,10 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
|
|
|
1656
1659
|
}
|
|
1657
1660
|
|
|
1658
1661
|
// clipping
|
|
1662
|
+
self.currentContainerView.layer.mask = nil;
|
|
1659
1663
|
if (self.currentContainerView.clipsToBounds) {
|
|
1660
1664
|
BOOL clipToPaddingBox = ReactNativeFeatureFlags::enableIOSViewClipToPaddingBox();
|
|
1661
|
-
if (clipToPaddingBox) {
|
|
1662
|
-
CALayer *maskLayer = [self createMaskLayer:RCTCGRectFromRect(_layoutMetrics.getPaddingFrame())
|
|
1663
|
-
cornerInsets:RCTGetCornerInsets(
|
|
1664
|
-
RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii),
|
|
1665
|
-
RCTUIEdgeInsetsFromEdgeInsets(borderMetrics.borderWidths))];
|
|
1666
|
-
self.currentContainerView.layer.mask = maskLayer;
|
|
1667
|
-
} else {
|
|
1665
|
+
if (!clipToPaddingBox) {
|
|
1668
1666
|
if (borderMetrics.borderRadii.isUniform()) {
|
|
1669
1667
|
self.currentContainerView.layer.cornerRadius = borderMetrics.borderRadii.topLeft.horizontal;
|
|
1670
1668
|
} else {
|
|
@@ -1686,9 +1684,17 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
|
|
|
1686
1684
|
subview.layer.mask = [self createMaskLayer:subview.bounds cornerInsets:cornerInsets];
|
|
1687
1685
|
}
|
|
1688
1686
|
}
|
|
1687
|
+
} else if (
|
|
1688
|
+
!borderMetrics.borderWidths.isUniform() || borderMetrics.borderWidths.left != 0 ||
|
|
1689
|
+
!borderMetrics.borderRadii.isUniform()) {
|
|
1690
|
+
CALayer *maskLayer = [self createMaskLayer:RCTCGRectFromRect(_layoutMetrics.getPaddingFrame())
|
|
1691
|
+
cornerInsets:RCTGetCornerInsets(
|
|
1692
|
+
RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii),
|
|
1693
|
+
RCTUIEdgeInsetsFromEdgeInsets(borderMetrics.borderWidths))];
|
|
1694
|
+
self.currentContainerView.layer.mask = maskLayer;
|
|
1695
|
+
} else {
|
|
1696
|
+
self.currentContainerView.layer.cornerRadius = borderMetrics.borderRadii.topLeft.horizontal;
|
|
1689
1697
|
}
|
|
1690
|
-
} else {
|
|
1691
|
-
self.currentContainerView.layer.mask = nil;
|
|
1692
1698
|
}
|
|
1693
1699
|
}
|
|
1694
1700
|
|
package/React/Views/RCTTVView.m
CHANGED
|
@@ -303,7 +303,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
|
|
|
303
303
|
[self handleFocusGuide];
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
-
if (context.nextFocusedView == self
|
|
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
|
-
|
|
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
|
-
//
|
|
1029
|
-
//
|
|
1030
|
-
// Similarly
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
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
|
|
1101
|
-
BOOL
|
|
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 (
|
|
1104
|
-
(
|
|
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
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
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,
|
|
1180
|
+
limitedOffset = MIN(limitedOffset, maxOffset);
|
|
1181
|
+
|
|
1159
1182
|
[UIView animateWithDuration:[self swipeDuration] animations:^{
|
|
1160
1183
|
self.scrollView.contentOffset =
|
|
1161
|
-
|
|
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,
|
|
1204
|
+
limitedOffset = MIN(limitedOffset, maxOffset);
|
|
1205
|
+
|
|
1173
1206
|
[UIView animateWithDuration:[self swipeDuration] animations:^{
|
|
1174
1207
|
self.scrollView.contentOffset =
|
|
1175
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|