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
6 changes: 6 additions & 0 deletions .changeset/rotten-zebras-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'react-native-bottom-tabs': patch
'@bottom-tabs/react-navigation': patch
---

feat: introduce scene style
7 changes: 7 additions & 0 deletions docs/docs/docs/guides/standalone-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ Each route in the `routes` array can have the following properties:
- `lazy`: Whether to lazy load this tab's content
- `freezeOnBlur`: Whether to freeze the tab's content when it's not visible
- `role`: A value that defines the purpose of the tab
- `style`: Style object for the component wrapping the screen content

### Helper Props

Expand Down Expand Up @@ -267,3 +268,9 @@ Function to get the test ID for a tab item.
Function to get the role for a tab item.

- Default: Uses `route.role`

#### `getSceneStyle`

Function to get the style for a tab scene.

- Default: Uses `route.style`
14 changes: 14 additions & 0 deletions docs/docs/docs/guides/usage-with-react-navigation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,20 @@ Available options:

- `search` - The search role.

#### `sceneStyle`

Style object for the component wrapping the screen content.

```tsx
<Tab.Screen
name="Home"
component={HomeScreen}
options={{
sceneStyle: { backgroundColor: 'red' },
}}
/>
```

### Events

The navigator can emit events on certain actions. Supported events are:
Expand Down
11 changes: 11 additions & 0 deletions packages/react-native-bottom-tabs/src/TabView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
type DimensionValue,
Image,
Platform,
type StyleProp,
StyleSheet,
View,
type ViewStyle,
processColor,
} from 'react-native';
import { BottomTabBarHeightContext } from './utils/BottomTabBarHeightContext';
Expand Down Expand Up @@ -139,6 +141,11 @@
*/
getFreezeOnBlur?: (props: { route: Route }) => boolean | undefined;

/**
* Get style for the scene, uses `route.style` by default.
*/
getSceneStyle?: (props: { route: Route }) => StyleProp<ViewStyle>;

tabBarStyle?: {
/**
* Background color of the tab bar.
Expand Down Expand Up @@ -196,6 +203,7 @@
getActiveTintColor = ({ route }: { route: Route }) => route.activeTintColor,
getTestID = ({ route }: { route: Route }) => route.testID,
getRole = ({ route }: { route: Route }) => route.role,
getSceneStyle = ({ route }: { route: Route }) => route.style,
hapticFeedbackEnabled = false,
// Android's native behavior is to show labels when there are less than 4 tabs. We leave it as undefined to use the platform default behavior.
labeled = Platform.OS !== 'android' ? true : undefined,
Expand Down Expand Up @@ -233,7 +241,7 @@

if (!loaded.includes(focusedKey)) {
// Set the current tab to be loaded if it was not loaded before
setLoaded((loaded) => [...loaded, focusedKey]);

Check warning on line 244 in packages/react-native-bottom-tabs/src/TabView.tsx

View workflow job for this annotation

GitHub Actions / lint

'loaded' is already declared in the upper scope on line 240 column 10
}

const icons = React.useMemo(
Expand Down Expand Up @@ -374,12 +382,15 @@
const focused = route.key === focusedKey;
const freeze = !focused ? getFreezeOnBlur({ route }) : false;

const customStyle = getSceneStyle({ route });

return (
<View
key={route.key}
style={[
styles.screen,
renderCustomTabBar ? styles.fullWidth : measuredDimensions,
customStyle,
]}
collapsable={false}
pointerEvents={focused ? 'auto' : 'none'}
Expand Down
3 changes: 2 additions & 1 deletion packages/react-native-bottom-tabs/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ImageSourcePropType } from 'react-native';
import type { ImageSourcePropType, StyleProp, ViewStyle } from 'react-native';
import type { SFSymbol } from 'sf-symbols-typescript';

export type IconSource = string | ImageSourcePropType;
Expand All @@ -19,6 +19,7 @@ export type BaseRoute = {
testID?: string;
role?: TabRole;
freezeOnBlur?: boolean;
style?: StyleProp<ViewStyle>;
};

export type NavigationState<Route extends BaseRoute> = {
Expand Down
8 changes: 7 additions & 1 deletion packages/react-navigation/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {
TabActionHelpers,
TabNavigationState,
} from '@react-navigation/native';
import type { ImageSourcePropType } from 'react-native';
import type { ImageSourcePropType, StyleProp, ViewStyle } from 'react-native';
import type TabView from 'react-native-bottom-tabs';
import type { AppleIcon, TabRole } from 'react-native-bottom-tabs';

Expand Down Expand Up @@ -101,6 +101,11 @@ export type NativeBottomTabNavigationOptions = {
* Whether inactive screens should be suspended from re-rendering. Defaults to `false`.
*/
freezeOnBlur?: boolean;

/**
* Style object for the component wrapping the screen content.
*/
sceneStyle?: StyleProp<ViewStyle>;
};

export type NativeBottomTabDescriptor = Descriptor<
Expand Down Expand Up @@ -139,6 +144,7 @@ export type NativeBottomTabNavigationConfig = Partial<
| 'getRole'
| 'tabBar'
| 'getFreezeOnBlur'
| 'getSceneStyle'
>
> & {
tabBar?: (props: BottomTabBarProps) => React.ReactNode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export default function NativeBottomTabView({
getFreezeOnBlur={({ route }) =>
descriptors[route.key]?.options.freezeOnBlur
}
getSceneStyle={({ route }) => descriptors[route.key]?.options.sceneStyle}
onTabLongPress={(index) => {
const route = state.routes[index];
if (!route) {
Expand Down
Loading