mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-09 17:23:18 +08:00
Make header configurable (#1220)
* Initial commit * Remove null mention Technically, it's possible, though it's not recommended approach. People should use `headerVisible`. * Update SimpleStack.js * Updates * Remove headerVisible * Fix docs * Fix flow * Bring back validation * Fix import
This commit is contained in:
@@ -96,9 +96,9 @@ Visual options:
|
||||
|
||||
Generic title that can be used as a fallback for `headerTitle` and `tabBarLabel`
|
||||
|
||||
#### `headerVisible`
|
||||
#### `header`
|
||||
|
||||
True or false to show or hide the header. Only works when `headerMode` is `screen`. Default value is `true`.
|
||||
React Element or a function that given `HeaderProps` returns a React Element, to display as a header. Setting to `null` hides header.
|
||||
|
||||
#### `headerTitle`
|
||||
|
||||
|
||||
@@ -67,12 +67,10 @@ Your `TabNavigator` represents one of the screens in the app, and is nested with
|
||||
StackNavigator({
|
||||
route1: { screen: RouteOne },
|
||||
route2: { screen: MyTabNavigator },
|
||||
}, {
|
||||
headerMode: 'screen',
|
||||
});
|
||||
```
|
||||
|
||||
Now, when `route2` is active, you would like to hide the header. It's easy to hide the header for `route1`, and it should also be easy to do it for `route2`. This is what Default Navigation Options are for - they are simply `navigationOptions` set on a navigator:
|
||||
Now, when `route2` is active, you would like to change the tint color of a header. It's easy to do it for `route1`, and it should also be easy to do it for `route2`. This is what Default Navigation Options are for - they are simply `navigationOptions` set on a navigator:
|
||||
|
||||
```js
|
||||
const MyTabNavigator = TabNavigator({
|
||||
@@ -80,25 +78,22 @@ const MyTabNavigator = TabNavigator({
|
||||
...
|
||||
}, {
|
||||
navigationOptions: {
|
||||
headerVisible: false,
|
||||
headerTintColor: 'blue',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Note that you can still decide to **also** specify the `navigationOptions` on the screens at the leaf level - e.g. the `ProfileScreen` above. The `navigationOptions` from the screen will be merged key-by-key with the default options coming from the navigator. Whenever both the navigator and screen define the same option (e.g. `headerVisible`), the screen wins. Therefore, you could make the header visible again when `ProfileScreen` is active.
|
||||
Note that you can still decide to **also** specify the `navigationOptions` on the screens at the leaf level - e.g. the `ProfileScreen` above. The `navigationOptions` from the screen will be merged key-by-key with the default options coming from the navigator. Whenever both the navigator and screen define the same option (e.g. `headerTintColor`), the screen wins. Therefore, you could change the tint color when `ProfileScreen` is active by doing the following:
|
||||
|
||||
```js
|
||||
class ProfileScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
headerVisible: true,
|
||||
headerTintColor: 'black',
|
||||
};
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The 2nd argument passed to the function are the default values for the `header` as defined on the navigator.
|
||||
|
||||
|
||||
## Navigation Option Reference
|
||||
|
||||
List of available navigation options depends on the `navigator` the screen is added to.
|
||||
|
||||
@@ -207,7 +207,6 @@ export type NavigationUriAction = {
|
||||
export type NavigationStackViewConfig = {
|
||||
mode?: 'card' | 'modal',
|
||||
headerMode?: HeaderMode,
|
||||
headerComponent?: ReactClass<HeaderProps<*>>,
|
||||
cardStyle?: Style,
|
||||
transitionConfig?: () => TransitionConfig,
|
||||
onTransitionStart?: () => void,
|
||||
@@ -215,6 +214,7 @@ export type NavigationStackViewConfig = {
|
||||
};
|
||||
|
||||
export type NavigationStackScreenOptions = NavigationScreenOptions & {
|
||||
header?: ?(React.Element<*> | ((HeaderProps) => React.Element<*>)),
|
||||
headerTitle?: string | React.Element<*>,
|
||||
headerTitleStyle?: Style,
|
||||
headerTintColor?: string,
|
||||
@@ -224,7 +224,6 @@ export type NavigationStackScreenOptions = NavigationScreenOptions & {
|
||||
headerPressColorAndroid?: string,
|
||||
headerRight?: React.Element<*>,
|
||||
headerStyle?: Style,
|
||||
headerVisible?: boolean,
|
||||
gesturesEnabled?: boolean,
|
||||
};
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ export default (
|
||||
initialRouteName,
|
||||
initialRouteParams,
|
||||
paths,
|
||||
headerComponent,
|
||||
headerMode,
|
||||
mode,
|
||||
cardStyle,
|
||||
@@ -52,7 +51,6 @@ export default (
|
||||
)((props: *) => (
|
||||
<CardStackTransitioner
|
||||
{...props}
|
||||
headerComponent={headerComponent}
|
||||
headerMode={headerMode}
|
||||
mode={mode}
|
||||
cardStyle={cardStyle}
|
||||
|
||||
@@ -14,7 +14,7 @@ test('should get config for screen', () => {
|
||||
class HomeScreen extends Component {
|
||||
static navigationOptions = ({ navigation }: *) => ({
|
||||
title: `Welcome ${navigation.state.params ? navigation.state.params.user : 'anonymous'}`,
|
||||
headerVisible: true,
|
||||
gesturesEnabled: true,
|
||||
});
|
||||
|
||||
render() {
|
||||
@@ -25,7 +25,7 @@ test('should get config for screen', () => {
|
||||
class SettingsScreen extends Component {
|
||||
static navigationOptions = {
|
||||
title: 'Settings!!!',
|
||||
headerVisible: false,
|
||||
gesturesEnabled: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -36,7 +36,7 @@ test('should get config for screen', () => {
|
||||
class NotificationScreen extends Component {
|
||||
static navigationOptions = ({ navigation }: *) => ({
|
||||
title: '42',
|
||||
headerVisible: navigation.state.params
|
||||
gesturesEnabled: navigation.state.params
|
||||
? !navigation.state.params.fullscreen
|
||||
: true,
|
||||
});
|
||||
@@ -83,7 +83,7 @@ test('should get config for screen', () => {
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[0], dispatch: () => false }),
|
||||
{},
|
||||
).headerVisible,
|
||||
).gesturesEnabled,
|
||||
).toEqual(true);
|
||||
expect(
|
||||
getScreenOptions(
|
||||
@@ -95,7 +95,7 @@ test('should get config for screen', () => {
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[2], dispatch: () => false }),
|
||||
{},
|
||||
).headerVisible,
|
||||
).gesturesEnabled,
|
||||
).toEqual(false);
|
||||
expect(
|
||||
getScreenOptions(
|
||||
@@ -107,13 +107,13 @@ test('should get config for screen', () => {
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[3], dispatch: () => false }),
|
||||
{},
|
||||
).headerVisible,
|
||||
).gesturesEnabled,
|
||||
).toEqual(true);
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[4], dispatch: () => false }),
|
||||
{},
|
||||
).headerVisible,
|
||||
).gesturesEnabled,
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
@@ -123,7 +123,7 @@ test('should throw if the route does not exist', () => {
|
||||
const HomeScreen = () => null;
|
||||
HomeScreen.navigationOptions = {
|
||||
title: 'Home screen',
|
||||
headerVisible: true,
|
||||
gesturesEnabled: true,
|
||||
};
|
||||
|
||||
const getScreenOptions = createConfigGetter({
|
||||
|
||||
@@ -3,7 +3,7 @@ import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
import type { NavigationRoute } from '../TypeDefinition';
|
||||
|
||||
const deprecatedKeys = ['tabBar', 'header'];
|
||||
const deprecatedKeys = ['tabBar'];
|
||||
|
||||
/**
|
||||
* Make sure screen options returned by the `getScreenOption`
|
||||
|
||||
33
packages/react-navigation/src/views/CardStack.js
vendored
33
packages/react-navigation/src/views/CardStack.js
vendored
@@ -13,6 +13,7 @@ import {
|
||||
} from 'react-native';
|
||||
|
||||
import Card from './Card';
|
||||
import Header from './Header';
|
||||
import NavigationActions from '../NavigationActions';
|
||||
import addNavigationHelpers from '../addNavigationHelpers';
|
||||
import SceneView from './SceneView';
|
||||
@@ -163,14 +164,24 @@ class CardStack extends Component {
|
||||
scene: NavigationScene,
|
||||
headerMode: HeaderMode,
|
||||
): ?React.Element<*> {
|
||||
return (
|
||||
<this.props.headerComponent
|
||||
{...this.props}
|
||||
scene={scene}
|
||||
mode={headerMode}
|
||||
getScreenDetails={this._getScreenDetails}
|
||||
/>
|
||||
);
|
||||
const { header } = this._getScreenDetails(scene).options;
|
||||
|
||||
if (typeof header !== 'undefined' && typeof header !== 'function') {
|
||||
return header;
|
||||
}
|
||||
|
||||
const renderHeader = header || ((props: *) => <Header {...props} />);
|
||||
|
||||
// We need to explicitly exclude `mode` since Flow doesn't see
|
||||
// mode: headerMode override below and reports prop mismatch
|
||||
const { mode, ...passProps } = this.props;
|
||||
|
||||
return renderHeader({
|
||||
...passProps,
|
||||
scene,
|
||||
mode: headerMode,
|
||||
getScreenDetails: this._getScreenDetails,
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
@@ -369,10 +380,6 @@ class CardStack extends Component {
|
||||
const { screenProps } = this.props;
|
||||
const headerMode = this._getHeaderMode();
|
||||
if (headerMode === 'screen') {
|
||||
const isHeaderHidden = options.headerVisible === false;
|
||||
const maybeHeader = isHeaderHidden
|
||||
? null
|
||||
: this._renderHeader(scene, headerMode);
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={{ flex: 1 }}>
|
||||
@@ -383,7 +390,7 @@ class CardStack extends Component {
|
||||
navigationOptions={options}
|
||||
/>
|
||||
</View>
|
||||
{maybeHeader}
|
||||
{this._renderHeader(scene, headerMode)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import CardStack from './CardStack';
|
||||
import CardStackStyleInterpolator from './CardStackStyleInterpolator';
|
||||
import Transitioner from './Transitioner';
|
||||
import TransitionConfigs from './TransitionConfigs';
|
||||
import Header from './Header';
|
||||
|
||||
import type {
|
||||
NavigationAction,
|
||||
@@ -28,7 +27,6 @@ const NativeAnimatedModule = NativeModules &&
|
||||
type Props = {
|
||||
screenProps?: {},
|
||||
headerMode: HeaderMode,
|
||||
headerComponent?: ReactClass<*>,
|
||||
mode: 'card' | 'modal',
|
||||
navigation: NavigationScreenProp<NavigationState, NavigationAction>,
|
||||
router: NavigationRouter<NavigationState, NavigationAction, NavigationStackScreenOptions>,
|
||||
@@ -44,7 +42,6 @@ type Props = {
|
||||
|
||||
type DefaultProps = {
|
||||
mode: 'card' | 'modal',
|
||||
headerComponent: ReactClass<*>,
|
||||
};
|
||||
|
||||
class CardStackTransitioner extends Component<DefaultProps, Props, void> {
|
||||
@@ -52,7 +49,6 @@ class CardStackTransitioner extends Component<DefaultProps, Props, void> {
|
||||
|
||||
static defaultProps: DefaultProps = {
|
||||
mode: 'card',
|
||||
headerComponent: Header,
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -99,7 +95,6 @@ class CardStackTransitioner extends Component<DefaultProps, Props, void> {
|
||||
_render = (props: NavigationTransitionProps): React.Element<*> => {
|
||||
const {
|
||||
screenProps,
|
||||
headerComponent,
|
||||
headerMode,
|
||||
mode,
|
||||
router,
|
||||
@@ -110,7 +105,6 @@ class CardStackTransitioner extends Component<DefaultProps, Props, void> {
|
||||
return (
|
||||
<CardStack
|
||||
screenProps={screenProps}
|
||||
headerComponent={headerComponent}
|
||||
headerMode={headerMode}
|
||||
mode={mode}
|
||||
router={router}
|
||||
|
||||
Reference in New Issue
Block a user