From e51f2e8a038f91bd11ee792f0093784db68e9820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Kwas=CC=81niewski?= Date: Sun, 6 Jul 2025 17:26:07 +0200 Subject: [PATCH] fix: adjust behavior of empty string in badge prop --- .changeset/khaki-maps-lay.md | 5 ++++ apps/example/src/Examples/FourTabs.tsx | 1 + docs/docs/docs/guides/standalone-usage.md | 29 +++++++++++++++---- .../guides/usage-with-react-navigation.mdx | 6 +++- .../main/java/com/rcttabview/RCTTabView.kt | 2 +- .../java/com/rcttabview/RCTTabViewImpl.kt | 4 +-- .../ios/TabViewImpl.swift | 2 +- .../ios/TabViewProvider.swift | 6 ++-- .../react-native-bottom-tabs/src/TabView.tsx | 2 +- 9 files changed, 43 insertions(+), 14 deletions(-) create mode 100644 .changeset/khaki-maps-lay.md diff --git a/.changeset/khaki-maps-lay.md b/.changeset/khaki-maps-lay.md new file mode 100644 index 00000000..bb4b9d7d --- /dev/null +++ b/.changeset/khaki-maps-lay.md @@ -0,0 +1,5 @@ +--- +'react-native-bottom-tabs': minor +--- + +fix: adjust the behavior of empty string for badge prop diff --git a/apps/example/src/Examples/FourTabs.tsx b/apps/example/src/Examples/FourTabs.tsx index 93ed60ff..4c32ece6 100644 --- a/apps/example/src/Examples/FourTabs.tsx +++ b/apps/example/src/Examples/FourTabs.tsx @@ -52,6 +52,7 @@ export default function FourTabs({ key: 'contacts', focusedIcon: require('../../assets/icons/person_dark.png'), title: 'Contacts', + badge: ' ', }, { key: 'chat', diff --git a/docs/docs/docs/guides/standalone-usage.md b/docs/docs/docs/guides/standalone-usage.md index a2766d21..545b9f25 100644 --- a/docs/docs/docs/guides/standalone-usage.md +++ b/docs/docs/docs/guides/standalone-usage.md @@ -32,12 +32,12 @@ export default function TabViewExample() { { key: 'home', title: 'Home', - focusedIcon: { sfSymbol: 'house' } + focusedIcon: { sfSymbol: 'house' }, }, { key: 'settings', title: 'Settings', - focusedIcon: { sfSymbol: 'gear' } + focusedIcon: { sfSymbol: 'gear' }, }, ]); @@ -91,6 +91,7 @@ const renderScene = SceneMap({ #### `navigationState` State for the tab view. The state should contain: + - `routes`: Array of route objects containing `key` and `title` props - `index`: Current selected tab index @@ -107,6 +108,7 @@ Callback that is called when the tab index changes. #### `labeled` Whether to show labels in tabs. When `false`, only icons will be displayed. + - Type: `boolean` - Default : `true` - Default : `false` @@ -114,6 +116,7 @@ Whether to show labels in tabs. When `false`, only icons will be displayed. #### `sidebarAdaptable` A tab bar style that adapts to each platform: + - iPadOS: Top tab bar that can adapt into a sidebar - iOS: Bottom tab bar - macOS/tvOS: Sidebar @@ -122,19 +125,21 @@ A tab bar style that adapts to each platform: #### `disablePageAnimations` Whether to disable animations between tabs. + - Type: `boolean` #### `hapticFeedbackEnabled` Whether to enable haptic feedback on tab press. + - Type: `boolean` - Default: `false` - #### `tabLabelStyle` Object containing styles for the tab label. Supported properties: + - `fontFamily` - `fontSize` - `fontWeight` @@ -142,18 +147,21 @@ Supported properties: #### `scrollEdgeAppearance` Appearance attributes for the tab bar when a scroll view is at the bottom. + - Type: `'default' | 'opaque' | 'transparent'` #### `minimizeBehavior` Controls how the tab bar behaves when content is scrolled. + - Type: `'automatic' | 'onScrollDown' | 'onScrollUp' | 'never'` - Default: `undefined` (uses system default) Options: + - `automatic`: Platform determines the behavior - `onScrollDown`: Tab bar minimizes when scrolling down -- `onScrollUp`: Tab bar minimizes when scrolling up +- `onScrollUp`: Tab bar minimizes when scrolling up - `never`: Tab bar never minimizes :::note @@ -163,6 +171,7 @@ This feature requires iOS 26.0 or later and is only available on iOS. On older v #### `tabBarActiveTintColor` Color for the active tab. + - Type: `ColorValue` #### `tabBarInactiveTintColor` @@ -182,11 +191,13 @@ Supported properties: #### `translucent` Whether the tab bar is translucent. + - Type: `boolean` #### `activeIndicatorColor` Color of tab indicator. + - Type: `ColorValue` ### Route Configuration @@ -207,21 +218,29 @@ Each route in the `routes` array can have the following properties: #### `getLazy` Function to determine if a screen should be lazy loaded. + - Default: Uses `route.lazy` #### `getLabelText` Function to get the label text for a tab. + - Default: Uses `route.title` #### `getBadge` Function to get the badge text for a tab. + - Default: Uses `route.badge` +:::warning +To display a badge without text (just a dot), you need to pass a string with a space character (`" "`). +::: + #### `getActiveTintColor` Function to get the active tint color for a tab. + - Default: Uses `route.activeTintColor` #### `getIcon` @@ -230,7 +249,6 @@ Function to get the icon for a tab. - Default: Uses `route.focusedIcon` and `route.unfocusedIcon` - #### `getHidden` Function to determine if a tab should be hidden. @@ -240,4 +258,5 @@ Function to determine if a tab should be hidden. #### `getTestID` Function to get the test ID for a tab item. + - Default: Uses `route.testID` diff --git a/docs/docs/docs/guides/usage-with-react-navigation.mdx b/docs/docs/docs/guides/usage-with-react-navigation.mdx index 9efcea87..c04a8c95 100644 --- a/docs/docs/docs/guides/usage-with-react-navigation.mdx +++ b/docs/docs/docs/guides/usage-with-react-navigation.mdx @@ -168,7 +168,7 @@ Controls how the tab bar behaves when content is scrolled. Options: - `automatic`: Platform determines the behavior - `onScrollDown`: Tab bar minimizes when scrolling down -- `onScrollUp`: Tab bar minimizes when scrolling up +- `onScrollUp`: Tab bar minimizes when scrolling up - `never`: Tab bar never minimizes :::note @@ -298,6 +298,10 @@ tabBarIcon: () => require('person.svgx') Badge to show on the tab icon. +:::warning +To display a badge without text (just a dot), you need to pass a string with a space character (`" "`). +::: + #### `tabBarItemHidden` Whether the tab bar item is hidden. 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 ab9b1dd6..41547fd1 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 @@ -245,7 +245,7 @@ class ReactBottomNavigationView(context: Context) : LinearLayout(context) { } } - if (item.badge.isNotEmpty()) { + if (item.badge?.isNotEmpty() == true) { val badge = bottomNavigation.getOrCreateBadge(index) badge.isVisible = true badge.text = item.badge 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 424e27ff..0e6df67b 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 @@ -13,7 +13,7 @@ import com.rcttabview.events.TabLongPressEvent data class TabInfo( val key: String, val title: String, - val badge: String, + val badge: String?, val activeTintColor: Int?, val hidden: Boolean, val testID: String? @@ -32,7 +32,7 @@ class RCTTabViewImpl { TabInfo( key = item.getString("key") ?: "", title = item.getString("title") ?: "", - badge = item.getString("badge") ?: "", + badge = if (item.hasKey("badge")) item.getString("badge") else null, activeTintColor = if (item.hasKey("activeTintColor")) item.getInt("activeTintColor") else null, hidden = if (item.hasKey("hidden")) item.getBoolean("hidden") else false, testID = item.getString("testID") diff --git a/packages/react-native-bottom-tabs/ios/TabViewImpl.swift b/packages/react-native-bottom-tabs/ios/TabViewImpl.swift index 7b78993d..92b9539a 100644 --- a/packages/react-native-bottom-tabs/ios/TabViewImpl.swift +++ b/packages/react-native-bottom-tabs/ios/TabViewImpl.swift @@ -272,7 +272,7 @@ extension View { @ViewBuilder func tabBadge(_ data: String?) -> some View { if #available(iOS 15.0, macOS 15.0, visionOS 2.0, tvOS 15.0, *) { - if let data, !data.isEmpty { + if let data { #if !os(tvOS) self.badge(data) #else diff --git a/packages/react-native-bottom-tabs/ios/TabViewProvider.swift b/packages/react-native-bottom-tabs/ios/TabViewProvider.swift index 4c2e0003..9a304713 100644 --- a/packages/react-native-bottom-tabs/ios/TabViewProvider.swift +++ b/packages/react-native-bottom-tabs/ios/TabViewProvider.swift @@ -8,7 +8,7 @@ import SwiftUI public final class TabInfo: NSObject { public let key: String public let title: String - public let badge: String + public let badge: String? public let sfSymbol: String public let activeTintColor: PlatformColor? public let hidden: Bool @@ -17,7 +17,7 @@ public final class TabInfo: NSObject { public init( key: String, title: String, - badge: String, + badge: String?, sfSymbol: String, activeTintColor: PlatformColor?, hidden: Bool, @@ -269,7 +269,7 @@ public final class TabInfo: NSObject { TabInfo( key: itemDict["key"] as? String ?? "", title: itemDict["title"] as? String ?? "", - badge: itemDict["badge"] as? String ?? "", + badge: itemDict["badge"] as? String, sfSymbol: itemDict["sfSymbol"] as? String ?? "", activeTintColor: RCTConvert.uiColor(itemDict["activeTintColor"] as? NSNumber), hidden: itemDict["hidden"] as? Bool ?? false, diff --git a/packages/react-native-bottom-tabs/src/TabView.tsx b/packages/react-native-bottom-tabs/src/TabView.tsx index 2921f88b..ed30dd13 100644 --- a/packages/react-native-bottom-tabs/src/TabView.tsx +++ b/packages/react-native-bottom-tabs/src/TabView.tsx @@ -175,10 +175,10 @@ const TabView = ({ renderScene, onIndexChange, onTabLongPress, - getBadge, rippleColor, tabBarActiveTintColor: activeTintColor, tabBarInactiveTintColor: inactiveTintColor, + getBadge = ({ route }: { route: Route }) => route.badge, getLazy = ({ route }: { route: Route }) => route.lazy, getLabelText = ({ route }: { route: Route }) => route.title, getIcon = ({ route, focused }: { route: Route; focused: boolean }) =>