diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/VirtualView/RCTVirtualViewComponentView.h b/packages/react-native/React/Fabric/Mounting/ComponentViews/VirtualView/RCTVirtualViewComponentView.h deleted file mode 100644 index 14d1774bf69139..00000000000000 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/VirtualView/RCTVirtualViewComponentView.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface RCTVirtualViewComponentView : RCTViewComponentView - -+ (instancetype)new NS_UNAVAILABLE; -- (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE; - -- (instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/VirtualView/RCTVirtualViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/VirtualView/RCTVirtualViewComponentView.mm deleted file mode 100644 index 0efe1d7a18171a..00000000000000 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/VirtualView/RCTVirtualViewComponentView.mm +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "RCTVirtualViewComponentView.h" - -#import -#import -#import -#import -#import -#import - -#import -#import -#import -#import -#import -#import - -#import "../VirtualViewExperimental/RCTVirtualViewMode.h" -#import "../VirtualViewExperimental/RCTVirtualViewRenderState.h" -#import "RCTFabricComponentsPlugins.h" - -using namespace facebook; -using namespace facebook::react; - -/** - * Checks whether one CGRect overlaps with another CGRect. - * - * This is different from CGRectIntersectsRect because a CGRect representing - * a line or a point is considered to overlap with another CGRect if the line - * or point is within the rect bounds. However, two CGRects are not considered - * to overlap if they only share a boundary. - */ -static BOOL CGRectOverlaps(CGRect rect1, CGRect rect2) -{ - CGFloat minY1 = CGRectGetMinY(rect1); - CGFloat maxY1 = CGRectGetMaxY(rect1); - CGFloat minY2 = CGRectGetMinY(rect2); - CGFloat maxY2 = CGRectGetMaxY(rect2); - if (minY1 >= maxY2 || minY2 >= maxY1) { - // No overlap on the y-axis. - return NO; - } - CGFloat minX1 = CGRectGetMinX(rect1); - CGFloat maxX1 = CGRectGetMaxX(rect1); - CGFloat minX2 = CGRectGetMinX(rect2); - CGFloat maxX2 = CGRectGetMaxX(rect2); - if (minX1 >= maxX2 || minX2 >= maxX1) { - // No overlap on the x-axis. - return NO; - } - return YES; -} - -@interface RCTVirtualViewComponentView () -@end - -@implementation RCTVirtualViewComponentView { - RCTScrollViewComponentView *_lastParentScrollViewComponentView; - std::optional _mode; - RCTVirtualViewRenderState _renderState; - std::optional _targetRect; -} - -- (instancetype)initWithFrame:(CGRect)frame -{ - if (self = [super initWithFrame:frame]) { - _props = VirtualViewShadowNode::defaultSharedProps(); - _renderState = RCTVirtualViewRenderStateUnknown; - } - - return self; -} - -- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps -{ - const auto &newViewProps = static_cast(*props); - - if (!_mode.has_value()) { - _mode = newViewProps.initialHidden ? RCTVirtualViewModeHidden : RCTVirtualViewModeVisible; - if (ReactNativeFeatureFlags::hideOffscreenVirtualViewsOnIOS()) { - self.hidden = newViewProps.initialHidden && !sIsAccessibilityUsed; - } - } - - // If disabled, `_renderState` will always be `RCTVirtualViewRenderStateUnknown`. - if (ReactNativeFeatureFlags::enableVirtualViewRenderState()) { - switch (newViewProps.renderState) { - case 1: - _renderState = RCTVirtualViewRenderStateRendered; - break; - case 2: - _renderState = RCTVirtualViewRenderStateNone; - break; - default: - _renderState = RCTVirtualViewRenderStateUnknown; - break; - } - } - - [super updateProps:props oldProps:oldProps]; -} - -- (RCTScrollViewComponentView *)getParentScrollViewComponentView -{ - UIView *view = self.superview; - while (view != nil) { - if ([view isKindOfClass:[RCTScrollViewComponentView class]]) { - return (RCTScrollViewComponentView *)view; - } - view = view.superview; - } - return nil; -} - -/** - * Static flag that tracks whether accessibility services are being used. - * When accessibility is detected, virtual views will remain visible even when - * they would normally be hidden when off-screen, ensuring that accessibility - * features will work correctly. - */ -static BOOL sIsAccessibilityUsed = NO; - -- (void)_unhideIfNeeded -{ - if (!sIsAccessibilityUsed) { - // accessibility is detected for the first time. Make views visible. - sIsAccessibilityUsed = YES; - } - - if (self.hidden) { - self.hidden = NO; - } -} - -- (NSInteger)accessibilityElementCount -{ - // From empirical testing, method `accessibilityElementCount` is called lazily only - // when accessibility is used. - [self _unhideIfNeeded]; - return [super accessibilityElementCount]; -} - -- (NSArray> *)focusItemsInRect:(CGRect)rect -{ - // From empirical testing, method `focusItemsInRect:` is called lazily only - // when keyboard navigation is used. - [self _unhideIfNeeded]; - return [super focusItemsInRect:rect]; -} - -- (void)prepareForRecycle -{ - [super prepareForRecycle]; - - // No need to remove the scroll listener here since the view is always removed from window before being recycled and - // we do that in didMoveToWindow, which gets called when the view is removed from window. - RCTAssert( - _lastParentScrollViewComponentView == nil, - @"_lastParentScrollViewComponentView should already have been cleared in didMoveToWindow."); - - self.hidden = NO; - _mode.reset(); - _targetRect.reset(); -} - -// Handles case when sibling changes size. -// TODO(T202601695): This doesn't yet handle the case of elements in the ScrollView outside a VirtualColumn changing -// size. -- (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics - oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics -{ - [super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:_layoutMetrics]; - - [self dispatchOnModeChangeIfNeeded:YES]; -} - -- (void)didMoveToWindow -{ - [super didMoveToWindow]; - - if (_lastParentScrollViewComponentView) { - [_lastParentScrollViewComponentView removeScrollListener:self]; - _lastParentScrollViewComponentView = nil; - } - - if (RCTScrollViewComponentView *parentScrollViewComponentView = [self getParentScrollViewComponentView]) { - if (self.window) { - // TODO(T202601695): We also want the ScrollView to emit layout changes from didLayoutSubviews so that any event - // that may affect visibily of this view notifies the listeners. - [parentScrollViewComponentView addScrollListener:self]; - _lastParentScrollViewComponentView = parentScrollViewComponentView; - - // We want to dispatch the event immediately when the view is added to the window before any scrolling occurs. - [self dispatchOnModeChangeIfNeeded:NO]; - } - } -} - -- (void)scrollViewDidScroll:(UIScrollView *)scrollView -{ - [self dispatchOnModeChangeIfNeeded:NO]; -} - -- (void)dispatchOnModeChangeIfNeeded:(BOOL)checkForTargetRectChange -{ - if (!_lastParentScrollViewComponentView) { - return; - } - - UIScrollView *scrollView = _lastParentScrollViewComponentView.scrollView; - CGRect targetRect = [self convertRect:self.bounds toView:scrollView]; - - // While scrolling, the `targetRect` does not change, so we don't check for changed `targetRect` in that case. - if (checkForTargetRectChange) { - if (_targetRect.has_value() && CGRectEqualToRect(targetRect, _targetRect.value())) { - return; - } - _targetRect = targetRect; - } - - RCTVirtualViewMode newMode; - CGRect thresholdRect = CGRectMake( - scrollView.contentOffset.x, - scrollView.contentOffset.y, - scrollView.frame.size.width, - scrollView.frame.size.height); - const CGFloat visibleWidth = thresholdRect.size.width; - const CGFloat visibleHeight = thresholdRect.size.height; - - if (CGRectOverlaps(targetRect, thresholdRect)) { - newMode = RCTVirtualViewModeVisible; - } else { - auto prerender = false; - const CGFloat prerenderRatio = ReactNativeFeatureFlags::virtualViewPrerenderRatio(); - if (prerenderRatio > 0) { - thresholdRect = CGRectInset(thresholdRect, -visibleWidth * prerenderRatio, -visibleHeight * prerenderRatio); - prerender = CGRectOverlaps(targetRect, thresholdRect); - } - if (prerender) { - newMode = RCTVirtualViewModePrerender; - } else { - const CGFloat hysteresisRatio = ReactNativeFeatureFlags::virtualViewHysteresisRatio(); - if (_mode.has_value() && hysteresisRatio > 0) { - thresholdRect = CGRectInset(thresholdRect, -visibleWidth * hysteresisRatio, -visibleHeight * hysteresisRatio); - if (CGRectOverlaps(targetRect, thresholdRect)) { - newMode = _mode.value(); - } else { - newMode = RCTVirtualViewModeHidden; - thresholdRect = CGRectZero; - } - } else { - newMode = RCTVirtualViewModeHidden; - thresholdRect = CGRectZero; - } - } - } - - if (_mode.has_value() && newMode == _mode.value()) { - return; - } - - // NOTE: Make sure to keep these props in sync with dispatchSyncModeChange below where we have to explicitly copy all - // props. - VirtualViewEventEmitter::OnModeChange event = { - .mode = (int)newMode, - .targetRect = - {.x = targetRect.origin.x, - .y = targetRect.origin.y, - .width = targetRect.size.width, - .height = targetRect.size.height}, - .thresholdRect = - {.x = thresholdRect.origin.x, - .y = thresholdRect.origin.y, - .width = thresholdRect.size.width, - .height = thresholdRect.size.height}, - }; - - const std::optional oldMode = _mode; - _mode = newMode; - - switch (newMode) { - case RCTVirtualViewModeVisible: - if (_renderState == RCTVirtualViewRenderStateUnknown) { - // Feature flag is disabled, so use the former logic. - [self dispatchSyncModeChange:event]; - } else { - // If the previous mode was prerender and the result of dispatching that event was committed, we do not need to - // dispatch an event for visible. - const auto wasPrerenderCommitted = oldMode.has_value() && oldMode == RCTVirtualViewModePrerender && - _renderState == RCTVirtualViewRenderStateRendered; - if (!wasPrerenderCommitted) { - [self dispatchSyncModeChange:event]; - } - } - break; - case RCTVirtualViewModePrerender: - if (!oldMode.has_value() || oldMode != RCTVirtualViewModeVisible) { - [self dispatchAsyncModeChange:event]; - } - break; - case RCTVirtualViewModeHidden: - [self dispatchAsyncModeChange:event]; - break; - } - - if (ReactNativeFeatureFlags::hideOffscreenVirtualViewsOnIOS()) { - switch (newMode) { - case RCTVirtualViewModeVisible: - self.hidden = NO; - break; - case RCTVirtualViewModePrerender: - self.hidden = !sIsAccessibilityUsed; - break; - case RCTVirtualViewModeHidden: - self.hidden = YES; - break; - } - } -} - -- (void)dispatchAsyncModeChange:(VirtualViewEventEmitter::OnModeChange &)event -{ - if (!_eventEmitter) { - return; - } - - auto &emitter = static_cast(*_eventEmitter); - emitter.onModeChange(event); -} - -- (void)dispatchSyncModeChange:(VirtualViewEventEmitter::OnModeChange &)event -{ - if (!_eventEmitter) { - return; - } - - auto &emitter = static_cast(*_eventEmitter); - - // TODO: Move this into a custom event emitter. We had to duplicate the codegen code here from onModeChange in order - // to dispatch synchronously and discrete. - emitter.experimental_flushSync([&emitter, &event]() { - emitter.dispatchEvent( - "modeChange", - [event](jsi::Runtime &runtime) { - auto payload = jsi::Object(runtime); - payload.setProperty(runtime, "mode", event.mode); - { - auto targetRect = jsi::Object(runtime); - targetRect.setProperty(runtime, "x", event.targetRect.x); - targetRect.setProperty(runtime, "y", event.targetRect.y); - targetRect.setProperty(runtime, "width", event.targetRect.width); - targetRect.setProperty(runtime, "height", event.targetRect.height); - payload.setProperty(runtime, "targetRect", targetRect); - } - { - auto thresholdRect = jsi::Object(runtime); - thresholdRect.setProperty(runtime, "x", event.thresholdRect.x); - thresholdRect.setProperty(runtime, "y", event.thresholdRect.y); - thresholdRect.setProperty(runtime, "width", event.thresholdRect.width); - thresholdRect.setProperty(runtime, "height", event.thresholdRect.height); - payload.setProperty(runtime, "thresholdRect", thresholdRect); - } - return payload; - }, - RawEvent::Category::Discrete); - }); -} - -+ (ComponentDescriptorProvider)componentDescriptorProvider -{ - return concreteComponentDescriptorProvider(); -} - -@end - -Class VirtualViewCls(void) -{ - return RCTVirtualViewComponentView.class; -} diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/VirtualViewExperimental/RCTVirtualViewExperimentalComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/VirtualViewExperimental/RCTVirtualViewExperimentalComponentView.mm index 089bc52dbcb3b9..81187c7b2ecc0f 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/VirtualViewExperimental/RCTVirtualViewExperimentalComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/VirtualViewExperimental/RCTVirtualViewExperimentalComponentView.mm @@ -177,7 +177,7 @@ - (void)onModeChange:(RCTVirtualViewMode)newMode targetRect:(CGRect)targetRect t // NOTE: Make sure to keep these props in sync with dispatchSyncModeChange below where we have to explicitly copy // all props. - VirtualViewEventEmitter::OnModeChange event = { + VirtualViewExperimentalEventEmitter::OnModeChange event = { .mode = (int)newMode, .targetRect = {.x = targetRect.origin.x, @@ -260,23 +260,23 @@ - (void)_unhideIfNeeded return nil; } -- (void)_dispatchAsyncModeChange:(VirtualViewEventEmitter::OnModeChange &)event +- (void)_dispatchAsyncModeChange:(VirtualViewExperimentalEventEmitter::OnModeChange &)event { if (!_eventEmitter) { return; } - auto &emitter = static_cast(*_eventEmitter); + auto &emitter = static_cast(*_eventEmitter); emitter.onModeChange(event); } -- (void)_dispatchSyncModeChange:(VirtualViewEventEmitter::OnModeChange &)event +- (void)_dispatchSyncModeChange:(VirtualViewExperimentalEventEmitter::OnModeChange &)event { if (!_eventEmitter) { return; } - auto &emitter = static_cast(*_eventEmitter); + auto &emitter = static_cast(*_eventEmitter); // TODO: Move this into a custom event emitter. We had to duplicate the codegen code here from onModeChange in order // to dispatch synchronously and discrete. diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 2a0ba29bfdd8ae..64da10d59b68c3 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -5240,17 +5240,6 @@ public abstract interface class com/facebook/react/viewmanagers/VirtualViewExper public abstract fun setRenderState (Landroid/view/View;I)V } -public class com/facebook/react/viewmanagers/VirtualViewManagerDelegate : com/facebook/react/uimanager/BaseViewManagerDelegate { - public fun (Lcom/facebook/react/uimanager/BaseViewManager;)V - public fun setProperty (Landroid/view/View;Ljava/lang/String;Ljava/lang/Object;)V -} - -public abstract interface class com/facebook/react/viewmanagers/VirtualViewManagerInterface : com/facebook/react/uimanager/ViewManagerWithGeneratedInterface { - public abstract fun setInitialHidden (Landroid/view/View;Z)V - public abstract fun setRemoveClippedSubviews (Landroid/view/View;Z)V - public abstract fun setRenderState (Landroid/view/View;I)V -} - public final class com/facebook/react/views/drawer/ReactDrawerLayout : androidx/drawerlayout/widget/DrawerLayout { public fun (Lcom/facebook/react/bridge/ReactContext;)V public fun onInterceptTouchEvent (Landroid/view/MotionEvent;)Z @@ -6702,41 +6691,6 @@ public final class com/facebook/react/views/virtual/VirtualViewMode : java/lang/ public static fun values ()[Lcom/facebook/react/views/virtual/VirtualViewMode; } -public final class com/facebook/react/views/virtual/view/ReactVirtualView : com/facebook/react/views/view/ReactViewGroup, android/view/View$OnLayoutChangeListener, com/facebook/react/views/scroll/ReactScrollViewHelper$LayoutChangeListener, com/facebook/react/views/scroll/ReactScrollViewHelper$ScrollListener { - public fun (Landroid/content/Context;)V - public fun onLayout (Landroid/view/ViewGroup;)V - public fun onLayoutChange (Landroid/view/View;IIIIIIII)V - public fun onLayoutChange (Landroid/view/ViewGroup;)V - public fun onScroll (Landroid/view/ViewGroup;Lcom/facebook/react/views/scroll/ScrollEventType;FF)V - public synthetic fun recycleView$xplat_js_react_native_github_packages_react_native_ReactAndroid_src_main_java_com_facebook_react_views_view_viewAndroid ()V - public fun updateClippingRect (Ljava/util/Set;)V -} - -public final class com/facebook/react/views/virtual/view/ReactVirtualViewManager : com/facebook/react/views/view/ReactClippingViewManager, com/facebook/react/viewmanagers/VirtualViewManagerInterface { - public static final field Companion Lcom/facebook/react/views/virtual/view/ReactVirtualViewManager$Companion; - public static final field REACT_CLASS Ljava/lang/String; - public fun ()V - public synthetic fun addEventEmitters (Lcom/facebook/react/uimanager/ThemedReactContext;Landroid/view/View;)V - public synthetic fun createViewInstance (Lcom/facebook/react/uimanager/ThemedReactContext;)Landroid/view/View; - public fun getName ()Ljava/lang/String; - public synthetic fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Landroid/view/View;)Landroid/view/View; - public synthetic fun setInitialHidden (Landroid/view/View;Z)V - public fun setInitialHidden (Lcom/facebook/react/views/virtual/view/ReactVirtualView;Z)V - public synthetic fun setNativeId (Landroid/view/View;Ljava/lang/String;)V - public fun setNativeId (Lcom/facebook/react/views/virtual/view/ReactVirtualView;Ljava/lang/String;)V - public synthetic fun setRemoveClippedSubviews (Landroid/view/View;Z)V - public synthetic fun setRenderState (Landroid/view/View;I)V - public fun setRenderState (Lcom/facebook/react/views/virtual/view/ReactVirtualView;I)V -} - -public final class com/facebook/react/views/virtual/view/ReactVirtualViewManager$Companion { -} - -public final class com/facebook/react/views/virtual/view/VirtualViewEventEmitter : com/facebook/react/views/virtual/VirtualViewModeChangeEmitter { - public fun (IILcom/facebook/react/uimanager/events/EventDispatcher;)V - public fun emitModeChange (Lcom/facebook/react/views/virtual/VirtualViewMode;Landroid/graphics/Rect;Landroid/graphics/Rect;Z)V -} - public final class com/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimental : com/facebook/react/views/view/ReactViewGroup, android/view/View$OnLayoutChangeListener, com/facebook/react/views/scroll/VirtualView { public fun (Landroid/content/Context;)V public fun getContainerRelativeRect ()Landroid/graphics/Rect; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/virtual/view/ReactVirtualView.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/virtual/view/ReactVirtualView.kt deleted file mode 100644 index aed3036909b0c1..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/virtual/view/ReactVirtualView.kt +++ /dev/null @@ -1,445 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.virtual.view - -import android.content.Context -import android.graphics.Rect -import android.view.View -import android.view.ViewGroup -import android.view.ViewParent -import android.view.ViewTreeObserver -import androidx.annotation.VisibleForTesting -import com.facebook.common.logging.FLog -import com.facebook.react.R -import com.facebook.react.common.build.ReactBuildConfig -import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags -import com.facebook.react.uimanager.ReactClippingViewGroup -import com.facebook.react.uimanager.ReactRoot -import com.facebook.react.views.scroll.ReactHorizontalScrollView -import com.facebook.react.views.scroll.ReactScrollView -import com.facebook.react.views.scroll.ReactScrollViewHelper -import com.facebook.react.views.scroll.ScrollEventType -import com.facebook.react.views.view.ReactViewGroup -import com.facebook.react.views.virtual.VirtualViewMode -import com.facebook.react.views.virtual.VirtualViewModeChangeEmitter -import com.facebook.react.views.virtual.VirtualViewRenderState -import com.facebook.systrace.Systrace - -public class ReactVirtualView(context: Context) : - ReactViewGroup(context), - ReactScrollViewHelper.ScrollListener, - ReactScrollViewHelper.LayoutChangeListener, - View.OnLayoutChangeListener { - - internal var mode: VirtualViewMode? = null - internal var renderState: VirtualViewRenderState = VirtualViewRenderState.Unknown - internal var modeChangeEmitter: VirtualViewModeChangeEmitter? = null - internal var prerenderRatio: Double = ReactNativeFeatureFlags.virtualViewPrerenderRatio() - internal val debugLogEnabled: Boolean = ReactNativeFeatureFlags.enableVirtualViewDebugFeatures() - private val hysteresisRatio: Double = ReactNativeFeatureFlags.virtualViewHysteresisRatio() - - private val onWindowFocusChangeListener = - if (ReactNativeFeatureFlags.enableVirtualViewWindowFocusDetection()) { - ViewTreeObserver.OnWindowFocusChangeListener { - dispatchOnModeChangeIfNeeded(checkRectChange = false) - } - } else { - null - } - - private var parentScrollView: View? = null - - // preallocate Rects to avoid allocation during layout - private val lastRect: Rect = Rect() - private val targetRect: Rect = Rect() - private val thresholdRect: Rect = Rect() - private val lastClippingRect: Rect = Rect() - - /** Cumulative offset of parents' `left` values within the scroll view */ - private var offsetX: Int = 0 - /** Cumulative offset of parents' `top` values within the scroll view */ - private var offsetY: Int = 0 - private var offsetChanged: Boolean = false - - internal val nativeId: String? - get() = getTag(R.id.view_tag_native_id) as? String - - override internal fun recycleView() { - ReactScrollViewHelper.removeScrollListener(this) - ReactScrollViewHelper.removeLayoutChangeListener(this) - cleanupLayoutListeners() - mode = null - modeChangeEmitter = null - lastRect.setEmpty() - parentScrollView = null - offsetX = 0 - offsetY = 0 - offsetChanged = false - lastClippingRect.setEmpty() - } - - override fun onAttachedToWindow() { - super.onAttachedToWindow() - doAttachedToWindow() - } - - @VisibleForTesting - internal fun doAttachedToWindow() { - parentScrollView = - getParentScrollView()?.also { - offsetChanged = true - ReactScrollViewHelper.addScrollListener(this) - ReactScrollViewHelper.addLayoutChangeListener(this) - } - debugLog("onAttachedToWindow") - if (onWindowFocusChangeListener != null) { - viewTreeObserver.addOnWindowFocusChangeListener(onWindowFocusChangeListener) - } - dispatchOnModeChangeIfNeeded(checkRectChange = false) - } - - override fun onDetachedFromWindow() { - super.onDetachedFromWindow() - ReactScrollViewHelper.removeScrollListener(this) - ReactScrollViewHelper.removeLayoutChangeListener(this) - if (onWindowFocusChangeListener != null) { - viewTreeObserver.removeOnWindowFocusChangeListener(onWindowFocusChangeListener) - } - cleanupLayoutListeners() - } - - /** From [View#onLayout] */ - override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { - super.onLayout(changed, left, top, right, bottom) - if (changed) { - offsetChanged = true - dispatchOnModeChangeIfNeeded(checkRectChange = false) - } - } - - override fun onLayoutChange( - v: View?, - left: Int, - top: Int, - right: Int, - bottom: Int, - oldLeft: Int, - oldTop: Int, - oldRight: Int, - oldBottom: Int, - ) { - offsetChanged = offsetChanged || oldLeft != left || oldTop != top - dispatchOnModeChangeIfNeeded(true) - } - - /** - * From ReactScrollViewHelper#onScroll, triggered by [ReactScrollView] and - * [ReactHorizontalScrollView] - */ - override fun onScroll( - scrollView: ViewGroup?, - scrollEventType: ScrollEventType?, - xVelocity: Float, - yVelocity: Float, - ) { - if (scrollView == parentScrollView) { - dispatchOnModeChangeIfNeeded(checkRectChange = false) - } - } - - /** - * From ReactScrollViewHelper#onLayout, triggered by [ReactScrollView] and - * [ReactHorizontalScrollView] - */ - override fun onLayout(scrollView: ViewGroup?) { - if (scrollView == parentScrollView) { - dispatchOnModeChangeIfNeeded(checkRectChange = false) - } - } - - /** - * From ReactScrollViewHelper#onLayoutChange, triggered by [ReactScrollView] and - * [ReactHorizontalScrollView] - */ - override fun onLayoutChange(scrollView: ViewGroup) { - if (scrollView == parentScrollView) { - offsetChanged = true - dispatchOnModeChangeIfNeeded(false) - } - } - - override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { - super.onSizeChanged(w, h, oldw, oldh) - dispatchOnModeChangeIfNeeded(true) - } - - // Note: We co-opt subview clipping on ReactVirtualView by returning the - // clipping rect of the ScrollView. This means we clip the children of ReactVirtualView - // when they are out of the viewport, but not ReactVirtualView itself. - override fun updateClippingRect(excludedViews: Set?) { - if (!_removeClippedSubviews) { - return - } - - // If no ScrollView, or ScrollView has disabled removeClippedSubviews, use default behavior - if (parentScrollView == null) { - super.updateClippingRect(excludedViews) - return - } - - val clippingRect = checkNotNull(clippingRect) - val parentScrollView = checkNotNull(parentScrollView) as ReactClippingViewGroup - - if (parentScrollView.removeClippedSubviews) { - parentScrollView.getClippingRect(clippingRect) - } else { - (parentScrollView as View).getDrawingRect(clippingRect) - } - - clippingRect.intersect(targetRect) - clippingRect.offset(-targetRect.left, -targetRect.top) - - if (lastClippingRect == clippingRect) { - return - } - - updateClippingToRect(clippingRect, excludedViews) - lastClippingRect.set(clippingRect) - } - - private fun dispatchOnModeChangeIfNeeded(checkRectChange: Boolean) { - modeChangeEmitter ?: return - val scrollView = parentScrollView ?: return - - if (offsetChanged) { - updateParentOffset() - } - targetRect.set( - left + offsetX, - top + offsetY, - right + offsetX, - bottom + offsetY, - ) - scrollView.getDrawingRect(thresholdRect) - val visibleHeight = thresholdRect.height() - val visibleWidth = thresholdRect.width() - - // TODO: Validate whether this is still the case and whether these checks are still needed. - // updateRects will initially get called before the targetRect has any dimensions set, so if - // it's both zero width and height, we need to skip dispatching an incorrect mode change. - // The correct mode change will be dispatched later. We can't use targetRect.isEmpty because it - // will return true if either there's a zero width or height, but that case is valid. - if ((targetRect.width() == 0 && targetRect.height() == 0) || thresholdRect.isEmpty) { - debugLog("dispatchOnModeChangeIfNeeded") { - "empty rects target=${targetRect.toShortString()} threshold=${thresholdRect.toShortString()}" - } - return - } - - updateClippingRect() - - if (checkRectChange) { - if (!lastRect.isEmpty && lastRect == targetRect) { - debugLog("dispatchOnModeChangeIfNeeded") { "no rect change" } - return - } - lastRect.set(targetRect) - } - - val newMode: VirtualViewMode - if (rectsOverlap(targetRect, thresholdRect)) { - if (onWindowFocusChangeListener != null) { - if (hasWindowFocus()) { - newMode = VirtualViewMode.Visible - } else { - newMode = VirtualViewMode.Prerender - } - } else { - newMode = VirtualViewMode.Visible - } - } else { - var prerender = false - if (prerenderRatio > 0.0) { - thresholdRect.inset( - (-visibleWidth * prerenderRatio).toInt(), - (-visibleHeight * prerenderRatio).toInt(), - ) - prerender = rectsOverlap(targetRect, thresholdRect) - } - if (prerender) { - newMode = VirtualViewMode.Prerender - } else { - val _mode = mode // local variable so Kotlin knows its not nullable - if (_mode != null && hysteresisRatio > 0.0) { - thresholdRect.inset( - (-visibleWidth * hysteresisRatio).toInt(), - (-visibleHeight * hysteresisRatio).toInt(), - ) - if (rectsOverlap(targetRect, thresholdRect)) { - // In hysteresis window, no change to mode - newMode = _mode - debugLog("dispatchOnModeChangeIfNeeded") { "hysteresis, mode=$newMode" } - } else { - newMode = VirtualViewMode.Hidden - thresholdRect.setEmpty() - } - } else { - newMode = VirtualViewMode.Hidden - thresholdRect.setEmpty() - } - } - } - debugLog("dispatchOnModeChangeIfNeeded") { - "mode=$mode target=${targetRect.toShortString()} threshold=${thresholdRect.toShortString()}" - } - - if (newMode == mode) { - return - } - val oldMode = mode - mode = newMode - maybeEmitModeChanges(oldMode, newMode) - } - - /** - * Checks whether one Rect overlaps with another Rect. - * - * This is different from [Rect.intersects] because a Rect representing a line or a point is - * considered to overlap with another Rect if the line or point is within the rect bounds. - * However, two Rects are not considered to overlap if they only share a boundary. - */ - private fun rectsOverlap(rect1: Rect, rect2: Rect): Boolean { - if (rect1.top >= rect2.bottom || rect2.top >= rect1.bottom) { - // No overlap on the y-axis. - return false - } - if (rect1.left >= rect2.right || rect2.left >= rect1.right) { - // No overlap on the x-axis. - return false - } - return true - } - - /** - * Evaluate the mode change and emit 0, 1, or 2 mode change events depending on the type of - * transition, [noActivity], and [asyncPrerender] - */ - private fun maybeEmitModeChanges( - oldMode: VirtualViewMode?, - newMode: VirtualViewMode, - ) { - debugLog("Mode change") { "$oldMode->$newMode" } - Systrace.beginSection( - Systrace.TRACE_TAG_REACT, - "VirtualView::mode change $oldMode -> $newMode, nativeID=$nativeId", - ) - when (newMode) { - VirtualViewMode.Visible -> { - if (renderState == VirtualViewRenderState.Unknown) { - // Feature flag is disabled, so use the former logic. - emitSyncModeChange(VirtualViewMode.Visible) - } else { - // If the previous mode was prerender and the result of dispatching that event was - // committed, we do not need to dispatch an event for visible. - val wasPrerenderCommitted = - oldMode == VirtualViewMode.Prerender && renderState == VirtualViewRenderState.Rendered - if (!wasPrerenderCommitted) { - emitSyncModeChange(VirtualViewMode.Visible) - } - } - } - VirtualViewMode.Prerender -> { - if (oldMode != VirtualViewMode.Visible) { - emitAsyncModeChange(VirtualViewMode.Prerender) - } - } - VirtualViewMode.Hidden -> { - emitAsyncModeChange(VirtualViewMode.Hidden) - } - } - Systrace.endSection(Systrace.TRACE_TAG_REACT) - } - - private fun emitAsyncModeChange(mode: VirtualViewMode) { - modeChangeEmitter?.emitModeChange(mode, targetRect, thresholdRect, synchronous = false) - } - - private fun emitSyncModeChange(mode: VirtualViewMode) { - modeChangeEmitter?.emitModeChange(mode, targetRect, thresholdRect, synchronous = true) - } - - private fun getParentScrollView(): ViewGroup? = traverseParentStack(true) - - private fun cleanupLayoutListeners() { - traverseParentStack(false) - } - - /** - * Navigate up through the view hierarchy until we reach the scroll view or root view, and - * maintain layout change listeners on any intermediate views. - * - * @param addListeners Whether to call [View.addOnLayoutChangeListener] to views in the hierarchy. - * If false, existing listeners will be removed. - */ - private fun traverseParentStack(addListeners: Boolean): ViewGroup? { - var parent: ViewParent? = parent - while (parent != null) { - if (parent is ReactScrollView) { - return parent - } - if (parent is ReactHorizontalScrollView) { - return parent - } - if (parent is ReactRoot) { - // don't look past the root - it could traverse into a separate hierarchy - return null - } - if (parent is View) { - // always remove, to ensure listeners aren't added more than once - parent.removeOnLayoutChangeListener(this) - if (addListeners) { - parent.addOnLayoutChangeListener(this) - } - } - parent = parent.parent - } - return null - } - - /** Navigate up the view hierarchy to record parents' offsets within the scroll view */ - private fun updateParentOffset() { - val scrollView = parentScrollView ?: return - offsetX = 0 - offsetY = 0 - offsetChanged = false - var parent: ViewParent? = parent - while (parent != null && parent != scrollView) { - if (parent is View) { - offsetX += parent.left - offsetY += parent.top - } - parent = parent.parent - } - } - - internal inline fun debugLog(subtag: String, block: () -> String = { "" }) { - if (debugLogEnabled) { - if (IS_DEBUG_BUILD) { - FLog.d("$DEBUG_TAG:$subtag", "${block()} [$id][$nativeId]") - } else { - // production builds only log warnings/errors - FLog.w("$DEBUG_TAG:$subtag", "${block()} [$id][$nativeId]") - } - } - } -} - -private const val DEBUG_TAG: String = "ReactVirtualView" - -private val IS_DEBUG_BUILD = - ReactBuildConfig.DEBUG || ReactBuildConfig.IS_INTERNAL_BUILD || ReactBuildConfig.ENABLE_PERFETTO diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/virtual/view/ReactVirtualViewManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/virtual/view/ReactVirtualViewManager.kt deleted file mode 100644 index da9e8aa4223dc0..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/virtual/view/ReactVirtualViewManager.kt +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.virtual.view - -import android.graphics.Rect -import androidx.annotation.VisibleForTesting -import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags -import com.facebook.react.module.annotations.ReactModule -import com.facebook.react.uimanager.ThemedReactContext -import com.facebook.react.uimanager.UIManagerHelper -import com.facebook.react.uimanager.ViewManagerDelegate -import com.facebook.react.uimanager.annotations.ReactProp -import com.facebook.react.uimanager.events.EventDispatcher -import com.facebook.react.viewmanagers.VirtualViewManagerDelegate -import com.facebook.react.viewmanagers.VirtualViewManagerInterface -import com.facebook.react.views.view.ReactClippingViewManager -import com.facebook.react.views.virtual.VirtualViewMode -import com.facebook.react.views.virtual.VirtualViewModeChangeEmitter -import com.facebook.react.views.virtual.VirtualViewModeChangeEvent -import com.facebook.react.views.virtual.VirtualViewRenderState - -@ReactModule(name = ReactVirtualViewManager.REACT_CLASS) -public class ReactVirtualViewManager : - ReactClippingViewManager(), VirtualViewManagerInterface { - - private val _delegate = VirtualViewManagerDelegate(this) - - override fun getDelegate(): ViewManagerDelegate = _delegate - - override fun getName(): String = REACT_CLASS - - override fun createViewInstance(reactContext: ThemedReactContext): ReactVirtualView = - ReactVirtualView(reactContext) - - @ReactProp(name = "initialHidden") - override fun setInitialHidden(view: ReactVirtualView, value: Boolean) { - if (view.mode == null) { - view.mode = if (value) VirtualViewMode.Hidden else VirtualViewMode.Visible - } - } - - @ReactProp(name = "renderState") - override fun setRenderState(view: ReactVirtualView, value: Int) { - // If disabled, `renderState` will always be `VirtualViewRenderState.Unknown`. - if (ReactNativeFeatureFlags.enableVirtualViewRenderState()) { - view.renderState = - when (value) { - 1 -> VirtualViewRenderState.Rendered - 2 -> VirtualViewRenderState.None - else -> VirtualViewRenderState.Unknown - } - } - } - - override fun setNativeId(view: ReactVirtualView, nativeId: String?) { - super.setNativeId(view, nativeId) - view.debugLog("setNativeId") { "${view.id}" } - } - - override fun addEventEmitters(reactContext: ThemedReactContext, view: ReactVirtualView) { - val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.id) ?: return - view.modeChangeEmitter = - VirtualViewEventEmitter(view.id, UIManagerHelper.getSurfaceId(reactContext), dispatcher) - } - - override fun prepareToRecycleView( - reactContext: ThemedReactContext, - view: ReactVirtualView, - ): ReactVirtualView? { - view.recycleView() - return super.prepareToRecycleView(reactContext, view) - } - - public companion object { - public const val REACT_CLASS: String = "VirtualView" - } -} - -@VisibleForTesting -public class VirtualViewEventEmitter( - private val viewId: Int, - private val surfaceId: Int, - private val dispatcher: EventDispatcher, -) : VirtualViewModeChangeEmitter { - override fun emitModeChange( - mode: VirtualViewMode, - targetRect: Rect, - thresholdRect: Rect, - synchronous: Boolean, - ) { - dispatcher.dispatchEvent( - VirtualViewModeChangeEvent( - surfaceId, - viewId, - mode, - targetRect, - thresholdRect, - synchronous, - ) - ) - } -} diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/CoreComponentsRegistry.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/CoreComponentsRegistry.cpp index 6df5502d223efc..9f9624b8848b9e 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/CoreComponentsRegistry.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/CoreComponentsRegistry.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include namespace facebook::react::CoreComponentsRegistry { @@ -81,8 +80,6 @@ void addCoreComponents( providerRegistry->add( concreteComponentDescriptorProvider< LayoutConformanceComponentDescriptor>()); - providerRegistry->add( - concreteComponentDescriptorProvider()); providerRegistry->add( concreteComponentDescriptorProvider< VirtualViewExperimentalComponentDescriptor>()); diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/virtual/view/ReactVirtualViewTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/virtual/view/ReactVirtualViewTest.kt deleted file mode 100644 index caf669b2a76a83..00000000000000 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/virtual/view/ReactVirtualViewTest.kt +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.virtual.view - -import android.app.Activity -import android.content.Context -import android.graphics.Rect -import android.util.DisplayMetrics -import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsForTests -import com.facebook.react.uimanager.DisplayMetricsHolder -import com.facebook.react.uimanager.events.Event -import com.facebook.react.uimanager.events.EventDispatcher -import com.facebook.react.views.scroll.ReactScrollView -import com.facebook.react.views.virtual.VirtualViewMode -import com.facebook.react.views.virtual.VirtualViewModeChangeEvent -import com.facebook.testutils.shadows.ShadowArguments -import org.assertj.core.api.Assertions.assertThat -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito.mockStatic -import org.mockito.kotlin.argumentCaptor -import org.mockito.kotlin.mock -import org.mockito.kotlin.times -import org.mockito.kotlin.verify -import org.robolectric.Robolectric -import org.robolectric.RobolectricTestRunner -import org.robolectric.annotation.Config - -/** Tests [ReactVirtualView] */ -@Config(shadows = [ShadowArguments::class]) -@RunWith(RobolectricTestRunner::class) -class ReactVirtualViewTest { - - private lateinit var context: Context - - @Before - fun setUp() { - ReactNativeFeatureFlagsForTests.setUp() - - context = Robolectric.buildActivity(Activity::class.java).create().get() - - val displayMetricsHolder = mockStatic(DisplayMetricsHolder::class.java) - displayMetricsHolder - .`when` { DisplayMetricsHolder.getScreenDisplayMetrics() } - .thenAnswer { DisplayMetrics().apply { density = 1f } } - } - - @Test - fun `ensure mode change from visible to hidden`() { - val dispatcher = mock() - val view = ReactVirtualView(context) - view.layout(0, 0, 100, 50) - view.modeChangeEmitter = VirtualViewEventEmitter(1, 1, dispatcher) - val newScrollView = TestScrollView(context) - newScrollView.addView(view) - - // initial layout with visible view - newScrollView.drawingRect.set(0, 0, 100, 200) - // trigger first event - view.doAttachedToWindow() - - // "scroll" to make view "hidden" - // Set offset >= prerenderRatio (default 5) * newScrollView.height + height of view - // Note: boundaries are not considered in overlapping - newScrollView.drawingRect.offsetTo(0, 1050) - // trigger second event - view.onScroll(newScrollView, null, 0f, 0f) - - // change view rect to verify this does not affect previous events - view.layout(0, 20, 100, 40) - - argumentCaptor>().apply { - verify(dispatcher, times(2)).dispatchEvent(capture()) - verifyEvent( - allValues[0], - expectedMode = VirtualViewMode.Visible, - expectedTargetY = 0, - expectedTargetHeight = 50, - expectedThresholdY = 0, - expectedThresholdHeight = 200, - ) - verifyEvent( - allValues[1], - expectedMode = VirtualViewMode.Hidden, - expectedTargetY = 0, - expectedTargetHeight = 50, - expectedThresholdY = 0, - expectedThresholdHeight = 0, - ) - } - } - - private fun verifyEvent( - event: Event<*>, - expectedMode: VirtualViewMode, - expectedTargetY: Int, - expectedTargetHeight: Int, - expectedThresholdY: Int, - expectedThresholdHeight: Int, - ) { - val modeChangeEvent = event as VirtualViewModeChangeEvent - val mode = checkNotNull(modeChangeEvent.getEventData().getInt("mode")) - assertThat(mode).isEqualTo(expectedMode.value) - val targetRect = checkNotNull(modeChangeEvent.getEventData().getMap("targetRect")) - assertThat(targetRect.getDouble("y").toInt()).isEqualTo(expectedTargetY) - assertThat(targetRect.getDouble("height").toInt()).isEqualTo(expectedTargetHeight) - val thresholdRect = checkNotNull(modeChangeEvent.getEventData().getMap("thresholdRect")) - assertThat(thresholdRect.getDouble("y").toInt()).isEqualTo(expectedThresholdY) - assertThat(thresholdRect.getDouble("height").toInt()).isEqualTo(expectedThresholdHeight) - } -} - -private class TestScrollView(context: Context) : ReactScrollView(context) { - val drawingRect: Rect = Rect() - - /** Used by [ReactVirtualView.updateRects] to determine the parent rect. */ - override fun getDrawingRect(outRect: Rect?) { - outRect?.set(drawingRect) - } -} diff --git a/packages/react-native/ReactCommon/react/renderer/components/virtualview/VirtualViewComponentDescriptor.h b/packages/react-native/ReactCommon/react/renderer/components/virtualview/VirtualViewComponentDescriptor.h deleted file mode 100644 index a8b739d0911e79..00000000000000 --- a/packages/react-native/ReactCommon/react/renderer/components/virtualview/VirtualViewComponentDescriptor.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include - -namespace facebook::react { - -/* - * Descriptor for component. - */ -using VirtualViewComponentDescriptor = ConcreteComponentDescriptor; -} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/components/virtualview/VirtualViewShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/virtualview/VirtualViewShadowNode.h deleted file mode 100644 index 7b1d1fc6cc0dc8..00000000000000 --- a/packages/react-native/ReactCommon/react/renderer/components/virtualview/VirtualViewShadowNode.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include - -namespace facebook::react { - -constexpr const char VirtualViewComponentName[] = "VirtualView"; - -/* - * `ShadowNode` for component. - */ -class VirtualViewShadowNode final - : public ConcreteViewShadowNode { - public: - using ConcreteViewShadowNode::ConcreteViewShadowNode; - - static ShadowNodeTraits BaseTraits() - { - auto traits = ConcreteViewShadowNode::BaseTraits(); - // has a side effect: it listens to scroll events. - // It must not be culled, otherwise Fling will not work. - traits.set(ShadowNodeTraits::Trait::Unstable_uncullableView); - return traits; - } -}; - -} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/components/virtualviewexperimental/VirtualViewExperimentalShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/virtualviewexperimental/VirtualViewExperimentalShadowNode.h index 38ff24c7b59f6a..04e1badac21e5d 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/virtualviewexperimental/VirtualViewExperimentalShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/virtualviewexperimental/VirtualViewExperimentalShadowNode.h @@ -21,7 +21,7 @@ constexpr const char VirtualViewExperimentalComponentName[] = "VirtualViewExperi class VirtualViewExperimentalShadowNode final : public ConcreteViewShadowNode< VirtualViewExperimentalComponentName, VirtualViewExperimentalProps, - VirtualViewEventEmitter> { + VirtualViewExperimentalEventEmitter> { public: using ConcreteViewShadowNode::ConcreteViewShadowNode; diff --git a/packages/react-native/src/private/components/virtualview/VirtualViewNativeComponent.js b/packages/react-native/src/private/components/virtualview/VirtualViewNativeComponent.js deleted file mode 100644 index 4ba5b68398edc8..00000000000000 --- a/packages/react-native/src/private/components/virtualview/VirtualViewNativeComponent.js +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict-local - * @format - */ - -import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes'; -import type { - DirectEventHandler, - Double, - Int32, -} from '../../../../Libraries/Types/CodegenTypes'; -import type {HostComponent} from '../../types/HostComponent'; - -import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent'; - -export type NativeModeChangeEvent = Readonly<{ - /** - * Virtualization mode of the target view. - * - * - `0`: Target view is visible. - * - `1`: Target view is hidden, but can be prerendered. - * - `2`: Target view is hidden. - * - * WORKAROUND: As of this writing, codegen doesn't support enums, so we need - * to convert `number` into an enum in `VirtualView`. - */ - mode: Int32, - - /** - * Rect of the target view, relative to the nearest ancestor scroll container. - */ - targetRect: Readonly<{ - x: Double, - y: Double, - width: Double, - height: Double, - }>, - - /** - * Rect of the threshold that determines the mode of the target view, relative - * to the nearest ancestor scroll container. - * - * - `Visible`: Rect in which the target view is visible. - * - `Prerender`: Rect in which the target view is prerendered. - * - `Hidden`: Unused, without any guarantees. - * - * This can be used to determine whether and how much new content to render. - */ - thresholdRect: Readonly<{ - x: Double, - y: Double, - width: Double, - height: Double, - }>, -}>; - -type VirtualViewNativeProps = Readonly<{ - ...ViewProps, - - /** - * Whether the initial mode should be `Hidden`. - */ - initialHidden?: boolean, - - /** - * This was needed to get VirtualViewManagerDelegate to set this property. - * TODO: Investigate why spread ViewProps doesn't call setter - */ - removeClippedSubviews?: boolean, - - /** - * Render state of children. - * - * - `0`: Reserved to represent unknown future values. - * - `1`: Children are rendered. - * - `2`: Children are not rendered. - * - * WORKAROUND: As of this writing, codegen doesn't support enums, so we need - * to convert `number` into an enum in `VirtualView`. - */ - renderState: Int32, - - /** - * See `NativeModeChangeEvent`. - */ - onModeChange?: ?DirectEventHandler, -}>; - -export default codegenNativeComponent('VirtualView', { - interfaceOnly: true, -}) as HostComponent;