mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-04-28 20:35:19 +08:00
feat: add custom theme support (#211)
This commit is contained in:
@@ -33,10 +33,12 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.0.0-alpha.16"
|
||||
"@react-navigation/routers": "^5.0.0-alpha.16",
|
||||
"color": "^3.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.7.0",
|
||||
"@types/color": "^3.0.0",
|
||||
"@types/react": "^16.9.11",
|
||||
"@types/react-native": "^0.60.22",
|
||||
"del-cli": "^3.0.0",
|
||||
|
||||
@@ -9,7 +9,11 @@ import {
|
||||
ScaledSize,
|
||||
Dimensions,
|
||||
} from 'react-native';
|
||||
import { NavigationContext, CommonActions } from '@react-navigation/native';
|
||||
import {
|
||||
NavigationContext,
|
||||
CommonActions,
|
||||
useTheme,
|
||||
} from '@react-navigation/native';
|
||||
import { SafeAreaConsumer } from 'react-native-safe-area-context';
|
||||
|
||||
import BottomTabItem from './BottomTabItem';
|
||||
@@ -41,6 +45,8 @@ export default function BottomTabBar({
|
||||
style,
|
||||
tabStyle,
|
||||
}: Props) {
|
||||
const { colors } = useTheme();
|
||||
|
||||
const [dimensions, setDimensions] = React.useState(Dimensions.get('window'));
|
||||
const [layout, setLayout] = React.useState({ height: 0, width: 0 });
|
||||
const [keyboardShown, setKeyboardShown] = React.useState(false);
|
||||
@@ -162,6 +168,10 @@ export default function BottomTabBar({
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.tabBar,
|
||||
{
|
||||
backgroundColor: colors.card,
|
||||
borderTopColor: colors.border,
|
||||
},
|
||||
keyboardHidesTabBar
|
||||
? {
|
||||
// When the keyboard is shown, slide down the tab bar
|
||||
@@ -267,9 +277,7 @@ const styles = StyleSheet.create({
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: '#fff',
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderTopColor: 'rgba(0, 0, 0, .3)',
|
||||
elevation: 8,
|
||||
},
|
||||
content: {
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
ViewStyle,
|
||||
TextStyle,
|
||||
} from 'react-native';
|
||||
import { Route } from '@react-navigation/native';
|
||||
import { Route, useTheme } from '@react-navigation/native';
|
||||
import Color from 'color';
|
||||
|
||||
import TabBarIcon from './TabBarIcon';
|
||||
import { BottomTabBarButtonProps } from '../types';
|
||||
@@ -113,8 +114,8 @@ export default function BottomTabBarItem({
|
||||
onPress,
|
||||
onLongPress,
|
||||
horizontal,
|
||||
activeTintColor = '#007AFF',
|
||||
inactiveTintColor = '#8E8E93',
|
||||
activeTintColor: customActiveTintColor,
|
||||
inactiveTintColor: customInactiveTintColor,
|
||||
activeBackgroundColor = 'transparent',
|
||||
inactiveBackgroundColor = 'transparent',
|
||||
showLabel = true,
|
||||
@@ -123,6 +124,20 @@ export default function BottomTabBarItem({
|
||||
labelStyle,
|
||||
style,
|
||||
}: Props) {
|
||||
const { colors } = useTheme();
|
||||
|
||||
const activeTintColor =
|
||||
customActiveTintColor === undefined
|
||||
? colors.primary
|
||||
: customActiveTintColor;
|
||||
|
||||
const inactiveTintColor =
|
||||
customInactiveTintColor === undefined
|
||||
? Color(colors.text)
|
||||
.mix(Color(colors.card), 0.5)
|
||||
.hex()
|
||||
: customInactiveTintColor;
|
||||
|
||||
const renderLabel = ({ focused }: { focused: boolean }) => {
|
||||
if (showLabel === false) {
|
||||
return null;
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as React from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
|
||||
import { TabNavigationState } from '@react-navigation/routers';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { ScreenContainer } from 'react-native-screens';
|
||||
|
||||
@@ -25,6 +26,26 @@ type State = {
|
||||
loaded: number[];
|
||||
};
|
||||
|
||||
function SceneContent({
|
||||
isFocused,
|
||||
children,
|
||||
}: {
|
||||
isFocused: boolean;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<View
|
||||
accessibilityElementsHidden={!isFocused}
|
||||
importantForAccessibility={isFocused ? 'auto' : 'no-hide-descendants'}
|
||||
style={[styles.content, { backgroundColor: colors.background }]}
|
||||
>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default class BottomTabView extends React.Component<Props, State> {
|
||||
static defaultProps = {
|
||||
lazy: true,
|
||||
@@ -97,15 +118,9 @@ export default class BottomTabView extends React.Component<Props, State> {
|
||||
style={StyleSheet.absoluteFill}
|
||||
isVisible={isFocused}
|
||||
>
|
||||
<View
|
||||
accessibilityElementsHidden={!isFocused}
|
||||
importantForAccessibility={
|
||||
isFocused ? 'auto' : 'no-hide-descendants'
|
||||
}
|
||||
style={styles.content}
|
||||
>
|
||||
<SceneContent isFocused={isFocused}>
|
||||
{descriptors[route.key].render()}
|
||||
</View>
|
||||
</SceneContent>
|
||||
</ResourceSavingScene>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -34,7 +34,8 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.0.0-alpha.16"
|
||||
"@react-navigation/routers": "^5.0.0-alpha.16",
|
||||
"color": "^3.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.7.0",
|
||||
|
||||
@@ -10,6 +10,7 @@ export { default as DrawerView } from './views/DrawerView';
|
||||
export { default as DrawerItem } from './views/DrawerItem';
|
||||
export { default as DrawerItemList } from './views/DrawerItemList';
|
||||
export { default as DrawerContent } from './views/DrawerContent';
|
||||
export { default as DrawerContentScrollView } from './views/DrawerContentScrollView';
|
||||
|
||||
/**
|
||||
* Utilities
|
||||
|
||||
@@ -121,7 +121,7 @@ export type DrawerNavigationOptions = {
|
||||
|
||||
export type DrawerContentComponentProps<T = DrawerContentOptions> = T & {
|
||||
state: DrawerNavigationState;
|
||||
navigation: NavigationHelpers<ParamListBase>;
|
||||
navigation: DrawerNavigationHelpers;
|
||||
descriptors: DrawerDescriptorMap;
|
||||
/**
|
||||
* Animated node which represents the current progress of the drawer's open state.
|
||||
|
||||
@@ -1,36 +1,12 @@
|
||||
import * as React from 'react';
|
||||
import { ScrollView, StyleSheet } from 'react-native';
|
||||
import { useSafeArea } from 'react-native-safe-area-context';
|
||||
import DrawerItemList from './DrawerItemList';
|
||||
import { DrawerContentComponentProps } from '../types';
|
||||
import DrawerContentScrollView from './DrawerContentScrollView';
|
||||
|
||||
export default function DrawerContent({
|
||||
contentContainerStyle,
|
||||
style,
|
||||
drawerPosition,
|
||||
...rest
|
||||
}: DrawerContentComponentProps) {
|
||||
const insets = useSafeArea();
|
||||
|
||||
export default function DrawerContent(props: DrawerContentComponentProps) {
|
||||
return (
|
||||
<ScrollView
|
||||
contentContainerStyle={[
|
||||
{
|
||||
paddingTop: insets.top + 4,
|
||||
paddingLeft: drawerPosition === 'left' ? insets.left : 0,
|
||||
paddingRight: drawerPosition === 'right' ? insets.right : 0,
|
||||
},
|
||||
contentContainerStyle,
|
||||
]}
|
||||
style={[styles.container, style]}
|
||||
>
|
||||
<DrawerItemList {...rest} />
|
||||
</ScrollView>
|
||||
<DrawerContentScrollView {...props}>
|
||||
<DrawerItemList {...props} />
|
||||
</DrawerContentScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
43
packages/drawer/src/views/DrawerContentScrollView.tsx
Normal file
43
packages/drawer/src/views/DrawerContentScrollView.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import * as React from 'react';
|
||||
import { ScrollView, StyleSheet, ScrollViewProps } from 'react-native';
|
||||
import { useSafeArea } from 'react-native-safe-area-context';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
|
||||
type Props = ScrollViewProps & {
|
||||
drawerPosition: 'left' | 'right';
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export default function DrawerContentScrollView({
|
||||
contentContainerStyle,
|
||||
style,
|
||||
drawerPosition,
|
||||
children,
|
||||
...rest
|
||||
}: Props) {
|
||||
const insets = useSafeArea();
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
{...rest}
|
||||
contentContainerStyle={[
|
||||
{
|
||||
paddingTop: insets.top + 4,
|
||||
paddingLeft: drawerPosition === 'left' ? insets.left : 0,
|
||||
paddingRight: drawerPosition === 'right' ? insets.right : 0,
|
||||
},
|
||||
contentContainerStyle,
|
||||
]}
|
||||
style={[styles.container, { backgroundColor: colors.card }, style]}
|
||||
>
|
||||
{children}
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
@@ -7,6 +7,8 @@ import {
|
||||
ViewStyle,
|
||||
TextStyle,
|
||||
} from 'react-native';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
import Color from 'color';
|
||||
import TouchableItem from './TouchableItem';
|
||||
|
||||
type Props = {
|
||||
@@ -61,19 +63,29 @@ type Props = {
|
||||
/**
|
||||
* A component used to show an action item with an icon and a label in a navigation drawer.
|
||||
*/
|
||||
export default function DrawerItem({
|
||||
icon,
|
||||
label,
|
||||
labelStyle,
|
||||
focused = false,
|
||||
activeTintColor = '#6200ee',
|
||||
inactiveTintColor = 'rgba(0, 0, 0, .68)',
|
||||
activeBackgroundColor = 'rgba(98, 0, 238, 0.12)',
|
||||
inactiveBackgroundColor = 'transparent',
|
||||
style,
|
||||
onPress,
|
||||
...rest
|
||||
}: Props) {
|
||||
export default function DrawerItem(props: Props) {
|
||||
const { colors } = useTheme();
|
||||
|
||||
const {
|
||||
icon,
|
||||
label,
|
||||
labelStyle,
|
||||
focused = false,
|
||||
activeTintColor = colors.primary,
|
||||
inactiveTintColor = Color(colors.text)
|
||||
.alpha(0.68)
|
||||
.rgb()
|
||||
.string(),
|
||||
activeBackgroundColor = Color(activeTintColor)
|
||||
.alpha(0.12)
|
||||
.rgb()
|
||||
.string(),
|
||||
inactiveBackgroundColor = 'transparent',
|
||||
style,
|
||||
onPress,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const { borderRadius = 4 } = StyleSheet.flatten(style || {});
|
||||
const color = focused ? activeTintColor : inactiveTintColor;
|
||||
const backgroundColor = focused
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { BottomNavigation } from 'react-native-paper';
|
||||
import { BottomNavigation, DefaultTheme, DarkTheme } from 'react-native-paper';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import { Route } from '@react-navigation/native';
|
||||
import { Route, useTheme } from '@react-navigation/native';
|
||||
import { TabNavigationState, TabActions } from '@react-navigation/routers';
|
||||
|
||||
import {
|
||||
@@ -25,9 +25,25 @@ export default function MaterialBottomTabView({
|
||||
descriptors,
|
||||
...rest
|
||||
}: Props) {
|
||||
const { dark, colors } = useTheme();
|
||||
|
||||
const theme = React.useMemo(() => {
|
||||
const t = dark ? DarkTheme : DefaultTheme;
|
||||
|
||||
return {
|
||||
...t,
|
||||
colors: {
|
||||
...t.colors,
|
||||
...colors,
|
||||
surface: colors.card,
|
||||
},
|
||||
};
|
||||
}, [colors, dark]);
|
||||
|
||||
return (
|
||||
<BottomNavigation
|
||||
{...rest}
|
||||
theme={theme}
|
||||
navigationState={state}
|
||||
onIndexChange={(index: number) =>
|
||||
navigation.dispatch({
|
||||
|
||||
@@ -34,7 +34,8 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.0.0-alpha.16"
|
||||
"@react-navigation/routers": "^5.0.0-alpha.16",
|
||||
"color": "^3.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.7.0",
|
||||
|
||||
@@ -1,29 +1,46 @@
|
||||
import * as React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import { TabBar } from 'react-native-tab-view';
|
||||
import { Route } from '@react-navigation/native';
|
||||
import { Route, useTheme } from '@react-navigation/native';
|
||||
import Color from 'color';
|
||||
|
||||
import { MaterialTopTabBarProps } from '../types';
|
||||
|
||||
export default function TabBarTop({
|
||||
state,
|
||||
navigation,
|
||||
descriptors,
|
||||
activeTintColor = 'rgba(255, 255, 255, 1)',
|
||||
inactiveTintColor = 'rgba(255, 255, 255, 0.7)',
|
||||
allowFontScaling = true,
|
||||
iconStyle,
|
||||
labelStyle,
|
||||
showIcon = false,
|
||||
showLabel = true,
|
||||
...rest
|
||||
}: MaterialTopTabBarProps) {
|
||||
export default function TabBarTop(props: MaterialTopTabBarProps) {
|
||||
const { colors } = useTheme();
|
||||
|
||||
const {
|
||||
state,
|
||||
navigation,
|
||||
descriptors,
|
||||
activeTintColor = colors.text,
|
||||
inactiveTintColor = Color(activeTintColor)
|
||||
.alpha(0.5)
|
||||
.rgb()
|
||||
.string(),
|
||||
allowFontScaling = true,
|
||||
showIcon = false,
|
||||
showLabel = true,
|
||||
pressColor = Color(activeTintColor)
|
||||
.alpha(0.08)
|
||||
.rgb()
|
||||
.string(),
|
||||
iconStyle,
|
||||
labelStyle,
|
||||
indicatorStyle,
|
||||
style,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<TabBar
|
||||
{...rest}
|
||||
navigationState={state}
|
||||
activeColor={activeTintColor}
|
||||
inactiveColor={inactiveTintColor}
|
||||
indicatorStyle={[{ backgroundColor: colors.primary }, indicatorStyle]}
|
||||
style={[{ backgroundColor: colors.card }, style]}
|
||||
pressColor={pressColor}
|
||||
getAccessibilityLabel={({ route }) =>
|
||||
descriptors[route.key].options.tabBarAccessibilityLabel
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { TabView, SceneRendererProps } from 'react-native-tab-view';
|
||||
import { Route } from '@react-navigation/native';
|
||||
import { Route, useTheme } from '@react-navigation/native';
|
||||
import { TabNavigationState, TabActions } from '@react-navigation/routers';
|
||||
|
||||
import MaterialTopTabBar from './MaterialTopTabBar';
|
||||
@@ -18,6 +19,16 @@ type Props = MaterialTopTabNavigationConfig & {
|
||||
tabBarPosition: 'top' | 'bottom';
|
||||
};
|
||||
|
||||
function SceneContent({ children }: { children: React.ReactNode }) {
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default class MaterialTopTabView extends React.PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
tabBarPosition: 'top',
|
||||
@@ -93,7 +104,9 @@ export default class MaterialTopTabView extends React.PureComponent<Props> {
|
||||
target: state.key,
|
||||
})
|
||||
}
|
||||
renderScene={({ route }) => descriptors[route.key].render()}
|
||||
renderScene={({ route }) => (
|
||||
<SceneContent>{descriptors[route.key].render()}</SceneContent>
|
||||
)}
|
||||
navigationState={state}
|
||||
renderTabBar={this.renderTabBar}
|
||||
renderLazyPlaceholder={this.renderLazyPlaceholder}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
ScreenStackHeaderRightView,
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
} from 'react-native-screens';
|
||||
import { Route } from '@react-navigation/native';
|
||||
import { Route, useTheme } from '@react-navigation/native';
|
||||
import { NativeStackNavigationOptions } from '../types';
|
||||
|
||||
type Props = NativeStackNavigationOptions & {
|
||||
@@ -14,6 +14,7 @@ type Props = NativeStackNavigationOptions & {
|
||||
};
|
||||
|
||||
export default function HeaderConfig(props: Props) {
|
||||
const { colors } = useTheme();
|
||||
const {
|
||||
route,
|
||||
title,
|
||||
@@ -52,17 +53,23 @@ export default function HeaderConfig(props: Props) {
|
||||
titleColor={
|
||||
headerTitleStyle.color !== undefined
|
||||
? headerTitleStyle.color
|
||||
: headerTintColor
|
||||
: headerTintColor !== undefined
|
||||
? headerTintColor
|
||||
: colors.text
|
||||
}
|
||||
backTitle={headerBackTitleVisible ? headerBackTitle : ''}
|
||||
backTitleFontFamily={headerBackTitleStyle.fontFamily}
|
||||
backTitleFontSize={headerBackTitleStyle.fontSize}
|
||||
color={headerTintColor}
|
||||
color={headerTintColor !== undefined ? headerTintColor : colors.primary}
|
||||
gestureEnabled={gestureEnabled === undefined ? true : gestureEnabled}
|
||||
largeTitle={headerLargeTitle}
|
||||
largeTitleFontFamily={headerLargeTitleStyle.fontFamily}
|
||||
largeTitleFontSize={headerLargeTitleStyle.fontSize}
|
||||
backgroundColor={headerStyle.backgroundColor}
|
||||
backgroundColor={
|
||||
headerStyle.backgroundColor !== undefined
|
||||
? headerStyle.backgroundColor
|
||||
: colors.card
|
||||
}
|
||||
>
|
||||
{headerRight !== undefined ? (
|
||||
<ScreenStackHeaderRightView>{headerRight()}</ScreenStackHeaderRightView>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ScreenProps,
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
} from 'react-native-screens';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
import HeaderConfig from './HeaderConfig';
|
||||
import {
|
||||
NativeStackNavigationHelpers,
|
||||
@@ -34,8 +35,10 @@ export default function NativeStackView({
|
||||
navigation,
|
||||
descriptors,
|
||||
}: Props) {
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<ScreenStack style={styles.scenes}>
|
||||
<ScreenStack style={styles.container}>
|
||||
{state.routes.map(route => {
|
||||
const { options, render: renderScene } = descriptors[route.key];
|
||||
const { presentation = 'push', animation, contentStyle } = options;
|
||||
@@ -55,7 +58,15 @@ export default function NativeStackView({
|
||||
}}
|
||||
>
|
||||
<HeaderConfig {...options} route={route} />
|
||||
<View style={[styles.content, contentStyle]}>{renderScene()}</View>
|
||||
<View
|
||||
style={[
|
||||
styles.container,
|
||||
{ backgroundColor: colors.background },
|
||||
contentStyle,
|
||||
]}
|
||||
>
|
||||
{renderScene()}
|
||||
</View>
|
||||
</Screen>
|
||||
);
|
||||
})}
|
||||
@@ -64,11 +75,7 @@ export default function NativeStackView({
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
content: {
|
||||
flex: 1,
|
||||
backgroundColor: '#eee',
|
||||
},
|
||||
scenes: {
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -4,7 +4,14 @@ import {
|
||||
NavigationContainerProps,
|
||||
NavigationContainerRef,
|
||||
} from '@react-navigation/core';
|
||||
import ThemeProvider from './theming/ThemeProvider';
|
||||
import DefaultTheme from './theming/DefaultTheme';
|
||||
import useBackButton from './useBackButton';
|
||||
import { Theme } from './types';
|
||||
|
||||
type Props = NavigationContainerProps & {
|
||||
theme?: Theme;
|
||||
};
|
||||
|
||||
/**
|
||||
* Container component which holds the navigation state
|
||||
@@ -13,11 +20,12 @@ import useBackButton from './useBackButton';
|
||||
*
|
||||
* @param props.initialState Initial state object for the navigation tree.
|
||||
* @param props.onStateChange Callback which is called with the latest navigation state when it changes.
|
||||
* @param props.theme Theme object for the navigators.
|
||||
* @param props.children Child elements to render the content.
|
||||
* @param props.ref Ref object which refers to the navigation object containing helper methods.
|
||||
*/
|
||||
const NavigationNativeContainer = React.forwardRef(function NativeContainer(
|
||||
props: NavigationContainerProps,
|
||||
{ theme = DefaultTheme, ...rest }: Props,
|
||||
ref: React.Ref<NavigationContainerRef>
|
||||
) {
|
||||
const refContainer = React.useRef<NavigationContainerRef>(null);
|
||||
@@ -27,11 +35,9 @@ const NavigationNativeContainer = React.forwardRef(function NativeContainer(
|
||||
React.useImperativeHandle(ref, () => refContainer.current);
|
||||
|
||||
return (
|
||||
<NavigationContainer
|
||||
{...props}
|
||||
ref={refContainer}
|
||||
children={props.children}
|
||||
/>
|
||||
<ThemeProvider value={theme}>
|
||||
<NavigationContainer {...rest} ref={refContainer} />
|
||||
</ThemeProvider>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -5,3 +5,8 @@ export { default as NavigationNativeContainer } from './NavigationNativeContaine
|
||||
export { default as useBackButton } from './useBackButton';
|
||||
export { default as useLinking } from './useLinking';
|
||||
export { default as useScrollToTop } from './useScrollToTop';
|
||||
|
||||
export { default as DefaultTheme } from './theming/DefaultTheme';
|
||||
export { default as DarkTheme } from './theming/DarkTheme';
|
||||
export { default as ThemeProvider } from './theming/ThemeProvider';
|
||||
export { default as useTheme } from './theming/useTheme';
|
||||
|
||||
14
packages/native/src/theming/DarkTheme.tsx
Normal file
14
packages/native/src/theming/DarkTheme.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Theme } from '../types';
|
||||
|
||||
const DarkTheme: Theme = {
|
||||
dark: true,
|
||||
colors: {
|
||||
primary: 'rgb(10, 132, 255)',
|
||||
background: 'rgb(1, 1, 1)',
|
||||
card: 'rgb(18, 18, 18)',
|
||||
text: 'rgb(229, 229, 231)',
|
||||
border: 'rgb(39, 39, 41)',
|
||||
},
|
||||
};
|
||||
|
||||
export default DarkTheme;
|
||||
14
packages/native/src/theming/DefaultTheme.tsx
Normal file
14
packages/native/src/theming/DefaultTheme.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Theme } from '../types';
|
||||
|
||||
const DefaultTheme: Theme = {
|
||||
dark: false,
|
||||
colors: {
|
||||
primary: 'rgb(0, 122, 255)',
|
||||
background: 'rgb(242, 242, 242)',
|
||||
card: 'rgb(255, 255, 255)',
|
||||
text: 'rgb(28, 28, 30)',
|
||||
border: 'rgb(199, 199, 204)',
|
||||
},
|
||||
};
|
||||
|
||||
export default DefaultTheme;
|
||||
7
packages/native/src/theming/ThemeContext.tsx
Normal file
7
packages/native/src/theming/ThemeContext.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import DefaultTheme from './DefaultTheme';
|
||||
import { Theme } from '../types';
|
||||
|
||||
const ThemeContext = React.createContext<Theme>(DefaultTheme);
|
||||
|
||||
export default ThemeContext;
|
||||
14
packages/native/src/theming/ThemeProvider.tsx
Normal file
14
packages/native/src/theming/ThemeProvider.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import ThemeContext from './ThemeContext';
|
||||
import { Theme } from '../types';
|
||||
|
||||
type Props = {
|
||||
value: Theme;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export default function ThemeProvider({ value, children }: Props) {
|
||||
return (
|
||||
<ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
|
||||
);
|
||||
}
|
||||
8
packages/native/src/theming/useTheme.tsx
Normal file
8
packages/native/src/theming/useTheme.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import ThemeContext from './ThemeContext';
|
||||
|
||||
export default function useTheme() {
|
||||
const theme = React.useContext(ThemeContext);
|
||||
|
||||
return theme;
|
||||
}
|
||||
10
packages/native/src/types.tsx
Normal file
10
packages/native/src/types.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
export type Theme = {
|
||||
dark: boolean;
|
||||
colors: {
|
||||
primary: string;
|
||||
background: string;
|
||||
card: string;
|
||||
text: string;
|
||||
border: string;
|
||||
};
|
||||
};
|
||||
@@ -406,6 +406,10 @@ export type StackHeaderTitleProps = {
|
||||
* Whether title font should scale to respect Text Size accessibility settings.
|
||||
*/
|
||||
allowFontScaling?: boolean;
|
||||
/**
|
||||
* Tint color for the header.
|
||||
*/
|
||||
tintColor?: string;
|
||||
/**
|
||||
* Content of the title element. Usually the title string.
|
||||
*/
|
||||
|
||||
@@ -8,45 +8,56 @@ import {
|
||||
LayoutChangeEvent,
|
||||
} from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
import MaskedView from '../MaskedView';
|
||||
import TouchableItem from '../TouchableItem';
|
||||
import { StackHeaderLeftButtonProps } from '../../types';
|
||||
|
||||
type Props = StackHeaderLeftButtonProps & {
|
||||
tintColor: string;
|
||||
};
|
||||
type Props = StackHeaderLeftButtonProps;
|
||||
|
||||
type State = {
|
||||
fullLabelWidth?: number;
|
||||
};
|
||||
export default function HeaderBackButton({
|
||||
disabled,
|
||||
allowFontScaling,
|
||||
backImage,
|
||||
label,
|
||||
labelStyle,
|
||||
labelVisible = Platform.OS === 'ios',
|
||||
onLabelLayout,
|
||||
onPress,
|
||||
pressColorAndroid: customPressColorAndroid,
|
||||
screenLayout,
|
||||
tintColor: customTintColor,
|
||||
titleLayout,
|
||||
truncatedLabel = 'Back',
|
||||
}: Props) {
|
||||
const { dark, colors } = useTheme();
|
||||
|
||||
class HeaderBackButton extends React.Component<Props, State> {
|
||||
static defaultProps = {
|
||||
pressColorAndroid: 'rgba(0, 0, 0, .32)',
|
||||
tintColor: Platform.select({
|
||||
ios: '#037aff',
|
||||
web: '#5f6368',
|
||||
}),
|
||||
labelVisible: Platform.OS === 'ios',
|
||||
truncatedLabel: 'Back',
|
||||
};
|
||||
const [initialLabelWidth, setInitialLabelWidth] = React.useState<
|
||||
undefined | number
|
||||
>(undefined);
|
||||
|
||||
state: State = {};
|
||||
const tintColor =
|
||||
customTintColor !== undefined
|
||||
? customTintColor
|
||||
: Platform.select({
|
||||
ios: colors.primary,
|
||||
default: colors.text,
|
||||
});
|
||||
|
||||
private handleLabelLayout = (e: LayoutChangeEvent) => {
|
||||
const { onLabelLayout } = this.props;
|
||||
const pressColorAndroid =
|
||||
customPressColorAndroid !== undefined
|
||||
? customPressColorAndroid
|
||||
: dark
|
||||
? 'rgba(255, 255, 255, .32)'
|
||||
: 'rgba(0, 0, 0, .32)';
|
||||
|
||||
const handleLabelLayout = (e: LayoutChangeEvent) => {
|
||||
onLabelLayout && onLabelLayout(e);
|
||||
|
||||
this.setState({
|
||||
fullLabelWidth: e.nativeEvent.layout.x + e.nativeEvent.layout.width,
|
||||
});
|
||||
setInitialLabelWidth(e.nativeEvent.layout.x + e.nativeEvent.layout.width);
|
||||
};
|
||||
|
||||
private shouldTruncateLabel = () => {
|
||||
const { titleLayout, screenLayout, label } = this.props;
|
||||
const { fullLabelWidth: initialLabelWidth } = this.state;
|
||||
|
||||
const shouldTruncateLabel = () => {
|
||||
return (
|
||||
!label ||
|
||||
(initialLabelWidth &&
|
||||
@@ -56,9 +67,7 @@ class HeaderBackButton extends React.Component<Props, State> {
|
||||
);
|
||||
};
|
||||
|
||||
private renderBackImage = () => {
|
||||
const { backImage, labelVisible, tintColor } = this.props;
|
||||
|
||||
const renderBackImage = () => {
|
||||
if (backImage) {
|
||||
return backImage({ tintColor });
|
||||
} else {
|
||||
@@ -76,19 +85,8 @@ class HeaderBackButton extends React.Component<Props, State> {
|
||||
}
|
||||
};
|
||||
|
||||
private renderLabel() {
|
||||
const {
|
||||
label,
|
||||
truncatedLabel,
|
||||
allowFontScaling,
|
||||
labelVisible,
|
||||
backImage,
|
||||
labelStyle,
|
||||
tintColor,
|
||||
screenLayout,
|
||||
} = this.props;
|
||||
|
||||
const leftLabelText = this.shouldTruncateLabel() ? truncatedLabel : label;
|
||||
const renderLabel = () => {
|
||||
const leftLabelText = shouldTruncateLabel() ? truncatedLabel : label;
|
||||
|
||||
if (!labelVisible || leftLabelText === undefined) {
|
||||
return null;
|
||||
@@ -109,7 +107,7 @@ class HeaderBackButton extends React.Component<Props, State> {
|
||||
onLayout={
|
||||
// This measurement is used to determine if we should truncate the label when it doesn't fit
|
||||
// Only measure it when label is not truncated because we want the measurement of full label
|
||||
leftLabelText === label ? this.handleLabelLayout : undefined
|
||||
leftLabelText === label ? handleLabelLayout : undefined
|
||||
}
|
||||
style={[
|
||||
styles.label,
|
||||
@@ -145,42 +143,37 @@ class HeaderBackButton extends React.Component<Props, State> {
|
||||
{labelElement}
|
||||
</MaskedView>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
private handlePress = () =>
|
||||
this.props.onPress && requestAnimationFrame(this.props.onPress);
|
||||
const handlePress = () => onPress && requestAnimationFrame(onPress);
|
||||
|
||||
render() {
|
||||
const { pressColorAndroid, label, disabled } = this.props;
|
||||
|
||||
return (
|
||||
<TouchableItem
|
||||
disabled={disabled}
|
||||
accessible
|
||||
accessibilityRole="button"
|
||||
accessibilityComponentType="button"
|
||||
accessibilityLabel={
|
||||
label && label !== 'Back' ? `${label}, back` : 'Go back'
|
||||
}
|
||||
accessibilityTraits="button"
|
||||
testID="header-back"
|
||||
delayPressIn={0}
|
||||
onPress={disabled ? undefined : this.handlePress}
|
||||
pressColor={pressColorAndroid}
|
||||
style={[styles.container, disabled && styles.disabled]}
|
||||
hitSlop={Platform.select({
|
||||
ios: undefined,
|
||||
default: { top: 16, right: 16, bottom: 16, left: 16 },
|
||||
})}
|
||||
borderless
|
||||
>
|
||||
<React.Fragment>
|
||||
{this.renderBackImage()}
|
||||
{this.renderLabel()}
|
||||
</React.Fragment>
|
||||
</TouchableItem>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<TouchableItem
|
||||
disabled={disabled}
|
||||
accessible
|
||||
accessibilityRole="button"
|
||||
accessibilityComponentType="button"
|
||||
accessibilityLabel={
|
||||
label && label !== 'Back' ? `${label}, back` : 'Go back'
|
||||
}
|
||||
accessibilityTraits="button"
|
||||
testID="header-back"
|
||||
delayPressIn={0}
|
||||
onPress={disabled ? undefined : handlePress}
|
||||
pressColor={pressColorAndroid}
|
||||
style={[styles.container, disabled && styles.disabled]}
|
||||
hitSlop={Platform.select({
|
||||
ios: undefined,
|
||||
default: { top: 16, right: 16, bottom: 16, left: 16 },
|
||||
})}
|
||||
borderless
|
||||
>
|
||||
<React.Fragment>
|
||||
{renderBackImage()}
|
||||
{renderLabel()}
|
||||
</React.Fragment>
|
||||
</TouchableItem>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@@ -253,5 +246,3 @@ const styles = StyleSheet.create({
|
||||
transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }],
|
||||
},
|
||||
});
|
||||
|
||||
export default HeaderBackButton;
|
||||
|
||||
@@ -1,21 +1,35 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet, Platform, ViewProps } from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
|
||||
export default function HeaderBackground({ style, ...rest }: ViewProps) {
|
||||
return <Animated.View style={[styles.container, style]} {...rest} />;
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.container,
|
||||
{
|
||||
backgroundColor: colors.card,
|
||||
borderBottomColor: colors.border,
|
||||
shadowColor: colors.border,
|
||||
},
|
||||
style,
|
||||
]}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: 'white',
|
||||
...Platform.select({
|
||||
android: {
|
||||
elevation: 4,
|
||||
},
|
||||
ios: {
|
||||
shadowColor: 'rgba(0, 0, 0, 0.3)',
|
||||
shadowOpacity: 0.85,
|
||||
shadowRadius: 0,
|
||||
shadowOffset: {
|
||||
@@ -25,7 +39,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
default: {
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomColor: 'rgba(0, 0, 0, 0.20)',
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -339,7 +339,8 @@ export default class HeaderSegment extends React.Component<Props, State> {
|
||||
children: currentTitle,
|
||||
onLayout: this.handleTitleLayout,
|
||||
allowFontScaling: titleAllowFontScaling,
|
||||
style: [{ color: headerTintColor }, customTitleStyle],
|
||||
tintColor: headerTintColor,
|
||||
style: customTitleStyle,
|
||||
})}
|
||||
</Animated.View>
|
||||
{right ? (
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet, Platform, TextProps } from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
|
||||
type Props = TextProps & {
|
||||
tintColor?: string;
|
||||
children?: string;
|
||||
};
|
||||
|
||||
export default function HeaderTitle({ style, ...rest }: Props) {
|
||||
export default function HeaderTitle({ tintColor, style, ...rest }: Props) {
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<Animated.Text numberOfLines={1} {...rest} style={[styles.title, style]} />
|
||||
<Animated.Text
|
||||
numberOfLines={1}
|
||||
{...rest}
|
||||
style={[
|
||||
styles.title,
|
||||
{ color: tintColor === undefined ? colors.text : tintColor },
|
||||
style,
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,17 +29,14 @@ const styles = StyleSheet.create({
|
||||
ios: {
|
||||
fontSize: 17,
|
||||
fontWeight: '600',
|
||||
color: 'rgba(0, 0, 0, .9)',
|
||||
},
|
||||
android: {
|
||||
fontSize: 20,
|
||||
fontWeight: '500',
|
||||
color: 'rgba(0, 0, 0, .9)',
|
||||
},
|
||||
default: {
|
||||
fontSize: 18,
|
||||
fontWeight: '500',
|
||||
color: '#3c4043',
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -853,11 +853,7 @@ export default class Card extends React.Component<Props> {
|
||||
<PointerEventsView
|
||||
active={active}
|
||||
progress={this.props.current}
|
||||
style={[
|
||||
styles.content,
|
||||
transparent ? styles.transparent : styles.opaque,
|
||||
contentStyle,
|
||||
]}
|
||||
style={[styles.content, contentStyle]}
|
||||
>
|
||||
<StackCardAnimationContext.Provider value={animationContext}>
|
||||
{children}
|
||||
@@ -905,10 +901,4 @@ const styles = StyleSheet.create({
|
||||
height: 3,
|
||||
shadowOffset: { width: 1, height: -1 },
|
||||
},
|
||||
transparent: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
opaque: {
|
||||
backgroundColor: '#eee',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as React from 'react';
|
||||
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import { StackNavigationState } from '@react-navigation/routers';
|
||||
import { Route } from '@react-navigation/native';
|
||||
import { Route, useTheme } from '@react-navigation/native';
|
||||
import { Props as HeaderContainerProps } from '../Header/HeaderContainer';
|
||||
import Card from './Card';
|
||||
import { Scene, Layout, StackHeaderMode, TransitionPreset } from '../../types';
|
||||
@@ -53,30 +53,58 @@ type Props = TransitionPreset & {
|
||||
floatingHeaderHeight: number;
|
||||
};
|
||||
|
||||
export default class CardContainer extends React.PureComponent<Props> {
|
||||
private handleOpen = () => {
|
||||
const { scene, onTransitionEnd, onOpenRoute } = this.props;
|
||||
|
||||
export default function CardContainer({
|
||||
active,
|
||||
cardOverlayEnabled,
|
||||
cardShadowEnabled,
|
||||
cardStyle,
|
||||
cardStyleInterpolator,
|
||||
cardTransparent,
|
||||
closing,
|
||||
current,
|
||||
floatingHeaderHeight,
|
||||
focused,
|
||||
gestureDirection,
|
||||
gestureEnabled,
|
||||
gestureResponseDistance,
|
||||
gestureVelocityImpact,
|
||||
getPreviousRoute,
|
||||
headerMode,
|
||||
headerShown,
|
||||
headerStyleInterpolator,
|
||||
headerTransparent,
|
||||
index,
|
||||
layout,
|
||||
onCloseRoute,
|
||||
onGoBack,
|
||||
onOpenRoute,
|
||||
onPageChangeCancel,
|
||||
onPageChangeConfirm,
|
||||
onPageChangeStart,
|
||||
onTransitionEnd,
|
||||
onTransitionStart,
|
||||
previousScene,
|
||||
renderHeader,
|
||||
renderScene,
|
||||
safeAreaInsetBottom,
|
||||
safeAreaInsetLeft,
|
||||
safeAreaInsetRight,
|
||||
safeAreaInsetTop,
|
||||
scene,
|
||||
state,
|
||||
transitionSpec,
|
||||
}: Props) {
|
||||
const handleOpen = () => {
|
||||
onTransitionEnd && onTransitionEnd({ route: scene.route }, false);
|
||||
onOpenRoute({ route: scene.route });
|
||||
};
|
||||
|
||||
private handleClose = () => {
|
||||
const { scene, onTransitionEnd, onCloseRoute } = this.props;
|
||||
|
||||
const handleClose = () => {
|
||||
onTransitionEnd && onTransitionEnd({ route: scene.route }, true);
|
||||
onCloseRoute({ route: scene.route });
|
||||
};
|
||||
|
||||
private handleTransitionStart = ({ closing }: { closing: boolean }) => {
|
||||
const {
|
||||
scene,
|
||||
onTransitionStart,
|
||||
onPageChangeConfirm,
|
||||
onPageChangeCancel,
|
||||
onGoBack,
|
||||
} = this.props;
|
||||
|
||||
const handleTransitionStart = ({ closing }: { closing: boolean }) => {
|
||||
if (closing) {
|
||||
onPageChangeConfirm && onPageChangeConfirm();
|
||||
} else {
|
||||
@@ -87,103 +115,70 @@ export default class CardContainer extends React.PureComponent<Props> {
|
||||
closing && onGoBack({ route: scene.route });
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
index,
|
||||
layout,
|
||||
active,
|
||||
focused,
|
||||
closing,
|
||||
current,
|
||||
state,
|
||||
scene,
|
||||
previousScene,
|
||||
safeAreaInsetTop,
|
||||
safeAreaInsetRight,
|
||||
safeAreaInsetBottom,
|
||||
safeAreaInsetLeft,
|
||||
cardTransparent,
|
||||
cardOverlayEnabled,
|
||||
cardShadowEnabled,
|
||||
cardStyle,
|
||||
onPageChangeStart,
|
||||
onPageChangeCancel,
|
||||
gestureEnabled,
|
||||
gestureResponseDistance,
|
||||
gestureVelocityImpact,
|
||||
floatingHeaderHeight,
|
||||
headerShown,
|
||||
getPreviousRoute,
|
||||
headerMode,
|
||||
headerTransparent,
|
||||
renderHeader,
|
||||
renderScene,
|
||||
gestureDirection,
|
||||
transitionSpec,
|
||||
cardStyleInterpolator,
|
||||
headerStyleInterpolator,
|
||||
} = this.props;
|
||||
const insets = {
|
||||
top: safeAreaInsetTop,
|
||||
right: safeAreaInsetRight,
|
||||
bottom: safeAreaInsetBottom,
|
||||
left: safeAreaInsetLeft,
|
||||
};
|
||||
|
||||
const insets = {
|
||||
top: safeAreaInsetTop,
|
||||
right: safeAreaInsetRight,
|
||||
bottom: safeAreaInsetBottom,
|
||||
left: safeAreaInsetLeft,
|
||||
};
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<Card
|
||||
index={index}
|
||||
active={active}
|
||||
transparent={cardTransparent}
|
||||
gestureDirection={gestureDirection}
|
||||
layout={layout}
|
||||
insets={insets}
|
||||
current={current}
|
||||
next={scene.progress.next}
|
||||
closing={closing}
|
||||
onOpen={this.handleOpen}
|
||||
onClose={this.handleClose}
|
||||
overlayEnabled={cardOverlayEnabled}
|
||||
shadowEnabled={cardShadowEnabled}
|
||||
onTransitionStart={this.handleTransitionStart}
|
||||
onGestureBegin={onPageChangeStart}
|
||||
onGestureCanceled={onPageChangeCancel}
|
||||
gestureEnabled={gestureEnabled}
|
||||
gestureResponseDistance={gestureResponseDistance}
|
||||
gestureVelocityImpact={gestureVelocityImpact}
|
||||
transitionSpec={transitionSpec}
|
||||
styleInterpolator={cardStyleInterpolator}
|
||||
accessibilityElementsHidden={!focused}
|
||||
importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'}
|
||||
pointerEvents="box-none"
|
||||
containerStyle={
|
||||
headerMode === 'float' && !headerTransparent && headerShown !== false
|
||||
? { marginTop: floatingHeaderHeight }
|
||||
: null
|
||||
}
|
||||
contentStyle={cardStyle}
|
||||
style={StyleSheet.absoluteFill}
|
||||
>
|
||||
<View style={styles.container}>
|
||||
<View style={styles.scene}>
|
||||
{renderScene({ route: scene.route })}
|
||||
</View>
|
||||
{headerMode === 'screen'
|
||||
? renderHeader({
|
||||
mode: 'screen',
|
||||
layout,
|
||||
insets,
|
||||
scenes: [previousScene, scene],
|
||||
state,
|
||||
getPreviousRoute,
|
||||
styleInterpolator: headerStyleInterpolator,
|
||||
})
|
||||
: null}
|
||||
</View>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Card
|
||||
index={index}
|
||||
active={active}
|
||||
transparent={cardTransparent}
|
||||
gestureDirection={gestureDirection}
|
||||
layout={layout}
|
||||
insets={insets}
|
||||
current={current}
|
||||
next={scene.progress.next}
|
||||
closing={closing}
|
||||
onOpen={handleOpen}
|
||||
onClose={handleClose}
|
||||
overlayEnabled={cardOverlayEnabled}
|
||||
shadowEnabled={cardShadowEnabled}
|
||||
onTransitionStart={handleTransitionStart}
|
||||
onGestureBegin={onPageChangeStart}
|
||||
onGestureCanceled={onPageChangeCancel}
|
||||
gestureEnabled={gestureEnabled}
|
||||
gestureResponseDistance={gestureResponseDistance}
|
||||
gestureVelocityImpact={gestureVelocityImpact}
|
||||
transitionSpec={transitionSpec}
|
||||
styleInterpolator={cardStyleInterpolator}
|
||||
accessibilityElementsHidden={!focused}
|
||||
importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'}
|
||||
pointerEvents="box-none"
|
||||
containerStyle={
|
||||
headerMode === 'float' && !headerTransparent && headerShown !== false
|
||||
? { marginTop: floatingHeaderHeight }
|
||||
: null
|
||||
}
|
||||
contentStyle={[
|
||||
{
|
||||
backgroundColor: cardTransparent ? 'transparent' : colors.background,
|
||||
},
|
||||
cardStyle,
|
||||
]}
|
||||
style={StyleSheet.absoluteFill}
|
||||
>
|
||||
<View style={styles.container}>
|
||||
<View style={styles.scene}>{renderScene({ route: scene.route })}</View>
|
||||
{headerMode === 'screen'
|
||||
? renderHeader({
|
||||
mode: 'screen',
|
||||
layout,
|
||||
insets,
|
||||
scenes: [previousScene, scene],
|
||||
state,
|
||||
getPreviousRoute,
|
||||
styleInterpolator: headerStyleInterpolator,
|
||||
})
|
||||
: null}
|
||||
</View>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
||||
Reference in New Issue
Block a user