()V
@@ -6125,11 +6125,14 @@ public final class com/facebook/react/views/text/ReactTextViewManager : com/face
public fun createViewInstance (Lcom/facebook/react/uimanager/ThemedReactContext;)Lcom/facebook/react/views/text/ReactTextView;
public fun getExportedCustomDirectEventTypeConstants ()Ljava/util/Map;
public fun getName ()Ljava/lang/String;
+ protected final fun getReactTextViewManagerCallback ()Lcom/facebook/react/views/text/ReactTextViewManagerCallback;
public fun getShadowNodeClass ()Ljava/lang/Class;
public fun needsCustomLayoutForChildren ()Z
public synthetic fun onAfterUpdateTransaction (Landroid/view/View;)V
+ protected fun onAfterUpdateTransaction (Lcom/facebook/react/views/text/ReactTextView;)V
public fun onPostProcessSpannable (Landroid/text/Spannable;)V
public synthetic fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Landroid/view/View;)Landroid/view/View;
+ protected fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Lcom/facebook/react/views/text/ReactTextView;)Lcom/facebook/react/views/text/ReactTextView;
public final fun setAccessible (Lcom/facebook/react/views/text/ReactTextView;Z)V
public final fun setAdjustFontSizeToFit (Lcom/facebook/react/views/text/ReactTextView;Z)V
public final fun setAndroidHyphenationFrequency (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V
@@ -6147,6 +6150,7 @@ public final class com/facebook/react/views/text/ReactTextViewManager : com/face
public final fun setOverflow (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V
public synthetic fun setPadding (Landroid/view/View;IIII)V
public fun setPadding (Lcom/facebook/react/views/text/ReactTextView;IIII)V
+ protected final fun setReactTextViewManagerCallback (Lcom/facebook/react/views/text/ReactTextViewManagerCallback;)V
public final fun setSelectable (Lcom/facebook/react/views/text/ReactTextView;Z)V
public final fun setSelectionColor (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/Integer;)V
public final fun setTextAlignVertical (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V
@@ -6155,6 +6159,7 @@ public final class com/facebook/react/views/text/ReactTextViewManager : com/face
public synthetic fun updateState (Landroid/view/View;Lcom/facebook/react/uimanager/ReactStylesDiffMap;Lcom/facebook/react/uimanager/StateWrapper;)Ljava/lang/Object;
public fun updateState (Lcom/facebook/react/views/text/ReactTextView;Lcom/facebook/react/uimanager/ReactStylesDiffMap;Lcom/facebook/react/uimanager/StateWrapper;)Ljava/lang/Object;
public synthetic fun updateViewAccessibility (Landroid/view/View;)V
+ protected fun updateViewAccessibility (Lcom/facebook/react/views/text/ReactTextView;)V
}
public final class com/facebook/react/views/text/ReactTextViewManager$Companion {
@@ -6172,6 +6177,9 @@ public final class com/facebook/react/views/text/ReactTypefaceUtils {
public static final fun parseFontWeight (Ljava/lang/String;)I
}
+public final class com/facebook/react/views/text/SelectableTextViewManager$Companion {
+}
+
public final class com/facebook/react/views/text/TextAttributeProps {
public static final field Companion Lcom/facebook/react/views/text/TextAttributeProps$Companion;
public static final field TA_KEY_ACCESSIBILITY_ROLE I
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/FabricNameComponentMapping.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/FabricNameComponentMapping.kt
index c032448a7a33..31d93c32b695 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/FabricNameComponentMapping.kt
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/FabricNameComponentMapping.kt
@@ -18,6 +18,7 @@ internal object FabricNameComponentMapping {
"Slider" to "RCTSlider",
"ModalHostView" to "RCTModalHostView",
"Paragraph" to "RCTText",
+ "SelectableParagraph" to "RCTSelectableText",
"Text" to "RCTText",
"RawText" to "RCTRawText",
"ActivityIndicatorView" to "AndroidProgressBar",
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.kt
index 9927bf571536..b3baad46a9aa 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.kt
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.kt
@@ -15,6 +15,7 @@ import com.facebook.react.bridge.ModuleSpec
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.common.ClassFinder
+import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.module.annotations.ReactModuleList
@@ -58,6 +59,7 @@ import com.facebook.react.views.swiperefresh.SwipeRefreshLayoutManager
import com.facebook.react.views.switchview.ReactSwitchManager
import com.facebook.react.views.text.PreparedLayoutTextViewManager
import com.facebook.react.views.text.ReactTextViewManager
+import com.facebook.react.views.text.SelectableTextViewManager
import com.facebook.react.views.textinput.ReactTextInputManager
import com.facebook.react.views.unimplementedview.ReactUnimplementedViewManager
import com.facebook.react.views.view.ReactViewManager
@@ -96,6 +98,7 @@ import com.facebook.react.views.view.ReactViewManager
WebSocketModule::class,
]
)
+@OptIn(UnstableReactNativeAPI::class)
public class MainReactPackage
@JvmOverloads
constructor(private val config: MainPackageConfig? = null) :
@@ -150,6 +153,7 @@ constructor(private val config: MainPackageConfig? = null) :
ReactTextInputManager(),
if (ReactNativeFeatureFlags.enablePreparedTextLayout()) PreparedLayoutTextViewManager()
else ReactTextViewManager(),
+ SelectableTextViewManager(),
ReactViewManager(),
ReactUnimplementedViewManager(),
)
@@ -192,6 +196,8 @@ constructor(private val config: MainPackageConfig? = null) :
PreparedLayoutTextViewManager()
else ReactTextViewManager()
},
+ SelectableTextViewManager.REACT_CLASS to
+ ModuleSpec.viewManagerSpec { SelectableTextViewManager() },
ReactViewManager.REACT_CLASS to ModuleSpec.viewManagerSpec { ReactViewManager() },
ReactUnimplementedViewManager.REACT_CLASS to
ModuleSpec.viewManagerSpec { ReactUnimplementedViewManager() },
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/PreparedLayoutTextViewManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/PreparedLayoutTextViewManager.kt
index ede2a678a1fe..66a2bf753135 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/PreparedLayoutTextViewManager.kt
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/PreparedLayoutTextViewManager.kt
@@ -118,8 +118,9 @@ internal class PreparedLayoutTextViewManager :
@ReactProp(name = "selectable", defaultBoolean = false)
fun setSelectable(view: PreparedLayoutTextView, isSelectable: Boolean): Unit {
- // T222052152: Implement fine-grained text selection for PreparedLayoutTextView
- // view.setTextIsSelectable(isSelectable);
+ check(!isSelectable) {
+ "selectable Text should use SelectableTextViewManager instead of PreparedLayoutViewManager"
+ }
}
@ReactProp(name = "selectionColor", customType = "Color")
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java
index 5e3cf945fdac..52f8fd58e361 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java
@@ -37,6 +37,7 @@
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.ReactConstants;
+import com.facebook.react.common.annotations.UnstableReactNativeAPI;
import com.facebook.react.common.build.ReactBuildConfig;
import com.facebook.react.internal.SystraceSection;
import com.facebook.react.uimanager.BackgroundStyleApplicator;
@@ -52,6 +53,7 @@
import com.facebook.react.uimanager.style.BorderStyle;
import com.facebook.react.uimanager.style.LogicalEdge;
import com.facebook.react.uimanager.style.Overflow;
+import com.facebook.react.views.text.internal.span.ReactFragmentIndexSpan;
import com.facebook.react.views.text.internal.span.ReactTagSpan;
import com.facebook.react.views.text.internal.span.TextInlineViewPlaceholderSpan;
import com.facebook.yoga.YogaMeasureMode;
@@ -77,6 +79,7 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
private Overflow mOverflow = Overflow.VISIBLE;
private @Nullable Spannable mSpanned;
+ private @Nullable PreparedLayout mPreparedLayout;
public ReactTextView(Context context) {
super(context);
@@ -100,6 +103,7 @@ private void initView() {
mLetterSpacing = 0.f;
mOverflow = Overflow.VISIBLE;
mSpanned = null;
+ mPreparedLayout = null;
}
/* package */ void recycleView() {
@@ -444,16 +448,33 @@ public int reactTagForTouch(float touchX, float touchY) {
// if no such span can be found we will send the textview's react id as a touch handler
// In case when there are more than one spans with same length we choose the last one
// from the spans[] array, since it correspond to the most inner react element
- ReactTagSpan[] spans = spannedText.getSpans(index, index, ReactTagSpan.class);
-
- if (spans != null) {
- int targetSpanTextLength = text.length();
- for (int i = 0; i < spans.length; i++) {
- int spanStart = spannedText.getSpanStart(spans[i]);
- int spanEnd = spannedText.getSpanEnd(spans[i]);
- if (spanEnd >= index && (spanEnd - spanStart) <= targetSpanTextLength) {
- target = spans[i].getReactTag();
- targetSpanTextLength = (spanEnd - spanStart);
+ if (mPreparedLayout != null) {
+ ReactFragmentIndexSpan[] fragmentSpans =
+ spannedText.getSpans(index, index, ReactFragmentIndexSpan.class);
+
+ if (fragmentSpans != null) {
+ int targetSpanTextLength = text.length();
+ for (int i = 0; i < fragmentSpans.length; i++) {
+ int spanStart = spannedText.getSpanStart(fragmentSpans[i]);
+ int spanEnd = spannedText.getSpanEnd(fragmentSpans[i]);
+ if (spanEnd >= index && (spanEnd - spanStart) <= targetSpanTextLength) {
+ target = mPreparedLayout.getReactTags()[fragmentSpans[i].getFragmentIndex()];
+ targetSpanTextLength = (spanEnd - spanStart);
+ }
+ }
+ }
+ } else {
+ ReactTagSpan[] spans = spannedText.getSpans(index, index, ReactTagSpan.class);
+
+ if (spans != null) {
+ int targetSpanTextLength = text.length();
+ for (int i = 0; i < spans.length; i++) {
+ int spanStart = spannedText.getSpanStart(spans[i]);
+ int spanEnd = spannedText.getSpanEnd(spans[i]);
+ if (spanEnd >= index && (spanEnd - spanStart) <= targetSpanTextLength) {
+ target = spans[i].getReactTag();
+ targetSpanTextLength = (spanEnd - spanStart);
+ }
}
}
}
@@ -623,6 +644,22 @@ public void setSpanned(Spannable spanned) {
return mSpanned;
}
+ /**
+ * Get the PreparedLayout originally generated by the Fabric renderer, if using {@code
+ * enablePreparedTextLayout()}
+ *
+ * TODO: Should be made internal when ReactTextView is converted to Kotlin
+ */
+ @UnstableReactNativeAPI
+ @Nullable
+ public PreparedLayout getPreparedLayout() {
+ return mPreparedLayout;
+ }
+
+ /* package */ void setPreparedLayout(@Nullable PreparedLayout preparedLayout) {
+ mPreparedLayout = preparedLayout;
+ }
+
public void setLinkifyMask(int mask) {
mLinkifyMaskType = mask;
}
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewAccessibilityDelegate.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewAccessibilityDelegate.kt
index 7d19d6f70172..6834a329ac6c 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewAccessibilityDelegate.kt
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewAccessibilityDelegate.kt
@@ -18,9 +18,11 @@ import androidx.core.view.ViewCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
import androidx.core.view.accessibility.AccessibilityNodeProviderCompat
import com.facebook.react.R
+import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.uimanager.ReactAccessibilityDelegate
import com.facebook.react.views.text.internal.span.ReactClickableSpan
+@OptIn(UnstableReactNativeAPI::class)
internal class ReactTextViewAccessibilityDelegate(
view: View,
originalFocus: Boolean,
@@ -154,6 +156,8 @@ internal class ReactTextViewAccessibilityDelegate(
private fun getLayoutFromHost(): Layout? {
return if (hostView is PreparedLayoutTextView) {
(hostView as PreparedLayoutTextView).preparedLayout?.layout
+ } else if (hostView is ReactTextView && (hostView as ReactTextView).preparedLayout != null) {
+ (hostView as ReactTextView).preparedLayout?.layout
} else if (hostView is TextView) {
(hostView as TextView).layout
} else {
@@ -171,6 +175,8 @@ internal class ReactTextViewAccessibilityDelegate(
val host = hostView
return if (host is PreparedLayoutTextView) {
host.preparedLayout?.layout?.text as? Spanned
+ } else if (host is ReactTextView && host.preparedLayout != null) {
+ host.preparedLayout?.layout?.text as? Spanned
} else if (host is TextView) {
host.text as? Spanned
} else {
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.kt
index 2e3669c113d7..1216d0864f7a 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.kt
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.kt
@@ -12,6 +12,7 @@ package com.facebook.react.views.text
import android.os.Build
import android.text.Layout
import android.text.Spannable
+import android.text.SpannableString
import android.text.Spanned
import android.text.TextUtils
import android.text.util.Linkify
@@ -31,6 +32,7 @@ import com.facebook.react.uimanager.LayoutShadowNode
import com.facebook.react.uimanager.LengthPercentage
import com.facebook.react.uimanager.LengthPercentageType
import com.facebook.react.uimanager.ReactStylesDiffMap
+import com.facebook.react.uimanager.ReferenceStateWrapper
import com.facebook.react.uimanager.StateWrapper
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.ViewDefaults
@@ -46,7 +48,7 @@ import java.util.HashMap
/** View manager for `` nodes. */
@ReactModule(name = ReactTextViewManager.REACT_CLASS)
@OptIn(UnstableReactNativeAPI::class)
-public class ReactTextViewManager
+public open class ReactTextViewManager
@JvmOverloads
public constructor(
protected var reactTextViewManagerCallback: ReactTextViewManagerCallback? = null
@@ -131,6 +133,11 @@ public constructor(
stateWrapper: StateWrapper,
): Any? {
SystraceSection("ReactTextViewManager.updateState").use { s ->
+ val refState = (stateWrapper as? ReferenceStateWrapper)?.stateDataReference
+ if (refState is PreparedLayout) {
+ return getReactTextUpdateFromPreparedLayout(view, refState)
+ }
+
val stateMapBuffer = stateWrapper.stateDataMapBuffer
return if (stateMapBuffer != null) {
getReactTextUpdate(view, props, stateMapBuffer)
@@ -160,6 +167,9 @@ public constructor(
paragraphAttributes.getDouble(TextLayoutManager.PA_KEY_MINIMUM_FONT_SIZE).toFloat()
view.setMinimumFontSize(minimumFontSize)
+ // Clear any stale PreparedLayout from a previous update
+ view.setPreparedLayout(null)
+
val textBreakStrategy =
TextAttributeProps.getTextBreakStrategy(
paragraphAttributes.getString(TextLayoutManager.PA_KEY_TEXT_BREAK_STRATEGY)
@@ -176,6 +186,35 @@ public constructor(
)
}
+ /**
+ * Constructs a [ReactTextUpdate] from a [PreparedLayout] received via [ReferenceStateWrapper].
+ */
+ private fun getReactTextUpdateFromPreparedLayout(
+ view: ReactTextView,
+ preparedLayout: PreparedLayout,
+ ): ReactTextUpdate {
+ val layout = preparedLayout.layout
+ val text = layout.text
+ val spanned = if (text is Spannable) text else SpannableString(text)
+ view.setSpanned(spanned)
+ view.setPreparedLayout(preparedLayout)
+
+ val textAlign =
+ when (layout.alignment) {
+ Layout.Alignment.ALIGN_CENTER -> Gravity.CENTER_HORIZONTAL
+ Layout.Alignment.ALIGN_OPPOSITE -> Gravity.END
+ else -> Gravity.START
+ }
+
+ return ReactTextUpdate(
+ spanned,
+ -1,
+ textAlign,
+ Layout.BREAK_STRATEGY_HIGH_QUALITY,
+ 0,
+ )
+ }
+
override fun getExportedCustomDirectEventTypeConstants(): MutableMap? {
val baseEventTypeConstants = super.getExportedCustomDirectEventTypeConstants()
val eventTypeConstants = baseEventTypeConstants ?: HashMap()
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/SelectableTextViewManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/SelectableTextViewManager.kt
new file mode 100644
index 000000000000..b1f15c461903
--- /dev/null
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/SelectableTextViewManager.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.text
+
+import com.facebook.react.common.annotations.UnstableReactNativeAPI
+
+/**
+ * A [ReactTextViewManager] registered under the name "RCTSelectableText". Used to route selectable
+ * text through [ReactTextView] (a real [android.widget.TextView]) instead of
+ * [PreparedLayoutTextView] when enablePreparedTextLayout is on, since [PreparedLayoutTextView] does
+ * not support native text selection.
+ */
+@UnstableReactNativeAPI
+public class SelectableTextViewManager
+@JvmOverloads
+public constructor(reactTextViewManagerCallback: ReactTextViewManagerCallback? = null) :
+ ReactTextViewManager(reactTextViewManagerCallback) {
+
+ override fun getName(): String = REACT_CLASS
+
+ public companion object {
+ public const val REACT_CLASS: String = "RCTSelectableText"
+ }
+}
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/ReactLinkSpan.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/ReactLinkSpan.kt
index 82cb779ac2f9..aba28b094ac7 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/ReactLinkSpan.kt
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/ReactLinkSpan.kt
@@ -11,8 +11,10 @@ import android.text.TextPaint
import android.text.style.ClickableSpan
import android.view.View
import com.facebook.react.bridge.ReactContext
+import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.views.text.PreparedLayoutTextView
+import com.facebook.react.views.text.ReactTextView
import com.facebook.react.views.text.TextLayoutManager
import com.facebook.react.views.view.ViewGroupClickEvent
@@ -29,11 +31,16 @@ import com.facebook.react.views.view.ViewGroupClickEvent
*
* ```
*/
+@OptIn(UnstableReactNativeAPI::class)
internal class ReactLinkSpan(val fragmentIndex: Int) : ClickableSpan(), ReactSpan {
override fun onClick(view: View) {
val context = view.context as ReactContext
- val textView = view as? PreparedLayoutTextView ?: return
- val preparedLayout = textView.preparedLayout ?: return
+ val preparedLayout =
+ when (view) {
+ is PreparedLayoutTextView -> view.preparedLayout
+ is ReactTextView -> view.preparedLayout
+ else -> null
+ } ?: return
val reactTag = preparedLayout.reactTags[fragmentIndex]
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, reactTag)
eventDispatcher?.dispatchEvent(
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 03d455ee92f4..3b15aa501986 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
@@ -21,6 +21,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -71,6 +72,9 @@ void addCoreComponents(
AndroidHorizontalScrollContentViewComponentDescriptor>());
providerRegistry->add(
concreteComponentDescriptorProvider());
+ providerRegistry->add(
+ concreteComponentDescriptorProvider<
+ SelectableParagraphComponentDescriptor>());
providerRegistry->add(
concreteComponentDescriptorProvider<
AndroidDrawerLayoutComponentDescriptor>());
diff --git a/packages/react-native/ReactCommon/react/renderer/componentregistry/componentNameByReactViewName.cpp b/packages/react-native/ReactCommon/react/renderer/componentregistry/componentNameByReactViewName.cpp
index 25e3b2cb1072..fe63407de827 100644
--- a/packages/react-native/ReactCommon/react/renderer/componentregistry/componentNameByReactViewName.cpp
+++ b/packages/react-native/ReactCommon/react/renderer/componentregistry/componentNameByReactViewName.cpp
@@ -27,6 +27,9 @@ std::string componentNameByReactViewName(std::string viewName) {
if (viewName == "Text") {
return "Paragraph";
}
+ if (viewName == "SelectableText") {
+ return "SelectableParagraph";
+ }
if (viewName == "VirtualText") {
return "Text";
diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/BaseParagraphComponentDescriptor.h b/packages/react-native/ReactCommon/react/renderer/components/text/BaseParagraphComponentDescriptor.h
new file mode 100644
index 000000000000..8c7c8aab798f
--- /dev/null
+++ b/packages/react-native/ReactCommon/react/renderer/components/text/BaseParagraphComponentDescriptor.h
@@ -0,0 +1,49 @@
+/*
+ * 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 *const TextLayoutManagerKey = "TextLayoutManager";
+
+template
+class BaseParagraphComponentDescriptor : public ConcreteComponentDescriptor {
+ public:
+ explicit BaseParagraphComponentDescriptor(const ComponentDescriptorParameters ¶meters)
+ : ConcreteComponentDescriptor(parameters),
+ textLayoutManager_(getManagerByName(this->contextContainer_, TextLayoutManagerKey))
+ {
+ }
+
+ ComponentName getComponentName() const override
+ {
+ return ShadowNodeT::Name();
+ }
+
+ protected:
+ void adopt(ShadowNode &shadowNode) const override
+ {
+ ConcreteComponentDescriptor::adopt(shadowNode);
+
+ auto ¶graphShadowNode = static_cast(shadowNode);
+
+ // `ParagraphShadowNode` uses `TextLayoutManager` to measure text content
+ // and communicate text rendering metrics to mounting layer.
+ paragraphShadowNode.setTextLayoutManager(textLayoutManager_);
+ }
+
+ private:
+ // Every `ParagraphShadowNode` has a reference to a shared `TextLayoutManager`
+ const std::shared_ptr textLayoutManager_;
+};
+
+} // namespace facebook::react
diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphComponentDescriptor.cpp b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphComponentDescriptor.cpp
deleted file mode 100644
index ee4e014b6564..000000000000
--- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphComponentDescriptor.cpp
+++ /dev/null
@@ -1,14 +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.
- */
-
-#include "ParagraphComponentDescriptor.h"
-
-namespace facebook::react {
-
-extern const char TextLayoutManagerKey[] = "TextLayoutManager";
-
-} // namespace facebook::react
diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphComponentDescriptor.h b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphComponentDescriptor.h
index 3ed1a2a7aefc..3f64c6e8cb7a 100644
--- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphComponentDescriptor.h
+++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphComponentDescriptor.h
@@ -7,41 +7,16 @@
#pragma once
+#include
#include
-#include
-#include
-#include
namespace facebook::react {
-
-extern const char TextLayoutManagerKey[];
-
/*
* Descriptor for component.
*/
-class ParagraphComponentDescriptor final : public ConcreteComponentDescriptor {
+class ParagraphComponentDescriptor final : public BaseParagraphComponentDescriptor {
public:
- explicit ParagraphComponentDescriptor(const ComponentDescriptorParameters ¶meters)
- : ConcreteComponentDescriptor(parameters),
- textLayoutManager_(getManagerByName(contextContainer_, TextLayoutManagerKey))
- {
- }
-
- protected:
- void adopt(ShadowNode &shadowNode) const override
- {
- ConcreteComponentDescriptor::adopt(shadowNode);
-
- auto ¶graphShadowNode = static_cast(shadowNode);
-
- // `ParagraphShadowNode` uses `TextLayoutManager` to measure text content
- // and communicate text rendering metrics to mounting layer.
- paragraphShadowNode.setTextLayoutManager(textLayoutManager_);
- }
-
- private:
- // Every `ParagraphShadowNode` has a reference to a shared `TextLayoutManager`
- const std::shared_ptr textLayoutManager_;
+ using BaseParagraphComponentDescriptor::BaseParagraphComponentDescriptor;
};
} // namespace facebook::react
diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h
index 91709d035d05..c518ccf4b90b 100644
--- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h
+++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h
@@ -26,7 +26,7 @@ extern const char ParagraphComponentName[];
* containing and displaying text. Text content is represented as nested
* and components.
*/
-class ParagraphShadowNode final
+class ParagraphShadowNode
: public ConcreteViewShadowNode,
public BaseTextShadowNode {
public:
diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/SelectableParagraphComponentDescriptor.h b/packages/react-native/ReactCommon/react/renderer/components/text/SelectableParagraphComponentDescriptor.h
new file mode 100644
index 000000000000..b008580f5885
--- /dev/null
+++ b/packages/react-native/ReactCommon/react/renderer/components/text/SelectableParagraphComponentDescriptor.h
@@ -0,0 +1,24 @@
+/*
+ * 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, which may render to a
+ * different native view than .
+ */
+class SelectableParagraphComponentDescriptor final
+ : public BaseParagraphComponentDescriptor {
+ public:
+ using BaseParagraphComponentDescriptor::BaseParagraphComponentDescriptor;
+};
+
+} // namespace facebook::react
diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/SelectableParagraphShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/text/SelectableParagraphShadowNode.h
new file mode 100644
index 000000000000..8574dd601abd
--- /dev/null
+++ b/packages/react-native/ReactCommon/react/renderer/components/text/SelectableParagraphShadowNode.h
@@ -0,0 +1,32 @@
+/*
+ * 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
+
+namespace facebook::react {
+
+/*
+ * ShadowNode for selectable Paragraph components, which may map to different native component than Paragraph.
+ */
+class SelectableParagraphShadowNode : public ParagraphShadowNode {
+ public:
+ using ParagraphShadowNode::ParagraphShadowNode;
+
+ static constexpr ComponentName Name()
+ {
+ return "SelectableParagraph";
+ }
+
+ static ComponentHandle Handle()
+ {
+ return ComponentHandle(Name());
+ }
+};
+
+} // namespace facebook::react
diff --git a/packages/react-native/ReactNativeApi.d.ts b/packages/react-native/ReactNativeApi.d.ts
index 9229bb4336ff..3cb8fa700fc8 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<<6463b0a4f3acbeb07e6759c345b927d8>>
+ * @generated SignedSource<>
*
* This file was generated by scripts/js-api/build-types/index.js.
*/
@@ -322,6 +322,7 @@ declare const NativeModules: typeof NativeModules_default
declare let NativeModules_default: {
[moduleName: string]: any
}
+declare const NativeSelectableText: HostComponent
declare const NativeText: HostComponent
declare const NativeTouchable:
| typeof TouchableNativeFeedback
@@ -3368,6 +3369,7 @@ declare type NativeScrollVelocity = {
readonly x: number
readonly y: number
}
+declare type NativeSelectableText = typeof NativeSelectableText
declare type NativeSwitchChangeEvent = {
readonly target: Int32
readonly value: boolean
@@ -5160,7 +5162,7 @@ declare type TextContentType =
| "URL"
| "username"
declare type TextForwardRef = React.ComponentRef<
- typeof NativeText | typeof NativeVirtualText
+ typeof NativeSelectableText | typeof NativeText | typeof NativeVirtualText
>
declare type TextInput = typeof TextInput
declare type TextInputAndroidProps = {
@@ -5995,7 +5997,7 @@ export {
AlertOptions, // a0cdac0f
AlertType, // 5ab91217
AndroidKeyboardEvent, // e03becc8
- Animated, // ed7eb912
+ Animated, // f39d3c6f
AppConfig, // ebddad4b
AppRegistry, // 6cdee1d6
AppState, // 12012be5
@@ -6215,7 +6217,7 @@ export {
TVViewPropsIOS, // 330ce7b5
TargetedEvent, // 16e98910
TaskProvider, // 266dedf2
- Text, // e55ac2e2
+ Text, // 0620c789
TextContentType, // 239b3ecc
TextInput, // 2e89b91d
TextInputAndroidProps, // 3f09ce49