react-native-tvos 0.76.1-0 → 0.76.1-1

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 (41) hide show
  1. package/Libraries/Components/Pressable/Pressable.d.ts +1 -1
  2. package/Libraries/Components/Pressable/Pressable.js +0 -15
  3. package/Libraries/Components/TV/TVViewPropTypes.js +2 -1
  4. package/Libraries/Components/TextInput/TextInput.d.ts +1 -1
  5. package/Libraries/Components/Touchable/Touchable.js +0 -43
  6. package/Libraries/Components/Touchable/TouchableBounce.js +0 -33
  7. package/Libraries/Components/Touchable/TouchableHighlight.js +12 -47
  8. package/Libraries/Components/Touchable/TouchableNativeFeedback.js +0 -33
  9. package/Libraries/Components/Touchable/TouchableOpacity.js +12 -44
  10. package/Libraries/Components/Touchable/TouchableWithoutFeedback.js +0 -19
  11. package/Libraries/Components/View/ViewNativeComponent.js +6 -0
  12. package/Libraries/Components/View/ViewPropTypes.d.ts +12 -1
  13. package/Libraries/Components/View/ViewPropTypes.js +7 -0
  14. package/Libraries/Core/ReactNativeVersion.js +1 -1
  15. package/Libraries/NativeComponent/BaseViewConfig.android.js +19 -0
  16. package/Libraries/NativeComponent/BaseViewConfig.ios.js +6 -0
  17. package/Libraries/NativeComponent/TVViewConfig.js +4 -0
  18. package/Libraries/Pressability/Pressability.js +45 -28
  19. package/Libraries/Types/CoreEventTypes.d.ts +21 -0
  20. package/Libraries/Types/CoreEventTypes.js +6 -0
  21. package/React/Base/RCTVersion.m +1 -1
  22. package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +39 -25
  23. package/React/Views/RCTTVView.h +13 -0
  24. package/React/Views/RCTTVView.m +54 -34
  25. package/React/Views/RCTViewManager.m +4 -0
  26. package/ReactAndroid/gradle.properties +1 -1
  27. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +1 -1
  28. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java +16 -0
  29. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java +20 -0
  30. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlurEvent.kt +16 -0
  31. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/FocusEvent.kt +16 -0
  32. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PressInEvent.kt +16 -0
  33. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PressOutEvent.kt +16 -0
  34. package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java +212 -4
  35. package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java +47 -4
  36. package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
  37. package/ReactCommon/react/renderer/components/view/BaseViewEventEmitter.cpp +18 -0
  38. package/ReactCommon/react/renderer/components/view/BaseViewEventEmitter.h +8 -0
  39. package/package.json +2 -2
  40. package/types/public/ReactNativeTVTypes.d.ts +1 -1
  41. package/Libraries/Components/Touchable/TVTouchable.js +0 -71
@@ -157,7 +157,8 @@ export type EventHandlers = $ReadOnly<{|
157
157
  onResponderTerminate: (event: PressEvent) => void,
158
158
  onResponderTerminationRequest: () => boolean,
159
159
  onStartShouldSetResponder: () => boolean,
160
- onTVEvent: (event: any) => void,
160
+ onPressIn: (event: any) => void,
161
+ onPressOut: (event: any) => void,
161
162
  |}>;
162
163
 
163
164
  type TouchState =
@@ -397,6 +398,7 @@ export default class Pressability {
397
398
  |}>;
398
399
  _touchActivateTime: ?number;
399
400
  _touchState: TouchState = 'NOT_RESPONDER';
401
+ _longPressSent: boolean = false;
400
402
 
401
403
  constructor(config: PressabilityConfig) {
402
404
  this.configure(config);
@@ -436,35 +438,50 @@ export default class Pressability {
436
438
  }
437
439
 
438
440
  _createEventHandlers(): EventHandlers {
439
- const tvEventHandlers = {
440
- onTVEvent: (evt: any): void => {
441
- if (this._config.disabled !== false) {
442
- // $FlowFixMe[prop-missing]
443
- if (evt?.eventType === 'focus') {
444
- const {onFocus} = this._config;
445
- onFocus && onFocus(evt);
446
- // $FlowFixMe[prop-missing]
447
- } else if (evt.eventType === 'blur') {
448
- const {onBlur} = this._config;
449
- onBlur && onBlur(evt);
450
- } else if (evt.eventType === 'select') {
451
- const {onPress, onPressIn, onPressOut} = this._config;
452
- // $FlowFixMe[incompatible-exact]
453
- onPressIn && onPressIn(evt);
454
- onPress && onPress(evt);
455
- setTimeout(() => {
456
- onPressOut && onPressOut(evt);
457
- }, this._config.minPressDuration ?? DEFAULT_MIN_PRESS_DURATION);
458
- } else if (evt.eventType === 'longSelect') {
459
- const {onLongPress, onPressIn, onPressOut} = this._config;
460
- onLongPress && onLongPress(evt);
461
- evt?.eventKeyAction === 0
462
- ? onPressIn && onPressIn(evt)
463
- : onPressOut && onPressOut(evt);
441
+ const tvPressEventHandlers = {
442
+ onPressIn: (evt: any): void => {
443
+ if (this._config.disabled === false) {
444
+ return;
445
+ }
446
+
447
+ this._longPressSent = false;
448
+
449
+ const {onPressIn, onLongPress} = this._config;
450
+ onPressIn && onPressIn(evt);
451
+
452
+ const delayPressIn = normalizeDelay(this._config.delayPressIn);
453
+ const delayLongPress = normalizeDelay(
454
+ this._config.delayLongPress,
455
+ 10,
456
+ DEFAULT_LONG_PRESS_DELAY_MS - delayPressIn,
457
+ );
458
+ this._longPressDelayTimeout = setTimeout(() => {
459
+ onLongPress && onLongPress(evt);
460
+ this._longPressSent = true;
461
+ }, delayLongPress + delayPressIn);
462
+ },
463
+ onPressOut: (evt: any): void => {
464
+ if (this._config.disabled === false) {
465
+ return;
466
+ }
467
+ this._cancelLongPressDelayTimeout();
468
+ const {onPress, onLongPress, onPressOut, android_disableSound} =
469
+ this._config;
470
+ onPressOut && onPressOut(evt);
471
+
472
+ if (onPress != null) {
473
+ const isPressCanceledByLongPress =
474
+ onLongPress != null && this._longPressSent;
475
+ if (!isPressCanceledByLongPress) {
476
+ if (Platform.OS === 'android' && android_disableSound !== true) {
477
+ SoundManager.playTouchSound();
478
+ }
479
+ onPress(evt);
464
480
  }
465
481
  }
466
482
  },
467
483
  };
484
+
468
485
  const focusEventHandlers = {
469
486
  onBlur: (event: BlurEvent): void => {
470
487
  const {onBlur} = this._config;
@@ -645,7 +662,7 @@ export default class Pressability {
645
662
  };
646
663
  }
647
664
  return {
648
- ...tvEventHandlers,
665
+ ...tvPressEventHandlers,
649
666
  ...focusEventHandlers,
650
667
  ...responderEventHandlers,
651
668
  ...hoverPointerEvents,
@@ -698,7 +715,7 @@ export default class Pressability {
698
715
  },
699
716
  };
700
717
  return {
701
- ...tvEventHandlers,
718
+ ...tvPressEventHandlers,
702
719
  ...focusEventHandlers,
703
720
  ...responderEventHandlers,
704
721
  ...mouseEventHandlers,
@@ -246,6 +246,15 @@ export interface GestureResponderEvent
246
246
 
247
247
  export interface MouseEvent extends NativeSyntheticEvent<NativeMouseEvent> {}
248
248
 
249
+ export interface NativeFocusEvent {}
250
+ export interface FocusEvent extends NativeSyntheticEvent<NativeFocusEvent> {}
251
+
252
+ export interface NativeBlurEvent {}
253
+ export interface BlurEvent extends NativeSyntheticEvent<NativeBlurEvent> {}
254
+
255
+ export interface NativePressEvent {}
256
+ export interface PressEvent extends NativeSyntheticEvent<NativePressEvent> {}
257
+
249
258
  export interface TargetedEvent {
250
259
  target: number;
251
260
  }
@@ -265,6 +274,18 @@ export interface PointerEvents {
265
274
  onPointerUpCapture?: ((event: PointerEvent) => void) | undefined;
266
275
  }
267
276
 
277
+ export interface PressEvents {
278
+ onPressIn?: ((event: PressEvent) => void) | undefined;
279
+ onPressOut?: ((event: PressEvent) => void) | undefined;
280
+ }
281
+
282
+ export interface FocusEvents {
283
+ onFocus?: ((event: FocusEvent) => void) | undefined;
284
+ onFocusCapture?: ((event: FocusEvent) => void) | undefined;
285
+ onBlur?: ((event: BlurEvent) => void) | undefined;
286
+ onBlurCapture?: ((event: BlurEvent) => void) | undefined;
287
+ }
288
+
268
289
  export interface TVRemoteEvent {
269
290
  tag?: number | undefined;
270
291
  target?: number | undefined;
@@ -282,6 +282,12 @@ export type FocusEvent = SyntheticEvent<
282
282
  |}>,
283
283
  >;
284
284
 
285
+ export type RemotePressEvent = SyntheticEvent<
286
+ $ReadOnly<{|
287
+ target: number,
288
+ |}>,
289
+ >;
290
+
285
291
  export type MouseEvent = SyntheticEvent<
286
292
  $ReadOnly<{|
287
293
  clientX: number,
@@ -24,7 +24,7 @@ NSDictionary* RCTGetReactNativeVersion(void)
24
24
  RCTVersionMajor: @(0),
25
25
  RCTVersionMinor: @(76),
26
26
  RCTVersionPatch: @(1),
27
- RCTVersionPrerelease: @"0",
27
+ RCTVersionPrerelease: @"1",
28
28
  };
29
29
  });
30
30
  return __rnVersion;
@@ -63,8 +63,7 @@ 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;
66
+ UILongPressGestureRecognizer * _pressRecognizer;
68
67
  NSSet<NSString *> *_Nullable _propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN;
69
68
  UIView *_containerView;
70
69
  BOOL _useCustomContainerView;
@@ -278,7 +277,7 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
278
277
  [[NSNotificationCenter defaultCenter] postNavigationPressEventWithType:RCTTVRemoteEventLongSelect keyAction:recognizer.eventKeyAction tag:@(self.tag) target:@(self.tag)];
279
278
  }
280
279
 
281
- - (void)handleSelect:(UIGestureRecognizer *)r
280
+ - (void)animatePress
282
281
  {
283
282
  if (_tvParallaxProperties.enabled == YES) {
284
283
  float magnification = _tvParallaxProperties.magnification;
@@ -302,18 +301,26 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
302
301
  self.transform = CGAffineTransformMakeScale(magnification, magnification);
303
302
  }
304
303
  completion:^(__unused BOOL finished2) {
305
- [self sendSelectNotification:r];
306
304
  }];
307
305
  }];
308
306
 
309
- } else {
310
- [self sendSelectNotification:r];
311
307
  }
312
308
  }
313
309
 
314
- - (void)handleLongSelect:(UIGestureRecognizer *)r
310
+ - (void)handlePress:(UIGestureRecognizer *)r
315
311
  {
316
- [self sendLongSelectNotification:r];
312
+ switch (r.state) {
313
+ case UIGestureRecognizerStateBegan:
314
+ _eventEmitter->onPressIn();
315
+ break;
316
+ case UIGestureRecognizerStateEnded:
317
+ case UIGestureRecognizerStateCancelled:
318
+ [self animatePress];
319
+ _eventEmitter->onPressOut();
320
+ break;
321
+ default:
322
+ break;
323
+ }
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,24 +1040,15 @@ 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
+ UILongPressGestureRecognizer *pressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handlePress:)];
1044
+ pressRecognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ];
1045
+ pressRecognizer.minimumPressDuration = 0;
1046
+
1047
+ [self addGestureRecognizer:pressRecognizer];
1048
+ _pressRecognizer = pressRecognizer;
1044
1049
  } else {
1045
- if (_selectRecognizer) {
1046
- [self removeGestureRecognizer:_selectRecognizer];
1047
- }
1048
- if (_longSelectRecognizer) {
1049
- [self removeGestureRecognizer:_longSelectRecognizer];
1050
+ if (_pressRecognizer) {
1051
+ [self removeGestureRecognizer:_pressRecognizer];
1050
1052
  }
1051
1053
  }
1052
1054
  }
@@ -1077,6 +1079,9 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
1077
1079
  _nextFocusUp = [rootView viewWithTag:newViewProps.nextFocusUp.value()];
1078
1080
  [self enableDirectionalFocusGuides];
1079
1081
  } else {
1082
+ if (self.focusGuideUp != nil) {
1083
+ [[self containingRootView] removeLayoutGuide:self.focusGuideUp];
1084
+ }
1080
1085
  _nextFocusUp = nil;
1081
1086
  }
1082
1087
  }
@@ -1087,6 +1092,9 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
1087
1092
  _nextFocusDown = [rootView viewWithTag:newViewProps.nextFocusDown.value()];
1088
1093
  [self enableDirectionalFocusGuides];
1089
1094
  } else {
1095
+ if (self.focusGuideDown != nil) {
1096
+ [[self containingRootView] removeLayoutGuide:self.focusGuideDown];
1097
+ }
1090
1098
  _nextFocusDown = nil;
1091
1099
  }
1092
1100
  }
@@ -1097,6 +1105,9 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
1097
1105
  _nextFocusLeft = [rootView viewWithTag:newViewProps.nextFocusLeft.value()];
1098
1106
  [self enableDirectionalFocusGuides];
1099
1107
  } else {
1108
+ if (self.focusGuideLeft != nil) {
1109
+ [[self containingRootView] removeLayoutGuide:self.focusGuideLeft];
1110
+ }
1100
1111
  _nextFocusLeft = nil;
1101
1112
  }
1102
1113
  }
@@ -1107,6 +1118,9 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
1107
1118
  _nextFocusRight = [rootView viewWithTag:newViewProps.nextFocusRight.value()];
1108
1119
  [self enableDirectionalFocusGuides];
1109
1120
  } else {
1121
+ if (self.focusGuideRight != nil) {
1122
+ [[self containingRootView] removeLayoutGuide:self.focusGuideRight];
1123
+ }
1110
1124
  _nextFocusRight = nil;
1111
1125
  }
1112
1126
  }
@@ -52,6 +52,19 @@
52
52
  @property (nonatomic, assign) BOOL trapFocusLeft;
53
53
  @property (nonatomic, assign) BOOL trapFocusRight;
54
54
 
55
+
56
+ /**
57
+ * Focus
58
+ */
59
+ @property (nonatomic, copy) RCTBubblingEventBlock onFocus;
60
+ @property (nonatomic, copy) RCTBubblingEventBlock onBlur;
61
+
62
+ /**
63
+ * TV Press Handlers
64
+ */
65
+ @property (nonatomic, copy) RCTDirectEventBlock onPressIn;
66
+ @property (nonatomic, copy) RCTDirectEventBlock onPressOut;
67
+
55
68
  - (instancetype)initWithBridge:(RCTBridge *)bridge;
56
69
 
57
70
  /**
@@ -22,8 +22,7 @@
22
22
 
23
23
  @implementation RCTTVView {
24
24
  __weak RCTBridge *_bridge;
25
- UITapGestureRecognizer *_selectRecognizer;
26
- UILongPressGestureRecognizer * _longSelectRecognizer;
25
+ UILongPressGestureRecognizer * _pressRecognizer;
27
26
  BOOL motionEffectsAdded;
28
27
  NSArray* focusDestinations;
29
28
  id<UIFocusItem> previouslyFocusedItem;
@@ -77,29 +76,21 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
77
76
  {
78
77
  self->_isTVSelectable = isTVSelectable;
79
78
  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];
79
+ UILongPressGestureRecognizer *pressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handlePress:)];
80
+ pressRecognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ];
81
+ pressRecognizer.minimumPressDuration = 0;
82
+
83
+ _pressRecognizer = pressRecognizer;
84
+
85
+ [self addGestureRecognizer:_pressRecognizer];
92
86
  } else {
93
- if (_selectRecognizer) {
94
- [self removeGestureRecognizer:_selectRecognizer];
95
- }
96
- if (_longSelectRecognizer) {
97
- [self removeGestureRecognizer:_longSelectRecognizer];
87
+ if (_pressRecognizer) {
88
+ [self removeGestureRecognizer:_pressRecognizer];
98
89
  }
99
90
  }
100
91
  }
101
92
 
102
- - (void)handleSelect:(UIGestureRecognizer *)r
93
+ - (void)animatePress
103
94
  {
104
95
  if ([self.tvParallaxProperties[@"enabled"] boolValue] == YES) {
105
96
  float magnification = [self.tvParallaxProperties[@"magnification"] floatValue];
@@ -123,18 +114,25 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
123
114
  self.transform = CGAffineTransformMakeScale(magnification, magnification);
124
115
  }
125
116
  completion:^(__unused BOOL finished2) {
126
- [self sendSelectNotification:r];
127
117
  }];
128
118
  }];
129
-
130
- } else {
131
- [self sendSelectNotification:r];
132
119
  }
133
120
  }
134
121
 
135
- - (void)handleLongSelect:(UIGestureRecognizer *)r
122
+ - (void)handlePress:(UIGestureRecognizer *)r
136
123
  {
137
- [self sendLongSelectNotification:r];
124
+ switch (r.state) {
125
+ case UIGestureRecognizerStateBegan:
126
+ if (self.onPressIn) self.onPressIn(nil);
127
+ break;
128
+ case UIGestureRecognizerStateEnded:
129
+ case UIGestureRecognizerStateCancelled:
130
+ [self animatePress];
131
+ if (self.onPressOut) self.onPressOut(nil);
132
+ break;
133
+ default:
134
+ break;
135
+ }
138
136
  }
139
137
 
140
138
  - (BOOL)isTVFocusGuide
@@ -323,6 +321,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
323
321
  }
324
322
 
325
323
  if (context.nextFocusedView == self && ![self isTVFocusGuide] && self.isTVSelectable ) {
324
+ if (self.onFocus) self.onFocus(nil);
326
325
  [self becomeFirstResponder];
327
326
  [self enableDirectionalFocusGuides];
328
327
  [coordinator addCoordinatedAnimations:^(void){
@@ -330,6 +329,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
330
329
  [self sendFocusNotification:context];
331
330
  } completion:^(void){}];
332
331
  } else {
332
+ if (self.onBlur) self.onBlur(nil);
333
333
  [self disableDirectionalFocusGuides];
334
334
  [coordinator addCoordinatedAnimations:^(void){
335
335
  [self sendBlurNotification:context];
@@ -458,23 +458,43 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
458
458
  }
459
459
 
460
460
  - (void)setNextFocusUp:(NSNumber *)nextFocusUp {
461
- self->_nextFocusUp = [self getViewById: nextFocusUp];
462
- [self enableDirectionalFocusGuides];
461
+ if (self.focusGuideUp != nil && nextFocusUp == nil) {
462
+ [[self rootView] removeLayoutGuide:self.focusGuideUp];
463
+ self.focusGuideUp = nil;
464
+ } else {
465
+ self->_nextFocusUp = [self getViewById: nextFocusUp];
466
+ [self enableDirectionalFocusGuides];
467
+ }
463
468
  }
464
469
 
465
470
  - (void)setNextFocusDown:(NSNumber *)nextFocusDown {
466
- self->_nextFocusDown = [self getViewById: nextFocusDown];
467
- [self enableDirectionalFocusGuides];
471
+ if (self.focusGuideDown != nil && nextFocusDown) {
472
+ [[self rootView] removeLayoutGuide:self.focusGuideDown];
473
+ self.focusGuideDown = nil;
474
+ } else {
475
+ self->_nextFocusDown = [self getViewById: nextFocusDown];
476
+ [self enableDirectionalFocusGuides];
477
+ }
468
478
  }
469
479
 
470
480
  - (void)setNextFocusLeft:(NSNumber *)nextFocusLeft {
471
- self->_nextFocusLeft = [self getViewById: nextFocusLeft];
472
- [self enableDirectionalFocusGuides];
481
+ if (self.focusGuideLeft != nil && nextFocusLeft == nil) {
482
+ [[self rootView] removeLayoutGuide:self.focusGuideLeft];
483
+ self.focusGuideLeft = nil;
484
+ } else {
485
+ self->_nextFocusLeft = [self getViewById: nextFocusLeft];
486
+ [self enableDirectionalFocusGuides];
487
+ }
473
488
  }
474
489
 
475
490
  - (void)setNextFocusRight:(NSNumber *)nextFocusRight {
476
- self->_nextFocusRight = [self getViewById: nextFocusRight];
477
- [self enableDirectionalFocusGuides];
491
+ if (self.focusGuideRight != nil && nextFocusRight) {
492
+ [[self rootView] removeLayoutGuide:self.focusGuideRight];
493
+ self.focusGuideRight = nil;
494
+ } else {
495
+ self->_nextFocusRight = [self getViewById: nextFocusRight];
496
+ [self enableDirectionalFocusGuides];
497
+ }
478
498
  }
479
499
 
480
500
  - (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
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.76.1-0
1
+ VERSION_NAME=0.76.1-1
2
2
  react.internal.publishingGroup=io.github.react-native-tvos
3
3
 
4
4
  android.useAndroidX=true
@@ -18,5 +18,5 @@ public class ReactNativeVersion {
18
18
  "major", 0,
19
19
  "minor", 76,
20
20
  "patch", 1,
21
- "prerelease", "0");
21
+ "prerelease", "1");
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
+ }
@@ -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 PressOutEvent(surfaceId: Int, viewId: Int) :
7
+ Event<PressOutEvent>(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 = "topPressOut"
15
+ }
16
+ }