diff --git a/.changeset/smart-tomatoes-serve.md b/.changeset/smart-tomatoes-serve.md
new file mode 100644
index 00000000..1d708c4f
--- /dev/null
+++ b/.changeset/smart-tomatoes-serve.md
@@ -0,0 +1,5 @@
+---
+'react-native-bottom-tabs': minor
+---
+
+feat: refactor android, change views on the native side, add page animations
diff --git a/apps/example/android/gradle.properties b/apps/example/android/gradle.properties
index 66ddb865..490e2deb 100644
--- a/apps/example/android/gradle.properties
+++ b/apps/example/android/gradle.properties
@@ -40,7 +40,7 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
# Note that this is incompatible with web debugging.
-newArchEnabled=false
+newArchEnabled=true
#bridgelessEnabled=true
# Uncomment the line below to build React Native from source.
diff --git a/apps/example/src/App.tsx b/apps/example/src/App.tsx
index f4a42d55..23f5994f 100644
--- a/apps/example/src/App.tsx
+++ b/apps/example/src/App.tsx
@@ -27,6 +27,7 @@ import TintColorsExample from './Examples/TintColors';
import NativeBottomTabsEmbeddedStacks from './Examples/NativeBottomTabsEmbeddedStacks';
import NativeBottomTabsSVGs from './Examples/NativeBottomTabsSVGs';
import NativeBottomTabsRemoteIcons from './Examples/NativeBottomTabsRemoteIcons';
+import NativeBottomTabsUnmounting from './Examples/NativeBottomTabsUnmounting';
const FourTabsIgnoreSafeArea = () => {
return ;
@@ -95,7 +96,6 @@ const examples = [
{
component: FourTabsNoAnimations,
name: 'Four Tabs - no animations',
- platform: 'ios',
},
{
component: FourTabsTransparentScrollEdgeAppearance,
@@ -128,6 +128,10 @@ const examples = [
component: NativeBottomTabsSVGs,
name: 'Native Bottom Tabs with SVG Icons',
},
+ {
+ component: NativeBottomTabsUnmounting,
+ name: 'Native Bottom Tabs unmounting',
+ },
{
component: NativeBottomTabsRemoteIcons,
name: 'Native Bottom Tabs with SVG Remote Icons',
diff --git a/apps/example/src/Examples/NativeBottomTabsUnmounting.tsx b/apps/example/src/Examples/NativeBottomTabsUnmounting.tsx
new file mode 100644
index 00000000..0d8bc02d
--- /dev/null
+++ b/apps/example/src/Examples/NativeBottomTabsUnmounting.tsx
@@ -0,0 +1,63 @@
+import { Article } from '../Screens/Article';
+import { Albums } from '../Screens/Albums';
+import { Contacts } from '../Screens/Contacts';
+import { Chat } from '../Screens/Chat';
+import { createNativeBottomTabNavigator } from '@bottom-tabs/react-navigation';
+import React from 'react';
+import { Alert } from 'react-native';
+
+const Tab = createNativeBottomTabNavigator();
+
+function NativeBottomTabsUnmounting() {
+ const [isTabMounted, setIsTabMounted] = React.useState(true);
+
+ React.useEffect(() => {
+ const id = setTimeout(() => {
+ setIsTabMounted(false);
+ Alert.alert('Tab is unmounted');
+ }, 1000);
+
+ return () => clearTimeout(id);
+ }, []);
+ return (
+
+
+ focused
+ ? require('../../assets/icons/person_dark.png')
+ : require('../../assets/icons/article_dark.png'),
+ }}
+ />
+ require('../../assets/icons/grid_dark.png'),
+ }}
+ />
+ require('../../assets/icons/person_dark.png'),
+ }}
+ />
+ {isTabMounted && (
+ require('../../assets/icons/chat_dark.png'),
+ }}
+ />
+ )}
+
+ );
+}
+
+export default NativeBottomTabsUnmounting;
diff --git a/apps/example/src/Examples/ThreeTabs.tsx b/apps/example/src/Examples/ThreeTabs.tsx
index e5b44634..12c7b081 100644
--- a/apps/example/src/Examples/ThreeTabs.tsx
+++ b/apps/example/src/Examples/ThreeTabs.tsx
@@ -5,7 +5,7 @@ import { Albums } from '../Screens/Albums';
import { Contacts } from '../Screens/Contacts';
export default function ThreeTabs() {
- const [index, setIndex] = useState(0);
+ const [index, setIndex] = useState(1);
const [routes] = useState([
{
key: 'article',
diff --git a/packages/react-native-bottom-tabs/android/build.gradle b/packages/react-native-bottom-tabs/android/build.gradle
index a8fabb4a..a9ac2953 100644
--- a/packages/react-native-bottom-tabs/android/build.gradle
+++ b/packages/react-native-bottom-tabs/android/build.gradle
@@ -14,11 +14,6 @@ buildscript {
}
}
-def reactNativeArchitectures() {
- def value = rootProject.getProperties().get("reactNativeArchitectures")
- return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
-}
-
def isNewArchitectureEnabled() {
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
}
diff --git a/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabView.kt b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabView.kt
index 08ef3cc0..61bad386 100644
--- a/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabView.kt
+++ b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabView.kt
@@ -1,38 +1,57 @@
package com.rcttabview
import android.annotation.SuppressLint
-import android.content.Context
import android.content.res.ColorStateList
-import android.graphics.Typeface
+import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.os.Build
+import android.transition.TransitionManager
import android.util.Log
+import android.util.Size
import android.util.TypedValue
import android.view.Choreographer
import android.view.HapticFeedbackConstants
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
import android.widget.TextView
-import androidx.appcompat.content.res.AppCompatResources
+import androidx.core.view.children
+import androidx.core.view.forEachIndexed
+import androidx.core.view.isGone
+import androidx.core.view.isVisible
+import androidx.viewpager2.widget.ViewPager2
import coil3.ImageLoader
import coil3.asDrawable
+import coil3.request.ImageRequest
+import coil3.svg.SvgDecoder
+import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.common.assets.ReactFontManager
import com.facebook.react.modules.core.ReactChoreographer
import com.facebook.react.views.text.ReactTypefaceUtils
import com.google.android.material.bottomnavigation.BottomNavigationView
-import coil3.request.ImageRequest
-import coil3.svg.SvgDecoder
+import com.google.android.material.navigation.NavigationBarView.LABEL_VISIBILITY_AUTO
+import com.google.android.material.navigation.NavigationBarView.LABEL_VISIBILITY_LABELED
+import com.google.android.material.navigation.NavigationBarView.LABEL_VISIBILITY_UNLABELED
+import com.google.android.material.transition.platform.MaterialFadeThrough
+class ReactBottomNavigationView(context: ReactContext) : LinearLayout(context) {
+ private val reactContext: ReactContext = context
+ private val bottomNavigation = BottomNavigationView(context)
+ val layoutHolder = FrameLayout(context)
-class ReactBottomNavigationView(context: Context) : BottomNavigationView(context) {
- private val iconSources: MutableMap = mutableMapOf()
- private var isLayoutEnqueued = false
- var items: MutableList? = null
var onTabSelectedListener: ((key: String) -> Unit)? = null
var onTabLongPressedListener: ((key: String) -> Unit)? = null
+ var onNativeLayoutListener: ((width: Double, height: Double) -> Unit)? = null
+ var disablePageAnimations = false
+ var items: MutableList = mutableListOf()
+
+ private var isLayoutEnqueued = false
+ private var selectedItem: String? = null
+ private val iconSources: MutableMap = mutableMapOf()
private var activeTintColor: Int? = null
private var inactiveTintColor: Int? = null
private val checkedStateSet = intArrayOf(android.R.attr.state_checked)
@@ -41,6 +60,7 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
private var fontSize: Int? = null
private var fontFamily: String? = null
private var fontWeight: Int? = null
+ private var lastReportedSize: Size? = null
private val imageLoader = ImageLoader.Builder(context)
.components {
@@ -48,8 +68,45 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
}
.build()
+ init {
+ orientation = VERTICAL
+
+ addView(
+ layoutHolder, LayoutParams(
+ LayoutParams.MATCH_PARENT,
+ 0,
+ ).apply { weight = 1f }
+ )
+ layoutHolder.isSaveEnabled = false
+
+ addView(bottomNavigation, LayoutParams(
+ LayoutParams.MATCH_PARENT,
+ LayoutParams.WRAP_CONTENT
+ ))
+
+ post {
+ addOnLayoutChangeListener { _, left, top, right, bottom,
+ _, _, _, _ ->
+ val newWidth = right - left
+ val newHeight = bottom - top
+
+ if (newWidth != lastReportedSize?.width || newHeight != lastReportedSize?.height) {
+ val dpWidth = Utils.convertPixelsToDp(context, layoutHolder.width)
+ val dpHeight = Utils.convertPixelsToDp(context, layoutHolder.height)
+
+ onNativeLayoutListener?.invoke(dpWidth, dpHeight)
+ lastReportedSize = Size(newWidth, newHeight)
+ }
+ }
+ }
+ }
+
private val layoutCallback = Choreographer.FrameCallback {
isLayoutEnqueued = false
+ refreshLayout()
+ }
+
+ private fun refreshLayout() {
measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY),
@@ -57,14 +114,6 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
layout(left, top, right, bottom)
}
- private fun onTabLongPressed(item: MenuItem) {
- val longPressedItem = items?.firstOrNull { it.title == item.title }
- longPressedItem?.let {
- onTabLongPressedListener?.invoke(longPressedItem.key)
- emitHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
- }
- }
-
override fun requestLayout() {
super.requestLayout()
@Suppress("SENSELESS_COMPARISON") // layoutCallback can be null here since this method can be called in init
@@ -82,37 +131,111 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
}
}
- private fun onTabSelected(item: MenuItem) {
- if (isLayoutEnqueued) {
+ fun setSelectedItem(value: String) {
+ selectedItem = value
+ setSelectedIndex(items.indexOfFirst { it.key == value })
+ }
+
+ override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams?) {
+ if (child === layoutHolder || child === bottomNavigation) {
+ super.addView(child, index, params)
return
}
- val selectedItem = items?.first { it.title == item.title }
- selectedItem?.let {
+
+ val container = createContainer()
+ child.isEnabled = false
+ container.addView(child, params)
+ layoutHolder.addView(container, index)
+
+ val itemKey = items[index].key
+ if (selectedItem == itemKey) {
+ setSelectedIndex(index)
+ refreshLayout()
+ }
+ }
+
+ private fun createContainer(): FrameLayout {
+ val container = FrameLayout(context).apply {
+ layoutParams = FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT
+ )
+ visibility = INVISIBLE
+ isEnabled = false
+ }
+ return container
+ }
+
+ private fun setSelectedIndex(itemId: Int) {
+ bottomNavigation.selectedItemId = itemId
+ if (!disablePageAnimations) {
+ val fadeThrough = MaterialFadeThrough()
+ TransitionManager.beginDelayedTransition(layoutHolder, fadeThrough)
+ }
+
+ layoutHolder.forEachIndexed { index, view ->
+ if (itemId == index) {
+ toggleViewVisibility(view, true)
+ } else {
+ toggleViewVisibility(view, false)
+ }
+ }
+
+ layoutHolder.requestLayout()
+ layoutHolder.invalidate()
+ }
+
+ private fun toggleViewVisibility(view: View, isVisible: Boolean) {
+ check(view is ViewGroup) { "Native component tree is corrupted." }
+
+ view.visibility = if (isVisible) VISIBLE else INVISIBLE
+ view.isEnabled = isVisible
+
+ // Container has only 1 child, wrapped React Native view.
+ val reactNativeView = view.children.first()
+ reactNativeView.isEnabled = isVisible
+ }
+
+ private fun onTabSelected(item: MenuItem) {
+ val selectedItem = items.first { it.title == item.title }
+ selectedItem.let {
onTabSelectedListener?.invoke(selectedItem.key)
emitHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
}
}
+ private fun onTabLongPressed(item: MenuItem) {
+ val longPressedItem = items.firstOrNull { it.title == item.title }
+ longPressedItem?.let {
+ onTabLongPressedListener?.invoke(longPressedItem.key)
+ emitHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
+ }
+ }
+
fun updateItems(items: MutableList) {
+ // If an item got removed, let's re-add all items
+ if (items.size < this.items.size) {
+ bottomNavigation.menu.clear()
+ }
this.items = items
items.forEachIndexed { index, item ->
val menuItem = getOrCreateItem(index, item.title)
menuItem.isVisible = !item.hidden
if (iconSources.containsKey(index)) {
- getDrawable(iconSources[index]!!) {
+ getDrawable(iconSources[index]!!) {
menuItem.icon = it
}
}
if (item.badge.isNotEmpty()) {
- val badge = this.getOrCreateBadge(index)
+ val badge = bottomNavigation.getOrCreateBadge(index)
badge.isVisible = true
badge.text = item.badge
} else {
- removeBadge(index)
+ bottomNavigation.removeBadge(index)
}
post {
- val itemView = findViewById(menuItem.itemId)
+ val itemView = bottomNavigation.findViewById(menuItem.itemId)
itemView?.let { view ->
view.setOnLongClickListener {
onTabLongPressed(menuItem)
@@ -124,9 +247,10 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
}
item.testID?.let { testId ->
- view.findViewById(com.google.android.material.R.id.navigation_bar_item_content_container)?.apply {
+ view.findViewById(com.google.android.material.R.id.navigation_bar_item_content_container)
+ ?.apply {
tag = testId
- }
+ }
}
}
updateTextAppearance()
@@ -136,7 +260,7 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
}
private fun getOrCreateItem(index: Int, title: String): MenuItem {
- return menu.findItem(index) ?: menu.add(0, index, 0, title)
+ return bottomNavigation.menu.findItem(index) ?: bottomNavigation.menu.add(0, index, 0, title)
}
fun setIcons(icons: ReadableArray?) {
@@ -146,7 +270,7 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
for (idx in 0 until icons.size()) {
val source = icons.getMap(idx)
- val uri = source.getString("uri")
+ val uri = source?.getString("uri")
if (uri.isNullOrEmpty()) {
continue
}
@@ -155,8 +279,8 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
this.iconSources[idx] = imageSource
// Update existing item if exists.
- menu.findItem(idx)?.let { menuItem ->
- getDrawable(imageSource) {
+ bottomNavigation.menu.findItem(idx)?.let { menuItem ->
+ getDrawable(imageSource) {
menuItem.icon = it
}
}
@@ -164,21 +288,21 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
}
fun setLabeled(labeled: Boolean?) {
- labelVisibilityMode = when (labeled) {
- false -> {
- LABEL_VISIBILITY_UNLABELED
- }
- true -> {
- LABEL_VISIBILITY_LABELED
- }
- else -> {
- LABEL_VISIBILITY_AUTO
- }
+ bottomNavigation.labelVisibilityMode = when (labeled) {
+ false -> {
+ LABEL_VISIBILITY_UNLABELED
+ }
+ true -> {
+ LABEL_VISIBILITY_LABELED
+ }
+ else -> {
+ LABEL_VISIBILITY_AUTO
+ }
}
}
fun setRippleColor(color: ColorStateList) {
- itemRippleColor = color
+ bottomNavigation.itemRippleColor = color
}
@SuppressLint("CheckResult")
@@ -200,13 +324,18 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
fun setBarTintColor(color: Int?) {
// Set the color, either using the active background color or a default color.
- val backgroundColor = color ?: getDefaultColorFor(android.R.attr.colorPrimary) ?: return
+ val backgroundColor =
+ color ?: Utils.getDefaultColorFor(context, android.R.attr.colorPrimary) ?: return
// Apply the same color to both active and inactive states
val colorDrawable = ColorDrawable(backgroundColor)
- itemBackground = colorDrawable
+ bottomNavigation.itemBackground = colorDrawable
backgroundTintList = ColorStateList.valueOf(backgroundColor)
+ // Set navigationBarColor for edge-to-edge.
+ if (Utils.isEdgeToEdge()) {
+ reactContext.currentActivity?.window?.navigationBarColor = backgroundColor
+ }
}
fun setActiveTintColor(color: Int?) {
@@ -220,11 +349,7 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
}
fun setActiveIndicatorColor(color: ColorStateList) {
- itemActiveIndicatorColor = color
- }
-
- fun setHapticFeedback(enabled: Boolean) {
- hapticFeedbackEnabled = enabled
+ bottomNavigation.itemActiveIndicatorColor = color
}
fun setFontSize(size: Int) {
@@ -243,18 +368,13 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
updateTextAppearance()
}
- private fun getTypefaceStyle(weight: Int?) = when (weight) {
- 700 -> Typeface.BOLD
- else -> Typeface.NORMAL
- }
-
private fun updateTextAppearance() {
if (fontSize != null || fontFamily != null || fontWeight != null) {
val menuView = getChildAt(0) as? ViewGroup ?: return
val size = fontSize?.toFloat()?.takeIf { it > 0 } ?: 12f
val typeface = ReactFontManager.getInstance().getTypeface(
fontFamily ?: "",
- getTypefaceStyle(fontWeight),
+ Utils.getTypefaceStyle(fontWeight),
context.assets
)
@@ -283,32 +403,30 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
private fun updateTintColors(item: MenuItem? = null) {
// First let's check current item color.
- val currentItemTintColor = items?.find { it.title == item?.title }?.activeTintColor
+ val currentItemTintColor = items.find { it.title == item?.title }?.activeTintColor
// getDefaultColor will always return a valid color but to satisfy the compiler we need to check for null
- val colorPrimary = currentItemTintColor ?: activeTintColor ?: getDefaultColorFor(android.R.attr.colorPrimary) ?: return
+ val colorPrimary = currentItemTintColor ?: activeTintColor ?: Utils.getDefaultColorFor(
+ context,
+ android.R.attr.colorPrimary
+ ) ?: return
val colorSecondary =
- inactiveTintColor ?: getDefaultColorFor(android.R.attr.textColorSecondary) ?: return
+ inactiveTintColor ?: Utils.getDefaultColorFor(context, android.R.attr.textColorSecondary)
+ ?: return
val states = arrayOf(uncheckedStateSet, checkedStateSet)
val colors = intArrayOf(colorSecondary, colorPrimary)
ColorStateList(states, colors).apply {
- this@ReactBottomNavigationView.itemTextColor = this
- this@ReactBottomNavigationView.itemIconTintList = this
+ this@ReactBottomNavigationView.bottomNavigation.itemTextColor = this
+ this@ReactBottomNavigationView.bottomNavigation.itemIconTintList = this
}
}
- private fun getDefaultColorFor(baseColorThemeAttr: Int): Int? {
- val value = TypedValue()
- if (!context.theme.resolveAttribute(baseColorThemeAttr, value, true)) {
- return null
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
+ if (Utils.isEdgeToEdge()) {
+ reactContext.currentActivity?.window?.navigationBarColor = Color.TRANSPARENT
}
- val baseColor = AppCompatResources.getColorStateList(
- context, value.resourceId
- )
- return baseColor.defaultColor
+ imageLoader.shutdown()
}
}
-
-
-
diff --git a/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabViewImpl.kt b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabViewImpl.kt
index 3d51039f..e8693eb5 100644
--- a/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabViewImpl.kt
+++ b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabViewImpl.kt
@@ -1,12 +1,13 @@
package com.rcttabview
import android.content.res.ColorStateList
-import android.graphics.Color
-import androidx.core.view.ViewCompat
-import androidx.core.view.WindowInsetsCompat
-import com.facebook.react.bridge.ReactContext
+import android.view.View
+import android.view.ViewGroup
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.common.MapBuilder
+import com.rcttabview.events.OnNativeLayoutEvent
+import com.rcttabview.events.PageSelectedEvent
+import com.rcttabview.events.TabLongPressEvent
data class TabInfo(
val key: String,
@@ -25,26 +26,24 @@ class RCTTabViewImpl {
fun setItems(view: ReactBottomNavigationView, items: ReadableArray) {
val itemsArray = mutableListOf()
for (i in 0 until items.size()) {
- items.getMap(i).let { item ->
- itemsArray.add(
- TabInfo(
- key = item.getString("key") ?: "",
- title = item.getString("title") ?: "",
- badge = item.getString("badge") ?: "",
- activeTintColor = if (item.hasKey("activeTintColor")) item.getInt("activeTintColor") else null,
- hidden = if (item.hasKey("hidden")) item.getBoolean("hidden") else false,
- testID = item.getString("testID")
+ items.getMap(i)?.let { item ->
+ itemsArray.add(
+ TabInfo(
+ key = item.getString("key") ?: "",
+ title = item.getString("title") ?: "",
+ badge = item.getString("badge") ?: "",
+ activeTintColor = if (item.hasKey("activeTintColor")) item.getInt("activeTintColor") else null,
+ hidden = if (item.hasKey("hidden")) item.getBoolean("hidden") else false,
+ testID = item.getString("testID")
+ )
)
- )
}
}
view.updateItems(itemsArray)
}
fun setSelectedPage(view: ReactBottomNavigationView, key: String) {
- view.items?.indexOfFirst { it.key == key }?.let {
- view.selectedItemId = it
- }
+ view.setSelectedItem(key)
}
fun setLabeled(view: ReactBottomNavigationView, flag: Boolean?) {
@@ -82,7 +81,7 @@ class RCTTabViewImpl {
}
fun setHapticFeedbackEnabled(view: ReactBottomNavigationView, enabled: Boolean) {
- view.setHapticFeedback(enabled)
+ view.isHapticFeedbackEnabled = enabled
}
fun getExportedCustomDirectEventTypeConstants(): MutableMap? {
@@ -90,32 +89,37 @@ class RCTTabViewImpl {
PageSelectedEvent.EVENT_NAME,
MapBuilder.of("registrationName", "onPageSelected"),
TabLongPressEvent.EVENT_NAME,
- MapBuilder.of("registrationName", "onTabLongPress")
+ MapBuilder.of("registrationName", "onTabLongPress"),
+ OnNativeLayoutEvent.EVENT_NAME,
+ MapBuilder.of("registrationName", "onNativeLayout")
)
}
- companion object {
- const val NAME = "RNCTabView"
+ fun getChildCount(parent: ReactBottomNavigationView): Int {
+ return parent.layoutHolder.childCount ?: 0
+ }
- // Detect `react-native-edge-to-edge` (https://github.com/zoontek/react-native-edge-to-edge)
- private val EDGE_TO_EDGE = try {
- Class.forName("com.zoontek.rnedgetoedge.EdgeToEdgePackage")
- true
- } catch (exception: ClassNotFoundException) {
- false
- }
+ fun getChildAt(parent: ReactBottomNavigationView, index: Int): View? {
+ return parent.layoutHolder.getChildAt(index)
+ }
- fun getNavigationBarInset(context: ReactContext): Int {
- val window = context.currentActivity?.window
+ fun removeView(parent: ReactBottomNavigationView, view: View) {
+ parent.layoutHolder.removeView(view)
+ }
- val isSystemBarTransparent = EDGE_TO_EDGE || window?.navigationBarColor == Color.TRANSPARENT
+ fun removeAllViews(parent: ReactBottomNavigationView) {
+ parent.layoutHolder.removeAllViews()
+ }
- if (!isSystemBarTransparent) {
- return 0
- }
+ fun removeViewAt(parent: ReactBottomNavigationView, index: Int) {
+ parent.layoutHolder.removeViewAt(index)
+ }
- val windowInsets = ViewCompat.getRootWindowInsets(window?.decorView ?: return 0)
- return windowInsets?.getInsets(WindowInsetsCompat.Type.navigationBars())?.bottom ?: 0
- }
+ fun needsCustomLayoutForChildren(): Boolean {
+ return true
+ }
+
+ companion object {
+ const val NAME = "RNCTabView"
}
}
diff --git a/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/Utils.kt b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/Utils.kt
new file mode 100644
index 00000000..e7c9f4b5
--- /dev/null
+++ b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/Utils.kt
@@ -0,0 +1,39 @@
+package com.rcttabview
+
+import android.content.Context
+import android.graphics.Typeface
+import android.util.TypedValue
+import androidx.appcompat.content.res.AppCompatResources
+
+class Utils {
+ companion object {
+ fun convertPixelsToDp(context: Context, value: Int): Double {
+ val displayDensity = context.resources.displayMetrics.density
+ return (value / displayDensity).toDouble()
+ }
+
+ fun getTypefaceStyle(weight: Int?) = when (weight) {
+ 700 -> Typeface.BOLD
+ else -> Typeface.NORMAL
+ }
+
+ fun getDefaultColorFor(context: Context, baseColorThemeAttr: Int): Int? {
+ val value = TypedValue()
+ if (!context.theme.resolveAttribute(baseColorThemeAttr, value, true)) {
+ return null
+ }
+ val baseColor = AppCompatResources.getColorStateList(
+ context, value.resourceId
+ )
+ return baseColor.defaultColor
+ }
+
+ // Detect `react-native-edge-to-edge` (https://github.com/zoontek/react-native-edge-to-edge)
+ fun isEdgeToEdge() = try {
+ Class.forName("com.zoontek.rnedgetoedge.EdgeToEdgePackage")
+ true
+ } catch (exception: ClassNotFoundException) {
+ false
+ }
+ }
+}
diff --git a/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/events/OnNativeLayoutEvent.kt b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/events/OnNativeLayoutEvent.kt
new file mode 100644
index 00000000..8391ac8a
--- /dev/null
+++ b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/events/OnNativeLayoutEvent.kt
@@ -0,0 +1,25 @@
+package com.rcttabview.events
+
+import com.facebook.react.bridge.Arguments
+import com.facebook.react.uimanager.events.Event
+import com.facebook.react.uimanager.events.RCTEventEmitter
+
+class OnNativeLayoutEvent(viewTag: Int, private val width: Double, private val height: Double) :
+ Event(viewTag) {
+
+ companion object {
+ const val EVENT_NAME = "onNativeLayout"
+ }
+
+ override fun getEventName(): String {
+ return EVENT_NAME
+ }
+
+ override fun dispatch(rctEventEmitter: RCTEventEmitter) {
+ val event = Arguments.createMap().apply {
+ putDouble("width", width)
+ putDouble("height", height)
+ }
+ rctEventEmitter.receiveEvent(viewTag, eventName, event)
+ }
+}
diff --git a/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/PageSelectedEvent.kt b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/events/PageSelectedEvent.kt
similarity index 96%
rename from packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/PageSelectedEvent.kt
rename to packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/events/PageSelectedEvent.kt
index 1ba97cb7..ca63ced2 100644
--- a/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/PageSelectedEvent.kt
+++ b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/events/PageSelectedEvent.kt
@@ -1,4 +1,4 @@
-package com.rcttabview
+package com.rcttabview.events
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
diff --git a/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/TabLongPressedEvent.kt b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/events/TabLongPressedEvent.kt
similarity index 95%
rename from packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/TabLongPressedEvent.kt
rename to packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/events/TabLongPressedEvent.kt
index 2e6186fe..bd4d8d47 100644
--- a/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/TabLongPressedEvent.kt
+++ b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/events/TabLongPressedEvent.kt
@@ -1,4 +1,4 @@
-package com.rcttabview
+package com.rcttabview.events
import com.facebook.react.bridge.Arguments
import com.facebook.react.uimanager.events.Event
diff --git a/packages/react-native-bottom-tabs/android/src/main/jni/CMakeLists.txt b/packages/react-native-bottom-tabs/android/src/main/jni/CMakeLists.txt
deleted file mode 100644
index 71147d64..00000000
--- a/packages/react-native-bottom-tabs/android/src/main/jni/CMakeLists.txt
+++ /dev/null
@@ -1,87 +0,0 @@
-cmake_minimum_required(VERSION 3.13)
-set(CMAKE_VERBOSE_MAKEFILE ON)
-
-set(LIB_LITERAL RNCTabView)
-set(LIB_TARGET_NAME react_codegen_${LIB_LITERAL})
-
-set(LIB_ANDROID_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..)
-set(LIB_COMMON_DIR ${LIB_ANDROID_DIR}/../common/cpp)
-set(LIB_ANDROID_GENERATED_JNI_DIR ${LIB_ANDROID_DIR}/build/generated/source/codegen/jni)
-set(LIB_ANDROID_GENERATED_COMPONENTS_DIR ${LIB_ANDROID_GENERATED_JNI_DIR}/react/renderer/components/${LIB_LITERAL})
-
-add_compile_options(
- -fexceptions
- -frtti
- -std=c++20
- -Wall
- -Wpedantic
- -Wno-gnu-zero-variadic-macro-arguments
-)
-
-file(GLOB LIB_CUSTOM_SRCS CONFIGURE_DEPENDS *.cpp ${LIB_COMMON_DIR}/react/renderer/components/${LIB_LITERAL}/*.cpp)
-file(GLOB LIB_CODEGEN_SRCS CONFIGURE_DEPENDS ${LIB_ANDROID_GENERATED_JNI_DIR}/*.cpp ${LIB_ANDROID_GENERATED_COMPONENTS_DIR}/*.cpp)
-
-add_library(
- ${LIB_TARGET_NAME}
- SHARED
- ${LIB_CUSTOM_SRCS}
- ${LIB_CODEGEN_SRCS}
-)
-
-target_include_directories(
- ${LIB_TARGET_NAME}
- PUBLIC
- .
- ${LIB_COMMON_DIR}
- ${LIB_ANDROID_GENERATED_JNI_DIR}
- ${LIB_ANDROID_GENERATED_COMPONENTS_DIR}
-)
-
-# https://github.com/react-native-community/discussions-and-proposals/discussions/816
-# This if-then-else can be removed once this library does not support version below 0.76
-if (REACTNATIVE_MERGED_SO)
- target_link_libraries(
- ${LIB_TARGET_NAME}
- fbjni
- jsi
- reactnative
- )
-else()
- target_link_libraries(
- ${LIB_TARGET_NAME}
- fbjni
- folly_runtime
- glog
- jsi
- react_codegen_rncore
- react_debug
- react_render_componentregistry
- react_render_core
- react_render_debug
- react_render_graphics
- react_render_imagemanager
- react_render_mapbuffer
- react_utils
- react_nativemodule_core
- rrc_image
- turbomodulejsijni
- rrc_view
- yoga
- )
-endif()
-
-target_compile_options(
- ${LIB_TARGET_NAME}
- PRIVATE
- -DLOG_TAG=\"ReactNative\"
- -fexceptions
- -frtti
- -std=c++20
- -Wall
-)
-
-target_include_directories(
- ${CMAKE_PROJECT_NAME}
- PUBLIC
- ${CMAKE_CURRENT_SOURCE_DIR}
-)
diff --git a/packages/react-native-bottom-tabs/android/src/main/jni/RNCTabView.h b/packages/react-native-bottom-tabs/android/src/main/jni/RNCTabView.h
deleted file mode 100644
index f7a1fb25..00000000
--- a/packages/react-native-bottom-tabs/android/src/main/jni/RNCTabView.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include
-#include
-#include
-#include
-
-namespace facebook::react {
-JSI_EXPORT
-std::shared_ptr RNCTabView_ModuleProvider(
- const std::string &moduleName,
- const JavaTurboModule::InitParams ¶ms);
-}
diff --git a/packages/react-native-bottom-tabs/android/src/newarch/RCTTabViewManager.kt b/packages/react-native-bottom-tabs/android/src/newarch/RCTTabViewManager.kt
index 0d3935d6..df3866b5 100644
--- a/packages/react-native-bottom-tabs/android/src/newarch/RCTTabViewManager.kt
+++ b/packages/react-native-bottom-tabs/android/src/newarch/RCTTabViewManager.kt
@@ -1,28 +1,26 @@
package com.rcttabview
-import android.content.Context
import android.view.View
+import android.view.ViewGroup
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReadableArray
-import com.facebook.react.bridge.ReadableMap
import com.facebook.react.module.annotations.ReactModule
-import com.facebook.react.uimanager.PixelUtil.toDIPFromPixel
-import com.facebook.react.uimanager.SimpleViewManager
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.UIManagerHelper
+import com.facebook.react.uimanager.ViewGroupManager
import com.facebook.react.uimanager.ViewManagerDelegate
import com.facebook.react.viewmanagers.RNCTabViewManagerDelegate
import com.facebook.react.viewmanagers.RNCTabViewManagerInterface
-import com.facebook.yoga.YogaMeasureMode
-import com.facebook.yoga.YogaMeasureOutput
+import com.rcttabview.events.OnNativeLayoutEvent
+import com.rcttabview.events.PageSelectedEvent
+import com.rcttabview.events.TabLongPressEvent
@ReactModule(name = RCTTabViewImpl.NAME)
class RCTTabViewManager(context: ReactApplicationContext) :
- SimpleViewManager(),
+ ViewGroupManager(),
RNCTabViewManagerInterface {
- private val contextInner: ReactApplicationContext = context
private val delegate: RNCTabViewManagerDelegate =
RNCTabViewManagerDelegate(this)
private val tabViewImpl: RCTTabViewImpl = RCTTabViewImpl()
@@ -37,6 +35,10 @@ class RCTTabViewManager(context: ReactApplicationContext) :
view.onTabLongPressedListener = { key ->
eventDispatcher?.dispatchEvent(TabLongPressEvent(viewTag = view.id, key))
}
+
+ view.onNativeLayoutListener = { width, height ->
+ eventDispatcher?.dispatchEvent(OnNativeLayoutEvent(viewTag = view.id, width, height))
+ }
return view
}
@@ -45,6 +47,30 @@ class RCTTabViewManager(context: ReactApplicationContext) :
return tabViewImpl.getName()
}
+ override fun getChildCount(parent: ReactBottomNavigationView): Int {
+ return tabViewImpl.getChildCount(parent)
+ }
+
+ override fun getChildAt(parent: ReactBottomNavigationView, index: Int): View? {
+ return tabViewImpl.getChildAt(parent, index)
+ }
+
+ override fun removeView(parent: ReactBottomNavigationView, view: View) {
+ tabViewImpl.removeView(parent, view)
+ }
+
+ override fun removeAllViews(parent: ReactBottomNavigationView) {
+ tabViewImpl.removeAllViews(parent)
+ }
+
+ override fun removeViewAt(parent: ReactBottomNavigationView, index: Int) {
+ tabViewImpl.removeViewAt(parent, index)
+ }
+
+ override fun needsCustomLayoutForChildren(): Boolean {
+ return tabViewImpl.needsCustomLayoutForChildren()
+ }
+
override fun setItems(view: ReactBottomNavigationView?, value: ReadableArray?) {
if (view != null && value != null)
tabViewImpl.setItems(view, value)
@@ -99,30 +125,6 @@ class RCTTabViewManager(context: ReactApplicationContext) :
tabViewImpl.setHapticFeedbackEnabled(view, value)
}
- public override fun measure(
- context: Context?,
- localData: ReadableMap?,
- props: ReadableMap?,
- state: ReadableMap?,
- width: Float,
- widthMode: YogaMeasureMode?,
- height: Float,
- heightMode: YogaMeasureMode?,
- attachmentsPositions: FloatArray?
- ): Long {
- val view = ReactBottomNavigationView(context ?: contextInner)
- val measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
- view.measure(measureSpec, measureSpec)
-
- val bottomInset = RCTTabViewImpl.getNavigationBarInset(contextInner)
-
- return YogaMeasureOutput.make(
- // TabBar should always stretch to the width of the screen.
- toDIPFromPixel(width),
- toDIPFromPixel(view.measuredHeight.toFloat() + bottomInset)
- )
- }
-
override fun setFontFamily(view: ReactBottomNavigationView?, value: String?) {
view?.setFontFamily(value)
}
@@ -135,22 +137,20 @@ class RCTTabViewManager(context: ReactApplicationContext) :
view?.setFontSize(value)
}
- // iOS Methods
+ override fun setDisablePageAnimations(view: ReactBottomNavigationView?, value: Boolean) {
+ view?.disablePageAnimations = value
+ }
+ // iOS Methods
override fun setTranslucent(view: ReactBottomNavigationView?, value: Boolean) {
}
override fun setIgnoresTopSafeArea(view: ReactBottomNavigationView?, value: Boolean) {
}
- override fun setDisablePageAnimations(view: ReactBottomNavigationView?, value: Boolean) {
- }
-
override fun setSidebarAdaptable(view: ReactBottomNavigationView?, value: Boolean) {
}
override fun setScrollEdgeAppearance(view: ReactBottomNavigationView?, value: String?) {
}
-
-
}
diff --git a/packages/react-native-bottom-tabs/android/src/oldarch/RCTTabViewManager.kt b/packages/react-native-bottom-tabs/android/src/oldarch/RCTTabViewManager.kt
index 8fce7a3d..5703a0d6 100644
--- a/packages/react-native-bottom-tabs/android/src/oldarch/RCTTabViewManager.kt
+++ b/packages/react-native-bottom-tabs/android/src/oldarch/RCTTabViewManager.kt
@@ -1,22 +1,20 @@
package com.rcttabview
-import android.view.View.MeasureSpec
-import com.facebook.react.bridge.ReadableArray
+import android.view.View
import com.facebook.react.module.annotations.ReactModule
-import com.facebook.react.uimanager.LayoutShadowNode
-import com.facebook.react.uimanager.SimpleViewManager
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.annotations.ReactProp
import com.facebook.react.uimanager.events.EventDispatcher
-import com.facebook.yoga.YogaMeasureFunction
-import com.facebook.yoga.YogaMeasureMode
-import com.facebook.yoga.YogaMeasureOutput
-import com.facebook.yoga.YogaNode
import com.facebook.react.bridge.ReactApplicationContext
+import com.facebook.react.bridge.ReadableArray
import com.facebook.react.uimanager.UIManagerModule
+import com.facebook.react.uimanager.ViewGroupManager
+import com.rcttabview.events.OnNativeLayoutEvent
+import com.rcttabview.events.PageSelectedEvent
+import com.rcttabview.events.TabLongPressEvent
@ReactModule(name = RCTTabViewImpl.NAME)
-class RCTTabViewManager(context: ReactApplicationContext) : SimpleViewManager() {
+class RCTTabViewManager(context: ReactApplicationContext) : ViewGroupManager() {
private lateinit var eventDispatcher: EventDispatcher
private var tabViewImpl = RCTTabViewImpl()
@@ -34,11 +32,35 @@ class RCTTabViewManager(context: ReactApplicationContext) : SimpleViewManager
eventDispatcher.dispatchEvent(TabLongPressEvent(viewTag = view.id, key))
}
+
+ view.onNativeLayoutListener = { width, height ->
+ eventDispatcher.dispatchEvent(OnNativeLayoutEvent(viewTag = view.id, width, height))
+ }
return view
}
- override fun createShadowNodeInstance(): LayoutShadowNode {
- return TabViewShadowNode()
+ override fun getChildCount(parent: ReactBottomNavigationView): Int {
+ return tabViewImpl.getChildCount(parent)
+ }
+
+ override fun getChildAt(parent: ReactBottomNavigationView, index: Int): View? {
+ return tabViewImpl.getChildAt(parent, index)
+ }
+
+ override fun removeView(parent: ReactBottomNavigationView, view: View) {
+ tabViewImpl.removeView(parent, view)
+ }
+
+ override fun removeAllViews(parent: ReactBottomNavigationView) {
+ tabViewImpl.removeAllViews(parent)
+ }
+
+ override fun removeViewAt(parent: ReactBottomNavigationView, index: Int) {
+ tabViewImpl.removeViewAt(parent, index)
+ }
+
+ override fun needsCustomLayoutForChildren(): Boolean {
+ return tabViewImpl.needsCustomLayoutForChildren()
}
override fun getExportedCustomDirectEventTypeConstants(): MutableMap? {
@@ -55,7 +77,6 @@ class RCTTabViewManager(context: ReactApplicationContext) : SimpleViewManager
-#include
-
-namespace facebook::react {
-
-class RNCTabViewComponentDescriptor final : public ConcreteComponentDescriptor
-{
-#ifdef ANDROID
-public:
- RNCTabViewComponentDescriptor(const ComponentDescriptorParameters ¶meters)
- : ConcreteComponentDescriptor(parameters), measurementsManager_(
- std::make_shared(contextContainer_)) {}
-
- void adopt(ShadowNode &shadowNode) const override
- {
- ConcreteComponentDescriptor::adopt(shadowNode);
-
- auto &rncTabViewShadowNode =
- static_cast(shadowNode);
-
- // `RNCTabViewShadowNode` uses `RNCTabViewMeasurementsManager` to
- // provide measurements to Yoga.
- rncTabViewShadowNode.setSliderMeasurementsManager(
- measurementsManager_);
-
- // All `RNCTabViewShadowNode`s must have leaf Yoga nodes with properly
- // setup measure function.
- rncTabViewShadowNode.enableMeasurement();
- }
-
-private:
- const std::shared_ptr measurementsManager_;
-#else
-public:
- RNCTabViewComponentDescriptor(const ComponentDescriptorParameters ¶meters)
- : ConcreteComponentDescriptor(parameters) {}
-#endif
-
-};
-
-}
-
-#endif
diff --git a/packages/react-native-bottom-tabs/common/cpp/react/renderer/components/RNCTabView/RNCTabViewMeasurementsManager.cpp b/packages/react-native-bottom-tabs/common/cpp/react/renderer/components/RNCTabView/RNCTabViewMeasurementsManager.cpp
deleted file mode 100644
index 996da9f4..00000000
--- a/packages/react-native-bottom-tabs/common/cpp/react/renderer/components/RNCTabView/RNCTabViewMeasurementsManager.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-#ifdef ANDROID
-#include "RNCTabViewMeasurementsManager.h"
-
-#include
-#include
-#include
-
-
-using namespace facebook::jni;
-
-namespace facebook::react
-{
-Size RNCTabViewMeasurementsManager::measure(
- SurfaceId surfaceId,
- LayoutConstraints layoutConstraints) const
-{
- {
- std::scoped_lock lock(mutex_);
- if (hasBeenMeasured_)
- {
- return cachedMeasurement_;
- }
- }
-
- const jni::global_ref& fabricUIManager =
- contextContainer_->at>("FabricUIManager");
-
- static auto measure = facebook::jni::findClassStatic(
- "com/facebook/react/fabric/FabricUIManager")
- ->getMethod("measure");
-
- auto minimumSize = layoutConstraints.minimumSize;
- auto maximumSize = layoutConstraints.maximumSize;
-
- local_ref componentName = make_jstring("RNCTabView");
-
- auto measurement = yogaMeassureToSize(measure(
- fabricUIManager,
- surfaceId,
- componentName.get(),
- nullptr,
- nullptr,
- nullptr,
- minimumSize.width,
- maximumSize.width,
- minimumSize.height,
- maximumSize.height));
-
- std::scoped_lock lock(mutex_);
- cachedMeasurement_ = measurement;
- hasBeenMeasured_ = true;
- return measurement;
-}
-}
-
-#endif
diff --git a/packages/react-native-bottom-tabs/common/cpp/react/renderer/components/RNCTabView/RNCTabViewMeasurementsManager.h b/packages/react-native-bottom-tabs/common/cpp/react/renderer/components/RNCTabView/RNCTabViewMeasurementsManager.h
deleted file mode 100644
index 1882ac43..00000000
--- a/packages/react-native-bottom-tabs/common/cpp/react/renderer/components/RNCTabView/RNCTabViewMeasurementsManager.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifdef __cplusplus
-
-#ifdef ANDROID
-#pragma once
-#include
-#include
-#include
-
-namespace facebook::react
-{
-
- class RNCTabViewMeasurementsManager
- {
- public:
- RNCTabViewMeasurementsManager(
- const ContextContainer::Shared &contextContainer)
- : contextContainer_(contextContainer) {}
-
- Size measure(SurfaceId surfaceId, LayoutConstraints layoutConstraints) const;
-
- private:
- const ContextContainer::Shared contextContainer_;
- mutable std::mutex mutex_;
- mutable bool hasBeenMeasured_ = false;
- mutable Size cachedMeasurement_{};
- };
-}
-
-#endif
-
-#endif
diff --git a/packages/react-native-bottom-tabs/common/cpp/react/renderer/components/RNCTabView/RNCTabViewShadowNode.cpp b/packages/react-native-bottom-tabs/common/cpp/react/renderer/components/RNCTabView/RNCTabViewShadowNode.cpp
deleted file mode 100644
index 47867419..00000000
--- a/packages/react-native-bottom-tabs/common/cpp/react/renderer/components/RNCTabView/RNCTabViewShadowNode.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "RNCTabViewShadowNode.h"
-#include "RNCTabViewMeasurementsManager.h"
-
-namespace facebook::react {
-
-extern const char RNCTabViewComponentName[] = "RNCTabView";
-
-#ifdef ANDROID
-void RNCTabViewShadowNode::setSliderMeasurementsManager(
- const std::shared_ptr &
- measurementsManager)
-{
- ensureUnsealed();
- measurementsManager_ = measurementsManager;
-}
-
-#pragma mark - LayoutableShadowNode
-
-Size RNCTabViewShadowNode::measureContent(
- const LayoutContext & /*layoutContext*/,
- const LayoutConstraints &layoutConstraints) const
-{
- return measurementsManager_->measure(getSurfaceId(), layoutConstraints);
-}
-
-#endif
-
-}
diff --git a/packages/react-native-bottom-tabs/common/cpp/react/renderer/components/RNCTabView/RNCTabViewShadowNode.h b/packages/react-native-bottom-tabs/common/cpp/react/renderer/components/RNCTabView/RNCTabViewShadowNode.h
deleted file mode 100644
index f725d903..00000000
--- a/packages/react-native-bottom-tabs/common/cpp/react/renderer/components/RNCTabView/RNCTabViewShadowNode.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#ifdef __cplusplus
-
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-
-#include "RNCTabViewMeasurementsManager.h"
-
-namespace facebook::react {
-
-JSI_EXPORT extern const char RNCTabViewComponentName[];
-
-/*
-* `ShadowNode` for component.
-*/
-class JSI_EXPORT RNCTabViewShadowNode final
-: public ConcreteViewShadowNode<
-RNCTabViewComponentName,
-RNCTabViewProps,
-RNCTabViewEventEmitter,
-RNCTabViewState>
-{
-public:
- using ConcreteViewShadowNode::ConcreteViewShadowNode;
-
-#ifdef ANDROID
- void setSliderMeasurementsManager(
- const std::shared_ptr &measurementsManager);
-
- #pragma mark - LayoutableShadowNode
-
- Size measureContent(
- const LayoutContext &layoutContext,
- const LayoutConstraints &layoutConstraints) const override;
-
-private:
- std::shared_ptr measurementsManager_;
-#endif
-
-};
-
-}
-
-#endif
diff --git a/packages/react-native-bottom-tabs/common/cpp/react/renderer/components/RNCTabView/RNCTabViewState.h b/packages/react-native-bottom-tabs/common/cpp/react/renderer/components/RNCTabView/RNCTabViewState.h
deleted file mode 100644
index cabea07e..00000000
--- a/packages/react-native-bottom-tabs/common/cpp/react/renderer/components/RNCTabView/RNCTabViewState.h
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifdef __cplusplus
-
-#pragma once
-
-#ifdef ANDROID
-#include
-#include
-#include
-#endif
-
-namespace facebook::react {
-
-class RNCTabViewState
-{
-public:
- RNCTabViewState() = default;
-
-#ifdef ANDROID
- RNCTabViewState(RNCTabViewState const &previousState, folly::dynamic data) {};
-
- folly::dynamic getDynamic() const
- {
- return {};
- };
-
- MapBuffer getMapBuffer() const
- {
- return MapBufferBuilder::EMPTY();
- };
-#endif
-};
-
-}
-
-#endif
diff --git a/packages/react-native-bottom-tabs/ios/Fabric/RCTTabViewComponentView.mm b/packages/react-native-bottom-tabs/ios/Fabric/RCTTabViewComponentView.mm
index 424081e2..67ba09b7 100644
--- a/packages/react-native-bottom-tabs/ios/Fabric/RCTTabViewComponentView.mm
+++ b/packages/react-native-bottom-tabs/ios/Fabric/RCTTabViewComponentView.mm
@@ -2,7 +2,6 @@
#import "RCTTabViewComponentView.h"
#import
-#import
#import
#import
#import
diff --git a/packages/react-native-bottom-tabs/package.json b/packages/react-native-bottom-tabs/package.json
index ba086449..70a12990 100644
--- a/packages/react-native-bottom-tabs/package.json
+++ b/packages/react-native-bottom-tabs/package.json
@@ -24,9 +24,7 @@
"files": [
"src",
"lib",
- "common",
"android",
- "react-navigation",
"ios",
"cpp",
"react-native.config.js",
diff --git a/packages/react-native-bottom-tabs/react-native-bottom-tabs.podspec b/packages/react-native-bottom-tabs/react-native-bottom-tabs.podspec
index cafd8e5e..e1d71432 100644
--- a/packages/react-native-bottom-tabs/react-native-bottom-tabs.podspec
+++ b/packages/react-native-bottom-tabs/react-native-bottom-tabs.podspec
@@ -22,13 +22,6 @@ Pod::Spec.new do |s|
s.source_files = "ios/**/*.{h,m,mm,cpp,swift}"
s.static_framework = true
- if new_arch_enabled
- s.subspec "common" do |ss|
- ss.source_files = "common/cpp/**/*.{cpp,h}"
- ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/common/cpp\"" }
- end
- end
-
s.dependency "SwiftUIIntrospect", '~> 1.0'
s.dependency 'SDWebImage', '>= 5.19.1'
s.dependency 'SDWebImageSVGCoder', '>= 1.7.0'
diff --git a/packages/react-native-bottom-tabs/react-native.config.js b/packages/react-native-bottom-tabs/react-native.config.js
deleted file mode 100644
index b3ebd7d7..00000000
--- a/packages/react-native-bottom-tabs/react-native.config.js
+++ /dev/null
@@ -1,11 +0,0 @@
-module.exports = {
- dependency: {
- platforms: {
- android: {
- libraryName: 'RNCTabView',
- componentDescriptors: ['RNCTabViewComponentDescriptor'],
- cmakeListsPath: 'src/main/jni/CMakeLists.txt',
- },
- },
- },
-};
diff --git a/packages/react-native-bottom-tabs/src/TabView.tsx b/packages/react-native-bottom-tabs/src/TabView.tsx
index 9fb9a324..570b1631 100644
--- a/packages/react-native-bottom-tabs/src/TabView.tsx
+++ b/packages/react-native-bottom-tabs/src/TabView.tsx
@@ -12,7 +12,7 @@ import { BottomTabBarHeightContext } from './utils/BottomTabBarHeightContext';
//@ts-ignore
import type { ImageSource } from 'react-native/Libraries/Image/ImageSource';
-import TabViewAdapter from './TabViewAdapter';
+import NativeTabView from './TabViewNativeComponent';
import useLatestCallback from 'use-latest-callback';
import type { BaseRoute, NavigationState } from './types';
@@ -270,7 +270,7 @@ const TabView = ({
return (
- ({
{trimmedRoutes.map((route) => {
if (getLazy({ route }) !== false && !loaded.includes(route.key)) {
// Don't render a screen if we've never navigated to it
- if (Platform.OS === 'android') {
- return null;
- }
return (
({
}
const focused = route.key === focusedKey;
- const opacity = focused ? 1 : 0;
- const zIndex = focused ? 0 : -1;
return (
({
}
style={
Platform.OS === 'android'
- ? [StyleSheet.absoluteFill, { zIndex, opacity }]
+ ? [measuredDimensions]
: [{ position: 'absolute' }, measuredDimensions]
}
>
@@ -337,7 +332,7 @@ const TabView = ({
);
})}
-
+
);
};
diff --git a/packages/react-native-bottom-tabs/src/TabViewAdapter.android.tsx b/packages/react-native-bottom-tabs/src/TabViewAdapter.android.tsx
deleted file mode 100644
index de739f17..00000000
--- a/packages/react-native-bottom-tabs/src/TabViewAdapter.android.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import NativeTabView from './TabViewNativeComponent';
-import type { TabViewProps } from './TabViewNativeComponent';
-import { StyleSheet, View } from 'react-native';
-
-const TabViewAdapter = ({ children, style: _, ...props }: TabViewProps) => {
- return (
- <>
- {children}
-
- >
- );
-};
-
-const styles = StyleSheet.create({
- container: {
- width: '100%',
- height: '100%',
- },
- content: {
- flex: 1,
- },
- tabContent: {
- flex: 1,
- width: '100%',
- height: '100%',
- },
-});
-
-export default TabViewAdapter;
diff --git a/packages/react-native-bottom-tabs/src/TabViewAdapter.tsx b/packages/react-native-bottom-tabs/src/TabViewAdapter.tsx
deleted file mode 100644
index 9b07cd35..00000000
--- a/packages/react-native-bottom-tabs/src/TabViewAdapter.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import NativeTabView, { type TabViewProps } from './TabViewNativeComponent';
-
-const TabViewAdapter = (props: TabViewProps) => {
- return ;
-};
-
-export default TabViewAdapter;
diff --git a/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts b/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts
index 113f990f..0a818b5b 100644
--- a/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts
+++ b/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts
@@ -57,6 +57,4 @@ export interface TabViewProps extends ViewProps {
fontSize?: Int32;
}
-export default codegenNativeComponent('RNCTabView', {
- interfaceOnly: true,
-});
+export default codegenNativeComponent('RNCTabView');