Skip to content

Commit 59408e3

Browse files
authored
Added support for rippleColor in android (#56)
* feat: ripple color support added for android * chore: readme updated * chore: comments resolved * chore: unnecessary code removed * fix: formatting changes
1 parent dad9de3 commit 59408e3

File tree

7 files changed

+66
-23
lines changed

7 files changed

+66
-23
lines changed

android/src/main/java/com/rcttabview/RCTTabView.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.rcttabview
22

33
import android.content.Context
4+
import android.content.res.ColorStateList
5+
import android.graphics.Color
46
import android.graphics.drawable.BitmapDrawable
57
import android.graphics.drawable.Drawable
68
import android.net.Uri
@@ -73,7 +75,7 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
7375

7476
fun updateItems(items: MutableList<TabInfo>) {
7577
this.items = items
76-
items.forEachIndexed {index, item ->
78+
items.forEachIndexed { index, item ->
7779
val menuItem = getOrCreateItem(index, item.title)
7880
if (icons.containsKey(index)) {
7981
menuItem.icon = getDrawable(icons[index]!!)
@@ -123,6 +125,10 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
123125
}
124126
}
125127

128+
fun setRippleColor(color: ColorStateList) {
129+
itemRippleColor = color
130+
}
131+
126132
private fun getDrawable(imageSource: ImageSource): Drawable {
127133
// TODO: Check if this can be done using some built-in React Native class
128134
val imageRequest = ImageRequestBuilder.newBuilderWithSource(imageSource.uri).build()

android/src/main/java/com/rcttabview/RCTTabViewViewManager.kt

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.rcttabview
22

3+
import android.content.res.ColorStateList
4+
import android.graphics.Color
35
import android.view.View.MeasureSpec
46
import com.facebook.react.bridge.ReadableArray
57
import com.facebook.react.common.MapBuilder
@@ -36,13 +38,13 @@ class RCTTabViewViewManager :
3638
val itemsArray = mutableListOf<TabInfo>()
3739
for (i in 0 until items.size()) {
3840
items.getMap(i).let { item ->
39-
itemsArray.add(
40-
TabInfo(
41-
key = item.getString("key") ?: "",
42-
title = item.getString("title") ?: "",
43-
badge = item.getString("badge") ?: ""
44-
)
41+
itemsArray.add(
42+
TabInfo(
43+
key = item.getString("key") ?: "",
44+
title = item.getString("title") ?: "",
45+
badge = item.getString("badge") ?: ""
4546
)
47+
)
4648
}
4749
}
4850
view.updateItems(itemsArray)
@@ -56,7 +58,6 @@ class RCTTabViewViewManager :
5658
}
5759

5860

59-
6061
@ReactProp(name = "labeled")
6162
fun setLabeled(view: ReactBottomNavigationView, flag: Boolean?) {
6263
view.setLabeled(flag)
@@ -67,12 +68,20 @@ class RCTTabViewViewManager :
6768
view.setIcons(icons)
6869
}
6970

71+
@ReactProp(name = "rippleColor")
72+
fun setRippleColor(view: ReactBottomNavigationView, rippleColor: Int?) {
73+
if (rippleColor != null) {
74+
val color = ColorStateList.valueOf(rippleColor)
75+
view.setRippleColor(color)
76+
}
77+
}
78+
7079
public override fun createViewInstance(context: ThemedReactContext): ReactBottomNavigationView {
7180
eventDispatcher = context.getNativeModule(UIManagerModule::class.java)!!.eventDispatcher
7281
val view = ReactBottomNavigationView(context)
7382
view.onTabSelectedListener = { data ->
7483
data.getString("key")?.let {
75-
eventDispatcher.dispatchEvent(PageSelectedEvent(viewTag = view.id, key = it ))
84+
eventDispatcher.dispatchEvent(PageSelectedEvent(viewTag = view.id, key = it))
7685
}
7786
}
7887
return view
@@ -133,11 +142,14 @@ class RCTTabViewViewManager :
133142
// iOS Props
134143

135144
@ReactProp(name = "sidebarAdaptable")
136-
fun setSidebarAdaptable(view: ReactBottomNavigationView, flag: Boolean) {}
145+
fun setSidebarAdaptable(view: ReactBottomNavigationView, flag: Boolean) {
146+
}
137147

138148
@ReactProp(name = "ignoresTopSafeArea")
139-
fun setIgnoresTopSafeArea(view: ReactBottomNavigationView, flag: Boolean) {}
149+
fun setIgnoresTopSafeArea(view: ReactBottomNavigationView, flag: Boolean) {
150+
}
140151

141152
@ReactProp(name = "disablePageAnimations")
142-
fun setDisablePageAnimations(view: ReactBottomNavigationView, flag: Boolean) {}
153+
fun setDisablePageAnimations(view: ReactBottomNavigationView, flag: Boolean) {
154+
}
143155
}

docs/docs/docs/guides/usage-with-react-navigation.mdx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ To use this navigator, ensure that you have [`@react-navigation/native` and its
77
:::
88

99
```tsx
10-
import {createNativeBottomTabNavigator} from 'react-native-bottom-tabs/react-navigation';
10+
import { createNativeBottomTabNavigator } from 'react-native-bottom-tabs/react-navigation';
1111

1212
const Tabs = createNativeBottomTabNavigator();
1313

@@ -17,15 +17,15 @@ function NativeBottomTabs() {
1717
<Tabs.Screen
1818
name="index"
1919
options={{
20-
title: "Home",
21-
tabBarIcon: () => ({ sfSymbol: "house" }),
20+
title: 'Home',
21+
tabBarIcon: () => ({ sfSymbol: 'house' }),
2222
}}
2323
/>
2424
<Tabs.Screen
2525
name="explore"
2626
options={{
27-
title: "Explore",
28-
tabBarIcon: () => ({ sfSymbol: "person" }),
27+
title: 'Explore',
28+
tabBarIcon: () => ({ sfSymbol: 'person' }),
2929
}}
3030
/>
3131
</Tabs.Navigator>
@@ -53,6 +53,10 @@ Default options to use for the screens in the navigator.
5353

5454
Whether to show labels in tabs. Defaults to true.
5555

56+
#### `rippleColor`
57+
58+
Changes ripple color on tab press. (Android Only)
59+
5660
#### `disablePageAnimations` <Badge text="iOS" type="info" />
5761

5862
Whether to disable page animations between tabs.
@@ -62,6 +66,7 @@ Whether to disable page animations between tabs.
6266
Describes the appearance attributes for the tabBar to use when an observable scroll view is scrolled to the bottom.
6367

6468
Available options:
69+
6570
- `default` - uses default background and shadow values.
6671
- `transparent` - uses transparent background and no shadow.
6772
- `opaque` - uses set of opaque colors that are appropriate for the current theme
@@ -70,18 +75,17 @@ Available options:
7075
It's recommended to use `transparent` or `opaque` without lazy loading as the tab bar background flashes when a view is rendered lazily.
7176
:::
7277

73-
7478
#### `sidebarAdaptable` <Badge text="iOS" type="info" />
7579

7680
A tab bar style that adapts to each platform.
7781

7882
Tab views using the sidebar adaptable style have an appearance
83+
7984
- iPadOS displays a top tab bar that can adapt into a sidebar.
8085
- iOS displays a bottom tab bar.
8186
- macOS and tvOS always show a sidebar.
8287
- visionOS shows an ornament and also shows a sidebar for secondary tabs within a `TabSection`.
8388

84-
8589
### Options
8690

8791
The following options can be used to configure the screens in the navigator. These can be specified under `screenOptions` prop of `Tab.navigator` or `options` prop of `Tab.Screen`.
@@ -98,8 +102,6 @@ Label text of the tab displayed in the navigation bar. When undefined, scene tit
98102

99103
Function that given `{ focused: boolean }` returns `ImageSource` or `AppleIcon` to display in the navigation bar.
100104

101-
102-
103105
```tsx
104106
<Tab.Screen
105107
name="Albums"

example/src/App.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ const FourTabsIgnoreSafeArea = () => {
2727
return <FourTabs ignoresTopSafeArea />;
2828
};
2929

30+
const FourTabsRippleColor = () => {
31+
return <FourTabs rippleColor={'#00ff00'} />;
32+
};
33+
3034
const FourTabsNoAnimations = () => {
3135
return <FourTabs disablePageAnimations />;
3236
};
@@ -45,6 +49,10 @@ const examples = [
4549
name: 'Four Tabs - No header',
4650
screenOptions: { headerShown: false },
4751
},
52+
{
53+
component: FourTabsRippleColor,
54+
name: 'Four Tabs with ripple Color',
55+
},
4856
{ component: FourTabsNoAnimations, name: 'Four Tabs - no animations' },
4957
{
5058
component: FourTabsTransparentScrollEdgeAppearance,

example/src/Examples/FourTabs.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,20 @@ import { Article } from '../Screens/Article';
44
import { Albums } from '../Screens/Albums';
55
import { Contacts } from '../Screens/Contacts';
66
import { Chat } from '../Screens/Chat';
7+
import { ColorValue } from 'react-native';
78

89
interface Props {
910
ignoresTopSafeArea?: boolean;
1011
disablePageAnimations?: boolean;
1112
scrollEdgeAppearance?: 'default' | 'opaque' | 'transparent';
13+
rippleColor?: ColorValue;
1214
}
1315

1416
export default function FourTabs({
1517
ignoresTopSafeArea = false,
1618
disablePageAnimations = false,
1719
scrollEdgeAppearance = 'default',
20+
rippleColor,
1821
}: Props) {
1922
const [index, setIndex] = useState(0);
2023
const [routes] = useState([
@@ -59,6 +62,7 @@ export default function FourTabs({
5962
navigationState={{ index, routes }}
6063
onIndexChange={setIndex}
6164
renderScene={renderScene}
65+
rippleColor={rippleColor}
6266
/>
6367
);
6468
}

src/TabView.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import type { TabViewItems } from './TabViewNativeComponent';
2-
import { Image, Platform, StyleSheet, View } from 'react-native';
2+
import {
3+
ColorValue,
4+
Image,
5+
Platform,
6+
StyleSheet,
7+
View,
8+
processColor,
9+
} from 'react-native';
310

411
//@ts-ignore
512
import type { ImageSource } from 'react-native/Libraries/Image/ImageSource';
@@ -77,6 +84,8 @@ interface Props<Route extends BaseRoute> {
7784
route: Route;
7885
focused: boolean;
7986
}) => ImageSource | undefined;
87+
88+
rippleColor?: ColorValue;
8089
}
8190

8291
const ANDROID_MAX_TABS = 6;
@@ -180,6 +189,7 @@ const TabView = <Route extends BaseRoute>({
180189
jumpTo(key);
181190
}}
182191
{...props}
192+
rippleColor={processColor(props.rippleColor)}
183193
>
184194
{trimmedRoutes.map((route) => {
185195
if (getLazy({ route }) !== false && !loaded.includes(route.key)) {

src/TabViewNativeComponent.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
2-
import type { ViewProps } from 'react-native';
2+
import type { ViewProps, ProcessedColorValue } from 'react-native';
33
import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes';
44
//@ts-ignore
55
import type { ImageSource } from 'react-native/Libraries/Image/ImageSource';
@@ -23,6 +23,7 @@ export interface TabViewProps extends ViewProps {
2323
labeled?: boolean;
2424
sidebarAdaptable?: boolean;
2525
scrollEdgeAppearance?: string;
26+
rippleColor?: ProcessedColorValue | null;
2627
}
2728

2829
export default codegenNativeComponent<TabViewProps>('RCTTabView');

0 commit comments

Comments
 (0)