react-native-tvos 0.83.4-0 → 0.83.4-2
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/Components/ScrollView/AndroidHorizontalScrollViewNativeComponent.js +1 -0
- package/Libraries/Components/ScrollView/ScrollView.d.ts +7 -0
- package/Libraries/Components/ScrollView/ScrollView.js +6 -0
- package/Libraries/Components/ScrollView/ScrollViewNativeComponent.js +2 -0
- package/Libraries/Components/ScrollView/ScrollViewNativeComponentType.js +1 -0
- package/Libraries/Core/ReactNativeVersion.js +1 -1
- package/README.md +52 -0
- package/React/Base/RCTVersion.m +1 -1
- package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.h +1 -0
- package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm +78 -0
- package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +32 -6
- package/React/Views/ScrollView/RCTScrollViewManager.m +1 -0
- package/ReactAndroid/gradle.properties +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +2 -2
- package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/views/common/UiModeUtils.kt +20 -0
- package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +69 -8
- package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.kt +5 -0
- package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +30 -2
- package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.kt +5 -0
- package/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java +28 -3
- package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.kt +1 -1
- package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +2 -2
- package/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.cpp +10 -0
- package/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.h +1 -0
- package/package.json +2 -2
- package/src/private/featureflags/ReactNativeFeatureFlags.js +2 -2
- package/types/public/ReactNativeTVTypes.d.ts +8 -0
|
@@ -745,6 +745,13 @@ export interface ScrollViewProps
|
|
|
745
745
|
*/
|
|
746
746
|
removeClippedSubviews?: boolean | undefined;
|
|
747
747
|
|
|
748
|
+
/**
|
|
749
|
+
* (TV only)
|
|
750
|
+
* When false, the scroll view will jump to the correct offset without animation
|
|
751
|
+
* when focus changes. Defaults to true.
|
|
752
|
+
*/
|
|
753
|
+
scrollAnimationEnabled?: boolean | undefined;
|
|
754
|
+
|
|
748
755
|
/**
|
|
749
756
|
* When true, shows a horizontal scroll indicator.
|
|
750
757
|
*/
|
|
@@ -662,6 +662,12 @@ type ScrollViewBaseProps = $ReadOnly<{
|
|
|
662
662
|
* true.
|
|
663
663
|
*/
|
|
664
664
|
removeClippedSubviews?: ?boolean,
|
|
665
|
+
/**
|
|
666
|
+
* (TV only)
|
|
667
|
+
* When false, the scroll view will jump to the correct offset without animation
|
|
668
|
+
* when focus changes. Defaults to true.
|
|
669
|
+
*/
|
|
670
|
+
scrollAnimationEnabled?: ?boolean,
|
|
665
671
|
/**
|
|
666
672
|
* A RefreshControl component, used to provide pull-to-refresh
|
|
667
673
|
* functionality for the ScrollView. Only works for vertical ScrollViews
|
|
@@ -87,6 +87,7 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig =
|
|
|
87
87
|
process: require('../../StyleSheet/processColor').default,
|
|
88
88
|
},
|
|
89
89
|
snapToItemPadding: true,
|
|
90
|
+
scrollAnimationEnabled: true,
|
|
90
91
|
pointerEvents: true,
|
|
91
92
|
isInvertedVirtualizedList: true,
|
|
92
93
|
},
|
|
@@ -152,6 +153,7 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig =
|
|
|
152
153
|
scrollsToTop: true,
|
|
153
154
|
showsHorizontalScrollIndicator: true,
|
|
154
155
|
showsVerticalScrollIndicator: true,
|
|
156
|
+
scrollAnimationEnabled: true,
|
|
155
157
|
showsScrollIndex: true,
|
|
156
158
|
snapToItemPadding: true,
|
|
157
159
|
snapToAlignment: true,
|
|
@@ -72,6 +72,7 @@ export type ScrollViewNativeProps = $ReadOnly<{
|
|
|
72
72
|
snapToItemPadding?: ?number,
|
|
73
73
|
sendMomentumEvents?: ?boolean,
|
|
74
74
|
showsHorizontalScrollIndicator?: ?boolean,
|
|
75
|
+
scrollAnimationEnabled?: ?boolean,
|
|
75
76
|
showsScrollIndex?: ?boolean,
|
|
76
77
|
showsVerticalScrollIndicator?: ?boolean,
|
|
77
78
|
snapToAlignment?: ?('start' | 'center' | 'end' | 'item'),
|
|
@@ -29,7 +29,7 @@ export default class ReactNativeVersion {
|
|
|
29
29
|
static major: number = 0;
|
|
30
30
|
static minor: number = 83;
|
|
31
31
|
static patch: number = 4;
|
|
32
|
-
static prerelease: string | null = '
|
|
32
|
+
static prerelease: string | null = '2';
|
|
33
33
|
|
|
34
34
|
static getVersionString(): string {
|
|
35
35
|
return `${this.major}.${this.minor}.${this.patch}${this.prerelease != null ? `-${this.prerelease}` : ''}`;
|
package/README.md
CHANGED
|
@@ -262,6 +262,58 @@ class Game2048 extends React.Component {
|
|
|
262
262
|
|
|
263
263
|
- _TVTextScrollView_: On Apple TV, a ScrollView will not scroll unless there are focusable items inside it or above/below it. This component works on both Apple TV and Android TV, using native code to allow scrolling using swipe gestures from the remote control.
|
|
264
264
|
|
|
265
|
+
- _ScrollView snap alignment_: The existing `snapToAlignment` prop has been extended with a new `'item'` value that enables per-item snap alignment on TV. Instead of snapping all items uniformly to `'start'`, `'center'`, or `'end'`, each child can specify its own alignment via the `scrollSnapAlign` View prop. Optional padding can be applied with `snapToItemPadding` on the ScrollView.
|
|
266
|
+
|
|
267
|
+
| Prop (ScrollView) | Value | Description |
|
|
268
|
+
|---|---|---|
|
|
269
|
+
| snapToAlignment | `'item'` | Enables per-item snap alignment (TV only). Requires `snapToInterval` to be set. |
|
|
270
|
+
| snapToItemPadding | number? | Extra padding (in dp/pt) applied around items when snapping. Only used with `snapToAlignment="item"`. |
|
|
271
|
+
|
|
272
|
+
| Prop (View) | Value | Description |
|
|
273
|
+
|---|---|---|
|
|
274
|
+
| scrollSnapAlign | `'start'` \| `'center'` \| `'end'` | Controls where this item snaps inside its parent ScrollView. Only used when the parent has `snapToAlignment="item"`. |
|
|
275
|
+
|
|
276
|
+
```jsx
|
|
277
|
+
<ScrollView
|
|
278
|
+
horizontal
|
|
279
|
+
snapToInterval={300}
|
|
280
|
+
snapToAlignment="item"
|
|
281
|
+
snapToItemPadding={20}>
|
|
282
|
+
<Pressable scrollSnapAlign="start" style={{width: 300}}>
|
|
283
|
+
<Text>Snaps to start</Text>
|
|
284
|
+
</Pressable>
|
|
285
|
+
<Pressable scrollSnapAlign="center" style={{width: 200}}>
|
|
286
|
+
<Text>Snaps to center</Text>
|
|
287
|
+
</Pressable>
|
|
288
|
+
<Pressable scrollSnapAlign="end" style={{width: 300}}>
|
|
289
|
+
<Text>Snaps to end</Text>
|
|
290
|
+
</Pressable>
|
|
291
|
+
</ScrollView>
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
- _ScrollView animation control_: The `scrollAnimationEnabled` prop allows disabling scroll animations when focus changes on TV. When set to `false`, the scroll view jumps instantly to the focused item instead of animating. This only affects TV platforms and has no effect on mobile.
|
|
295
|
+
|
|
296
|
+
| Prop (ScrollView) | Value | Description |
|
|
297
|
+
|---|---|---|
|
|
298
|
+
| scrollAnimationEnabled | boolean? | When `false`, disables animated scrolling on focus change. Defaults to `true`. TV only. |
|
|
299
|
+
|
|
300
|
+
```jsx
|
|
301
|
+
<ScrollView horizontal scrollAnimationEnabled={false}>
|
|
302
|
+
<Pressable style={{width: 300}}><Text>Item 1</Text></Pressable>
|
|
303
|
+
<Pressable style={{width: 300}}><Text>Item 2</Text></Pressable>
|
|
304
|
+
<Pressable style={{width: 300}}><Text>Item 3</Text></Pressable>
|
|
305
|
+
</ScrollView>
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
- _Interaction with existing ScrollView props_: The TV scroll props build on top of existing React Native ScrollView behavior. Here is how they interact:
|
|
309
|
+
|
|
310
|
+
- `snapToAlignment="item"` does not require `snapToInterval` to be set. It works as a standalone snapping mode where each child's `scrollSnapAlign` determines the snap position. If `snapToInterval` is also set, the interval is applied as an additional constraint after the item offset is computed.
|
|
311
|
+
- `snapToAlignment="item"` should not be combined with `pagingEnabled`. Both attempt to control scroll positioning independently, which leads to unpredictable behavior. Use one or the other.
|
|
312
|
+
- `snapToStart` and `snapToEnd` work independently of `snapToAlignment="item"`. They control edge behavior during swipe/drag momentum (whether the scroll view snaps to the first or last position), but do not affect focus driven item snapping.
|
|
313
|
+
- `scrollAnimationEnabled={false}` disables all scroll animations, including programmatic `scrollTo({animated: true})` calls. When disabled, all scrolling is instant.
|
|
314
|
+
- `decelerationRate` has no effect when `scrollAnimationEnabled={false}`, since there is no animation to decelerate.
|
|
315
|
+
- `scrollAnimationEnabled` and `snapToAlignment="item"` work well together. Snapping still occurs, but instantly instead of animated.
|
|
316
|
+
|
|
265
317
|
- _VirtualizedList_: We extend `VirtualizedList` to make virtualization work well with focus management in mind. All of the improvements that we made are automatically available to all the VirtualizedList based components such as `FlatList`.
|
|
266
318
|
- Defaults: VirtualizeList contents are automatically wrapped with a `TVFocusGuideView` with `trapFocus*` properties enabled depending on the orientation of the list. This default makes sure that focus doesn't leave the list accidentally due to a virtualization issue etc. until reaching the beginning or the end of the list.
|
|
267
319
|
- New Props:
|
package/React/Base/RCTVersion.m
CHANGED
|
@@ -52,6 +52,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
52
52
|
@property (nonatomic, assign) BOOL snapToEnd;
|
|
53
53
|
@property (nonatomic, copy) NSArray<NSNumber *> *snapToOffsets;
|
|
54
54
|
@property (nonatomic, assign) BOOL scrollSnapEnabled;
|
|
55
|
+
@property (nonatomic, assign) BOOL scrollAnimationEnabled;
|
|
55
56
|
|
|
56
57
|
/*
|
|
57
58
|
* Makes `setContentOffset:` method no-op when given `block` is executed.
|
|
@@ -9,6 +9,28 @@
|
|
|
9
9
|
#import <React/RCTUtils.h>
|
|
10
10
|
#import <react/utils/FloatComparison.h>
|
|
11
11
|
|
|
12
|
+
#if TARGET_OS_TV
|
|
13
|
+
// Layer subclass that can block all animations when scrollAnimationEnabled is NO.
|
|
14
|
+
// This intercepts animations at the point they're added to the layer, which catches
|
|
15
|
+
// all animation sources: UIView animations, focus coordinator animations, and
|
|
16
|
+
// Core Animation implicit/explicit animations.
|
|
17
|
+
@interface RCTScrollViewLayer : CALayer
|
|
18
|
+
@property (nonatomic, assign) BOOL blockAnimations;
|
|
19
|
+
@end
|
|
20
|
+
|
|
21
|
+
@implementation RCTScrollViewLayer
|
|
22
|
+
|
|
23
|
+
- (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key
|
|
24
|
+
{
|
|
25
|
+
if (_blockAnimations) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
[super addAnimation:anim forKey:key];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@end
|
|
32
|
+
#endif
|
|
33
|
+
|
|
12
34
|
@interface RCTEnhancedScrollView () <UIScrollViewDelegate>
|
|
13
35
|
@end
|
|
14
36
|
|
|
@@ -17,6 +39,13 @@
|
|
|
17
39
|
BOOL _isSetContentOffsetDisabled;
|
|
18
40
|
}
|
|
19
41
|
|
|
42
|
+
#if TARGET_OS_TV
|
|
43
|
+
+ (Class)layerClass
|
|
44
|
+
{
|
|
45
|
+
return [RCTScrollViewLayer class];
|
|
46
|
+
}
|
|
47
|
+
#endif
|
|
48
|
+
|
|
20
49
|
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
|
|
21
50
|
{
|
|
22
51
|
if ([key isEqualToString:@"delegate"]) {
|
|
@@ -28,9 +57,18 @@
|
|
|
28
57
|
return [super automaticallyNotifiesObserversForKey:key];
|
|
29
58
|
}
|
|
30
59
|
|
|
60
|
+
- (void)setScrollAnimationEnabled:(BOOL)scrollAnimationEnabled
|
|
61
|
+
{
|
|
62
|
+
_scrollAnimationEnabled = scrollAnimationEnabled;
|
|
63
|
+
#if TARGET_OS_TV
|
|
64
|
+
((RCTScrollViewLayer *)self.layer).blockAnimations = !scrollAnimationEnabled;
|
|
65
|
+
#endif
|
|
66
|
+
}
|
|
67
|
+
|
|
31
68
|
- (instancetype)initWithFrame:(CGRect)frame
|
|
32
69
|
{
|
|
33
70
|
if (self = [super initWithFrame:frame]) {
|
|
71
|
+
_scrollAnimationEnabled = YES;
|
|
34
72
|
// We set the default behavior to "never" so that iOS
|
|
35
73
|
// doesn't do weird things to UIScrollView insets automatically
|
|
36
74
|
// and keeps it as an opt-in behavior.
|
|
@@ -102,6 +140,46 @@
|
|
|
102
140
|
RCTSanitizeNaNValue(contentOffset.y, @"scrollView.contentOffset.y"));
|
|
103
141
|
}
|
|
104
142
|
|
|
143
|
+
- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated
|
|
144
|
+
{
|
|
145
|
+
if (_isSetContentOffsetDisabled) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
#if TARGET_OS_TV
|
|
149
|
+
animated = animated && _scrollAnimationEnabled;
|
|
150
|
+
#endif
|
|
151
|
+
[super setContentOffset:CGPointMake(
|
|
152
|
+
RCTSanitizeNaNValue(contentOffset.x, @"scrollView.contentOffset.x"),
|
|
153
|
+
RCTSanitizeNaNValue(contentOffset.y, @"scrollView.contentOffset.y"))
|
|
154
|
+
animated:animated];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated
|
|
158
|
+
{
|
|
159
|
+
#if TARGET_OS_TV
|
|
160
|
+
animated = animated && _scrollAnimationEnabled;
|
|
161
|
+
#endif
|
|
162
|
+
[super scrollRectToVisible:rect animated:animated];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
#if TARGET_OS_TV
|
|
166
|
+
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context
|
|
167
|
+
withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
|
|
168
|
+
{
|
|
169
|
+
if (_scrollAnimationEnabled) {
|
|
170
|
+
[super didUpdateFocusInContext:context withAnimationCoordinator:coordinator];
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// When scrollAnimationEnabled is NO, don't call super and don't scroll here.
|
|
175
|
+
// RCTScrollViewComponentView.didUpdateFocusInContext handles all scrolling
|
|
176
|
+
// (both snap and non-snap cases) when animation is disabled. Scrolling here
|
|
177
|
+
// as well would cause conflicts — e.g. at the end of a snapToInterval list,
|
|
178
|
+
// this method computes a "just make visible" position while the component view
|
|
179
|
+
// computes the correct snapped position, causing a bounce effect.
|
|
180
|
+
}
|
|
181
|
+
#endif
|
|
182
|
+
|
|
105
183
|
- (void)setFrame:(CGRect)frame
|
|
106
184
|
{
|
|
107
185
|
[super setFrame:frame];
|
|
@@ -434,6 +434,9 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
|
|
|
434
434
|
|
|
435
435
|
scrollView.snapToStart = newScrollViewProps.snapToStart;
|
|
436
436
|
scrollView.snapToEnd = newScrollViewProps.snapToEnd;
|
|
437
|
+
#if TARGET_OS_TV
|
|
438
|
+
scrollView.scrollAnimationEnabled = newScrollViewProps.scrollAnimationEnabled;
|
|
439
|
+
#endif
|
|
437
440
|
|
|
438
441
|
if (oldScrollViewProps.snapToOffsets != newScrollViewProps.snapToOffsets) {
|
|
439
442
|
NSMutableArray<NSNumber *> *snapToOffsets = [NSMutableArray array];
|
|
@@ -1235,7 +1238,7 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
|
|
|
1235
1238
|
? CGPointMake(targetOffset, scrollView.contentOffset.y)
|
|
1236
1239
|
: CGPointMake(scrollView.contentOffset.x, targetOffset);
|
|
1237
1240
|
self.preferredContentOffset = targetContentOffset;
|
|
1238
|
-
[_scrollView setContentOffset:targetContentOffset animated:
|
|
1241
|
+
[_scrollView setContentOffset:targetContentOffset animated:scrollProps.scrollAnimationEnabled];
|
|
1239
1242
|
}
|
|
1240
1243
|
|
|
1241
1244
|
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context
|
|
@@ -1244,7 +1247,18 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
|
|
|
1244
1247
|
self.preferredContentOffset = NO_PREFERRED_CONTENT_OFFSET;
|
|
1245
1248
|
const auto &scrollProps = static_cast<const ScrollViewProps &>(*_props);
|
|
1246
1249
|
BOOL hasItemSnapAlignment = scrollProps.snapToAlignment == ScrollViewSnapToAlignment::Item;
|
|
1247
|
-
if (context.previouslyFocusedView == context.nextFocusedView
|
|
1250
|
+
if (context.previouslyFocusedView == context.nextFocusedView) {
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
if (!_props->isTVSelectable && !hasItemSnapAlignment) {
|
|
1254
|
+
if (!scrollProps.scrollAnimationEnabled && [context.nextFocusedView isDescendantOfView:_scrollView]) {
|
|
1255
|
+
// When animations are disabled and there's no snap alignment, manually scroll
|
|
1256
|
+
// the focused view into view instantly. We can't let UIScrollView's default
|
|
1257
|
+
// focus handling do this because it uses deceleration (timer-based) that can't
|
|
1258
|
+
// be blocked by the animation-blocking layer.
|
|
1259
|
+
CGRect targetRect = [context.nextFocusedView convertRect:context.nextFocusedView.bounds toView:_scrollView];
|
|
1260
|
+
[_scrollView scrollRectToVisible:targetRect animated:NO];
|
|
1261
|
+
}
|
|
1248
1262
|
return;
|
|
1249
1263
|
}
|
|
1250
1264
|
if (_props->isTVSelectable && context.nextFocusedView == self) {
|
|
@@ -1410,10 +1424,16 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
|
|
|
1410
1424
|
limitedOffset = MAX(limitedOffset, 0.0);
|
|
1411
1425
|
limitedOffset = MIN(limitedOffset, maxOffset);
|
|
1412
1426
|
|
|
1413
|
-
|
|
1427
|
+
const auto &scrollProps = static_cast<const ScrollViewProps &>(*self->_props);
|
|
1428
|
+
if (scrollProps.scrollAnimationEnabled) {
|
|
1429
|
+
[UIView animateWithDuration:[self swipeDuration] animations:^{
|
|
1430
|
+
self.scrollView.contentOffset =
|
|
1431
|
+
CGPointMake(self.scrollView.contentOffset.x, limitedOffset);
|
|
1432
|
+
}];
|
|
1433
|
+
} else {
|
|
1414
1434
|
self.scrollView.contentOffset =
|
|
1415
1435
|
CGPointMake(self.scrollView.contentOffset.x, limitedOffset);
|
|
1416
|
-
}
|
|
1436
|
+
}
|
|
1417
1437
|
});
|
|
1418
1438
|
}
|
|
1419
1439
|
|
|
@@ -1434,10 +1454,16 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
|
|
|
1434
1454
|
limitedOffset = MAX(limitedOffset, 0.0);
|
|
1435
1455
|
limitedOffset = MIN(limitedOffset, maxOffset);
|
|
1436
1456
|
|
|
1437
|
-
|
|
1457
|
+
const auto &scrollProps = static_cast<const ScrollViewProps &>(*self->_props);
|
|
1458
|
+
if (scrollProps.scrollAnimationEnabled) {
|
|
1459
|
+
[UIView animateWithDuration:[self swipeDuration] animations:^{
|
|
1460
|
+
self.scrollView.contentOffset =
|
|
1461
|
+
CGPointMake(limitedOffset, self.scrollView.contentOffset.y);
|
|
1462
|
+
}];
|
|
1463
|
+
} else {
|
|
1438
1464
|
self.scrollView.contentOffset =
|
|
1439
1465
|
CGPointMake(limitedOffset, self.scrollView.contentOffset.y);
|
|
1440
|
-
}
|
|
1466
|
+
}
|
|
1441
1467
|
});
|
|
1442
1468
|
}
|
|
1443
1469
|
|
|
@@ -106,6 +106,7 @@ RCT_EXPORT_VIEW_PROPERTY(onMomentumScrollEnd, RCTDirectEventBlock)
|
|
|
106
106
|
RCT_EXPORT_VIEW_PROPERTY(inverted, BOOL)
|
|
107
107
|
#if TARGET_OS_TV
|
|
108
108
|
RCT_EXPORT_VIEW_PROPERTY(showsScrollIndex, BOOL)
|
|
109
|
+
RCT_EXPORT_VIEW_PROPERTY(scrollAnimationEnabled, BOOL)
|
|
109
110
|
#endif
|
|
110
111
|
RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustsScrollIndicatorInsets, BOOL)
|
|
111
112
|
RCT_EXPORT_VIEW_PROPERTY(contentInsetAdjustmentBehavior, UIScrollViewContentInsetAdjustmentBehavior)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*
|
|
7
|
-
* @generated SignedSource<<
|
|
7
|
+
* @generated SignedSource<<5a6e678b586f0ad291c6a4d090dce5b5>>
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -53,7 +53,7 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
|
|
|
53
53
|
|
|
54
54
|
override fun enableCppPropsIteratorSetter(): Boolean = false
|
|
55
55
|
|
|
56
|
-
override fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean =
|
|
56
|
+
override fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean = false
|
|
57
57
|
|
|
58
58
|
override fun enableDestroyShadowTreeRevisionAsync(): Boolean = false
|
|
59
59
|
|
|
@@ -7,12 +7,32 @@
|
|
|
7
7
|
|
|
8
8
|
package com.facebook.react.views.common
|
|
9
9
|
|
|
10
|
+
import android.app.UiModeManager
|
|
10
11
|
import android.content.Context
|
|
11
12
|
import android.content.res.Configuration
|
|
12
13
|
|
|
13
14
|
/** Utility object providing static methods for working with UI mode properties from Context. */
|
|
14
15
|
internal object UiModeUtils {
|
|
15
16
|
|
|
17
|
+
private var isTVDeviceCached: Boolean? = null
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Determines whether the device is a TV (Android TV / Fire TV).
|
|
21
|
+
* The result is cached after the first call.
|
|
22
|
+
*
|
|
23
|
+
* @param context The context to check the UI mode from
|
|
24
|
+
* @return true if the device is running in television mode, false otherwise
|
|
25
|
+
*/
|
|
26
|
+
@JvmStatic
|
|
27
|
+
fun isTVDevice(context: Context): Boolean {
|
|
28
|
+
return isTVDeviceCached ?: run {
|
|
29
|
+
val uiModeManager = context.getSystemService(Context.UI_MODE_SERVICE) as? UiModeManager
|
|
30
|
+
val result = uiModeManager?.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION
|
|
31
|
+
isTVDeviceCached = result
|
|
32
|
+
result
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
16
36
|
/**
|
|
17
37
|
* Determines whether the current UI mode is dark mode
|
|
18
38
|
*
|
package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java
CHANGED
|
@@ -29,6 +29,7 @@ import android.view.KeyEvent;
|
|
|
29
29
|
import android.view.MotionEvent;
|
|
30
30
|
import android.view.View;
|
|
31
31
|
import android.view.ViewGroup;
|
|
32
|
+
import android.view.ViewParent;
|
|
32
33
|
import android.view.accessibility.AccessibilityNodeInfo;
|
|
33
34
|
import android.widget.HorizontalScrollView;
|
|
34
35
|
import android.widget.OverScroller;
|
|
@@ -38,6 +39,7 @@ import androidx.core.view.ViewCompat.FocusDirection;
|
|
|
38
39
|
import com.facebook.common.logging.FLog;
|
|
39
40
|
import com.facebook.infer.annotation.Assertions;
|
|
40
41
|
import com.facebook.infer.annotation.Nullsafe;
|
|
42
|
+
import com.facebook.react.views.common.UiModeUtils;
|
|
41
43
|
import com.facebook.react.R;
|
|
42
44
|
import com.facebook.react.common.ReactConstants;
|
|
43
45
|
import com.facebook.react.common.build.ReactBuildConfig;
|
|
@@ -136,6 +138,8 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
|
|
|
136
138
|
private int mFadingEdgeLengthStart = 0;
|
|
137
139
|
private int mFadingEdgeLengthEnd = 0;
|
|
138
140
|
private int mSnapToItemPadding;
|
|
141
|
+
private boolean mScrollAnimationEnabled = true;
|
|
142
|
+
private boolean mBlockScrollDelta = false;
|
|
139
143
|
|
|
140
144
|
public ReactHorizontalScrollView(Context context) {
|
|
141
145
|
this(context, null);
|
|
@@ -247,9 +251,21 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
|
|
|
247
251
|
return mScrollEnabled && super.canScrollHorizontally(direction);
|
|
248
252
|
}
|
|
249
253
|
|
|
254
|
+
@Override
|
|
255
|
+
public void computeScroll() {
|
|
256
|
+
if (UiModeUtils.isTVDevice(getContext())
|
|
257
|
+
&& !mScrollAnimationEnabled
|
|
258
|
+
&& mScroller != null
|
|
259
|
+
&& !mScroller.isFinished()) {
|
|
260
|
+
mScroller.forceFinished(true);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
super.computeScroll();
|
|
264
|
+
}
|
|
265
|
+
|
|
250
266
|
@Override
|
|
251
267
|
protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
|
|
252
|
-
if (!mScrollEnabled) {
|
|
268
|
+
if (!mScrollEnabled || mBlockScrollDelta) {
|
|
253
269
|
return 0;
|
|
254
270
|
}
|
|
255
271
|
return super.computeScrollDeltaToGetChildRectOnScreen(rect);
|
|
@@ -388,6 +404,10 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
|
|
|
388
404
|
mSnapToItemPadding = snapToItemPadding;
|
|
389
405
|
}
|
|
390
406
|
|
|
407
|
+
public void setScrollAnimationEnabled(boolean scrollAnimationEnabled) {
|
|
408
|
+
mScrollAnimationEnabled = scrollAnimationEnabled;
|
|
409
|
+
}
|
|
410
|
+
|
|
391
411
|
@Override
|
|
392
412
|
protected float getLeftFadingEdgeStrength() {
|
|
393
413
|
float max = Math.max(mFadingEdgeLengthStart, mFadingEdgeLengthEnd);
|
|
@@ -603,7 +623,11 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
|
|
|
603
623
|
* unblocks such customization.
|
|
604
624
|
*/
|
|
605
625
|
protected void requestChildFocusWithoutScroll(View child, View focused) {
|
|
626
|
+
// Temporarily block HorizontalScrollView's internal scrollToChild from running
|
|
627
|
+
// during super.requestChildFocus — we've already handled scrolling in requestChildFocus.
|
|
628
|
+
mBlockScrollDelta = true;
|
|
606
629
|
super.requestChildFocus(child, focused);
|
|
630
|
+
mBlockScrollDelta = false;
|
|
607
631
|
}
|
|
608
632
|
|
|
609
633
|
@Override
|
|
@@ -654,6 +678,18 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
|
|
|
654
678
|
return scrollDelta != 0 && Math.abs(scrollDelta) < (mTempRect.width() / 2);
|
|
655
679
|
}
|
|
656
680
|
|
|
681
|
+
/** Returns whether the given view is a descendant of the given ancestor */
|
|
682
|
+
private static boolean isDescendantOf(View view, View ancestor) {
|
|
683
|
+
ViewParent parent = view.getParent();
|
|
684
|
+
while (parent != null) {
|
|
685
|
+
if (parent == ancestor) {
|
|
686
|
+
return true;
|
|
687
|
+
}
|
|
688
|
+
parent = parent.getParent();
|
|
689
|
+
}
|
|
690
|
+
return false;
|
|
691
|
+
}
|
|
692
|
+
|
|
657
693
|
private void scrollToChild(View child) {
|
|
658
694
|
int scrollDelta = getScrollDelta(child);
|
|
659
695
|
|
|
@@ -796,21 +832,42 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
|
|
|
796
832
|
public boolean arrowScroll(int direction) {
|
|
797
833
|
boolean handled = false;
|
|
798
834
|
|
|
799
|
-
if (
|
|
835
|
+
if (!mScrollAnimationEnabled) {
|
|
836
|
+
// When scroll animation is disabled, find the next focusable and request focus directly.
|
|
837
|
+
// This must come before the mPagingEnabled check because snapToInterval on Android
|
|
838
|
+
// auto-enables paging, and the paging branch uses smoothScrollToNextPage which
|
|
839
|
+
// scrolls by full page width instead of by snap interval.
|
|
840
|
+
// requestChildFocus → tryScrollSnapToChild/scrollToChild handles instant scrolling.
|
|
841
|
+
View currentFocused = findFocus();
|
|
842
|
+
View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction);
|
|
843
|
+
if (nextFocused != null && nextFocused != currentFocused && nextFocused != this) {
|
|
844
|
+
nextFocused.requestFocus(direction);
|
|
845
|
+
handled = true;
|
|
846
|
+
}
|
|
847
|
+
} else if (mPagingEnabled) {
|
|
800
848
|
mPagedArrowScrolling = true;
|
|
801
849
|
|
|
802
850
|
if (getChildCount() > 0) {
|
|
803
851
|
View currentFocused = findFocus();
|
|
804
852
|
View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction);
|
|
805
853
|
View rootChild = getContentView();
|
|
806
|
-
if (rootChild != null && nextFocused != null && nextFocused
|
|
807
|
-
if (
|
|
808
|
-
smoothScrollToNextPage(
|
|
854
|
+
if (rootChild != null && nextFocused != null && isDescendantOf(nextFocused, rootChild)) {
|
|
855
|
+
if (mSnapToAlignment == SNAP_ALIGNMENT_ITEM) {
|
|
856
|
+
// When snapToAlignment is "item", don't use smoothScrollToNextPage (which scrolls
|
|
857
|
+
// by full page width and ignores snapToItemPadding). Instead just request focus —
|
|
858
|
+
// requestChildFocus → tryScrollSnapToChild handles scrolling with correct padding.
|
|
859
|
+
nextFocused.requestFocus();
|
|
860
|
+
} else {
|
|
861
|
+
if (!isScrolledInView(nextFocused) && !isMostlyScrolledInView(nextFocused)) {
|
|
862
|
+
smoothScrollToNextPage(direction);
|
|
863
|
+
}
|
|
864
|
+
nextFocused.requestFocus();
|
|
809
865
|
}
|
|
810
|
-
nextFocused.requestFocus();
|
|
811
866
|
handled = true;
|
|
812
867
|
} else {
|
|
813
|
-
|
|
868
|
+
if (mSnapToAlignment != SNAP_ALIGNMENT_ITEM) {
|
|
869
|
+
smoothScrollToNextPage(direction);
|
|
870
|
+
}
|
|
814
871
|
handled = true;
|
|
815
872
|
}
|
|
816
873
|
}
|
|
@@ -1595,7 +1652,11 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
|
|
|
1595
1652
|
* scroll view and state. Calling raw `smoothScrollTo` doesn't update state.
|
|
1596
1653
|
*/
|
|
1597
1654
|
public void reactSmoothScrollTo(int x, int y) {
|
|
1598
|
-
|
|
1655
|
+
if (mScrollAnimationEnabled || !UiModeUtils.isTVDevice(getContext())) {
|
|
1656
|
+
ReactScrollViewHelper.smoothScrollTo(this, x, y);
|
|
1657
|
+
} else {
|
|
1658
|
+
scrollTo(x, y);
|
|
1659
|
+
}
|
|
1599
1660
|
setPendingContentOffsets(x, y);
|
|
1600
1661
|
}
|
|
1601
1662
|
|
|
@@ -164,6 +164,11 @@ constructor(private val fpsListener: FpsListener? = null) :
|
|
|
164
164
|
view.setSnapToItemPadding(px)
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
@ReactProp(name = "scrollAnimationEnabled", defaultBoolean = true)
|
|
168
|
+
public fun setScrollAnimationEnabled(view: ReactHorizontalScrollView, value: Boolean) {
|
|
169
|
+
view.setScrollAnimationEnabled(value)
|
|
170
|
+
}
|
|
171
|
+
|
|
167
172
|
@ReactProp(name = ReactClippingViewGroupHelper.PROP_REMOVE_CLIPPED_SUBVIEWS)
|
|
168
173
|
public fun setRemoveClippedSubviews(
|
|
169
174
|
view: ReactHorizontalScrollView,
|
|
@@ -38,6 +38,7 @@ import androidx.core.view.ViewCompat.FocusDirection;
|
|
|
38
38
|
import com.facebook.common.logging.FLog;
|
|
39
39
|
import com.facebook.infer.annotation.Assertions;
|
|
40
40
|
import com.facebook.infer.annotation.Nullsafe;
|
|
41
|
+
import com.facebook.react.views.common.UiModeUtils;
|
|
41
42
|
import com.facebook.react.R;
|
|
42
43
|
import com.facebook.react.bridge.ReadableMap;
|
|
43
44
|
import com.facebook.react.common.ReactConstants;
|
|
@@ -134,6 +135,8 @@ public class ReactScrollView extends ScrollView
|
|
|
134
135
|
private int mFadingEdgeLengthStart;
|
|
135
136
|
private int mFadingEdgeLengthEnd;
|
|
136
137
|
private int mSnapToItemPadding;
|
|
138
|
+
private boolean mScrollAnimationEnabled = true;
|
|
139
|
+
private boolean mBlockScrollDelta = false;
|
|
137
140
|
|
|
138
141
|
public ReactScrollView(Context context) {
|
|
139
142
|
this(context, null);
|
|
@@ -358,6 +361,10 @@ public class ReactScrollView extends ScrollView
|
|
|
358
361
|
mSnapToItemPadding = snapToItemPadding;
|
|
359
362
|
}
|
|
360
363
|
|
|
364
|
+
public void setScrollAnimationEnabled(boolean scrollAnimationEnabled) {
|
|
365
|
+
mScrollAnimationEnabled = scrollAnimationEnabled;
|
|
366
|
+
}
|
|
367
|
+
|
|
361
368
|
@Override
|
|
362
369
|
protected float getTopFadingEdgeStrength() {
|
|
363
370
|
float max = Math.max(mFadingEdgeLengthStart, mFadingEdgeLengthEnd);
|
|
@@ -562,7 +569,9 @@ public class ReactScrollView extends ScrollView
|
|
|
562
569
|
* unblocks such customization.
|
|
563
570
|
*/
|
|
564
571
|
protected void requestChildFocusWithoutScroll(View child, View focused) {
|
|
572
|
+
mBlockScrollDelta = true;
|
|
565
573
|
super.requestChildFocus(child, focused);
|
|
574
|
+
mBlockScrollDelta = false;
|
|
566
575
|
}
|
|
567
576
|
|
|
568
577
|
private int getScrollDelta(View descendent) {
|
|
@@ -606,9 +615,24 @@ public class ReactScrollView extends ScrollView
|
|
|
606
615
|
}
|
|
607
616
|
}
|
|
608
617
|
|
|
618
|
+
@Override
|
|
619
|
+
public void computeScroll() {
|
|
620
|
+
if (UiModeUtils.isTVDevice(getContext())
|
|
621
|
+
&& !mScrollAnimationEnabled
|
|
622
|
+
&& mScroller != null
|
|
623
|
+
&& !mScroller.isFinished()) {
|
|
624
|
+
// When scroll animation is disabled, just abort any in-flight smooth scroll.
|
|
625
|
+
// The correct position has already been set synchronously by
|
|
626
|
+
// requestChildFocus → tryScrollSnapToChild/scrollToChild.
|
|
627
|
+
mScroller.forceFinished(true);
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
super.computeScroll();
|
|
631
|
+
}
|
|
632
|
+
|
|
609
633
|
@Override
|
|
610
634
|
protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
|
|
611
|
-
if (!mScrollEnabled) {
|
|
635
|
+
if (!mScrollEnabled || mBlockScrollDelta) {
|
|
612
636
|
return 0;
|
|
613
637
|
}
|
|
614
638
|
return super.computeScrollDeltaToGetChildRectOnScreen(rect);
|
|
@@ -1362,7 +1386,11 @@ public class ReactScrollView extends ScrollView
|
|
|
1362
1386
|
* scroll view and state. Calling raw `smoothScrollTo` doesn't update state.
|
|
1363
1387
|
*/
|
|
1364
1388
|
public void reactSmoothScrollTo(int x, int y) {
|
|
1365
|
-
|
|
1389
|
+
if (mScrollAnimationEnabled || !UiModeUtils.isTVDevice(getContext())) {
|
|
1390
|
+
ReactScrollViewHelper.smoothScrollTo(this, x, y);
|
|
1391
|
+
} else {
|
|
1392
|
+
scrollTo(x, y);
|
|
1393
|
+
}
|
|
1366
1394
|
setPendingContentOffsets(x, y);
|
|
1367
1395
|
}
|
|
1368
1396
|
|
package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.kt
CHANGED
|
@@ -148,6 +148,11 @@ constructor(private val fpsListener: FpsListener? = null) :
|
|
|
148
148
|
view.setSnapToItemPadding(px)
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
@ReactProp(name = "scrollAnimationEnabled", defaultBoolean = true)
|
|
152
|
+
public fun setScrollAnimationEnabled(view: ReactScrollView, value: Boolean) {
|
|
153
|
+
view.setScrollAnimationEnabled(value)
|
|
154
|
+
}
|
|
155
|
+
|
|
151
156
|
@ReactProp(name = ReactClippingViewGroupHelper.PROP_REMOVE_CLIPPED_SUBVIEWS)
|
|
152
157
|
public fun setRemoveClippedSubviews(view: ReactScrollView, removeClippedSubviews: Boolean) {
|
|
153
158
|
view.removeClippedSubviews = removeClippedSubviews
|
|
@@ -30,6 +30,7 @@ import androidx.appcompat.widget.TintContextWrapper;
|
|
|
30
30
|
import androidx.core.view.AccessibilityDelegateCompat;
|
|
31
31
|
import androidx.core.view.ViewCompat;
|
|
32
32
|
import androidx.customview.widget.ExploreByTouchHelper;
|
|
33
|
+
import com.facebook.react.views.common.UiModeUtils;
|
|
33
34
|
import com.facebook.common.logging.FLog;
|
|
34
35
|
import com.facebook.infer.annotation.Assertions;
|
|
35
36
|
import com.facebook.infer.annotation.Nullsafe;
|
|
@@ -151,14 +152,38 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
|
|
|
151
152
|
// mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED |
|
|
152
153
|
// LAYOUT_DIRECTION_INHERIT;
|
|
153
154
|
setEnabled(true);
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
// Changed from FOCUSABLE_AUTO to NOT_FOCUSABLE to prevent Android TV's
|
|
156
|
+
// focus engine from landing on text views when no other views are available
|
|
157
|
+
setFocusable(View.NOT_FOCUSABLE);
|
|
157
158
|
|
|
158
159
|
setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE);
|
|
159
160
|
updateView(); // call after changing ellipsizeLocation in particular
|
|
160
161
|
}
|
|
161
162
|
|
|
163
|
+
// On Android TV, prevent text views from becoming focusable.
|
|
164
|
+
// ReactTextAnchorViewManager.setAccessible() ties isFocusable to the
|
|
165
|
+
// "accessible" prop, which causes Android TV's D-pad focus engine to land
|
|
166
|
+
// on text views inside recycled list items (FlashList), triggering
|
|
167
|
+
// requestChildFocus -> scrollToChild scroll jumps.
|
|
168
|
+
// On non-TV devices, focusability is left unchanged for accessibility.
|
|
169
|
+
@Override
|
|
170
|
+
public void setFocusable(boolean focusable) {
|
|
171
|
+
if (UiModeUtils.isTVDevice(getContext())) {
|
|
172
|
+
super.setFocusable(false);
|
|
173
|
+
} else {
|
|
174
|
+
super.setFocusable(focusable);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@Override
|
|
179
|
+
public void setFocusable(int focusable) {
|
|
180
|
+
if (UiModeUtils.isTVDevice(getContext())) {
|
|
181
|
+
super.setFocusable(View.NOT_FOCUSABLE);
|
|
182
|
+
} else {
|
|
183
|
+
super.setFocusable(focusable);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
162
187
|
private static WritableMap inlineViewJson(
|
|
163
188
|
int visibility, int index, int left, int top, int right, int bottom) {
|
|
164
189
|
WritableMap json = Arguments.createMap();
|
|
@@ -631,7 +631,7 @@ public open class ReactViewGroup public constructor(context: Context?) :
|
|
|
631
631
|
index++
|
|
632
632
|
}
|
|
633
633
|
|
|
634
|
-
return firstFocusableElement
|
|
634
|
+
return firstFocusableElement
|
|
635
635
|
}
|
|
636
636
|
|
|
637
637
|
private fun moveFocusToFirstFocusable(viewGroup: ReactViewGroup): Boolean {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*
|
|
7
|
-
* @generated SignedSource<<
|
|
7
|
+
* @generated SignedSource<<044be89d81fef6507ed9fe1638a49a78>>
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -88,7 +88,7 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
bool enableCustomFocusSearchOnClippedElementsAndroid() override {
|
|
91
|
-
return
|
|
91
|
+
return false;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
bool enableDestroyShadowTreeRevisionAsync() override {
|
|
@@ -41,6 +41,15 @@ BaseScrollViewProps::BaseScrollViewProps(
|
|
|
41
41
|
"snapToItemPadding",
|
|
42
42
|
sourceProps.snapToItemPadding,
|
|
43
43
|
(Float)0)),
|
|
44
|
+
scrollAnimationEnabled(
|
|
45
|
+
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
|
|
46
|
+
? sourceProps.scrollAnimationEnabled
|
|
47
|
+
: convertRawProp(
|
|
48
|
+
context,
|
|
49
|
+
rawProps,
|
|
50
|
+
"scrollAnimationEnabled",
|
|
51
|
+
sourceProps.scrollAnimationEnabled,
|
|
52
|
+
true)),
|
|
44
53
|
#endif
|
|
45
54
|
alwaysBounceHorizontal(
|
|
46
55
|
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
|
|
@@ -410,6 +419,7 @@ void BaseScrollViewProps::setProp(
|
|
|
410
419
|
#if TARGET_OS_TV
|
|
411
420
|
RAW_SET_PROP_SWITCH_CASE_BASIC(showsScrollIndex);
|
|
412
421
|
RAW_SET_PROP_SWITCH_CASE_BASIC(snapToItemPadding);
|
|
422
|
+
RAW_SET_PROP_SWITCH_CASE_BASIC(scrollAnimationEnabled);
|
|
413
423
|
#endif
|
|
414
424
|
RAW_SET_PROP_SWITCH_CASE_BASIC(alwaysBounceHorizontal);
|
|
415
425
|
RAW_SET_PROP_SWITCH_CASE_BASIC(alwaysBounceVertical);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-tvos",
|
|
3
|
-
"version": "0.83.4-
|
|
3
|
+
"version": "0.83.4-2",
|
|
4
4
|
"description": "A framework for building native apps using React",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -198,7 +198,7 @@
|
|
|
198
198
|
"whatwg-fetch": "^3.0.0",
|
|
199
199
|
"ws": "^7.5.10",
|
|
200
200
|
"yargs": "^17.6.2",
|
|
201
|
-
"@react-native-tvos/virtualized-lists": "0.83.4-
|
|
201
|
+
"@react-native-tvos/virtualized-lists": "0.83.4-2"
|
|
202
202
|
},
|
|
203
203
|
"codegenConfig": {
|
|
204
204
|
"libraries": [
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*
|
|
7
|
-
* @generated SignedSource<<
|
|
7
|
+
* @generated SignedSource<<c7dd7afe6bc76c1abb2b0499d3740ec2>>
|
|
8
8
|
* @flow strict
|
|
9
9
|
* @noformat
|
|
10
10
|
*/
|
|
@@ -279,7 +279,7 @@ export const enableCppPropsIteratorSetter: Getter<boolean> = createNativeFlagGet
|
|
|
279
279
|
/**
|
|
280
280
|
* This enables the fabric implementation of focus search so that we can focus clipped elements
|
|
281
281
|
*/
|
|
282
|
-
export const enableCustomFocusSearchOnClippedElementsAndroid: Getter<boolean> = createNativeFlagGetter('enableCustomFocusSearchOnClippedElementsAndroid',
|
|
282
|
+
export const enableCustomFocusSearchOnClippedElementsAndroid: Getter<boolean> = createNativeFlagGetter('enableCustomFocusSearchOnClippedElementsAndroid', false);
|
|
283
283
|
/**
|
|
284
284
|
* Enables destructor calls for ShadowTreeRevision in the background to reduce UI thread work.
|
|
285
285
|
*/
|
|
@@ -11,6 +11,14 @@ declare module 'react-native' {
|
|
|
11
11
|
* Only used when `snapToAlignment` is set to `'item'`.
|
|
12
12
|
*/
|
|
13
13
|
snapToItemPadding?: number | undefined;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* When false, the scroll view will jump to the correct offset without animation
|
|
17
|
+
* when focus changes. Defaults to true.
|
|
18
|
+
*
|
|
19
|
+
* @platform tv
|
|
20
|
+
*/
|
|
21
|
+
scrollAnimationEnabled?: boolean | undefined;
|
|
14
22
|
}
|
|
15
23
|
|
|
16
24
|
interface ViewProps {
|