diff --git a/packages/react-native/Libraries/Components/ScrollView/ScrollView.js b/packages/react-native/Libraries/Components/ScrollView/ScrollView.js index 5c7974f2ac30..47691aa748ea 100644 --- a/packages/react-native/Libraries/Components/ScrollView/ScrollView.js +++ b/packages/react-native/Libraries/Components/ScrollView/ScrollView.js @@ -442,21 +442,6 @@ export type ScrollViewPropsAndroid = Readonly<{ * @platform android */ persistentScrollbar?: ?boolean, - /** - * Controls the fading effect at the edges of the scroll content. - * - * A value greater than 0 will apply the fading effect, indicating more content is available - * to scroll. - * - * You can specify a single number to apply the same fading length to both edges. - * Alternatively, use an object with `start` and `end` properties to set different - * fading lengths for the start and end of the scroll content. - * - * The default value is 0. - * - * @platform android - */ - fadingEdgeLength?: ?number | {start: number, end: number}, /** * When false, the ScrollView will not automatically scroll to a focused child when * the child requests focus. This can be useful when you want to control the scroll @@ -523,6 +508,19 @@ type ScrollViewBaseProps = Readonly<{ * @default `'normal'` */ decelerationRate?: ?DecelerationRateType, + /** + * Controls the fading effect at the edges of the scroll content. + * + * A value greater than 0 will apply the fading effect, indicating more content is available + * to scroll. + * + * You can specify a single number to apply the same fading length to both edges. + * Alternatively, use an object with `start` and `end` properties to set different + * fading lengths for the start and end of the scroll content. + * + * The default value is 0. + */ + fadingEdgeLength?: ?number | {start: number, end: number}, /** * *Experimental, iOS Only*. The API is experimental and will change in future releases. diff --git a/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponent.js b/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponent.js index eeee1a603e72..c2808ad30088 100644 --- a/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponent.js +++ b/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponent.js @@ -124,6 +124,8 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = endDraggingSensitivityMultiplier: true, directionalLockEnabled: true, disableIntervalMomentum: true, + fadingEdgeLength: true, + horizontal: true, indicatorStyle: true, inverted: true, keyboardDismissMode: true, diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index ffd10bc6f236..01772da1dbcf 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -58,6 +58,22 @@ static UIScrollViewIndicatorStyle RCTUIScrollViewIndicatorStyleFromProps(const S } } +static CGFloat RCTFadingEdgeLengthFromValue(const folly::dynamic *value) +{ + return (value != nullptr && value->isNumber()) ? fmax(value->asDouble(), 0) : 0; +} + +static void RCTGetFadingEdgeLengths(const ScrollViewProps &props, CGFloat *start, CGFloat *end) +{ + const auto &fadingEdgeLength = props.fadingEdgeLength; + if (fadingEdgeLength.isObject()) { + *start = RCTFadingEdgeLengthFromValue(fadingEdgeLength.get_ptr("start")); + *end = RCTFadingEdgeLengthFromValue(fadingEdgeLength.get_ptr("end")); + } else { + *start = *end = RCTFadingEdgeLengthFromValue(&fadingEdgeLength); + } +} + // Once Fabric implements proper NativeAnimationDriver, this should be removed. // This is just a workaround to allow animations based on onScroll event. // This is only used to animate sticky headers in ScrollViews, and only the contentOffset and tag is used. @@ -125,6 +141,8 @@ @implementation RCTScrollViewComponentView { BOOL _avoidAdjustmentForMaintainVisibleContentPosition; RCTVirtualViewContainerState *_virtualViewContainerState; + + CAGradientLayer *_fadingEdgeMaskLayer; } + (RCTScrollViewComponentView *_Nullable)findScrollViewComponentViewForView:(UIView *)view @@ -315,6 +333,7 @@ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics _containerView.transform = transform; _scrollView.transform = transform; } + [self _updateFadingEdgeMask]; } - (bool)isInverted @@ -454,10 +473,77 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & scrollView.keyboardDismissMode = RCTUIKeyboardDismissModeFromProps(newScrollViewProps); } + BOOL fadingEdgeChanged = oldScrollViewProps.fadingEdgeLength != newScrollViewProps.fadingEdgeLength || + oldScrollViewProps.horizontal != newScrollViewProps.horizontal; + [super updateProps:props oldProps:oldProps]; // Must run after `super updateProps:` so it is based on the latest calculated view. ((RCTEnhancedScrollView *)_scrollView).inverted = [self isInverted]; + + if (fadingEdgeChanged) { + [self _updateFadingEdgeMask]; + } +} + +// Android's `View.draw` geometry: each edge fades over its own length, scaled so that neither passes the middle. +- (void)_updateFadingEdgeMask +{ + const auto &props = static_cast(*_props); + CGFloat start; + CGFloat end; + RCTGetFadingEdgeLengths(props, &start, &end); + CGFloat length = fmax(start, end); + CGFloat extent = props.horizontal ? _scrollView.bounds.size.width : _scrollView.bounds.size.height; + + if (length == 0 || extent == 0) { + // A mask costs an offscreen pass, so a scroll view without a fading edge carries none. + if (_fadingEdgeMaskLayer != nil) { + _scrollView.layer.mask = nil; + _fadingEdgeMaskLayer = nil; + } + return; + } + + CGFloat fadeLength = fmin(length, extent / 2); + if (_fadingEdgeMaskLayer == nil) { + _fadingEdgeMaskLayer = [CAGradientLayer layer]; + _fadingEdgeMaskLayer.colors = @[ + (id)UIColor.clearColor.CGColor, + (id)UIColor.blackColor.CGColor, + (id)UIColor.blackColor.CGColor, + (id)UIColor.clearColor.CGColor, + ]; + } + + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + // `start` and `end` are logical edges: in RTL the scroll view is mirrored, and its mask with it. + _fadingEdgeMaskLayer.startPoint = props.horizontal ? CGPointMake(0, 0.5) : CGPointMake(0.5, 0); + _fadingEdgeMaskLayer.endPoint = props.horizontal ? CGPointMake(1, 0.5) : CGPointMake(0.5, 1); + _fadingEdgeMaskLayer.locations = @[ + @0, + @(fadeLength * start / length / extent), + @(1 - fadeLength * end / length / extent), + @1, + ]; + _fadingEdgeMaskLayer.frame = _scrollView.bounds; + [CATransaction commit]; + + // The scroll view rather than this view: RCTViewComponentView owns `currentContainerView.layer.mask`. + _scrollView.layer.mask = _fadingEdgeMaskLayer; +} + +- (void)_layoutFadingEdgeMask +{ + if (_fadingEdgeMaskLayer == nil) { + return; + } + // The mask is laid out in the scroll view's bounds, whose origin is the content offset. + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + _fadingEdgeMaskLayer.frame = _scrollView.bounds; + [CATransaction commit]; } - (void)updateState:(const State::Shared &)state oldState:(const State::Shared &)oldState @@ -747,6 +833,8 @@ - (BOOL)touchesShouldCancelInContentView:(__unused UIView *)view - (void)scrollViewDidScroll:(UIScrollView *)scrollView { + [self _layoutFadingEdgeMask]; + auto scrollMetrics = [self _scrollViewMetrics]; [self _updateStateWithContentOffset]; diff --git a/packages/react-native/React/Tests/Mounting/RCTScrollViewComponentViewTests.mm b/packages/react-native/React/Tests/Mounting/RCTScrollViewComponentViewTests.mm index 82b350755668..d7652b7123ad 100644 --- a/packages/react-native/React/Tests/Mounting/RCTScrollViewComponentViewTests.mm +++ b/packages/react-native/React/Tests/Mounting/RCTScrollViewComponentViewTests.mm @@ -9,8 +9,19 @@ #import #import #import +#import +#import +#import +#import +#import +using facebook::react::ContextContainer; +using facebook::react::EmptyLayoutMetrics; +using facebook::react::LayoutMetrics; using facebook::react::Props; +using facebook::react::PropsParserContext; +using facebook::react::RawProps; +using facebook::react::RawPropsParser; using facebook::react::ScrollViewProps; using facebook::react::ScrollViewShadowNode; @@ -23,6 +34,25 @@ return props; } +static Props::Shared makeFadingEdgeProps(folly::dynamic fadingEdgeLength, bool horizontal = false) +{ + auto props = std::make_shared(); + props->fadingEdgeLength = std::move(fadingEdgeLength); + props->horizontal = horizontal; + return props; +} + +static Props::Shared parseScrollViewProps(const folly::dynamic &rawValue, const ScrollViewProps &sourceProps) +{ + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + auto rawProps = RawProps(rawValue); + auto parser = RawPropsParser(); + parser.prepare(); + rawProps.parse(parser); + return std::make_shared(parserContext, sourceProps, rawProps); +} + @interface RCTScrollViewComponentView (Tests) - (void)_keyboardWillChangeFrame:(NSNotification *)notification; @end @@ -61,6 +91,115 @@ - (void)testAutomaticallyAdjustKeyboardInsetsAcrossRecycling XCTAssertEqual(view.scrollView.contentInset.bottom, 50); } +- (CAGradientLayer *)fadingEdgeMaskOf:(RCTScrollViewComponentView *)view +{ + CALayer *mask = view.scrollView.layer.mask; + XCTAssertTrue([mask isKindOfClass:[CAGradientLayer class]]); + return (CAGradientLayer *)mask; +} + +- (void)assertLocations:(NSArray *)locations equal:(NSArray *)expected +{ + XCTAssertEqual(locations.count, expected.count); + for (NSUInteger index = 0; index < MIN(locations.count, expected.count); index++) { + XCTAssertEqualWithAccuracy(locations[index].doubleValue, expected[index].doubleValue, 1e-6); + } +} + +- (void)testFadingEdgeLengthFadesEachEdgeOverItsOwnLength +{ + RCTScrollViewComponentView *view = [[RCTScrollViewComponentView alloc] initWithFrame:CGRectMake(0, 0, 100, 200)]; + [view updateProps:makeFadingEdgeProps(folly::dynamic::object("start", 20)("end", 40)) + oldProps:ScrollViewShadowNode::defaultSharedProps()]; + + CAGradientLayer *mask = [self fadingEdgeMaskOf:view]; + XCTAssertTrue(CGPointEqualToPoint(mask.startPoint, CGPointMake(0.5, 0))); + XCTAssertTrue(CGPointEqualToPoint(mask.endPoint, CGPointMake(0.5, 1))); + [self assertLocations:mask.locations equal:@[ @0, @0.1, @0.8, @1 ]]; + XCTAssertTrue(CGRectEqualToRect(mask.frame, view.scrollView.bounds)); +} + +- (void)testFadingEdgeLengthNumberFadesBothEdges +{ + RCTScrollViewComponentView *view = [[RCTScrollViewComponentView alloc] initWithFrame:CGRectMake(0, 0, 100, 200)]; + [view updateProps:makeFadingEdgeProps(30) oldProps:ScrollViewShadowNode::defaultSharedProps()]; + + [self assertLocations:[self fadingEdgeMaskOf:view].locations equal:@[ @0, @0.15, @0.85, @1 ]]; +} + +- (void)testHorizontalFadingEdgeLengthFadesAlongTheWidth +{ + RCTScrollViewComponentView *view = [[RCTScrollViewComponentView alloc] initWithFrame:CGRectMake(0, 0, 100, 200)]; + [view updateProps:makeFadingEdgeProps(folly::dynamic::object("start", 20)("end", 0), true) + oldProps:ScrollViewShadowNode::defaultSharedProps()]; + + CAGradientLayer *mask = [self fadingEdgeMaskOf:view]; + XCTAssertTrue(CGPointEqualToPoint(mask.startPoint, CGPointMake(0, 0.5))); + XCTAssertTrue(CGPointEqualToPoint(mask.endPoint, CGPointMake(1, 0.5))); + [self assertLocations:mask.locations equal:@[ @0, @0.2, @1, @1 ]]; +} + +- (void)testParsedFadingEdgeFollowsTheAxisWhenHorizontalIsRemoved +{ + RCTScrollViewComponentView *view = [[RCTScrollViewComponentView alloc] initWithFrame:CGRectMake(0, 0, 100, 200)]; + auto horizontalProps = + parseScrollViewProps(folly::dynamic::object("fadingEdgeLength", 20)("horizontal", true), ScrollViewProps{}); + [view updateProps:horizontalProps oldProps:ScrollViewShadowNode::defaultSharedProps()]; + XCTAssertTrue(CGPointEqualToPoint([self fadingEdgeMaskOf:view].startPoint, CGPointMake(0, 0.5))); + + auto verticalProps = parseScrollViewProps( + folly::dynamic::object("horizontal", nullptr), static_cast(*horizontalProps)); + [view updateProps:verticalProps oldProps:horizontalProps]; + + CAGradientLayer *mask = [self fadingEdgeMaskOf:view]; + XCTAssertTrue(CGPointEqualToPoint(mask.startPoint, CGPointMake(0.5, 0))); + XCTAssertTrue(CGPointEqualToPoint(mask.endPoint, CGPointMake(0.5, 1))); + [self assertLocations:mask.locations equal:@[ @0, @0.1, @0.9, @1 ]]; +} + +- (void)testFadingEdgesNeverOverlap +{ + RCTScrollViewComponentView *view = [[RCTScrollViewComponentView alloc] initWithFrame:CGRectMake(0, 0, 100, 200)]; + [view updateProps:makeFadingEdgeProps(folly::dynamic::object("start", 300)("end", 150)) + oldProps:ScrollViewShadowNode::defaultSharedProps()]; + + [self assertLocations:[self fadingEdgeMaskOf:view].locations equal:@[ @0, @0.5, @0.75, @1 ]]; +} + +- (void)testZeroFadingEdgeLengthRemovesTheMask +{ + RCTScrollViewComponentView *view = [[RCTScrollViewComponentView alloc] initWithFrame:CGRectMake(0, 0, 100, 200)]; + [view updateProps:makeFadingEdgeProps(40) oldProps:ScrollViewShadowNode::defaultSharedProps()]; + XCTAssertNotNil(view.scrollView.layer.mask); + + [view updateProps:makeFadingEdgeProps(0) oldProps:nullptr]; + XCTAssertNil(view.scrollView.layer.mask); +} + +- (void)testFadingEdgeMaskFollowsTheContentOffset +{ + RCTScrollViewComponentView *view = [[RCTScrollViewComponentView alloc] initWithFrame:CGRectMake(0, 0, 100, 200)]; + [view updateProps:makeFadingEdgeProps(40) oldProps:ScrollViewShadowNode::defaultSharedProps()]; + + view.scrollView.contentSize = CGSizeMake(100, 1000); + view.scrollView.contentOffset = CGPointMake(0, 300); + + XCTAssertTrue(CGRectEqualToRect([self fadingEdgeMaskOf:view].frame, CGRectMake(0, 300, 100, 200))); +} + +- (void)testFadingEdgeMaskFollowsTheScrollViewSize +{ + RCTScrollViewComponentView *view = [[RCTScrollViewComponentView alloc] initWithFrame:CGRectMake(0, 0, 100, 200)]; + [view updateProps:makeFadingEdgeProps(40) oldProps:ScrollViewShadowNode::defaultSharedProps()]; + [self assertLocations:[self fadingEdgeMaskOf:view].locations equal:@[ @0, @0.2, @0.8, @1 ]]; + + LayoutMetrics layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.size = {100, 400}; + [view updateLayoutMetrics:layoutMetrics oldLayoutMetrics:EmptyLayoutMetrics]; + + [self assertLocations:[self fadingEdgeMaskOf:view].locations equal:@[ @0, @0.1, @0.9, @1 ]]; +} + @end #endif diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.cpp index 238fa494c142..410184890c8d 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.cpp @@ -170,7 +170,13 @@ BaseScrollViewProps::BaseScrollViewProps( rawProps, "horizontal", sourceProps.horizontal, - true)), + false)), + fadingEdgeLength(convertRawProp( + context, + rawProps, + "fadingEdgeLength", + sourceProps.fadingEdgeLength, + nullptr)), scrollEventThrottle(convertRawProp( context, rawProps, @@ -294,6 +300,7 @@ void BaseScrollViewProps::setProp( RAW_SET_PROP_SWITCH_CASE_BASIC(showsVerticalScrollIndicator); RAW_SET_PROP_SWITCH_CASE_BASIC(persistentScrollbar); RAW_SET_PROP_SWITCH_CASE_BASIC(horizontal); + RAW_SET_PROP_SWITCH_CASE_BASIC(fadingEdgeLength); RAW_SET_PROP_SWITCH_CASE_BASIC(scrollEventThrottle); RAW_SET_PROP_SWITCH_CASE_BASIC(zoomScale); RAW_SET_PROP_SWITCH_CASE_BASIC(contentInset); diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.h b/packages/react-native/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.h index 3a3ebbe84b9b..c2dc57620b4e 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.h +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.h @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -57,6 +58,7 @@ class BaseScrollViewProps : public ViewProps { bool showsVerticalScrollIndicator{true}; bool persistentScrollbar{false}; bool horizontal{false}; + folly::dynamic fadingEdgeLength{}; Float scrollEventThrottle{}; Float zoomScale{1.0f}; EdgeInsets contentInset{}; diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.cpp index d8812796d475..32070a2b2ac3 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.cpp @@ -32,12 +32,6 @@ HostPlatformScrollViewProps::HostPlatformScrollViewProps( "nestedScrollEnabled", sourceProps.nestedScrollEnabled, true)), - fadingEdgeLength(convertRawProp( - context, - rawProps, - "fadingEdgeLength", - sourceProps.fadingEdgeLength, - nullptr)), overScrollMode(convertRawProp( context, rawProps, @@ -66,7 +60,6 @@ void HostPlatformScrollViewProps::setProp( switch (hash) { RAW_SET_PROP_SWITCH_CASE_BASIC(sendMomentumEvents); RAW_SET_PROP_SWITCH_CASE_BASIC(nestedScrollEnabled); - RAW_SET_PROP_SWITCH_CASE_BASIC(fadingEdgeLength); RAW_SET_PROP_SWITCH_CASE_BASIC(overScrollMode); RAW_SET_PROP_SWITCH_CASE_BASIC(endFillColor); } @@ -276,6 +269,10 @@ folly::dynamic HostPlatformScrollViewProps::getDiffProps( result["horizontal"] = horizontal; } + if (fadingEdgeLength != oldProps->fadingEdgeLength) { + result["fadingEdgeLength"] = fadingEdgeLength; + } + if (scrollEventThrottle != oldProps->scrollEventThrottle) { result["scrollEventThrottle"] = scrollEventThrottle; } @@ -368,10 +365,6 @@ folly::dynamic HostPlatformScrollViewProps::getDiffProps( result["nestedScrollEnabled"] = nestedScrollEnabled; } - if (fadingEdgeLength != oldProps->fadingEdgeLength) { - result["fadingEdgeLength"] = fadingEdgeLength; - } - if (overScrollMode != oldProps->overScrollMode) { result["overScrollMode"] = overScrollMode; } diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.h b/packages/react-native/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.h index 7ac321ec89fd..e0e0ae2dc452 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.h +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.h @@ -35,7 +35,6 @@ class HostPlatformScrollViewProps : public BaseScrollViewProps { bool sendMomentumEvents{}; bool nestedScrollEnabled{}; - folly::dynamic fadingEdgeLength{}; std::string overScrollMode{"auto"}; SharedColor endFillColor{clearColor()}; diff --git a/packages/react-native/ReactNativeApi.d.ts b/packages/react-native/ReactNativeApi.d.ts index cb67856cb4b3..d4266b402acf 100644 --- a/packages/react-native/ReactNativeApi.d.ts +++ b/packages/react-native/ReactNativeApi.d.ts @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2ceaa8e472a53fc667994edb63ccd995>> + * @generated SignedSource<<564bb5bee47c364c4aecb358d05523fa>> * * This file was generated by scripts/js-api/build-types/index.js. */ @@ -4269,6 +4269,12 @@ declare type ScrollViewBaseProps = { readonly contentOffset?: PointProp readonly decelerationRate?: DecelerationRateType readonly disableIntervalMomentum?: boolean + readonly fadingEdgeLength?: + | (number | undefined) + | { + end: number + start: number + } readonly horizontal?: boolean readonly innerViewRef?: React.Ref readonly invertStickyHeaders?: boolean @@ -4348,12 +4354,6 @@ declare interface ScrollViewProps extends Readonly< > {} declare type ScrollViewPropsAndroid = { readonly endFillColor?: ColorValue - readonly fadingEdgeLength?: - | (number | undefined) - | { - end: number - start: number - } readonly nestedScrollEnabled?: boolean readonly overScrollMode?: "always" | "auto" | "never" readonly persistentScrollbar?: boolean @@ -5779,7 +5779,7 @@ export { AlertOptions, // 8a116d2a AlertType, // 5ab91217 AndroidKeyboardEvent, // e03becc8 - Animated, // ed4648e6 + Animated, // 3acee102 AppConfig, // 35c0ca70 AppRegistry, // edec2a30 AppState, // 12012be5 @@ -5831,9 +5831,9 @@ export { EventSubscription, // b8d084aa ExtendedExceptionData, // 5a6ccf5a FilterFunction, // bf24c0e3 - FlatList, // 76dfb3dc - FlatListInstance, // 1d6b2ce4 - FlatListProps, // 14bbe483 + FlatList, // c9d066e1 + FlatListInstance, // 92bb0df6 + FlatListProps, // 30029c49 FocusEvent, // f2e4363d FontVariant, // 7c7558bb GestureResponderEvent, // 5cd2a663 @@ -5975,18 +5975,18 @@ export { ScrollEvent, // 956fdb27 ScrollResponderType, // e41ccb2a ScrollToLocationParamsType, // d7ecdad1 - ScrollView, // 651915c2 + ScrollView, // 7b4f2a71 ScrollViewImperativeMethods, // cb73da62 ScrollViewInstance, // 099b48e2 - ScrollViewProps, // 7e38d97b - ScrollViewPropsAndroid, // 02f3df2e + ScrollViewProps, // d5685bcd + ScrollViewPropsAndroid, // e691594a ScrollViewPropsIOS, // c378379b ScrollViewScrollToOptions, // 3313411e SectionBase, // 9f13db00 - SectionList, // 542a3017 + SectionList, // 820679bc SectionListData, // 1a4de01a - SectionListInstance, // ea2f03e4 - SectionListProps, // d6282d2b + SectionListInstance, // 7454c87e + SectionListProps, // 667eca3f SectionListRenderItem, // 715b2086 SectionListRenderItemInfo, // 4a48a922 Separators, // 6a45f7e3 @@ -6061,10 +6061,10 @@ export { VirtualViewMode, // 6be59722 VirtualizedList, // 68c7345e VirtualizedListInstance, // 423ee7c0 - VirtualizedListProps, // 7a7c618a + VirtualizedListProps, // ceda6d7f VirtualizedSectionList, // 9fd9cd61 VirtualizedSectionListInstance, // 12b706d5 - VirtualizedSectionListProps, // 8373f27c + VirtualizedSectionListProps, // 4a5e66d2 WrapperComponentProvider, // 9b4247f6 codegenNativeCommands, // 322f3f4e codegenNativeComponent, // 3da62daa diff --git a/packages/react-native/types_DEPRECATED/Libraries/Components/ScrollView/ScrollView.d.ts b/packages/react-native/types_DEPRECATED/Libraries/Components/ScrollView/ScrollView.d.ts index d57d50b425e3..54f1636a9a7b 100644 --- a/packages/react-native/types_DEPRECATED/Libraries/Components/ScrollView/ScrollView.d.ts +++ b/packages/react-native/types_DEPRECATED/Libraries/Components/ScrollView/ScrollView.d.ts @@ -560,22 +560,6 @@ export interface ScrollViewPropsAndroid { */ nestedScrollEnabled?: boolean | undefined; - /** - * Controls the fading effect at the edges of the scroll content. - * - * A value greater than 0 will apply the fading effect, indicating more content is available - * to scroll. - * - * You can specify a single number to apply the same fading length to both edges. - * Alternatively, use an object with `start` and `end` properties to set different - * fading lengths for the start and end of the scroll content. - * - * The default value is 0. - * - * @platform android - */ - fadingEdgeLength?: number | {start: number; end: number} | undefined; - /** * Causes the scrollbars not to turn transparent when they are not in use. The default value is false. */ @@ -635,6 +619,20 @@ export interface ScrollViewProps */ decelerationRate?: 'fast' | 'normal' | number | undefined; + /** + * Controls the fading effect at the edges of the scroll content. + * + * A value greater than 0 will apply the fading effect, indicating more content is available + * to scroll. + * + * You can specify a single number to apply the same fading length to both edges. + * Alternatively, use an object with `start` and `end` properties to set different + * fading lengths for the start and end of the scroll content. + * + * The default value is 0. + */ + fadingEdgeLength?: number | {start: number; end: number} | undefined; + /** * When true the scroll view's children are arranged horizontally in a row * instead of vertically in a column. The default value is false. diff --git a/packages/react-native/types_DEPRECATED/Libraries/Lists/FlatList.d.ts b/packages/react-native/types_DEPRECATED/Libraries/Lists/FlatList.d.ts index 0914329f7833..a6e75f49eb37 100644 --- a/packages/react-native/types_DEPRECATED/Libraries/Lists/FlatList.d.ts +++ b/packages/react-native/types_DEPRECATED/Libraries/Lists/FlatList.d.ts @@ -157,8 +157,6 @@ export interface FlatListProps extends VirtualizedListProps { * fading lengths for the start and end of the scroll content. * * The default value is 0. - * - * @platform android */ fadingEdgeLength?: number | {start: number; end: number} | undefined; diff --git a/packages/rn-tester/js/examples/FlatList/FlatList-basic.js b/packages/rn-tester/js/examples/FlatList/FlatList-basic.js index cdcef788d216..cb4239aa1cdd 100644 --- a/packages/rn-tester/js/examples/FlatList/FlatList-basic.js +++ b/packages/rn-tester/js/examples/FlatList/FlatList-basic.js @@ -36,7 +36,6 @@ import { Alert, Animated, I18nManager, - Platform, StyleSheet, TextInput, View, @@ -227,20 +226,18 @@ class FlatListExample extends React.PureComponent { this.state.maintainVisibleContentPosition, this._setBooleanValue('maintainVisibleContentPosition'), )} - {Platform.OS === 'android' && ( - - - this.setState({ - fadingEdgeLength: Number(event.nativeEvent.text), - }) - } - /> - - )} + + + this.setState({ + fadingEdgeLength: Number(event.nativeEvent.text), + }) + } + /> + diff --git a/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js b/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js index ef937f99089c..4906870f9845 100644 --- a/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js +++ b/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js @@ -470,6 +470,15 @@ const examples: Array = [ return ; }, }, + { + name: 'fadingEdgeLength', + title: ' fadingEdgeLength\n', + description: + 'Fades the content out at the edges of the scroll view, with one length for both edges or one per edge.', + render(): React.Node { + return ; + }, + }, ]; if (Platform.OS === 'ios') { @@ -533,10 +542,10 @@ if (Platform.OS === 'ios') { }); } else if (Platform.OS === 'android') { examples.push({ - title: ' EndFillColor & FadingEdgeLength\n', - description: 'Toggle to set endFillColor and fadingEdgeLength.', + title: ' EndFillColor\n', + description: 'Toggle to set endFillColor.', render(): React.Node { - return ; + return ; }, }); examples.push({ @@ -653,15 +662,13 @@ const HorizontalScrollView = (props: { ); }; -const EndFillColorFadingEdgeLen = () => { +const EndFillColorExample = () => { const [endFillColor, setEndFillColor] = useState(''); - const [fadingEdgeLen, setFadingEdgeLen] = useState(0); return ( {ITEMS.map(createItemRow)} @@ -671,16 +678,42 @@ const EndFillColorFadingEdgeLen = () => { endFillColor === '' ? setEndFillColor('#A9DFD0') : setEndFillColor('') } /> -