mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-09 22:51:57 +08:00
139 lines
4.0 KiB
TypeScript
139 lines
4.0 KiB
TypeScript
import * as React from 'react';
|
|
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
|
|
import { NavigationContext, NavigationRoute } from 'react-navigation';
|
|
import {
|
|
Layout,
|
|
HeaderScene,
|
|
NavigationStackProp,
|
|
HeaderStyleInterpolator,
|
|
} from '../../types';
|
|
import Header from './Header';
|
|
import { forStatic } from '../../TransitionConfigs/HeaderStyleInterpolators';
|
|
|
|
export type Props = {
|
|
mode: 'float' | 'screen';
|
|
layout: Layout;
|
|
scenes: Array<HeaderScene | undefined>;
|
|
navigation: NavigationStackProp;
|
|
getPreviousRoute: (props: {
|
|
route: NavigationRoute;
|
|
}) => NavigationRoute | undefined;
|
|
onContentHeightChange?: (props: {
|
|
route: NavigationRoute;
|
|
height: number;
|
|
}) => void;
|
|
styleInterpolator: HeaderStyleInterpolator;
|
|
style?: StyleProp<ViewStyle>;
|
|
};
|
|
|
|
export default function HeaderContainer({
|
|
mode,
|
|
scenes,
|
|
layout,
|
|
navigation,
|
|
getPreviousRoute,
|
|
onContentHeightChange,
|
|
styleInterpolator,
|
|
style,
|
|
}: Props) {
|
|
const focusedRoute = navigation.state.routes[navigation.state.index];
|
|
|
|
return (
|
|
<View pointerEvents="box-none" style={style}>
|
|
{scenes.map((scene, i, self) => {
|
|
if ((mode === 'screen' && i !== self.length - 1) || !scene) {
|
|
return null;
|
|
}
|
|
|
|
const { options } = scene.descriptor;
|
|
const isFocused = focusedRoute.key === scene.route.key;
|
|
const previousRoute = getPreviousRoute({ route: scene.route });
|
|
|
|
let previous;
|
|
|
|
if (previousRoute) {
|
|
// The previous scene will be shortly before the current scene in the array
|
|
// So loop back from current index to avoid looping over the full array
|
|
for (let j = i - 1; j >= 0; j--) {
|
|
const s = self[j];
|
|
|
|
if (s && s.route.key === previousRoute.key) {
|
|
previous = s;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the screen is next to a headerless screen, we need to make the header appear static
|
|
// This makes the header look like it's moving with the screen
|
|
const previousScene = self[i - 1];
|
|
const nextScene = self[i + 1];
|
|
const isHeaderStatic =
|
|
mode === 'float'
|
|
? (previousScene &&
|
|
previousScene.descriptor.options.header === null &&
|
|
// We still need to animate when coming back from next scene
|
|
// A hacky way to check this is if the next scene exists
|
|
!nextScene) ||
|
|
(nextScene && nextScene.descriptor.options.header === null)
|
|
: false;
|
|
|
|
const props = {
|
|
mode,
|
|
layout,
|
|
scene,
|
|
previous,
|
|
navigation: scene.descriptor.navigation as NavigationStackProp,
|
|
styleInterpolator: isHeaderStatic ? forStatic : styleInterpolator,
|
|
};
|
|
|
|
return (
|
|
<NavigationContext.Provider
|
|
key={scene.route.key}
|
|
value={scene.descriptor.navigation}
|
|
>
|
|
<View
|
|
onLayout={
|
|
onContentHeightChange
|
|
? e =>
|
|
onContentHeightChange({
|
|
route: scene.route,
|
|
height: e.nativeEvent.layout.height,
|
|
})
|
|
: undefined
|
|
}
|
|
pointerEvents="box-none"
|
|
accessibilityElementsHidden={!isFocused}
|
|
importantForAccessibility={
|
|
isFocused ? 'auto' : 'no-hide-descendants'
|
|
}
|
|
style={
|
|
mode === 'float' || options.headerTransparent
|
|
? styles.header
|
|
: null
|
|
}
|
|
>
|
|
{options.header !== undefined ? (
|
|
options.header === null ? null : (
|
|
options.header(props)
|
|
)
|
|
) : (
|
|
<Header {...props} />
|
|
)}
|
|
</View>
|
|
</NavigationContext.Provider>
|
|
);
|
|
})}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
header: {
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
},
|
|
});
|