Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ class TabScreen(
updateMenuItemAttributesIfNeeded(oldValue, newValue)
}

// Badge
var badgeValue: String? by Delegates.observable(null) { _, oldValue, newValue ->
updateMenuItemAttributesIfNeeded(oldValue, newValue)
}

var tabBarItemBadgeTextColor: Int? by Delegates.observable(null) { _, oldValue, newValue ->
updateMenuItemAttributesIfNeeded(oldValue, newValue)
}

var tabBarItemBadgeBackgroundColor: Int? by Delegates.observable(null) { _, oldValue, newValue ->
updateMenuItemAttributesIfNeeded(oldValue, newValue)
}

// Icon
var iconResourceName: String? by Delegates.observable(null) { _, oldValue, newValue ->
if (newValue != oldValue) {
icon = getSystemDrawableResource(reactContext, newValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,13 @@ class TabScreenViewManager :
value: Int?,
) = Unit

@ReactProp(name = "tabBarItemBadgeBackgroundColor", customType = "Color")
override fun setTabBarItemBadgeBackgroundColor(
view: TabScreen,
value: Int?,
) = Unit
) {
view.tabBarItemBadgeBackgroundColor = value
}

override fun setTabBarItemTitlePositionAdjustment(
view: TabScreen?,
Expand Down Expand Up @@ -142,24 +145,18 @@ class TabScreenViewManager :
}

override fun setBadgeValue(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like that every other prop has tabBarItem... prefix and this one does not. But this is outside of the scope of this PR. I'll create ticket for this.

view: TabScreen?,
value: String?,
) = Unit

@ReactProp(name = "title")
override fun setTitle(
view: TabScreen,
value: String?,
) {
view.tabTitle = value
view.badgeValue = value
}

@ReactProp(name = "iconResourceName")
override fun setIconResourceName(
@ReactProp(name = "title")
override fun setTitle(
view: TabScreen,
value: String?,
) {
view.iconResourceName = value
view.tabTitle = value
}

override fun setSpecialEffects(
Expand All @@ -172,6 +169,23 @@ class TabScreenViewManager :
value: Boolean
) = Unit

// Android specific
@ReactProp(name = "tabBarItemBadgeTextColor", customType = "Color")
override fun setTabBarItemBadgeTextColor(
view: TabScreen,
value: Int?,
) {
view.tabBarItemBadgeTextColor = value
}

@ReactProp(name = "iconResourceName")
override fun setIconResourceName(
view: TabScreen,
value: String?,
) {
view.iconResourceName = value
}

companion object {
const val REACT_CLASS = "RNSBottomTabsScreen"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.swmansion.rnscreens.gamma.tabs

import com.swmansion.rnscreens.R
import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.util.Log
import android.view.Choreographer
Expand All @@ -23,6 +24,7 @@ import com.swmansion.rnscreens.BuildConfig
import com.swmansion.rnscreens.gamma.helpers.FragmentManagerHelper
import kotlin.properties.Delegates

@SuppressLint("PrivateResource") // We want to use variables from material design for default values
class TabsHost(
val reactContext: ThemedReactContext,
) : LinearLayout(reactContext),
Expand Down Expand Up @@ -92,8 +94,10 @@ class TabsHost(

private val containerUpdateCoordinator = ContainerUpdateCoordinator()

private val wrappedContext = ContextThemeWrapper(reactContext, R.style.custom)

private val bottomNavigationView: BottomNavigationView =
BottomNavigationView(ContextThemeWrapper(reactContext, R.style.custom)).apply {
BottomNavigationView(wrappedContext).apply {
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
}

Expand Down Expand Up @@ -302,7 +306,8 @@ class TabsHost(
bottomNavigationView.itemIconTintList = ColorStateList(states, iconColors)

// ActivityIndicator color
val activityIndicatorColor = tabBarItemActivityIndicatorColor ?: com.google.android.material.R.color.m3_sys_color_dynamic_light_on_secondary_container
val activityIndicatorColor =
tabBarItemActivityIndicatorColor ?: com.google.android.material.R.color.m3_sys_color_dynamic_light_on_secondary_container
bottomNavigationView.itemActiveIndicatorColor = ColorStateList.valueOf(activityIndicatorColor)

// First clean the menu, then populate it
Expand All @@ -318,7 +323,11 @@ class TabsHost(
fragment.tabScreen.tabTitle,
)

// Icon
item.icon = fragment.tabScreen.icon

// Badge
updateBadgeAppearance(index, fragment.tabScreen)
}

// Update font styles
Expand Down Expand Up @@ -370,6 +379,41 @@ class TabsHost(
}
}

private fun updateBadgeAppearance(
menuItemIndex: Int,
tabScreen: TabScreen,
) {
val badgeValue = tabScreen.badgeValue

if (badgeValue == null) {
val badge = bottomNavigationView.getBadge(menuItemIndex)
badge?.isVisible = false

return
}

val badgeValueNumber = badgeValue.toIntOrNull()

val badge = bottomNavigationView.getOrCreateBadge(menuItemIndex)
badge.isVisible = true

badge.clearText()
badge.clearNumber()

if (badgeValueNumber != null) {
badge.number = badgeValueNumber
} else if (badgeValue != "") {
badge.text = badgeValue
}

// Styling
badge.badgeTextColor =
tabScreen.tabBarItemBadgeTextColor ?: wrappedContext.getColor(com.google.android.material.R.color.m3_sys_color_light_on_error)
badge.backgroundColor =
tabScreen.tabBarItemBadgeBackgroundColor
?: wrappedContext.getColor(com.google.android.material.R.color.m3_sys_color_light_error)
}

private fun updateSelectedTab() {
val newFocusedTab =
checkNotNull(tabScreenFragments.find { it.tabScreen.isFocusedTab }) { "[RNScreens] No focused tab present" }
Expand Down Expand Up @@ -448,6 +492,9 @@ class TabsHost(
) {
menuItem.title = tabScreen.tabTitle
menuItem.icon = tabScreen.icon

// Badge
updateBadgeAppearance(bottomNavigationView.menu.children.indexOf(menuItem), tabScreen)
}

internal fun onViewManagerAddEventEmitters() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ public void setProperty(T view, String propName, @Nullable Object value) {
case "iconResourceName":
mViewManager.setIconResourceName(view, value == null ? null : (String) value);
break;
case "tabBarItemBadgeTextColor":
mViewManager.setTabBarItemBadgeTextColor(view, ColorPropConverter.getColor(value, view.getContext()));
break;
case "iconType":
mViewManager.setIconType(view, (String) value);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public interface RNSBottomTabsScreenManagerInterface<T extends View> {
void setTabBarItemBadgeBackgroundColor(T view, @Nullable Integer value);
void setTitle(T view, @Nullable String value);
void setIconResourceName(T view, @Nullable String value);
void setTabBarItemBadgeTextColor(T view, @Nullable Integer value);
void setIconType(T view, @Nullable String value);
void setIconImageSource(T view, @Nullable ReadableMap value);
void setIconSfSymbolName(T view, @Nullable String value);
Expand Down
14 changes: 7 additions & 7 deletions apps/src/tests/TestBottomTabs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const TAB_CONFIGS: TabConfiguration[] = [
{
tabScreenProps: {
tabKey: 'Tab1',
badgeValue: '1',
title: 'Tab1',
isFocused: true,
icon: {
Expand All @@ -34,8 +33,8 @@ const TAB_CONFIGS: TabConfiguration[] = [
{
tabScreenProps: {
tabKey: 'Tab2',
badgeValue: '2',
tabBarItemBadgeBackgroundColor: Colors.PurpleLight100,
badgeValue: 'NEW',
tabBarItemBadgeBackgroundColor: Colors.GreenDark100,
tabBarBackgroundColor: Colors.NavyDark140,
tabBarItemTitleFontSize: 20,
tabBarItemTitleFontStyle: 'italic',
Expand All @@ -60,16 +59,16 @@ const TAB_CONFIGS: TabConfiguration[] = [
{
tabScreenProps: {
tabKey: 'Tab3',
badgeValue: '3',
tabBarItemBadgeBackgroundColor: Colors.YellowDark120,
badgeValue: '2137',
tabBarItemBadgeBackgroundColor: Colors.RedDark40,
tabBarItemBadgeTextColor: Colors.RedDark120,
icon: {
imageSource: require('../../../assets/variableIcons/icon.png'),
},
selectedIcon: {
imageSource: require('../../../assets/variableIcons/icon_fill.png'),
},
tabBarItemIconColor: Colors.RedDark120,
iconResourceName: 'sym_call_outgoing', // Android specific
iconResourceName: 'sym_action_email', // Android specific
title: 'Tab3',
},
contentViewRenderFn: Tab3,
Expand All @@ -85,6 +84,7 @@ const TAB_CONFIGS: TabConfiguration[] = [
},
iconResourceName: 'sym_action_chat', // Android specific
title: 'Tab4',
badgeValue: '',
},
contentViewRenderFn: Tab4,
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/BottomTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export interface BottomTabsProps extends ViewProps {
tabBarItemTitleFontColorActive?: TextStyle['color'];
tabBarItemIconColorActive?: ColorValue;
tabBarItemActivityIndicatorColor?: ColorValue;

// Control

// Experimental support
Expand Down
1 change: 1 addition & 0 deletions src/components/BottomTabsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export interface BottomTabsScreenProps {

// Android specific
iconResourceName?: string;
tabBarItemBadgeTextColor?: ColorValue;

icon?: Icon;
selectedIcon?: Icon;
Expand Down
4 changes: 2 additions & 2 deletions src/fabric/BottomTabsNativeComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ export interface NativeProps extends ViewProps {

tabBarItemBadgeBackgroundColor?: ColorValue;

// Android
tabBarItemTitleFontColorActive?: ColorValue;
// Android
tabBarItemTitleFontColorActive?: ColorValue;
tabBarItemIconColorActive?: ColorValue;
tabBarItemTitleFontSizeActive?: Float;
tabBarItemActivityIndicatorColor?: ColorValue;
Expand Down
1 change: 1 addition & 0 deletions src/fabric/BottomTabsScreenNativeComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export interface NativeProps extends ViewProps {

// Android-specific image handling
iconResourceName?: string;
tabBarItemBadgeTextColor?: ColorValue;

// iOS-specific: SFSymbol usage
iconType?: WithDefault<IconType, 'sfSymbol'>;
Expand Down
Loading