react-native-tvos 0.76.1-0 → 0.76.2-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 (84) hide show
  1. package/Libraries/AppDelegate/React-RCTAppDelegate.podspec +1 -1
  2. package/Libraries/Components/Pressable/Pressable.d.ts +9 -1
  3. package/Libraries/Components/Pressable/Pressable.js +4 -16
  4. package/Libraries/Components/TV/TVViewPropTypes.js +2 -1
  5. package/Libraries/Components/TextInput/TextInput.d.ts +1 -1
  6. package/Libraries/Components/Touchable/Touchable.js +0 -43
  7. package/Libraries/Components/Touchable/TouchableBounce.js +0 -33
  8. package/Libraries/Components/Touchable/TouchableHighlight.js +12 -47
  9. package/Libraries/Components/Touchable/TouchableNativeFeedback.js +0 -33
  10. package/Libraries/Components/Touchable/TouchableOpacity.js +12 -44
  11. package/Libraries/Components/Touchable/TouchableWithoutFeedback.js +0 -19
  12. package/Libraries/Components/View/ViewNativeComponent.js +6 -0
  13. package/Libraries/Components/View/ViewPropTypes.d.ts +12 -1
  14. package/Libraries/Components/View/ViewPropTypes.js +7 -0
  15. package/Libraries/Core/ReactNativeVersion.js +1 -1
  16. package/Libraries/Core/setUpErrorHandling.js +1 -7
  17. package/Libraries/LogBox/Data/LogBoxData.js +2 -2
  18. package/Libraries/NativeComponent/BaseViewConfig.android.js +19 -0
  19. package/Libraries/NativeComponent/BaseViewConfig.ios.js +6 -0
  20. package/Libraries/NativeComponent/TVViewConfig.js +4 -0
  21. package/Libraries/Pressability/Pressability.js +45 -28
  22. package/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h +1 -0
  23. package/Libraries/Types/CoreEventTypes.d.ts +21 -0
  24. package/Libraries/Types/CoreEventTypes.js +6 -0
  25. package/README.md +9 -7
  26. package/React/Base/RCTTVRemoteHandler.m +0 -19
  27. package/React/Base/RCTTVRemoteSelectHandler.h +27 -0
  28. package/React/Base/RCTTVRemoteSelectHandler.m +120 -0
  29. package/React/Base/RCTVersion.m +1 -1
  30. package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +12 -8
  31. package/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +47 -3
  32. package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h +8 -0
  33. package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +51 -44
  34. package/React/Views/RCTTVView.h +19 -6
  35. package/React/Views/RCTTVView.m +63 -55
  36. package/React/Views/RCTViewManager.m +4 -0
  37. package/React/Views/ScrollView/RCTScrollView.m +12 -8
  38. package/ReactAndroid/api/ReactAndroid.api +0 -1
  39. package/ReactAndroid/cmake-utils/ReactNative-application.cmake +1 -1
  40. package/ReactAndroid/gradle.properties +1 -1
  41. package/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.kt +2 -0
  42. package/ReactAndroid/src/main/java/com/facebook/react/modules/core/TimingModule.kt +0 -8
  43. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +1 -1
  44. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java +16 -0
  45. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java +20 -0
  46. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlurEvent.kt +16 -0
  47. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/FocusEvent.kt +16 -0
  48. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PressInEvent.kt +16 -0
  49. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PressOutEvent.kt +16 -0
  50. package/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt +11 -3
  51. package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java +212 -4
  52. package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java +47 -4
  53. package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
  54. package/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp +3 -2
  55. package/ReactCommon/react/renderer/components/view/BaseViewEventEmitter.cpp +18 -0
  56. package/ReactCommon/react/renderer/components/view/BaseViewEventEmitter.h +8 -0
  57. package/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.h +12 -1
  58. package/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm +165 -2
  59. package/cli.js +1 -1
  60. package/index.js +0 -4
  61. package/package.json +8 -8
  62. package/scripts/codegen/generate-artifacts-executor.js +3 -3
  63. package/sdks/.hermesversion +1 -1
  64. package/sdks/hermesc/osx-bin/hermes +0 -0
  65. package/sdks/hermesc/osx-bin/hermesc +0 -0
  66. package/sdks/hermesc/win64-bin/hermesc.exe +0 -0
  67. package/types/modules/Codegen.d.ts +6 -0
  68. package/types/public/ReactNativeTVTypes.d.ts +2 -2
  69. package/Libraries/Components/TabBarIOS/RCTTabBarItemNativeComponent.js +0 -99
  70. package/Libraries/Components/TabBarIOS/RCTTabBarNativeComponent.js +0 -32
  71. package/Libraries/Components/TabBarIOS/TabBarIOS.ios.js +0 -59
  72. package/Libraries/Components/TabBarIOS/TabBarIOS.js +0 -52
  73. package/Libraries/Components/TabBarIOS/TabBarIOSProps.js +0 -52
  74. package/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js +0 -177
  75. package/Libraries/Components/TabBarIOS/TabBarItemIOS.js +0 -55
  76. package/Libraries/Components/Touchable/TVTouchable.js +0 -71
  77. package/React/Views/RCTTabBar.h +0 -22
  78. package/React/Views/RCTTabBar.m +0 -237
  79. package/React/Views/RCTTabBarItem.h +0 -35
  80. package/React/Views/RCTTabBarItem.m +0 -139
  81. package/React/Views/RCTTabBarItemManager.h +0 -12
  82. package/React/Views/RCTTabBarItemManager.m +0 -38
  83. package/React/Views/RCTTabBarManager.h +0 -12
  84. package/React/Views/RCTTabBarManager.m +0 -81
@@ -63,8 +63,6 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
63
63
  BOOL _removeClippedSubviews;
64
64
  NSMutableArray<UIView *> *_reactSubviews;
65
65
  BOOL _motionEffectsAdded;
66
- UITapGestureRecognizer *_selectRecognizer;
67
- UILongPressGestureRecognizer * _longSelectRecognizer;
68
66
  NSSet<NSString *> *_Nullable _propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN;
69
67
  UIView *_containerView;
70
68
  BOOL _useCustomContainerView;
@@ -268,52 +266,61 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
268
266
  [[NSNotificationCenter defaultCenter] postNavigationBlurEventWithTag:@(self.tag) target:@(self.tag)];
269
267
  }
270
268
 
271
- - (void)sendSelectNotification:(UIGestureRecognizer *)recognizer
269
+ - (void)sendSelectNotification
272
270
  {
273
- [[NSNotificationCenter defaultCenter] postNavigationPressEventWithType:RCTTVRemoteEventSelect keyAction:RCTTVRemoteEventKeyActionUp tag:@(self.tag) target:@(self.tag)];
271
+ [[NSNotificationCenter defaultCenter] postNavigationPressEventWithType:RCTTVRemoteEventSelect keyAction:RCTTVRemoteEventKeyActionUp tag:@(self.tag) target:@(self.tag)];
274
272
  }
275
273
 
276
- - (void)sendLongSelectNotification:(UIGestureRecognizer *)recognizer
274
+ - (void)sendLongSelectBeganNotification
277
275
  {
278
- [[NSNotificationCenter defaultCenter] postNavigationPressEventWithType:RCTTVRemoteEventLongSelect keyAction:recognizer.eventKeyAction tag:@(self.tag) target:@(self.tag)];
276
+ [[NSNotificationCenter defaultCenter] postNavigationPressEventWithType:RCTTVRemoteEventLongSelect keyAction:RCTTVRemoteEventKeyActionDown tag:@(self.tag) target:@(self.tag)];
279
277
  }
280
278
 
281
- - (void)handleSelect:(UIGestureRecognizer *)r
279
+ - (void)sendLongSelectEndedNotification
280
+ {
281
+ [[NSNotificationCenter defaultCenter] postNavigationPressEventWithType:RCTTVRemoteEventLongSelect keyAction:RCTTVRemoteEventKeyActionUp tag:@(self.tag) target:@(self.tag)];
282
+ }
283
+
284
+ - (void)animatePressIn
282
285
  {
283
286
  if (_tvParallaxProperties.enabled == YES) {
284
- float magnification = _tvParallaxProperties.magnification;
285
287
  float pressMagnification = _tvParallaxProperties.pressMagnification;
286
288
 
287
289
  // Duration of press animation
288
290
  float pressDuration = _tvParallaxProperties.pressDuration;
289
291
 
290
- // Delay of press animation
291
- float pressDelay = _tvParallaxProperties.pressDelay;
292
-
293
- [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:pressDelay]];
294
-
295
292
  [UIView animateWithDuration:(pressDuration/2)
296
293
  animations:^{
297
294
  self.transform = CGAffineTransformMakeScale(pressMagnification, pressMagnification);
298
295
  }
299
- completion:^(__unused BOOL finished1){
300
- [UIView animateWithDuration:(pressDuration/2)
301
- animations:^{
302
- self.transform = CGAffineTransformMakeScale(magnification, magnification);
303
- }
304
- completion:^(__unused BOOL finished2) {
305
- [self sendSelectNotification:r];
306
- }];
307
- }];
296
+ completion:^(__unused BOOL finished){}];
297
+ }
298
+ }
308
299
 
309
- } else {
310
- [self sendSelectNotification:r];
300
+ - (void) animatePressOut
301
+ {
302
+ if (_tvParallaxProperties.enabled == YES) {
303
+ float magnification = _tvParallaxProperties.magnification;
304
+
305
+ // Duration of press animation
306
+ float pressDuration = _tvParallaxProperties.pressDuration;
307
+
308
+ [UIView animateWithDuration:(pressDuration/2)
309
+ animations:^{
310
+ self.transform = CGAffineTransformMakeScale(magnification, magnification);
311
+ }
312
+ completion:^(__unused BOOL finished){}];
311
313
  }
312
314
  }
313
315
 
314
- - (void)handleLongSelect:(UIGestureRecognizer *)r
316
+ - (void)emitPressInEvent
317
+ {
318
+ _eventEmitter->onPressIn();
319
+ }
320
+
321
+ - (void)emitPressOutEvent
315
322
  {
316
- [self sendLongSelectNotification:r];
323
+ _eventEmitter->onPressOut();
317
324
  }
318
325
 
319
326
  - (void)addParallaxMotionEffects
@@ -576,6 +583,8 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
576
583
  }
577
584
 
578
585
  if (context.nextFocusedView == self && self.isUserInteractionEnabled && ![self isTVFocusGuide]) {
586
+ if(_eventEmitter) _eventEmitter->onFocus();
587
+
579
588
  [self becomeFirstResponder];
580
589
  [self enableDirectionalFocusGuides];
581
590
  [coordinator addCoordinatedAnimations:^(void){
@@ -583,6 +592,8 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
583
592
  [self sendFocusNotification:context];
584
593
  } completion:^(void){}];
585
594
  } else {
595
+ if (_eventEmitter) _eventEmitter->onBlur();
596
+
586
597
  [self disableDirectionalFocusGuides];
587
598
  [coordinator addCoordinatedAnimations:^(void){
588
599
  [self removeParallaxMotionEffects];
@@ -1029,25 +1040,9 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
1029
1040
  // `isTVSelectable`
1030
1041
  if (oldViewProps.isTVSelectable != newViewProps.isTVSelectable) {
1031
1042
  if (newViewProps.isTVSelectable && ![self isTVFocusGuide]) {
1032
- UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
1033
- action:@selector(handleSelect:)];
1034
- recognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ];
1035
- _selectRecognizer = recognizer;
1036
-
1037
- UILongPressGestureRecognizer *longRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongSelect:)];
1038
- recognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ];
1039
- [self addGestureRecognizer:longRecognizer];
1040
- _longSelectRecognizer = longRecognizer;
1041
-
1042
- [self addGestureRecognizer:_selectRecognizer];
1043
- [self addGestureRecognizer:_longSelectRecognizer];
1043
+ self.tvRemoteSelectHandler = [[RCTTVRemoteSelectHandler alloc]initWithView:self];
1044
1044
  } else {
1045
- if (_selectRecognizer) {
1046
- [self removeGestureRecognizer:_selectRecognizer];
1047
- }
1048
- if (_longSelectRecognizer) {
1049
- [self removeGestureRecognizer:_longSelectRecognizer];
1050
- }
1045
+ self.tvRemoteSelectHandler = nil;
1051
1046
  }
1052
1047
  }
1053
1048
  // `tvParallaxProperties
@@ -1077,6 +1072,9 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
1077
1072
  _nextFocusUp = [rootView viewWithTag:newViewProps.nextFocusUp.value()];
1078
1073
  [self enableDirectionalFocusGuides];
1079
1074
  } else {
1075
+ if (self.focusGuideUp != nil) {
1076
+ [[self containingRootView] removeLayoutGuide:self.focusGuideUp];
1077
+ }
1080
1078
  _nextFocusUp = nil;
1081
1079
  }
1082
1080
  }
@@ -1087,6 +1085,9 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
1087
1085
  _nextFocusDown = [rootView viewWithTag:newViewProps.nextFocusDown.value()];
1088
1086
  [self enableDirectionalFocusGuides];
1089
1087
  } else {
1088
+ if (self.focusGuideDown != nil) {
1089
+ [[self containingRootView] removeLayoutGuide:self.focusGuideDown];
1090
+ }
1090
1091
  _nextFocusDown = nil;
1091
1092
  }
1092
1093
  }
@@ -1097,6 +1098,9 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
1097
1098
  _nextFocusLeft = [rootView viewWithTag:newViewProps.nextFocusLeft.value()];
1098
1099
  [self enableDirectionalFocusGuides];
1099
1100
  } else {
1101
+ if (self.focusGuideLeft != nil) {
1102
+ [[self containingRootView] removeLayoutGuide:self.focusGuideLeft];
1103
+ }
1100
1104
  _nextFocusLeft = nil;
1101
1105
  }
1102
1106
  }
@@ -1107,6 +1111,9 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
1107
1111
  _nextFocusRight = [rootView viewWithTag:newViewProps.nextFocusRight.value()];
1108
1112
  [self enableDirectionalFocusGuides];
1109
1113
  } else {
1114
+ if (self.focusGuideRight != nil) {
1115
+ [[self containingRootView] removeLayoutGuide:self.focusGuideRight];
1116
+ }
1110
1117
  _nextFocusRight = nil;
1111
1118
  }
1112
1119
  }
@@ -10,9 +10,10 @@
10
10
 
11
11
  #import <React/RCTView.h>
12
12
  #import <React/RCTBridge.h>
13
+ #import <React/RCTTVRemoteSelectHandler.h>
13
14
 
14
15
  // A RCTView with additional properties and methods for user interaction using the Apple TV focus engine.
15
- @interface RCTTVView : RCTView
16
+ @interface RCTTVView : RCTView <RCTTVRemoteSelectHandlerDelegate>
16
17
 
17
18
  /**
18
19
  * TV event handlers
@@ -29,6 +30,10 @@
29
30
  */
30
31
  @property (nonatomic, assign) BOOL hasTVPreferredFocus;
31
32
 
33
+ /**
34
+ * Select and longSelect event handler
35
+ */
36
+ @property (nonatomic, strong) RCTTVRemoteSelectHandler *tvRemoteSelectHandler;
32
37
  /**
33
38
  * Focus direction tags
34
39
  */
@@ -52,6 +57,19 @@
52
57
  @property (nonatomic, assign) BOOL trapFocusLeft;
53
58
  @property (nonatomic, assign) BOOL trapFocusRight;
54
59
 
60
+
61
+ /**
62
+ * Focus
63
+ */
64
+ @property (nonatomic, copy) RCTBubblingEventBlock onFocus;
65
+ @property (nonatomic, copy) RCTBubblingEventBlock onBlur;
66
+
67
+ /**
68
+ * TV Press Handlers
69
+ */
70
+ @property (nonatomic, copy) RCTDirectEventBlock onPressIn;
71
+ @property (nonatomic, copy) RCTDirectEventBlock onPressOut;
72
+
55
73
  - (instancetype)initWithBridge:(RCTBridge *)bridge;
56
74
 
57
75
  /**
@@ -64,11 +82,6 @@
64
82
  */
65
83
  - (void)sendBlurNotification:(UIFocusUpdateContext *)context;
66
84
 
67
- /**
68
- * Send Select Notification to listeners
69
- */
70
- - (void)sendSelectNotification:(UIGestureRecognizer *)recognizer;
71
-
72
85
  /**
73
86
  * Adds Parallax Motion Effects if tvParallaxProperty is enabled
74
87
  */
@@ -22,8 +22,6 @@
22
22
 
23
23
  @implementation RCTTVView {
24
24
  __weak RCTBridge *_bridge;
25
- UITapGestureRecognizer *_selectRecognizer;
26
- UILongPressGestureRecognizer * _longSelectRecognizer;
27
25
  BOOL motionEffectsAdded;
28
26
  NSArray* focusDestinations;
29
27
  id<UIFocusItem> previouslyFocusedItem;
@@ -77,64 +75,47 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
77
75
  {
78
76
  self->_isTVSelectable = isTVSelectable;
79
77
  if (isTVSelectable && ![self isTVFocusGuide]) {
80
- UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
81
- action:@selector(handleSelect:)];
82
- recognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ];
83
- _selectRecognizer = recognizer;
84
-
85
- UILongPressGestureRecognizer *longRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongSelect:)];
86
- recognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ];
87
- [self addGestureRecognizer:longRecognizer];
88
- _longSelectRecognizer = longRecognizer;
89
-
90
- [self addGestureRecognizer:_selectRecognizer];
91
- [self addGestureRecognizer:_longSelectRecognizer];
78
+ self.tvRemoteSelectHandler = [[RCTTVRemoteSelectHandler alloc] initWithView:self];
92
79
  } else {
93
- if (_selectRecognizer) {
94
- [self removeGestureRecognizer:_selectRecognizer];
95
- }
96
- if (_longSelectRecognizer) {
97
- [self removeGestureRecognizer:_longSelectRecognizer];
98
- }
80
+ self.tvRemoteSelectHandler = nil;
99
81
  }
100
82
  }
101
83
 
102
- - (void)handleSelect:(UIGestureRecognizer *)r
84
+ - (void)animatePressIn
103
85
  {
104
86
  if ([self.tvParallaxProperties[@"enabled"] boolValue] == YES) {
105
- float magnification = [self.tvParallaxProperties[@"magnification"] floatValue];
106
87
  float pressMagnification = [self.tvParallaxProperties[@"pressMagnification"] floatValue];
107
-
108
- // Duration of press animation
109
88
  float pressDuration = [self.tvParallaxProperties[@"pressDuration"] floatValue];
110
-
111
- // Delay of press animation
112
- float pressDelay = [self.tvParallaxProperties[@"pressDelay"] floatValue];
113
-
114
- [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:pressDelay]];
115
-
116
89
  [UIView animateWithDuration:(pressDuration/2)
117
90
  animations:^{
118
91
  self.transform = CGAffineTransformMakeScale(pressMagnification, pressMagnification);
119
92
  }
120
- completion:^(__unused BOOL finished1){
121
- [UIView animateWithDuration:(pressDuration/2)
122
- animations:^{
123
- self.transform = CGAffineTransformMakeScale(magnification, magnification);
124
- }
125
- completion:^(__unused BOOL finished2) {
126
- [self sendSelectNotification:r];
127
- }];
128
- }];
129
-
130
- } else {
131
- [self sendSelectNotification:r];
93
+ completion:^(__unused BOOL finished){}];
132
94
  }
133
95
  }
134
96
 
135
- - (void)handleLongSelect:(UIGestureRecognizer *)r
97
+ - (void) animatePressOut
98
+ {
99
+ if ([self.tvParallaxProperties[@"enabled"] boolValue] == YES) {
100
+ float magnification = [self.tvParallaxProperties[@"magnification"] floatValue];
101
+ float pressDuration = [self.tvParallaxProperties[@"pressDuration"] floatValue];
102
+
103
+ [UIView animateWithDuration:(pressDuration/2)
104
+ animations:^{
105
+ self.transform = CGAffineTransformMakeScale(magnification, magnification);
106
+ }
107
+ completion:^(__unused BOOL finished){}];
108
+ }
109
+ }
110
+
111
+ - (void)emitPressInEvent
112
+ {
113
+ if (self.onPressIn) self.onPressIn(nil);
114
+ }
115
+
116
+ - (void)emitPressOutEvent
136
117
  {
137
- [self sendLongSelectNotification:r];
118
+ if (self.onPressOut) self.onPressOut(nil);
138
119
  }
139
120
 
140
121
  - (BOOL)isTVFocusGuide
@@ -323,6 +304,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
323
304
  }
324
305
 
325
306
  if (context.nextFocusedView == self && ![self isTVFocusGuide] && self.isTVSelectable ) {
307
+ if (self.onFocus) self.onFocus(nil);
326
308
  [self becomeFirstResponder];
327
309
  [self enableDirectionalFocusGuides];
328
310
  [coordinator addCoordinatedAnimations:^(void){
@@ -330,6 +312,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
330
312
  [self sendFocusNotification:context];
331
313
  } completion:^(void){}];
332
314
  } else {
315
+ if (self.onBlur) self.onBlur(nil);
333
316
  [self disableDirectionalFocusGuides];
334
317
  [coordinator addCoordinatedAnimations:^(void){
335
318
  [self sendBlurNotification:context];
@@ -441,14 +424,19 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
441
424
  [[NSNotificationCenter defaultCenter] postNavigationBlurEventWithTag:self.reactTag target:self.reactTag];
442
425
  }
443
426
 
444
- - (void)sendSelectNotification:(UIGestureRecognizer *)recognizer
427
+ - (void)sendSelectNotification
445
428
  {
446
429
  [[NSNotificationCenter defaultCenter] postNavigationPressEventWithType:RCTTVRemoteEventSelect keyAction:RCTTVRemoteEventKeyActionUp tag:self.reactTag target:self.reactTag];
447
430
  }
448
431
 
449
- - (void)sendLongSelectNotification:(UIGestureRecognizer *)recognizer
432
+ - (void)sendLongSelectBeganNotification
450
433
  {
451
- [[NSNotificationCenter defaultCenter] postNavigationPressEventWithType:RCTTVRemoteEventLongSelect keyAction:recognizer.eventKeyAction tag:self.reactTag target:self.reactTag];
434
+ [[NSNotificationCenter defaultCenter] postNavigationPressEventWithType:RCTTVRemoteEventLongSelect keyAction:RCTTVRemoteEventKeyActionDown tag:self.reactTag target:self.reactTag];
435
+ }
436
+
437
+ - (void)sendLongSelectEndedNotification
438
+ {
439
+ [[NSNotificationCenter defaultCenter] postNavigationPressEventWithType:RCTTVRemoteEventLongSelect keyAction:RCTTVRemoteEventKeyActionUp tag:self.reactTag target:self.reactTag];
452
440
  }
453
441
 
454
442
  - (RCTTVView *)getViewById:(NSNumber *)viewId {
@@ -458,23 +446,43 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
458
446
  }
459
447
 
460
448
  - (void)setNextFocusUp:(NSNumber *)nextFocusUp {
461
- self->_nextFocusUp = [self getViewById: nextFocusUp];
462
- [self enableDirectionalFocusGuides];
449
+ if (self.focusGuideUp != nil && nextFocusUp == nil) {
450
+ [[self rootView] removeLayoutGuide:self.focusGuideUp];
451
+ self.focusGuideUp = nil;
452
+ } else {
453
+ self->_nextFocusUp = [self getViewById: nextFocusUp];
454
+ [self enableDirectionalFocusGuides];
455
+ }
463
456
  }
464
457
 
465
458
  - (void)setNextFocusDown:(NSNumber *)nextFocusDown {
466
- self->_nextFocusDown = [self getViewById: nextFocusDown];
467
- [self enableDirectionalFocusGuides];
459
+ if (self.focusGuideDown != nil && nextFocusDown == nil) {
460
+ [[self rootView] removeLayoutGuide:self.focusGuideDown];
461
+ self.focusGuideDown = nil;
462
+ } else {
463
+ self->_nextFocusDown = [self getViewById: nextFocusDown];
464
+ [self enableDirectionalFocusGuides];
465
+ }
468
466
  }
469
467
 
470
468
  - (void)setNextFocusLeft:(NSNumber *)nextFocusLeft {
471
- self->_nextFocusLeft = [self getViewById: nextFocusLeft];
472
- [self enableDirectionalFocusGuides];
469
+ if (self.focusGuideLeft != nil && nextFocusLeft == nil) {
470
+ [[self rootView] removeLayoutGuide:self.focusGuideLeft];
471
+ self.focusGuideLeft = nil;
472
+ } else {
473
+ self->_nextFocusLeft = [self getViewById: nextFocusLeft];
474
+ [self enableDirectionalFocusGuides];
475
+ }
473
476
  }
474
477
 
475
478
  - (void)setNextFocusRight:(NSNumber *)nextFocusRight {
476
- self->_nextFocusRight = [self getViewById: nextFocusRight];
477
- [self enableDirectionalFocusGuides];
479
+ if (self.focusGuideRight != nil && nextFocusRight == nil) {
480
+ [[self rootView] removeLayoutGuide:self.focusGuideRight];
481
+ self.focusGuideRight = nil;
482
+ } else {
483
+ self->_nextFocusRight = [self getViewById: nextFocusRight];
484
+ [self enableDirectionalFocusGuides];
485
+ }
478
486
  }
479
487
 
480
488
  - (void)setPreferredFocus:(BOOL)hasTVPreferredFocus
@@ -197,6 +197,10 @@ RCT_EXPORT_VIEW_PROPERTY(trapFocusUp, BOOL)
197
197
  RCT_EXPORT_VIEW_PROPERTY(trapFocusDown, BOOL)
198
198
  RCT_EXPORT_VIEW_PROPERTY(trapFocusLeft, BOOL)
199
199
  RCT_EXPORT_VIEW_PROPERTY(trapFocusRight, BOOL)
200
+ RCT_EXPORT_VIEW_PROPERTY(onFocus, RCTBubblingEventBlock)
201
+ RCT_EXPORT_VIEW_PROPERTY(onBlur, RCTBubblingEventBlock)
202
+ RCT_EXPORT_VIEW_PROPERTY(onPressIn, RCTDirectEventBlock)
203
+ RCT_EXPORT_VIEW_PROPERTY(onPressOut, RCTDirectEventBlock)
200
204
  #endif
201
205
 
202
206
  // Accessibility related properties
@@ -23,6 +23,7 @@
23
23
  #if TARGET_OS_TV
24
24
  #import "RCTTVRemoteHandler.h"
25
25
  #import "RCTTVNavigationEventNotification.h"
26
+ #import "React/RCTI18nUtil.h"
26
27
  #endif
27
28
 
28
29
  #if !TARGET_OS_TV
@@ -1086,21 +1087,24 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1086
1087
 
1087
1088
  - (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
1088
1089
  {
1089
- // Keep focus inside the scroll view till the end of the content
1090
+ // Determine if the layout is Right-to-Left
1091
+ BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1092
+ // Adjust for horizontal scrolling with RTL support
1090
1093
  if ([self isHorizontal:self.scrollView]) {
1091
- if ((context.focusHeading == UIFocusHeadingLeft && self.scrollView.contentOffset.x > 0)
1092
- || (context.focusHeading == UIFocusHeadingRight && self.scrollView.contentOffset.x < self.scrollView.contentSize.width - self.scrollView.visibleSize.width)
1093
- ) {
1094
+ BOOL isNavigatingToEnd = (isRTL ? context.focusHeading == UIFocusHeadingLeft : context.focusHeading == UIFocusHeadingRight);
1095
+ BOOL isNavigatingToStart = (isRTL ? context.focusHeading == UIFocusHeadingRight : context.focusHeading == UIFocusHeadingLeft);
1096
+
1097
+ if ((isNavigatingToEnd && self.scrollView.contentOffset.x < self.scrollView.contentSize.width - self.scrollView.visibleSize.width) ||
1098
+ (isNavigatingToStart && self.scrollView.contentOffset.x > 0)) {
1094
1099
  return [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem];
1095
1100
  }
1096
1101
  } else {
1097
- if ((context.focusHeading == UIFocusHeadingUp && self.scrollView.contentOffset.y > 0)
1098
- || (context.focusHeading == UIFocusHeadingDown && self.scrollView.contentOffset.y < self.scrollView.contentSize.height - self.scrollView.visibleSize.height)
1099
- ) {
1102
+ // Handle vertical scrolling as before
1103
+ if ((context.focusHeading == UIFocusHeadingUp && self.scrollView.contentOffset.y > 0) ||
1104
+ (context.focusHeading == UIFocusHeadingDown && self.scrollView.contentOffset.y < self.scrollView.contentSize.height - self.scrollView.visibleSize.height)) {
1100
1105
  return [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem];
1101
1106
  }
1102
1107
  }
1103
-
1104
1108
  return [super shouldUpdateFocusInContext:context];
1105
1109
  }
1106
1110
 
@@ -3306,7 +3306,6 @@ public final class com/facebook/react/modules/core/TimingModule : com/facebook/f
3306
3306
  public fun createTimer (DDDZ)V
3307
3307
  public fun deleteTimer (D)V
3308
3308
  public fun emitTimeDriftWarning (Ljava/lang/String;)V
3309
- public fun initialize ()V
3310
3309
  public fun invalidate ()V
3311
3310
  public fun setSendIdleEvents (Z)V
3312
3311
  }
@@ -36,7 +36,7 @@ if(CMAKE_HOST_WIN32)
36
36
  endif()
37
37
 
38
38
  file(GLOB input_SRC CONFIGURE_DEPENDS
39
- *.cpp
39
+ ${REACT_ANDROID_DIR}/cmake-utils/default-app-setup/*.cpp
40
40
  ${BUILD_DIR}/generated/autolinking/src/main/jni/*.cpp)
41
41
 
42
42
  add_library(${CMAKE_PROJECT_NAME} SHARED ${input_SRC})
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.76.1-0
1
+ VERSION_NAME=0.76.2-0
2
2
  react.internal.publishingGroup=io.github.react-native-tvos
3
3
 
4
4
  android.useAndroidX=true
@@ -65,6 +65,7 @@ public open class JavaTimerManager(
65
65
 
66
66
  init {
67
67
  reactApplicationContext.addLifecycleEventListener(this)
68
+ HeadlessJsTaskContext.getInstance(reactApplicationContext).addTaskEventListener(this)
68
69
  }
69
70
 
70
71
  override fun onHostPause() {
@@ -103,6 +104,7 @@ public open class JavaTimerManager(
103
104
  }
104
105
 
105
106
  public open fun onInstanceDestroy() {
107
+ HeadlessJsTaskContext.getInstance(reactApplicationContext).removeTaskEventListener(this)
106
108
  reactApplicationContext.removeLifecycleEventListener(this)
107
109
  clearFrameCallback()
108
110
  clearChoreographerIdleCallback()
@@ -12,7 +12,6 @@ import com.facebook.react.bridge.ReactApplicationContext
12
12
  import com.facebook.react.bridge.WritableArray
13
13
  import com.facebook.react.common.annotations.VisibleForTesting
14
14
  import com.facebook.react.devsupport.interfaces.DevSupportManager
15
- import com.facebook.react.jstasks.HeadlessJsTaskContext
16
15
  import com.facebook.react.module.annotations.ReactModule
17
16
 
18
17
  /** Native module for JS timer execution. Timers fire on frame boundaries. */
@@ -24,11 +23,6 @@ public class TimingModule(
24
23
  private val javaTimerManager: JavaTimerManager =
25
24
  JavaTimerManager(reactContext, this, ReactChoreographer.getInstance(), devSupportManager)
26
25
 
27
- override fun initialize() {
28
- HeadlessJsTaskContext.getInstance(getReactApplicationContext())
29
- .addTaskEventListener(javaTimerManager)
30
- }
31
-
32
26
  override fun createTimer(
33
27
  callbackIDDouble: Double,
34
28
  durationDouble: Double,
@@ -68,8 +62,6 @@ public class TimingModule(
68
62
  }
69
63
 
70
64
  override fun invalidate() {
71
- val headlessJsTaskContext = HeadlessJsTaskContext.getInstance(getReactApplicationContext())
72
- headlessJsTaskContext.removeTaskEventListener(javaTimerManager)
73
65
  javaTimerManager.onInstanceDestroy()
74
66
  }
75
67
 
@@ -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", 1,
20
+ "patch", 2,
21
21
  "prerelease", "0");
22
22
  }
@@ -736,6 +736,16 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
736
736
  MapBuilder.of(
737
737
  "phasedRegistrationNames",
738
738
  MapBuilder.of("bubbled", "onClick", "captured", "onClickCapture")))
739
+ .put(
740
+ "topFocus",
741
+ MapBuilder.of(
742
+ "phasedRegistrationNames",
743
+ MapBuilder.of("bubbled", "onFocus", "captured", "onFocusCapture")))
744
+ .put(
745
+ "topBlur",
746
+ MapBuilder.of(
747
+ "phasedRegistrationNames",
748
+ MapBuilder.of("bubbled", "onBlur", "captured", "onBlurCapture")))
739
749
  .build());
740
750
  return eventTypeConstants;
741
751
  }
@@ -751,6 +761,12 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
751
761
  .put(
752
762
  "topAccessibilityAction",
753
763
  MapBuilder.of("registrationName", "onAccessibilityAction"))
764
+ .put(
765
+ "topPressIn",
766
+ MapBuilder.of("registrationName", "onPressIn"))
767
+ .put(
768
+ "topPressOut",
769
+ MapBuilder.of("registrationName", "onPressOut"))
754
770
  .build());
755
771
  return eventTypeConstants;
756
772
  }
@@ -568,6 +568,26 @@ public class ReactAccessibilityDelegate extends ExploreByTouchHelper {
568
568
  if (action == AccessibilityNodeInfoCompat.ACTION_EXPAND) {
569
569
  host.setTag(R.id.accessibility_state_expanded, true);
570
570
  }
571
+ if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
572
+ final WritableMap payload = Arguments.createMap();
573
+ final int reactTag = host.getId();
574
+ payload.putString("eventType", "select");
575
+ payload.putInt("eventKeyAction", 1);
576
+ payload.putInt("tag", reactTag);
577
+ payload.putInt("target", reactTag);
578
+ ReactContext reactContext = (ReactContext) host.getContext();
579
+ reactContext.emitDeviceEvent("onHWKeyEvent", payload);
580
+ }
581
+ if (action == AccessibilityNodeInfoCompat.ACTION_LONG_CLICK) {
582
+ final WritableMap payload = Arguments.createMap();
583
+ final int reactTag = host.getId();
584
+ payload.putString("eventType", "longSelect");
585
+ payload.putInt("eventKeyAction", 1);
586
+ payload.putInt("tag", reactTag);
587
+ payload.putInt("target", reactTag);
588
+ ReactContext reactContext = (ReactContext) host.getContext();
589
+ reactContext.emitDeviceEvent("onHWKeyEvent", payload);
590
+ }
571
591
  if (mAccessibilityActionsMap.containsKey(action)) {
572
592
  final WritableMap event = Arguments.createMap();
573
593
  event.putString("actionName", mAccessibilityActionsMap.get(action));
@@ -0,0 +1,16 @@
1
+ package com.facebook.react.uimanager.events
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.WritableMap
5
+
6
+ public class BlurEvent(surfaceId: Int, viewId: Int) :
7
+ Event<BlurEvent>(surfaceId, viewId) {
8
+
9
+ override fun getEventName(): String = EVENT_NAME
10
+
11
+ override fun getEventData(): WritableMap = Arguments.createMap()
12
+
13
+ private companion object {
14
+ private const val EVENT_NAME: String = "topBlur"
15
+ }
16
+ }
@@ -0,0 +1,16 @@
1
+ package com.facebook.react.uimanager.events
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.WritableMap
5
+
6
+ public class FocusEvent(surfaceId: Int, viewId: Int) :
7
+ Event<FocusEvent>(surfaceId, viewId) {
8
+
9
+ override fun getEventName(): String = EVENT_NAME
10
+
11
+ override fun getEventData(): WritableMap = Arguments.createMap()
12
+
13
+ private companion object {
14
+ private const val EVENT_NAME: String = "topFocus"
15
+ }
16
+ }
@@ -0,0 +1,16 @@
1
+ package com.facebook.react.uimanager.events
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.WritableMap
5
+
6
+ public class PressInEvent(surfaceId: Int, viewId: Int) :
7
+ Event<PressInEvent>(surfaceId, viewId) {
8
+
9
+ override fun getEventName(): String = EVENT_NAME
10
+
11
+ override fun getEventData(): WritableMap = Arguments.createMap()
12
+
13
+ private companion object {
14
+ private const val EVENT_NAME: String = "topPressIn"
15
+ }
16
+ }