Skip to content
Open
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
4 changes: 3 additions & 1 deletion docs/docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ const config = {
Banner: 'Banner',
BottomNavigation: {
BottomNavigation: 'BottomNavigation/BottomNavigation',
BottomNavigationBar: 'BottomNavigation/BottomNavigationBar',
},
Button: {
Button: 'Button/Button',
Expand Down Expand Up @@ -148,6 +147,9 @@ const config = {
MenuItem: 'Menu/MenuItem',
},
Modal: 'Modal',
NavigationBar: {
NavigationBar: 'NavigationBar/NavigationBar',
},
Comment on lines 86 to +152

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is the BottomNavigation component still needed? it'd be inconsistent to have a mix of old and new APIs

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Kept intentionally. BottomNavigation is the router-integrated scene component (owns tab screens + transitions); NavigationBar is the standalone bar for use with React Navigation's own navigator — same layering split as React Navigation itself, so it isn't an old-vs-new API mix. Every deprecation/alias is gone (13d34e9), so there's no MD2 surface remaining.

Portal: {
Portal: 'Portal/Portal',
PortalHost: 'Portal/PortalHost',
Expand Down
2 changes: 2 additions & 0 deletions example/src/ExampleList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import ListAccordionExampleGroup from './Examples/ListAccordionGroupExample';
import ListItemExample from './Examples/ListItemExample';
import ListSectionExample from './Examples/ListSectionExample';
import MenuExample from './Examples/MenuExample';
import NavigationBarExample from './Examples/NavigationBarExample';
import ProgressBarExample from './Examples/ProgressBarExample';
import RadioButtonExample from './Examples/RadioButtonExample';
import RadioButtonGroupExample from './Examples/RadioButtonGroupExample';
Expand Down Expand Up @@ -72,6 +73,7 @@ export const mainExamples = {
ListSection: ListSectionExample,
ListItem: ListItemExample,
Menu: MenuExample,
NavigationBar: NavigationBarExample,
Progressbar: ProgressBarExample,
Radio: RadioButtonExample,
RadioGroup: RadioButtonGroupExample,
Expand Down
6 changes: 3 additions & 3 deletions example/src/Examples/BottomNavigationBarExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
SFSymbol,
MaterialSymbol,
} from '@react-navigation/native';
import { Text, BottomNavigation } from 'react-native-paper';
import { Text, NavigationBar } from 'react-native-paper';

function HomeScreen() {
return (
Expand All @@ -34,7 +34,7 @@ const BottomNavigationBarExample = createBottomTabNavigator({
headerShown: false,
},
tabBar: ({ navigation, state, descriptors }) => (
<BottomNavigation.Bar
<NavigationBar
navigationState={state}
onTabPress={({ route, preventDefault }) => {
const event = navigation.emit({
Expand Down Expand Up @@ -119,7 +119,7 @@ const BottomNavigationBarExample = createBottomTabNavigator({
});

export default Object.assign(BottomNavigationBarExample, {
title: 'Bottom Navigation Bar',
title: 'Navigation Bar (React Navigation)',
});

const styles = StyleSheet.create({
Expand Down
108 changes: 108 additions & 0 deletions example/src/Examples/NavigationBarExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import * as React from 'react';
import { StyleSheet, View, useWindowDimensions } from 'react-native';

import {
NavigationBar,
SegmentedButtons,
Switch,
Text,
} from 'react-native-paper';

type VariantMode = 'auto' | 'stacked' | 'horizontal';

// The flexible navigation bar switches to a horizontal item arrangement in
// medium-width windows. M3 recommends ~600dp as the breakpoint, but the
// component leaves the decision to the consumer — here it is driven by
// `useWindowDimensions`.
const MEDIUM_WINDOW_WIDTH = 600;

const routes = [
{ key: 'album', title: 'Album', focusedIcon: 'image-album', badge: 3 },
{ key: 'library', title: 'Library', focusedIcon: 'bookshelf' },
{
key: 'favorites',
title: 'Favorites',
focusedIcon: 'heart',
unfocusedIcon: 'heart-outline',
},
{
key: 'settings',
title: 'Settings',
focusedIcon: 'cog',
unfocusedIcon: 'cog-outline',
},
];

const NavigationBarExample = () => {
const [index, setIndex] = React.useState(0);
const [labeled, setLabeled] = React.useState(true);
const [variantMode, setVariantMode] = React.useState<VariantMode>('auto');

const { width } = useWindowDimensions();
const autoVariant = width >= MEDIUM_WINDOW_WIDTH ? 'horizontal' : 'stacked';
const variant = variantMode === 'auto' ? autoVariant : variantMode;

return (
<View style={styles.container}>
<View style={styles.content}>
<Text variant="headlineSmall">{routes[index].title}</Text>

<View style={styles.controls}>
<View style={styles.row}>
<Text variant="labelLarge">Show labels</Text>
<Switch value={labeled} onValueChange={setLabeled} />
</View>

<SegmentedButtons
value={variantMode}
onValueChange={(value) => setVariantMode(value as VariantMode)}
buttons={[
{ value: 'auto', label: `Auto (${autoVariant})` },
{ value: 'stacked', label: 'Stacked' },
{ value: 'horizontal', label: 'Horizontal' },
]}
/>
</View>
</View>

<NavigationBar
navigationState={{ index, routes }}
labeled={labeled}
variant={variant}
onTabPress={({ route }) => {
const nextIndex = routes.findIndex((r) => r.key === route.key);
if (nextIndex !== -1) {
setIndex(nextIndex);
}
}}
/>
</View>
);
};

NavigationBarExample.title = 'Navigation Bar (flexible)';

export default NavigationBarExample;

const styles = StyleSheet.create({
container: {
flex: 1,
},
content: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 16,
},
controls: {
marginTop: 32,
width: '100%',
maxWidth: 480,
gap: 24,
},
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
});
32 changes: 5 additions & 27 deletions src/components/BottomNavigation/BottomNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import type {

import useLatestCallback from 'use-latest-callback';

import BottomNavigationBar from './BottomNavigationBar';
import BottomNavigationRouteScreen from './BottomNavigationRouteScreen';
import { useInternalTheme } from '../../core/theming';
import type { ThemeProp } from '../../types';
import useAnimatedValueArray from '../../utils/useAnimatedValueArray';
import type { IconSource } from '../Icon';
import NavigationBar from '../NavigationBar/NavigationBar';
import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple';

export type BaseRoute = {
Expand Down Expand Up @@ -48,14 +48,6 @@ type TouchableProps<Route extends BaseRoute> = TouchableRippleProps & {
};

export type Props<Route extends BaseRoute> = {
/**
* Whether the shifting style is used, the active tab icon shifts up to show the label and the inactive tabs won't have a label.
*
* By default, this is `false` with theme version 3 and `true` when you have more than 3 tabs.
* Pass `shifting={false}` to explicitly disable this animation, or `shifting={true}` to always use this animation.
* Note that you need at least 2 tabs be able to run this animation.
*/
shifting?: boolean;
/**
* Whether to show labels in tabs. When `false`, only icons will be displayed.
*/
Expand Down Expand Up @@ -200,8 +192,8 @@ export type Props<Route extends BaseRoute> = {
*/
inactiveColor?: string;
/**
* Whether animation is enabled for scenes transitions in `shifting` mode.
* By default, the scenes cross-fade during tab change when `shifting` is enabled.
* Whether animation is enabled for scene transitions.
* By default, the scenes cross-fade during tab change.
* Specify `sceneAnimationEnabled` as `false` to disable the animation.
*/
sceneAnimationEnabled?: boolean;
Expand Down Expand Up @@ -261,7 +253,7 @@ const SceneComponent = React.memo(({ component, ...rest }: any) =>

/**
* BottomNavigation provides quick navigation between top-level views of an app with a bottom navigation bar.
* It is primarily designed for use on mobile. If you want to use the navigation bar only see [`BottomNavigation.Bar`](BottomNavigationBar).
* It is primarily designed for use on mobile. If you want to use the navigation bar only see [`NavigationBar`](../NavigationBar).
*
* By default BottomNavigation uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.
* See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.
Expand Down Expand Up @@ -330,7 +322,6 @@ const BottomNavigation = <Route extends BaseRoute>({
onTabPress,
onTabLongPress,
onIndexChange,
shifting: shiftingProp,
safeAreaInsets,
labelMaxFontSizeMultiplier = 1,
compact: compactProp,
Expand All @@ -341,14 +332,6 @@ const BottomNavigation = <Route extends BaseRoute>({
const theme = useInternalTheme(themeOverrides);
const { scale } = theme.animation;
const compact = compactProp ?? false;
let shifting = shiftingProp ?? false;

if (shifting && navigationState.routes.length < 2) {
shifting = false;
console.warn(
'BottomNavigation needs at least 2 tabs to run shifting animation'
);
}

const focusedKey = navigationState.routes[navigationState.index].key;

Expand Down Expand Up @@ -556,7 +539,7 @@ const BottomNavigation = <Route extends BaseRoute>({
);
})}
</View>
<BottomNavigationBar
<NavigationBar
navigationState={navigationState}
renderIcon={renderIcon}
renderLabel={renderLabel}
Expand All @@ -571,10 +554,8 @@ const BottomNavigation = <Route extends BaseRoute>({
style={barStyle}
activeIndicatorStyle={activeIndicatorStyle}
labeled={labeled}
animationEasing={sceneAnimationEasing}
onTabPress={handleTabPress}
onTabLongPress={onTabLongPress}
shifting={shifting}
safeAreaInsets={safeAreaInsets}
labelMaxFontSizeMultiplier={labelMaxFontSizeMultiplier}
compact={compact}
Expand Down Expand Up @@ -612,9 +593,6 @@ BottomNavigation.SceneMap = <Route extends BaseRoute>(scenes: {
);
};

// @component ./BottomNavigationBar.tsx
BottomNavigation.Bar = BottomNavigationBar;

export default BottomNavigation;

const styles = StyleSheet.create({
Expand Down
Loading
Loading