chore: migrate to monorepo
64
example/src/AnimatedSwitch.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import * as React from 'react';
|
||||
import { Text, View, Button, StyleSheet } from 'react-native';
|
||||
import createAnimatedSwitchNavigator, {
|
||||
NavigationAnimatedSwitchProp,
|
||||
} from 'react-navigation-animated-switch';
|
||||
import { Transition } from 'react-native-reanimated';
|
||||
|
||||
function Home({ navigation }: { navigation: NavigationAnimatedSwitchProp }) {
|
||||
return (
|
||||
<View style={styles.content}>
|
||||
<Text>Home screen</Text>
|
||||
<Button
|
||||
title="Go to settings"
|
||||
onPress={() => navigation.navigate('Settings')}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function Settings({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NavigationAnimatedSwitchProp;
|
||||
}) {
|
||||
return (
|
||||
<View style={styles.content}>
|
||||
<Text>Settings screen</Text>
|
||||
<Button title="Go to home" onPress={() => navigation.navigate('Home')} />
|
||||
<Button
|
||||
title="Go back to examples"
|
||||
onPress={() => navigation.navigate('Index')}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const MySwitch = createAnimatedSwitchNavigator(
|
||||
{
|
||||
Home,
|
||||
Settings,
|
||||
},
|
||||
{
|
||||
transition: (
|
||||
<Transition.Together>
|
||||
<Transition.Out
|
||||
type="slide-bottom"
|
||||
durationMs={400}
|
||||
interpolation="easeIn"
|
||||
/>
|
||||
<Transition.In type="fade" durationMs={500} />
|
||||
</Transition.Together>
|
||||
),
|
||||
}
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
content: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export default MySwitch;
|
||||
80
example/src/BottomTabs.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import * as React from 'react';
|
||||
import { createBottomTabNavigator } from 'react-navigation-tabs';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import Albums from './Shared/Albums';
|
||||
import Article from './Shared/Article';
|
||||
import Chat from './Shared/Chat';
|
||||
import Contacts from './Shared/Contacts';
|
||||
|
||||
// @ts-ignore
|
||||
import TouchableBounce from 'react-native/Libraries/Components/Touchable/TouchableBounce';
|
||||
|
||||
const tabBarIcon = (name: string) => ({
|
||||
tintColor,
|
||||
horizontal,
|
||||
}: {
|
||||
tintColor: string;
|
||||
horizontal: boolean;
|
||||
}) => (
|
||||
<MaterialIcons name={name} color={tintColor} size={horizontal ? 17 : 24} />
|
||||
);
|
||||
|
||||
class AlbumsScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
tabBarLabel: 'Albums',
|
||||
tabBarIcon: tabBarIcon('photo-album'),
|
||||
tabBarButtonComponent: TouchableBounce,
|
||||
};
|
||||
|
||||
render() {
|
||||
return <Albums />;
|
||||
}
|
||||
}
|
||||
|
||||
class ArticleScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
tabBarLabel: 'Article',
|
||||
tabBarIcon: tabBarIcon('chrome-reader-mode'),
|
||||
tabBarButtonComponent: TouchableBounce,
|
||||
};
|
||||
|
||||
render() {
|
||||
return <Article />;
|
||||
}
|
||||
}
|
||||
|
||||
class ChatScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
tabBarLabel: 'Chat',
|
||||
tabBarIcon: tabBarIcon('chat-bubble'),
|
||||
tabBarButtonComponent: TouchableBounce,
|
||||
};
|
||||
|
||||
render() {
|
||||
return <Chat />;
|
||||
}
|
||||
}
|
||||
|
||||
class ContactsScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
tabBarLabel: 'Contacts',
|
||||
tabBarIcon: tabBarIcon('contacts'),
|
||||
tabBarButtonComponent: TouchableBounce,
|
||||
};
|
||||
|
||||
render() {
|
||||
return <Contacts />;
|
||||
}
|
||||
}
|
||||
|
||||
export default createBottomTabNavigator(
|
||||
{
|
||||
AlbumsScreen,
|
||||
ArticleScreen,
|
||||
ChatScreen,
|
||||
ContactsScreen,
|
||||
},
|
||||
{
|
||||
initialRouteName: 'AlbumsScreen',
|
||||
}
|
||||
);
|
||||
216
example/src/CustomTabUI.tsx
Normal file
@@ -0,0 +1,216 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Alert,
|
||||
TouchableOpacity,
|
||||
LayoutAnimation,
|
||||
StatusBar,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import {
|
||||
Themed,
|
||||
ThemeContext,
|
||||
NavigationScreenProp,
|
||||
NavigationState,
|
||||
SafeAreaView,
|
||||
} from 'react-navigation';
|
||||
import {
|
||||
createMaterialTopTabNavigator,
|
||||
MaterialTopTabBar,
|
||||
} from 'react-navigation-tabs';
|
||||
import { Button } from './Shared/ButtonWithMargin';
|
||||
|
||||
interface Props {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
}
|
||||
|
||||
class MyHomeScreen extends React.Component<Props> {
|
||||
static navigationOptions = {
|
||||
tabBarLabel: 'Home',
|
||||
tabBarIcon: ({
|
||||
tintColor,
|
||||
focused,
|
||||
horizontal,
|
||||
}: {
|
||||
tintColor: string;
|
||||
focused: boolean;
|
||||
horizontal: boolean;
|
||||
}) => (
|
||||
<Ionicons
|
||||
name={focused ? 'ios-home' : 'ios-home'}
|
||||
size={horizontal ? 20 : 26}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
),
|
||||
};
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
return (
|
||||
<SafeAreaView forceInset={{ horizontal: 'always', top: 'always' }}>
|
||||
<Themed.Text>Home Screen</Themed.Text>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Home')}
|
||||
title="Go to home tab"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class StarredScreen extends React.Component<Props> {
|
||||
static navigationOptions = {
|
||||
tabBarLabel: 'Starred',
|
||||
tabBarIcon: ({
|
||||
tintColor,
|
||||
focused,
|
||||
horizontal,
|
||||
}: {
|
||||
tintColor: string;
|
||||
focused: boolean;
|
||||
horizontal: boolean;
|
||||
}) => (
|
||||
<Ionicons
|
||||
name={focused ? 'ios-people' : 'ios-people'}
|
||||
size={horizontal ? 20 : 26}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
),
|
||||
};
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
return (
|
||||
<SafeAreaView forceInset={{ horizontal: 'always', top: 'always' }}>
|
||||
<Themed.Text>Starred Screen</Themed.Text>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Home')}
|
||||
title="Go to home tab"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
type MaterialTopTabBarProps = React.ComponentProps<typeof MaterialTopTabBar>;
|
||||
|
||||
class MaterialTopTabBarWrapper extends React.Component<MaterialTopTabBarProps> {
|
||||
render() {
|
||||
return (
|
||||
<SafeAreaView
|
||||
style={{ backgroundColor: '#000' }}
|
||||
forceInset={{ top: 'always', horizontal: 'never', bottom: 'never' }}
|
||||
>
|
||||
<MaterialTopTabBar {...this.props} />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FeaturedScreen extends React.Component<Props> {
|
||||
static navigationOptions = {
|
||||
tabBarLabel: 'Featured',
|
||||
tabBarIcon: ({
|
||||
tintColor,
|
||||
focused,
|
||||
horizontal,
|
||||
}: {
|
||||
tintColor: string;
|
||||
focused: boolean;
|
||||
horizontal: boolean;
|
||||
}) => (
|
||||
<Ionicons
|
||||
name={focused ? 'ios-star' : 'ios-star'}
|
||||
size={horizontal ? 20 : 26}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
return (
|
||||
<SafeAreaView forceInset={{ horizontal: 'always', top: 'always' }}>
|
||||
<Themed.Text>Featured Screen</Themed.Text>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Home')}
|
||||
title="Go to home tab"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const SimpleTabs = createMaterialTopTabNavigator(
|
||||
{
|
||||
Home: MyHomeScreen,
|
||||
Starred: StarredScreen,
|
||||
Featured: FeaturedScreen,
|
||||
},
|
||||
{
|
||||
tabBarComponent: MaterialTopTabBarWrapper,
|
||||
tabBarOptions: {
|
||||
style: {
|
||||
backgroundColor: '#000',
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
class TabNavigator extends React.Component<Props> {
|
||||
// eslint-disable-next-line react/sort-comp
|
||||
static contextType = ThemeContext;
|
||||
|
||||
static router = SimpleTabs.router;
|
||||
componentWillUpdate() {
|
||||
LayoutAnimation.easeInEaseOut();
|
||||
}
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
const { routes, index } = navigation.state;
|
||||
const activeRoute = routes[index];
|
||||
let bottom = null;
|
||||
if (activeRoute.routeName !== 'Home') {
|
||||
bottom = (
|
||||
<View
|
||||
style={{
|
||||
height: 50,
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
backgroundColor: this.context === 'light' ? '#000' : '#fff',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
Alert.alert('hello!');
|
||||
//
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 20,
|
||||
color: this.context === 'light' ? '#fff' : '#000',
|
||||
fontWeight: 'bold',
|
||||
}}
|
||||
>
|
||||
Check out
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<StatusBar barStyle="light-content" />
|
||||
<SimpleTabs navigation={navigation} />
|
||||
{bottom}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TabNavigator;
|
||||
139
example/src/CustomTabs.tsx
Normal file
@@ -0,0 +1,139 @@
|
||||
import React from 'react';
|
||||
import { ScrollView, StyleSheet } from 'react-native';
|
||||
import { BorderlessButton } from 'react-native-gesture-handler';
|
||||
import {
|
||||
createNavigator,
|
||||
NavigationState,
|
||||
SafeAreaView,
|
||||
TabRouter,
|
||||
Themed,
|
||||
useTheme,
|
||||
createAppContainer,
|
||||
NavigationScreenProp,
|
||||
} from 'react-navigation';
|
||||
|
||||
import { Button } from './Shared/ButtonWithMargin';
|
||||
import SampleText from './Shared/SampleText';
|
||||
|
||||
const MyNavScreen = ({
|
||||
navigation,
|
||||
banner,
|
||||
}: {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
banner: string;
|
||||
}) => (
|
||||
<ScrollView>
|
||||
<SafeAreaView forceInset={{ horizontal: 'always' }}>
|
||||
<SampleText>{banner}</SampleText>
|
||||
<Button
|
||||
onPress={() => {
|
||||
navigation.goBack(null);
|
||||
}}
|
||||
title="Go back"
|
||||
/>
|
||||
</SafeAreaView>
|
||||
<Themed.StatusBar />
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
const MyHomeScreen = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
}) => <MyNavScreen banner="Home Screen" navigation={navigation} />;
|
||||
|
||||
const MyNotificationsScreen = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
}) => <MyNavScreen banner="Notifications Screen" navigation={navigation} />;
|
||||
|
||||
const MySettingsScreen = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
}) => <MyNavScreen banner="Settings Screen" navigation={navigation} />;
|
||||
|
||||
const CustomTabBar = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
}) => {
|
||||
const { routes } = navigation.state;
|
||||
return (
|
||||
<SafeAreaView style={styles.tabContainer}>
|
||||
{routes.map(route => (
|
||||
<BorderlessButton
|
||||
onPress={() => navigation.navigate(route.routeName)}
|
||||
style={styles.tab}
|
||||
key={route.routeName}
|
||||
>
|
||||
<Themed.Text>{route.routeName}</Themed.Text>
|
||||
</BorderlessButton>
|
||||
))}
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
// @todo - how does the type definition for a custom navigator work?
|
||||
class CustomTabView extends React.Component<any> {
|
||||
render() {
|
||||
const { navigation, descriptors } = this.props;
|
||||
const { routes, index } = navigation.state;
|
||||
const descriptor = descriptors[routes[index].key];
|
||||
const ActiveScreen = descriptor.getComponent();
|
||||
return (
|
||||
<SafeAreaView forceInset={{ top: 'always' }}>
|
||||
<CustomTabBar navigation={navigation} />
|
||||
<ActiveScreen navigation={descriptor.navigation} />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const CustomTabRouter = TabRouter(
|
||||
{
|
||||
Home: {
|
||||
path: '',
|
||||
screen: MyHomeScreen,
|
||||
},
|
||||
Notifications: {
|
||||
path: 'notifications',
|
||||
screen: MyNotificationsScreen,
|
||||
},
|
||||
Settings: {
|
||||
path: 'settings',
|
||||
screen: MySettingsScreen,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Change this to start on a different tab
|
||||
initialRouteName: 'Home',
|
||||
}
|
||||
);
|
||||
|
||||
const CustomTabs = createAppContainer(
|
||||
createNavigator(CustomTabView, CustomTabRouter, {})
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
tab: {
|
||||
alignItems: 'center',
|
||||
borderColor: '#ddd',
|
||||
borderRadius: 4,
|
||||
borderWidth: 1,
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
margin: 4,
|
||||
},
|
||||
tabContainer: {
|
||||
flexDirection: 'row',
|
||||
height: 48,
|
||||
},
|
||||
});
|
||||
|
||||
export default () => {
|
||||
// Need to thread the theme through to detached nested navigator
|
||||
let theme = useTheme();
|
||||
|
||||
return <CustomTabs detached theme={theme} />;
|
||||
};
|
||||
48
example/src/DragLimitedToModal.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet, Dimensions } from 'react-native';
|
||||
import { NavigationStackScreenComponent } from 'react-navigation-stack';
|
||||
|
||||
const HEIGHT = Dimensions.get('screen').height;
|
||||
|
||||
const DragLimitedToModal: NavigationStackScreenComponent = () => (
|
||||
<View style={styles.modal}>
|
||||
<Text style={styles.text}>Adjusts to the size of text</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
DragLimitedToModal.navigationOptions = {
|
||||
cardStyle: { backgroundColor: 'transparent' },
|
||||
gestureDirection: 'vertical',
|
||||
gestureResponseDistance: { vertical: HEIGHT },
|
||||
cardStyleInterpolator: ({ current }) => {
|
||||
const translateY = current.progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [HEIGHT, 0],
|
||||
});
|
||||
|
||||
return {
|
||||
cardStyle: {
|
||||
transform: [{ translateY }],
|
||||
flex: undefined,
|
||||
},
|
||||
containerStyle: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
modal: {
|
||||
padding: 30,
|
||||
borderRadius: 15,
|
||||
backgroundColor: '#000',
|
||||
},
|
||||
text: {
|
||||
fontSize: 18,
|
||||
color: '#fff',
|
||||
},
|
||||
});
|
||||
|
||||
export default DragLimitedToModal;
|
||||
126
example/src/Drawer.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
import React from 'react';
|
||||
import { ScrollView, StyleProp, TextStyle } from 'react-native';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import {
|
||||
Themed,
|
||||
SafeAreaView,
|
||||
NavigationScreenProp,
|
||||
NavigationState,
|
||||
} from 'react-navigation';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackScreenProps,
|
||||
} from 'react-navigation-stack';
|
||||
import {
|
||||
createDrawerNavigator,
|
||||
NavigationDrawerScreenProps,
|
||||
} from 'react-navigation-drawer';
|
||||
import { Button } from './Shared/ButtonWithMargin';
|
||||
import SampleText from './Shared/SampleText';
|
||||
|
||||
const MyNavScreen = ({
|
||||
navigation,
|
||||
banner,
|
||||
}: {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
banner: string;
|
||||
}) => (
|
||||
<ScrollView>
|
||||
<SafeAreaView forceInset={{ top: 'always' }}>
|
||||
<SampleText>{banner}</SampleText>
|
||||
<Button
|
||||
onPress={() => {
|
||||
// @ts-ignore
|
||||
navigation.openDrawer();
|
||||
}}
|
||||
title="Open drawer"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Email')}
|
||||
title="Open other screen"
|
||||
/>
|
||||
<Button onPress={() => navigation.navigate('Index')} title="Go back" />
|
||||
</SafeAreaView>
|
||||
<Themed.StatusBar />
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
const InboxScreen = ({ navigation }: NavigationDrawerScreenProps) => (
|
||||
<MyNavScreen banner="Inbox Screen" navigation={navigation} />
|
||||
);
|
||||
|
||||
InboxScreen.navigationOptions = {
|
||||
headerTitle: 'Inbox',
|
||||
};
|
||||
|
||||
const EmailScreen = ({ navigation }: NavigationStackScreenProps) => (
|
||||
<MyNavScreen banner="Email Screen" navigation={navigation} />
|
||||
);
|
||||
|
||||
const DraftsScreen = ({ navigation }: NavigationStackScreenProps) => (
|
||||
<MyNavScreen banner="Drafts Screen" navigation={navigation} />
|
||||
);
|
||||
|
||||
DraftsScreen.navigationOptions = {
|
||||
headerTitle: 'Drafts',
|
||||
};
|
||||
|
||||
const InboxStack = createStackNavigator(
|
||||
{
|
||||
Email: { screen: EmailScreen },
|
||||
Inbox: { screen: InboxScreen },
|
||||
},
|
||||
{
|
||||
navigationOptions: {
|
||||
drawerIcon: ({ tintColor }: { tintColor: string }) => (
|
||||
<MaterialIcons
|
||||
name="move-to-inbox"
|
||||
size={24}
|
||||
style={{ color: tintColor } as StyleProp<TextStyle>}
|
||||
/>
|
||||
),
|
||||
drawerLabel: 'Inbox',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const DraftsStack = createStackNavigator(
|
||||
{
|
||||
Drafts: { screen: DraftsScreen },
|
||||
Email: { screen: EmailScreen },
|
||||
},
|
||||
{
|
||||
navigationOptions: {
|
||||
drawerIcon: ({ tintColor }: { tintColor: string }) => (
|
||||
<MaterialIcons
|
||||
name="drafts"
|
||||
size={24}
|
||||
style={{ color: tintColor } as StyleProp<TextStyle>}
|
||||
/>
|
||||
),
|
||||
drawerLabel: 'Drafts',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const DrawerExample = createDrawerNavigator(
|
||||
{
|
||||
Drafts: {
|
||||
path: '/sent',
|
||||
screen: DraftsStack,
|
||||
},
|
||||
Inbox: {
|
||||
path: '/',
|
||||
screen: InboxStack,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
contentOptions: {
|
||||
activeTintColor: '#e91e63',
|
||||
},
|
||||
initialRouteName: 'Drafts',
|
||||
}
|
||||
);
|
||||
|
||||
export default DrawerExample;
|
||||
136
example/src/EventsStack.tsx
Normal file
@@ -0,0 +1,136 @@
|
||||
import React from 'react';
|
||||
import { Button, ScrollView, View, Text } from 'react-native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackProp,
|
||||
NavigationStackScreenProps,
|
||||
} from 'react-navigation-stack';
|
||||
|
||||
const getColorOfEvent = (evt: string) => {
|
||||
switch (evt) {
|
||||
case 'willFocus':
|
||||
return 'purple';
|
||||
case 'didFocus':
|
||||
return 'blue';
|
||||
case 'willBlur':
|
||||
return 'brown';
|
||||
default:
|
||||
return 'black';
|
||||
}
|
||||
};
|
||||
class FocusTag extends React.Component<NavigationStackScreenProps> {
|
||||
state = { mode: 'didBlur' };
|
||||
|
||||
componentDidMount() {
|
||||
this.props.navigation.addListener('willFocus', () => {
|
||||
this.setMode('willFocus');
|
||||
});
|
||||
|
||||
this.props.navigation.addListener('willBlur', () => {
|
||||
this.setMode('willBlur');
|
||||
});
|
||||
|
||||
this.props.navigation.addListener('didFocus', () => {
|
||||
this.setMode('didFocus');
|
||||
});
|
||||
|
||||
this.props.navigation.addListener('didBlur', () => {
|
||||
this.setMode('didBlur');
|
||||
});
|
||||
}
|
||||
|
||||
setMode = (mode: string) => {
|
||||
this.setState({ mode });
|
||||
};
|
||||
|
||||
render() {
|
||||
const key = this.props.navigation.state.key;
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
padding: 20,
|
||||
backgroundColor: getColorOfEvent(this.state.mode),
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: 'white' }}>
|
||||
{key} {String(this.state.mode)}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SampleScreen extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NavigationStackProp;
|
||||
}) => ({
|
||||
title: 'Lorem Ipsum',
|
||||
headerRight: navigation.getParam('nextPage')
|
||||
? () => (
|
||||
<Button
|
||||
title="Next"
|
||||
onPress={() => navigation.navigate(navigation.getParam('nextPage'))}
|
||||
/>
|
||||
)
|
||||
: undefined,
|
||||
});
|
||||
|
||||
componentDidMount() {
|
||||
this.props.navigation.addListener('refocus', () => {
|
||||
if (this.props.navigation.isFocused()) {
|
||||
this.scrollView.current?.scrollTo({ x: 0, y: 0 });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private scrollView = React.createRef<ScrollView>();
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ScrollView ref={this.scrollView} style={{ flex: 1 }}>
|
||||
<FocusTag {...this.props} />
|
||||
<Button
|
||||
title="Push"
|
||||
onPress={() => {
|
||||
this.props.navigation.push('PageTwo');
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
title="Push and Pop Quickly"
|
||||
onPress={() => {
|
||||
const { push, goBack } = this.props.navigation;
|
||||
push('PageTwo');
|
||||
setTimeout(() => {
|
||||
goBack(null);
|
||||
}, 150);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
title="Back to Examples"
|
||||
onPress={() => {
|
||||
this.props.navigation.navigate('Index');
|
||||
}}
|
||||
/>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const SimpleStack = createStackNavigator(
|
||||
{
|
||||
PageOne: {
|
||||
screen: SampleScreen,
|
||||
},
|
||||
PageTwo: {
|
||||
screen: SampleScreen,
|
||||
},
|
||||
},
|
||||
{
|
||||
initialRouteName: 'PageOne',
|
||||
}
|
||||
);
|
||||
|
||||
export default SimpleStack;
|
||||
15
example/src/FullScreen.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import * as React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { createStackNavigator } from 'react-navigation-stack';
|
||||
|
||||
class Screen extends React.Component {
|
||||
static navigationOptions = {
|
||||
headerShown: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
return <View style={{ flex: 1, backgroundColor: 'red' }} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default createStackNavigator({ Screen });
|
||||
93
example/src/GestureInteraction.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import * as React from 'react';
|
||||
import { Button, View } from 'react-native';
|
||||
import { withNavigation } from 'react-navigation';
|
||||
import {
|
||||
createDrawerNavigator,
|
||||
DrawerGestureContext,
|
||||
NavigationDrawerProp,
|
||||
} from 'react-navigation-drawer';
|
||||
import MapView from 'react-native-maps';
|
||||
import { WebView } from 'react-native-webview';
|
||||
import { NativeViewGestureHandler } from 'react-native-gesture-handler';
|
||||
|
||||
const ContainerWithButtons = withNavigation(
|
||||
({
|
||||
children,
|
||||
navigation,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
navigation: NavigationDrawerProp;
|
||||
}) => (
|
||||
<View style={{ flex: 1 }}>
|
||||
{children}
|
||||
<View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
paddingBottom: 30,
|
||||
bottom: 0,
|
||||
paddingTop: 10,
|
||||
paddingHorizontal: 10,
|
||||
left: 0,
|
||||
flexDirection: 'row',
|
||||
right: 0,
|
||||
backgroundColor: 'rgba(255,255,255,0.7)',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<Button title="Open drawer" onPress={() => navigation.openDrawer()} />
|
||||
<Button title="Go back" onPress={() => navigation.navigate('Index')} />
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
);
|
||||
|
||||
const MapScreen = () => (
|
||||
<ContainerWithButtons>
|
||||
<DrawerGestureContext.Consumer>
|
||||
{ref => (
|
||||
<NativeViewGestureHandler waitFor={ref}>
|
||||
<MapView style={{ flex: 1 }} />
|
||||
</NativeViewGestureHandler>
|
||||
)}
|
||||
</DrawerGestureContext.Consumer>
|
||||
</ContainerWithButtons>
|
||||
);
|
||||
|
||||
MapScreen.navigationOptions = {
|
||||
title: 'MapView',
|
||||
};
|
||||
|
||||
const WebViewScreen = () => (
|
||||
<ContainerWithButtons>
|
||||
<DrawerGestureContext.Consumer>
|
||||
{ref => (
|
||||
<NativeViewGestureHandler waitFor={ref}>
|
||||
<WebView
|
||||
style={{ flex: 1 }}
|
||||
source={{ uri: 'https://news.google.com' }}
|
||||
/>
|
||||
</NativeViewGestureHandler>
|
||||
)}
|
||||
</DrawerGestureContext.Consumer>
|
||||
</ContainerWithButtons>
|
||||
);
|
||||
|
||||
WebViewScreen.navigationOptions = {
|
||||
title: 'WebView',
|
||||
};
|
||||
|
||||
const DrawerExample = createDrawerNavigator(
|
||||
{
|
||||
Map: MapScreen,
|
||||
Web: WebViewScreen,
|
||||
},
|
||||
{
|
||||
edgeWidth: 70,
|
||||
minSwipeDistance: 3,
|
||||
contentOptions: {
|
||||
activeTintColor: '#e91e63',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default DrawerExample;
|
||||
123
example/src/HeaderBackgrounds.tsx
Normal file
@@ -0,0 +1,123 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet, View, Text } from 'react-native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
TransitionPresets,
|
||||
HeaderStyleInterpolators,
|
||||
NavigationStackScreenProps,
|
||||
} from 'react-navigation-stack';
|
||||
|
||||
function createHeaderBackgroundExample(options = {}) {
|
||||
return createStackNavigator(
|
||||
{
|
||||
Login: {
|
||||
screen: ({ navigation }: NavigationStackScreenProps) => (
|
||||
<View style={styles.container}>
|
||||
<Text
|
||||
style={styles.tips}
|
||||
onPress={() => navigation.navigate('Games')}
|
||||
>
|
||||
Login Screen
|
||||
</Text>
|
||||
</View>
|
||||
),
|
||||
navigationOptions: {
|
||||
headerTitle: 'Login Screen',
|
||||
headerTintColor: '#fff',
|
||||
headerBackground: () => (
|
||||
<View style={{ flex: 1, backgroundColor: '#FF0066' }} />
|
||||
),
|
||||
},
|
||||
},
|
||||
Games: {
|
||||
screen: ({ navigation }: NavigationStackScreenProps) => (
|
||||
<View style={styles.container}>
|
||||
<Text
|
||||
style={styles.tips}
|
||||
onPress={() => navigation.navigate('Main')}
|
||||
>
|
||||
Games Screen
|
||||
</Text>
|
||||
</View>
|
||||
),
|
||||
navigationOptions: {
|
||||
headerTitle: 'Games Screen',
|
||||
headerTintColor: '#fff',
|
||||
headerBackground: () => (
|
||||
<View style={{ flex: 1, backgroundColor: '#3388FF' }} />
|
||||
),
|
||||
},
|
||||
},
|
||||
Main: {
|
||||
screen: ({ navigation }: NavigationStackScreenProps) => (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.tips} onPress={() => navigation.navigate('My')}>
|
||||
Main Screen
|
||||
</Text>
|
||||
</View>
|
||||
),
|
||||
navigationOptions: {
|
||||
headerTitle: 'Main Screen',
|
||||
},
|
||||
},
|
||||
My: {
|
||||
screen: ({ navigation }: NavigationStackScreenProps) => (
|
||||
<View style={styles.container}>
|
||||
<Text
|
||||
style={styles.tips}
|
||||
onPress={() => navigation.navigate('News')}
|
||||
>
|
||||
My Screen
|
||||
</Text>
|
||||
</View>
|
||||
),
|
||||
navigationOptions: {
|
||||
headerTitle: 'My Screen',
|
||||
},
|
||||
},
|
||||
News: {
|
||||
screen: () => (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.tips} onPress={() => {}}>
|
||||
News Screen
|
||||
</Text>
|
||||
</View>
|
||||
),
|
||||
navigationOptions: {
|
||||
headerTitle: 'News Screen',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
initialRouteName: 'Login',
|
||||
...options,
|
||||
}
|
||||
);
|
||||
}
|
||||
export const HeaderBackgroundDefault = createHeaderBackgroundExample({
|
||||
defaultNavigationOptions: {
|
||||
...TransitionPresets.SlideFromRightIOS,
|
||||
headerStyleInterpolator: HeaderStyleInterpolators.forUIKit,
|
||||
},
|
||||
headerMode: 'float',
|
||||
});
|
||||
|
||||
export const HeaderBackgroundFade = createHeaderBackgroundExample({
|
||||
defaultNavigationOptions: {
|
||||
...TransitionPresets.SlideFromRightIOS,
|
||||
headerStyleInterpolator: HeaderStyleInterpolators.forFade,
|
||||
},
|
||||
headerMode: 'float',
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#F5FCFF',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
tips: {
|
||||
fontSize: 20,
|
||||
},
|
||||
});
|
||||
120
example/src/HeaderPreset.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import * as React from 'react';
|
||||
import { Button, StatusBar, StyleProp, ViewStyle } from 'react-native';
|
||||
import { SafeAreaView } from 'react-navigation';
|
||||
import {
|
||||
createStackNavigator,
|
||||
TransitionPresets,
|
||||
HeaderStyleInterpolators,
|
||||
NavigationStackScreenProps,
|
||||
} from 'react-navigation-stack';
|
||||
|
||||
class HomeScreen extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Welcome',
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
const { push } = navigation;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ paddingTop: 30 }}>
|
||||
<Button onPress={() => push('Other')} title="Push another screen" />
|
||||
<Button
|
||||
onPress={() => push('ScreenWithNoHeader')}
|
||||
title="Push screen with no header"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go Home" />
|
||||
<StatusBar barStyle="default" />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OtherScreen extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Your title here',
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
const { push, pop } = navigation;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ paddingTop: 30 }}>
|
||||
<Button
|
||||
onPress={() => push('ScreenWithLongTitle')}
|
||||
title="Push another screen"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => push('ScreenWithNoHeader')}
|
||||
title="Push screen with no header"
|
||||
/>
|
||||
<Button onPress={() => pop()} title="Pop" />
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<StatusBar barStyle="default" />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ScreenWithLongTitle extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: "Another title that's kind of long",
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
const { pop } = navigation;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ paddingTop: 30 }}>
|
||||
<Button onPress={() => pop()} title="Pop" />
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<StatusBar barStyle="default" />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ScreenWithNoHeader extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'No Header',
|
||||
headerShown: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
const { push, pop } = navigation;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ paddingTop: 30 }}>
|
||||
<Button onPress={() => push('Other')} title="Push another screen" />
|
||||
<Button onPress={() => pop()} title="Pop" />
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<StatusBar barStyle="default" />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const StackWithHeaderPreset = createStackNavigator(
|
||||
{
|
||||
Home: HomeScreen,
|
||||
Other: OtherScreen,
|
||||
ScreenWithNoHeader: ScreenWithNoHeader,
|
||||
ScreenWithLongTitle: ScreenWithLongTitle,
|
||||
},
|
||||
{
|
||||
headerMode: 'float',
|
||||
defaultNavigationOptions: {
|
||||
...TransitionPresets.SlideFromRightIOS,
|
||||
headerStyleInterpolator: HeaderStyleInterpolators.forUIKit,
|
||||
// @ts-ignore
|
||||
headerTitleContainerStyle: { left: null } as StyleProp<ViewStyle>,
|
||||
gestureEnabled: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default StackWithHeaderPreset;
|
||||
64
example/src/IconTabs.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
createMaterialBottomTabNavigator,
|
||||
NavigationMaterialBottomTabOptions,
|
||||
} from 'react-navigation-material-bottom-tabs';
|
||||
import PhotoGrid from './Shared/PhotoGrid';
|
||||
import tabBarIcon from './Shared/tabBarIcon';
|
||||
|
||||
class Album extends React.Component {
|
||||
static navigationOptions: NavigationMaterialBottomTabOptions = {
|
||||
tabBarIcon: tabBarIcon('photo-album'),
|
||||
};
|
||||
|
||||
render() {
|
||||
return <PhotoGrid id="album" />;
|
||||
}
|
||||
}
|
||||
|
||||
class Library extends React.Component {
|
||||
static navigationOptions: NavigationMaterialBottomTabOptions = {
|
||||
tabBarIcon: tabBarIcon('inbox'),
|
||||
tabBarBadge: true,
|
||||
};
|
||||
|
||||
render() {
|
||||
return <PhotoGrid id="library" />;
|
||||
}
|
||||
}
|
||||
|
||||
class Favorites extends React.Component {
|
||||
static navigationOptions: NavigationMaterialBottomTabOptions = {
|
||||
tabBarIcon: tabBarIcon('favorite'),
|
||||
};
|
||||
|
||||
render() {
|
||||
return <PhotoGrid id="favorites" />;
|
||||
}
|
||||
}
|
||||
|
||||
class Purchased extends React.Component {
|
||||
static navigationOptions: NavigationMaterialBottomTabOptions = {
|
||||
tabBarIcon: tabBarIcon('shop'),
|
||||
};
|
||||
|
||||
render() {
|
||||
return <PhotoGrid id="purchased" />;
|
||||
}
|
||||
}
|
||||
|
||||
export default createMaterialBottomTabNavigator(
|
||||
{
|
||||
Album,
|
||||
Library,
|
||||
Favorites,
|
||||
Purchased,
|
||||
},
|
||||
{
|
||||
shifting: false,
|
||||
labeled: false,
|
||||
activeColor: '#f0edf6',
|
||||
inactiveColor: '#3e2465',
|
||||
barStyle: { backgroundColor: '#694fad' },
|
||||
}
|
||||
);
|
||||
92
example/src/ImageStack.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
import * as React from 'react';
|
||||
import { Dimensions, Button, Image, View } from 'react-native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackScreenProps,
|
||||
} from 'react-navigation-stack';
|
||||
import { FlatList, BorderlessButton } from 'react-native-gesture-handler';
|
||||
|
||||
class ListScreen extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = ({ navigation }: NavigationStackScreenProps) => ({
|
||||
title: 'Image list',
|
||||
headerBackTitle: 'Back',
|
||||
headerLeft: () => (
|
||||
<Button title="Back" onPress={() => navigation.navigate('Index')} />
|
||||
),
|
||||
});
|
||||
|
||||
state = {
|
||||
items: Array.apply(null, Array(60)).map((_, i) => {
|
||||
return {
|
||||
id: i,
|
||||
src: `https://source.unsplash.com/random/400x${400 + i}`,
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<FlatList
|
||||
data={this.state.items}
|
||||
renderItem={({ item }) => (
|
||||
<View style={{ flex: 1, flexDirection: 'column', margin: 1 }}>
|
||||
<BorderlessButton
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate('Details', {
|
||||
id: item.id,
|
||||
src: item.src,
|
||||
})
|
||||
}
|
||||
>
|
||||
<Image style={{ height: 100 }} source={{ uri: item.src }} />
|
||||
</BorderlessButton>
|
||||
</View>
|
||||
)}
|
||||
numColumns={3}
|
||||
keyExtractor={(_, index) => String(index)}
|
||||
style={{ flex: 1, backgroundColor: '#fff' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DetailsScreen extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Random image from Unsplash',
|
||||
};
|
||||
|
||||
render() {
|
||||
let id = this.props.navigation.getParam('id', 0);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: '#fff',
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
source={{
|
||||
uri: `https://source.unsplash.com/random/1080x${1920 + id}`,
|
||||
}}
|
||||
style={{ width: Dimensions.get('window').width, height: 400 }}
|
||||
resizeMode="cover"
|
||||
/>
|
||||
<Button
|
||||
title="Go back"
|
||||
onPress={() => this.props.navigation.goBack()}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default createStackNavigator(
|
||||
{
|
||||
List: ListScreen,
|
||||
Details: DetailsScreen,
|
||||
},
|
||||
{
|
||||
initialRouteName: 'List',
|
||||
}
|
||||
);
|
||||
105
example/src/ImmediateTransition.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackScreenProps,
|
||||
} from 'react-navigation-stack';
|
||||
import { createAppContainer } from 'react-navigation';
|
||||
|
||||
class ScreenOne extends React.Component<NavigationStackScreenProps> {
|
||||
componentDidMount() {
|
||||
setTimeout(() => {
|
||||
this.props.navigation.navigate('Screen2');
|
||||
}, 0);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.welcome}>Welcome to React Native!</Text>
|
||||
<Text style={styles.instructions}>Here is Screen One!</Text>
|
||||
<TouchableOpacity
|
||||
style={styles.button}
|
||||
onPress={() => {
|
||||
this.props.navigation.navigate('Screen2');
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: 'white', fontSize: 24 }}>Press me!</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ScreenTwo extends React.Component<NavigationStackScreenProps> {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.welcome}>Welcome to React Native!</Text>
|
||||
<Text style={styles.instructions}>Here is Screen Two!</Text>
|
||||
<TouchableOpacity
|
||||
style={styles.button}
|
||||
onPress={() => {
|
||||
this.props.navigation.navigate('Screen3');
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: 'white', fontSize: 24 }}>Press me!</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ScreenThree extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.welcome}>Welcome to React Native!</Text>
|
||||
<Text style={styles.instructions}>Here is Screen Three!</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const Stack = createStackNavigator(
|
||||
{
|
||||
Screen1: { screen: ScreenOne },
|
||||
Screen2: { screen: ScreenTwo },
|
||||
Screen3: { screen: ScreenThree },
|
||||
},
|
||||
{
|
||||
initialRouteName: 'Screen1',
|
||||
defaultNavigationOptions: {
|
||||
headerShown: false,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const PrimaryStack = createAppContainer(Stack);
|
||||
export default PrimaryStack;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
height: 50,
|
||||
width: 200,
|
||||
backgroundColor: 'dodgerblue',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5FCFF',
|
||||
},
|
||||
welcome: {
|
||||
fontSize: 20,
|
||||
textAlign: 'center',
|
||||
margin: 10,
|
||||
},
|
||||
instructions: {
|
||||
textAlign: 'center',
|
||||
color: '#333333',
|
||||
marginBottom: 5,
|
||||
},
|
||||
});
|
||||
75
example/src/LifecycleInteraction.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import * as React from 'react';
|
||||
import { Button, Text, View, StyleSheet } from 'react-native';
|
||||
import { BarCodeScanner } from 'expo-barcode-scanner';
|
||||
import { withNavigationFocus } from 'react-navigation';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackScreenComponent,
|
||||
NavigationStackScreenProps,
|
||||
} from 'react-navigation-stack';
|
||||
|
||||
const HomeScreen: NavigationStackScreenComponent = ({ navigation }) => (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Button
|
||||
title="Go to BarCodeScanner"
|
||||
onPress={() => navigation.navigate('BarCode')}
|
||||
/>
|
||||
<Button
|
||||
title="Return to other examples"
|
||||
onPress={() => navigation.navigate('Index')}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
||||
HomeScreen.navigationOptions = {
|
||||
title: 'Lifecycle Interactions',
|
||||
};
|
||||
|
||||
const BarCodeScreenBase = (
|
||||
props: NavigationStackScreenProps & { isFocused: boolean }
|
||||
) => {
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<BarCodeScanner
|
||||
onBarCodeScanned={
|
||||
props.isFocused
|
||||
? data => {
|
||||
console.log('scanned...');
|
||||
props.navigation.navigate('Info', { data });
|
||||
}
|
||||
: () => {}
|
||||
}
|
||||
style={StyleSheet.absoluteFill}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
BarCodeScreenBase.navigationOptions = {
|
||||
title: 'BarCodeView',
|
||||
};
|
||||
|
||||
const BarCodeScreen = withNavigationFocus(BarCodeScreenBase);
|
||||
|
||||
const InfoScreen: NavigationStackScreenComponent = props => {
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<Text>{JSON.stringify(props.navigation.getParam('data'))}</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
InfoScreen.navigationOptions = {
|
||||
title: 'Info',
|
||||
};
|
||||
|
||||
export default createStackNavigator(
|
||||
{
|
||||
Home: HomeScreen,
|
||||
BarCode: BarCodeScreen,
|
||||
Info: InfoScreen,
|
||||
},
|
||||
{
|
||||
initialRouteName: 'Home',
|
||||
}
|
||||
);
|
||||
49
example/src/MaterialTopTabs.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import * as React from 'react';
|
||||
import { createMaterialTopTabNavigator } from 'react-navigation-tabs';
|
||||
import Albums from './Shared/Albums';
|
||||
import Article from './Shared/Article';
|
||||
import Contacts from './Shared/Contacts';
|
||||
|
||||
class AlbumsScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
tabBarLabel: 'Albums',
|
||||
};
|
||||
|
||||
render() {
|
||||
return <Albums />;
|
||||
}
|
||||
}
|
||||
|
||||
class ArticleScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
tabBarLabel: 'Article',
|
||||
};
|
||||
|
||||
render() {
|
||||
return <Article />;
|
||||
}
|
||||
}
|
||||
|
||||
class ContactsScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
tabBarLabel: 'Contacts',
|
||||
};
|
||||
|
||||
render() {
|
||||
return <Contacts />;
|
||||
}
|
||||
}
|
||||
|
||||
export default createMaterialTopTabNavigator(
|
||||
{
|
||||
AlbumsScreen,
|
||||
ArticleScreen,
|
||||
ContactsScreen,
|
||||
},
|
||||
{
|
||||
lazy: true,
|
||||
tabBarOptions: {
|
||||
style: { backgroundColor: '#5620E4' },
|
||||
},
|
||||
}
|
||||
);
|
||||
75
example/src/ModalPresentation.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import * as React from 'react';
|
||||
import { Button, View, Text } from 'react-native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
TransitionPresets,
|
||||
NavigationStackScreenProps,
|
||||
} from 'react-navigation-stack';
|
||||
|
||||
class ListScreen extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'My Modal',
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Text>List Screen</Text>
|
||||
<Text>A list may go here</Text>
|
||||
<Button
|
||||
title="Go to Details"
|
||||
onPress={() => this.props.navigation.navigate('Details')}
|
||||
/>
|
||||
<Button
|
||||
title="Go back to all examples"
|
||||
onPress={() => this.props.navigation.navigate('Index')}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DetailsScreen extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
headerShown: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Text>Details Screen</Text>
|
||||
<Button
|
||||
title="Go to Details... again"
|
||||
onPress={() => this.props.navigation.push('Details')}
|
||||
/>
|
||||
<Button
|
||||
title="Go to List"
|
||||
onPress={() => this.props.navigation.navigate('List')}
|
||||
/>
|
||||
<Button
|
||||
title="Go back"
|
||||
onPress={() => this.props.navigation.goBack()}
|
||||
/>
|
||||
<Button
|
||||
title="Go back to all examples"
|
||||
onPress={() => this.props.navigation.navigate('Index')}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default createStackNavigator(
|
||||
{
|
||||
List: ListScreen,
|
||||
Details: DetailsScreen,
|
||||
},
|
||||
{
|
||||
mode: 'modal',
|
||||
defaultNavigationOptions: {
|
||||
...TransitionPresets.ModalPresentationIOS,
|
||||
cardOverlayEnabled: true,
|
||||
gestureEnabled: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
188
example/src/ModalStack.tsx
Normal file
@@ -0,0 +1,188 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Button,
|
||||
View,
|
||||
Text,
|
||||
Dimensions,
|
||||
Switch,
|
||||
TextInput,
|
||||
} from 'react-native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
CardStyleInterpolators,
|
||||
NavigationStackScreenProps,
|
||||
StackCardStyleInterpolator,
|
||||
} from 'react-navigation-stack';
|
||||
|
||||
const gestureResponseDistance = {
|
||||
vertical: Dimensions.get('window').height,
|
||||
};
|
||||
|
||||
const forVerticalInvertedIOS: StackCardStyleInterpolator = ({
|
||||
current: { progress },
|
||||
layouts: { screen },
|
||||
}) => {
|
||||
const translateY = progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [-screen.height, 0],
|
||||
});
|
||||
|
||||
return {
|
||||
cardStyle: {
|
||||
transform: [
|
||||
// Translation for the animation of the current card
|
||||
{ translateY },
|
||||
],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
class Modal extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = ({ navigation }: NavigationStackScreenProps) => {
|
||||
return {
|
||||
title: 'Modal',
|
||||
cardStyleInterpolator:
|
||||
navigation.getParam('gestureDirection', 'vertical') ===
|
||||
'vertical-inverted'
|
||||
? forVerticalInvertedIOS
|
||||
: CardStyleInterpolators.forVerticalIOS,
|
||||
gestureDirection: navigation.getParam('gestureDirection', 'vertical'),
|
||||
cardTransparent: true,
|
||||
gestureResponseDistance,
|
||||
};
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: 'white',
|
||||
paddingVertical: 20,
|
||||
paddingHorizontal: 20,
|
||||
height: Dimensions.get('screen').height,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 10 },
|
||||
shadowOpacity: 0.2,
|
||||
shadowRadius: 4,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ListScreen extends React.Component<
|
||||
NavigationStackScreenProps,
|
||||
{ isInverted: boolean }
|
||||
> {
|
||||
// eslint-disable-next-line react/sort-comp
|
||||
static navigationOptions = {
|
||||
title: 'My Modal',
|
||||
};
|
||||
|
||||
state = { isInverted: false };
|
||||
|
||||
onSwitch = () =>
|
||||
this.setState(prevState => ({ isInverted: !prevState.isInverted }));
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Text>List Screen</Text>
|
||||
<Text>A list may go here</Text>
|
||||
<Button
|
||||
title="Go to Details"
|
||||
onPress={() => this.props.navigation.navigate('Details')}
|
||||
/>
|
||||
<Button
|
||||
title="Go back to all examples"
|
||||
onPress={() => this.props.navigation.navigate('Index')}
|
||||
/>
|
||||
<Text>Invert modal gesture direction:</Text>
|
||||
<Switch
|
||||
style={{ margin: 10 }}
|
||||
onValueChange={this.onSwitch}
|
||||
value={this.state.isInverted}
|
||||
/>
|
||||
<Button
|
||||
title="Show Modal"
|
||||
onPress={() =>
|
||||
this.props.navigation.push('Modal', {
|
||||
gestureDirection: this.state.isInverted
|
||||
? 'vertical-inverted'
|
||||
: 'vertical',
|
||||
})
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DetailsScreen extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
// Uncomment below to test inverted modal gesture
|
||||
// gestureDirection: 'inverted',
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Text>Details Screen</Text>
|
||||
<Button
|
||||
title="Go to Details... again"
|
||||
onPress={() => this.props.navigation.push('Details')}
|
||||
/>
|
||||
<Button
|
||||
title="Go to inputs..."
|
||||
onPress={() => this.props.navigation.push('Inputs')}
|
||||
/>
|
||||
<Button
|
||||
title="Go to List"
|
||||
onPress={() => this.props.navigation.navigate('List')}
|
||||
/>
|
||||
<Button
|
||||
title="Go back"
|
||||
onPress={() => this.props.navigation.goBack()}
|
||||
/>
|
||||
<Button
|
||||
title="Go back to all examples"
|
||||
onPress={() => this.props.navigation.navigate('Index')}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
function InputsScreen({ navigation }: NavigationStackScreenProps) {
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Text>Inputs Screen</Text>
|
||||
<TextInput
|
||||
defaultValue="sample"
|
||||
autoFocus
|
||||
style={{ backgroundColor: 'blue' }}
|
||||
/>
|
||||
<TextInput defaultValue="sample" style={{ backgroundColor: 'red' }} />
|
||||
<TextInput defaultValue="sample" style={{ backgroundColor: 'green' }} />
|
||||
<Button
|
||||
title="Go to inputs... again"
|
||||
onPress={() => navigation.push('Inputs')}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default createStackNavigator(
|
||||
{
|
||||
List: ListScreen,
|
||||
Details: DetailsScreen,
|
||||
Modal: Modal,
|
||||
Inputs: {
|
||||
screen: InputsScreen,
|
||||
navigationOptions: { gestureDirection: 'vertical' },
|
||||
},
|
||||
},
|
||||
{
|
||||
initialRouteName: 'List',
|
||||
mode: 'modal',
|
||||
}
|
||||
);
|
||||
265
example/src/ParallaxDrawer.tsx
Normal file
@@ -0,0 +1,265 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
Dimensions,
|
||||
TouchableOpacity,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import {
|
||||
ThemeColors,
|
||||
useTheme,
|
||||
Themed,
|
||||
SafeAreaView,
|
||||
NavigationRoute,
|
||||
} from 'react-navigation';
|
||||
import {
|
||||
createDrawerNavigator,
|
||||
DrawerContentComponentProps,
|
||||
NavigationDrawerOptions,
|
||||
NavigationDrawerProp,
|
||||
NavigationDrawerScreenComponent,
|
||||
} from 'react-navigation-drawer';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
|
||||
type Params = { drawerLockMode: 'unlocked' | 'locked-open' | 'locked-closed' };
|
||||
|
||||
const SampleText = ({ children }: { children: React.ReactNode }) => (
|
||||
<Themed.Text>{children}</Themed.Text>
|
||||
);
|
||||
|
||||
const MyNavScreen = ({
|
||||
navigation,
|
||||
banner,
|
||||
}: {
|
||||
navigation: NavigationDrawerProp<NavigationRoute, Params>;
|
||||
banner: string;
|
||||
}) => {
|
||||
let theme = useTheme();
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
<SafeAreaView forceInset={{ top: 'always' }}>
|
||||
<View
|
||||
style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}
|
||||
>
|
||||
<SampleText>{banner}</SampleText>
|
||||
</View>
|
||||
<Themed.TextInput
|
||||
style={{
|
||||
flex: 1,
|
||||
height: 35,
|
||||
marginHorizontal: 10,
|
||||
marginVertical: 10,
|
||||
borderWidth: StyleSheet.hairlineWidth,
|
||||
borderColor: ThemeColors[theme].bodyBorder,
|
||||
textAlign: 'center',
|
||||
}}
|
||||
placeholder="Focus this TextInput then drag the drawer!"
|
||||
/>
|
||||
<Button onPress={() => navigation.openDrawer()} title="Open drawer" />
|
||||
<Button
|
||||
onPress={() => navigation.toggleDrawer()}
|
||||
title="Toggle drawer"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => {
|
||||
navigation.openDrawer();
|
||||
navigation.closeDrawer();
|
||||
}}
|
||||
title="Open and immediately close"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => {
|
||||
navigation.closeDrawer();
|
||||
navigation.openDrawer();
|
||||
}}
|
||||
title="Close and immediately open"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => {
|
||||
navigation.openDrawer();
|
||||
setTimeout(() => {
|
||||
navigation.closeDrawer();
|
||||
}, 150);
|
||||
}}
|
||||
title="Open then close drawer shortly after"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Email')}
|
||||
title="Open other screen"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Index')}
|
||||
title="Go back to list"
|
||||
/>
|
||||
{
|
||||
{
|
||||
'locked-open': (
|
||||
<Button
|
||||
onPress={() =>
|
||||
navigation.setParams({ drawerLockMode: 'locked-closed' })
|
||||
}
|
||||
title="Set locked-closed"
|
||||
/>
|
||||
),
|
||||
'locked-closed': (
|
||||
<Button
|
||||
onPress={() =>
|
||||
navigation.setParams({ drawerLockMode: 'unlocked' })
|
||||
}
|
||||
title="Set unlocked"
|
||||
/>
|
||||
),
|
||||
unlocked: (
|
||||
<Button
|
||||
onPress={() =>
|
||||
navigation.setParams({ drawerLockMode: 'locked-open' })
|
||||
}
|
||||
title="Set locked-open"
|
||||
/>
|
||||
),
|
||||
}[navigation.getParam('drawerLockMode', 'unlocked')]
|
||||
}
|
||||
</SafeAreaView>
|
||||
<Themed.StatusBar />
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
const InboxScreen: NavigationDrawerScreenComponent<Params> = ({
|
||||
navigation,
|
||||
}) => <MyNavScreen banner="Inbox Screen" navigation={navigation} />;
|
||||
|
||||
const EmailScreen: NavigationDrawerScreenComponent<Params> = ({
|
||||
navigation,
|
||||
}) => <MyNavScreen banner="Email Screen" navigation={navigation} />;
|
||||
|
||||
const DraftsScreen: NavigationDrawerScreenComponent<Params> = ({
|
||||
navigation,
|
||||
}) => <MyNavScreen banner="Drafts Screen" navigation={navigation} />;
|
||||
|
||||
const DrawerContents = ({
|
||||
drawerOpenProgress,
|
||||
descriptors,
|
||||
navigation,
|
||||
}: DrawerContentComponentProps) => {
|
||||
// `contentComponent` is passed an Animated.Value called drawerOpenProgress
|
||||
// that can be used to do interesting things like a simple parallax drawe
|
||||
const translateX = Animated.interpolate(drawerOpenProgress, {
|
||||
inputRange: [0, 1],
|
||||
outputRange: [-100, 0],
|
||||
});
|
||||
|
||||
return (
|
||||
<Animated.View style={{ transform: [{ translateX }] }}>
|
||||
<ScrollView>
|
||||
<SafeAreaView forceInset={{ top: 'always' }}>
|
||||
{navigation.state.routes.map(route => (
|
||||
<DrawerItem
|
||||
key={route.key}
|
||||
navigation={descriptors[route.key].navigation}
|
||||
item={route.routeName}
|
||||
/>
|
||||
))}
|
||||
</SafeAreaView>
|
||||
</ScrollView>
|
||||
</Animated.View>
|
||||
);
|
||||
};
|
||||
|
||||
const DrawerItem = (props: {
|
||||
navigation: NavigationDrawerProp;
|
||||
item: string;
|
||||
}) => {
|
||||
return (
|
||||
<TouchableOpacity onPress={() => props.navigation.navigate(props.item)}>
|
||||
<Themed.Text style={{ padding: 10, fontSize: 18, fontWeight: '600' }}>
|
||||
{props.item}
|
||||
</Themed.Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
function createDrawerExample(options = {}) {
|
||||
let DrawerExample = createDrawerNavigator(
|
||||
{
|
||||
Inbox: {
|
||||
path: '/',
|
||||
screen: InboxScreen,
|
||||
navigationOptions: ({ navigation }) => {
|
||||
const options: NavigationDrawerOptions = {
|
||||
drawerLabel: 'Inbox',
|
||||
drawerLockMode: (
|
||||
navigation.state.routes[navigation.state.index].params || {}
|
||||
).drawerLockMode,
|
||||
drawerIcon: ({ tintColor }) => (
|
||||
<MaterialIcons
|
||||
name="move-to-inbox"
|
||||
size={24}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
return options;
|
||||
},
|
||||
},
|
||||
Drafts: {
|
||||
path: '/sent',
|
||||
screen: DraftsScreen,
|
||||
navigationOptions: ({ navigation }) => {
|
||||
const options: NavigationDrawerOptions = {
|
||||
drawerLabel: 'Drafts',
|
||||
drawerLockMode: (
|
||||
navigation.state.routes[navigation.state.index].params || {}
|
||||
).drawerLockMode,
|
||||
drawerIcon: ({ tintColor }) => (
|
||||
<MaterialIcons
|
||||
name="drafts"
|
||||
size={24}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
return options;
|
||||
},
|
||||
},
|
||||
Email: {
|
||||
path: '/sent',
|
||||
screen: EmailScreen,
|
||||
},
|
||||
},
|
||||
{
|
||||
overlayColor: 'rgba(0,0,0,0)',
|
||||
drawerType: 'back',
|
||||
drawerBackgroundColor: {
|
||||
light: '#eee',
|
||||
dark: '#888',
|
||||
},
|
||||
screenContainerStyle: {
|
||||
shadowColor: '#000000',
|
||||
shadowOpacity: 0.4,
|
||||
shadowRadius: 8,
|
||||
shadowOffset: { height: 0, width: -4 },
|
||||
},
|
||||
contentComponent: DrawerContents,
|
||||
drawerWidth: Dimensions.get('window').width * 0.8,
|
||||
navigationOptions: {
|
||||
headerShown: false,
|
||||
},
|
||||
contentOptions: {
|
||||
activeTintColor: '#e91e63',
|
||||
},
|
||||
...options,
|
||||
}
|
||||
);
|
||||
|
||||
return DrawerExample;
|
||||
}
|
||||
|
||||
export default createDrawerExample();
|
||||
137
example/src/PerScreenTransitions.tsx
Normal file
@@ -0,0 +1,137 @@
|
||||
import * as React from 'react';
|
||||
import { Button, View, Text } from 'react-native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
TransitionPresets,
|
||||
TransitionSpecs,
|
||||
NavigationStackScreenProps,
|
||||
} from 'react-navigation-stack';
|
||||
|
||||
function SlideScreen({ navigation }: NavigationStackScreenProps) {
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Text>Slide Screen</Text>
|
||||
<Button title="Go to Modal" onPress={() => navigation.push('Modal')} />
|
||||
<Button title="Go to Reveal" onPress={() => navigation.push('Reveal')} />
|
||||
<Button
|
||||
title="Go to Transparent"
|
||||
onPress={() => navigation.push('Transparent')}
|
||||
/>
|
||||
<Button
|
||||
title="Go back to all examples"
|
||||
onPress={() => navigation.navigate('Index')}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function ModalScreen({ navigation }: NavigationStackScreenProps) {
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Text>Slide Screen</Text>
|
||||
<Button title="Go to Reveal" onPress={() => navigation.push('Reveal')} />
|
||||
<Button title="Go to Slide" onPress={() => navigation.push('Slide')} />
|
||||
<Button
|
||||
title="Go to Transparent"
|
||||
onPress={() => navigation.push('Transparent')}
|
||||
/>
|
||||
<Button
|
||||
title="Go back to all examples"
|
||||
onPress={() => navigation.navigate('Index')}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function RevealScreen({ navigation }: NavigationStackScreenProps) {
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Text>Slide Screen</Text>
|
||||
<Button title="Go to Slide" onPress={() => navigation.push('Slide')} />
|
||||
<Button title="Go to Modal" onPress={() => navigation.push('Modal')} />
|
||||
<Button
|
||||
title="Go to Transparent"
|
||||
onPress={() => navigation.push('Transparent')}
|
||||
/>
|
||||
<Button
|
||||
title="Go back to all examples"
|
||||
onPress={() => navigation.navigate('Index')}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function TransparentScreen({ navigation }: NavigationStackScreenProps) {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: 'rgba(0, 0, 0, .7)',
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: 'white',
|
||||
borderRadius: 4,
|
||||
padding: 16,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Text>Transparent Screen</Text>
|
||||
<Button title="Go to Slide" onPress={() => navigation.push('Slide')} />
|
||||
<Button title="Go to Modal" onPress={() => navigation.push('Modal')} />
|
||||
<Button
|
||||
title="Go to Reveal"
|
||||
onPress={() => navigation.push('Reveal')}
|
||||
/>
|
||||
<Button
|
||||
title="Go back to all examples"
|
||||
onPress={() => navigation.navigate('Index')}
|
||||
/>
|
||||
<Button title="Close" onPress={() => navigation.goBack()} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default createStackNavigator(
|
||||
{
|
||||
Slide: {
|
||||
screen: SlideScreen,
|
||||
navigationOptions: TransitionPresets.SlideFromRightIOS,
|
||||
},
|
||||
Modal: {
|
||||
screen: ModalScreen,
|
||||
navigationOptions: TransitionPresets.ModalSlideFromBottomIOS,
|
||||
},
|
||||
Reveal: {
|
||||
screen: RevealScreen,
|
||||
navigationOptions: TransitionPresets.RevealFromBottomAndroid,
|
||||
},
|
||||
Transparent: {
|
||||
screen: TransparentScreen,
|
||||
navigationOptions: {
|
||||
cardStyle: { backgroundColor: 'transparent' },
|
||||
headerShown: false,
|
||||
gestureEnabled: false,
|
||||
transitionSpec: {
|
||||
open: TransitionSpecs.TransitionIOSSpec,
|
||||
close: TransitionSpecs.TransitionIOSSpec,
|
||||
},
|
||||
cardStyleInterpolator: ({ current: { progress } }) => ({
|
||||
cardStyle: { opacity: progress },
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
headerMode: 'screen',
|
||||
defaultNavigationOptions: {
|
||||
cardOverlayEnabled: true,
|
||||
gestureEnabled: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
126
example/src/RTLDrawer.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
Text,
|
||||
View,
|
||||
ScrollView,
|
||||
Dimensions,
|
||||
TouchableOpacity,
|
||||
} from 'react-native';
|
||||
|
||||
import { Themed, NavigationActions } from 'react-navigation';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackScreenComponent,
|
||||
} from 'react-navigation-stack';
|
||||
import {
|
||||
createDrawerNavigator,
|
||||
DrawerContentComponentProps,
|
||||
DrawerActions,
|
||||
} from 'react-navigation-drawer';
|
||||
|
||||
class RightDrawer extends Component<DrawerContentComponentProps> {
|
||||
state = {
|
||||
categories: [
|
||||
{ i: 'c1', n: 'name1' },
|
||||
{ i: 'c2', n: 'name2' },
|
||||
],
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{ backgroundColor: 'white', flex: 1 }}>
|
||||
<ScrollView
|
||||
style={{ height: '100%', width: '100%', backgroundColor: '#333333' }}
|
||||
>
|
||||
{this.state.categories.map(key => {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={key.n}
|
||||
onPress={() => {
|
||||
let nid = key.i;
|
||||
this.props.navigation.dispatch(
|
||||
NavigationActions.navigate({
|
||||
routeName: 'CategoryScreen',
|
||||
params: {
|
||||
id: nid,
|
||||
title: key.n,
|
||||
},
|
||||
})
|
||||
);
|
||||
this.props.navigation.dispatch(DrawerActions.closeDrawer());
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: '100%',
|
||||
height: 60,
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: 'white',
|
||||
height: 32.5,
|
||||
width: '100%',
|
||||
paddingRight: 20,
|
||||
fontSize: 20,
|
||||
}}
|
||||
>
|
||||
{key.n}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</ScrollView>
|
||||
<Themed.StatusBar barStyle="light-content" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const CategoryScreen: NavigationStackScreenComponent = ({ navigation }) => {
|
||||
return (
|
||||
<View>
|
||||
<Themed.Text>CategoryScreen {navigation.getParam('title')}</Themed.Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const AppNavigator = createStackNavigator(
|
||||
{
|
||||
CategoryScreen,
|
||||
},
|
||||
{
|
||||
defaultNavigationOptions: {
|
||||
title: 'RTL Test',
|
||||
headerStyle: {
|
||||
backgroundColor: '#0f5599',
|
||||
},
|
||||
headerTintColor: 'white',
|
||||
headerBackTitleStyle: {
|
||||
color: 'white',
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
const DrawerNavigator = createDrawerNavigator(
|
||||
{
|
||||
Item1: {
|
||||
screen: AppNavigator,
|
||||
},
|
||||
},
|
||||
{
|
||||
contentComponent: RightDrawer,
|
||||
drawerLockMode: 'unlocked',
|
||||
drawerPosition: 'right',
|
||||
drawerWidth: () => {
|
||||
return Dimensions.get('window').width - 150;
|
||||
},
|
||||
drawerType: 'slide',
|
||||
drawerBackgroundColor: '#333333',
|
||||
backBehavior: 'none',
|
||||
}
|
||||
);
|
||||
|
||||
export default DrawerNavigator;
|
||||
138
example/src/RevealStack.tsx
Normal file
@@ -0,0 +1,138 @@
|
||||
import * as React from 'react';
|
||||
import { Dimensions, Button, View, Text } from 'react-native';
|
||||
import { withNavigation } from 'react-navigation';
|
||||
import {
|
||||
createStackNavigator,
|
||||
TransitionPresets,
|
||||
NavigationStackScreenProps,
|
||||
NavigationStackProp,
|
||||
} from 'react-navigation-stack';
|
||||
|
||||
const Buttons = withNavigation((props: { navigation: NavigationStackProp }) => (
|
||||
<React.Fragment>
|
||||
<Button
|
||||
title="Go to Details"
|
||||
onPress={() => props.navigation.navigate('Details')}
|
||||
/>
|
||||
<Button
|
||||
title="Go and then go to details quick"
|
||||
onPress={() => {
|
||||
props.navigation.pop();
|
||||
setTimeout(() => {
|
||||
props.navigation.navigate('Details');
|
||||
}, 100);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
title="Go to Headerless"
|
||||
onPress={() => props.navigation.navigate('Headerless')}
|
||||
/>
|
||||
<Button title="Go back" onPress={() => props.navigation.goBack()} />
|
||||
<Button
|
||||
title="Go back quick"
|
||||
onPress={() => {
|
||||
props.navigation.pop();
|
||||
setTimeout(() => {
|
||||
props.navigation.pop();
|
||||
}, 100);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
title="Go back to all examples"
|
||||
onPress={() => props.navigation.navigate('Index')}
|
||||
/>
|
||||
</React.Fragment>
|
||||
));
|
||||
|
||||
class ListScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
title: 'List',
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#fff',
|
||||
}}
|
||||
>
|
||||
<Text>List Screen</Text>
|
||||
<Text>A list may go here</Text>
|
||||
<Buttons />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DetailsScreen extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Details',
|
||||
gestureResponseDistance: {
|
||||
horizontal: Dimensions.get('window').width,
|
||||
},
|
||||
};
|
||||
|
||||
_goBackInTwoSeconds = () => {
|
||||
setTimeout(() => {
|
||||
this.props.navigation.goBack();
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#fff',
|
||||
}}
|
||||
>
|
||||
<Text>Details Screen</Text>
|
||||
<Button title="Go back in 2s" onPress={this._goBackInTwoSeconds} />
|
||||
<Buttons />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HeaderlessScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
headerShown: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#fff',
|
||||
}}
|
||||
>
|
||||
<Text>Headerless Screen</Text>
|
||||
<Buttons />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default createStackNavigator(
|
||||
{
|
||||
List: ListScreen,
|
||||
Details: DetailsScreen,
|
||||
Headerless: HeaderlessScreen,
|
||||
},
|
||||
{
|
||||
initialRouteName: 'List',
|
||||
headerMode: 'screen',
|
||||
defaultNavigationOptions: {
|
||||
...TransitionPresets.RevealFromBottomAndroid,
|
||||
cardOverlayEnabled: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
45
example/src/Shared/Albums.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
/* eslint-disable import/no-commonjs */
|
||||
|
||||
import * as React from 'react';
|
||||
import { Image, Dimensions, ScrollView, StyleSheet } from 'react-native';
|
||||
|
||||
const COVERS = [
|
||||
require('../assets/album-art-1.jpg'),
|
||||
require('../assets/album-art-2.jpg'),
|
||||
require('../assets/album-art-3.jpg'),
|
||||
require('../assets/album-art-4.jpg'),
|
||||
require('../assets/album-art-5.jpg'),
|
||||
require('../assets/album-art-6.jpg'),
|
||||
require('../assets/album-art-7.jpg'),
|
||||
require('../assets/album-art-8.jpg'),
|
||||
];
|
||||
|
||||
export default class Albums extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<ScrollView
|
||||
style={styles.container}
|
||||
contentContainerStyle={styles.content}
|
||||
>
|
||||
{COVERS.map((source, i) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<Image key={i} source={source} style={styles.cover} />
|
||||
))}
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: '#343C46',
|
||||
},
|
||||
content: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
cover: {
|
||||
width: '50%',
|
||||
height: Dimensions.get('window').width / 2,
|
||||
},
|
||||
});
|
||||
99
example/src/Shared/Article.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import * as React from 'react';
|
||||
import { View, Text, Image, ScrollView, StyleSheet } from 'react-native';
|
||||
|
||||
export default class Article extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<ScrollView
|
||||
style={styles.container}
|
||||
contentContainerStyle={styles.content}
|
||||
>
|
||||
<View style={styles.author}>
|
||||
<Image
|
||||
style={styles.avatar}
|
||||
source={require('../assets/avatar-1.png')}
|
||||
/>
|
||||
<View style={styles.meta}>
|
||||
<Text style={styles.name}>Knowledge Bot</Text>
|
||||
<Text style={styles.timestamp}>1st Jan 2025</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Text style={styles.title}>Lorem Ipsum</Text>
|
||||
<Text style={styles.paragraph}>
|
||||
Contrary to popular belief, Lorem Ipsum is not simply random text. It
|
||||
has roots in a piece of classical Latin literature from 45 BC, making
|
||||
it over 2000 years old.
|
||||
</Text>
|
||||
<Image style={styles.image} source={require('../assets/book.jpg')} />
|
||||
<Text style={styles.paragraph}>
|
||||
Richard McClintock, a Latin professor at Hampden-Sydney College in
|
||||
Virginia, looked up one of the more obscure Latin words, consectetur,
|
||||
from a Lorem Ipsum passage, and going through the cites of the word in
|
||||
classical literature, discovered the undoubtable source.
|
||||
</Text>
|
||||
<Text style={styles.paragraph}>
|
||||
Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de
|
||||
Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by
|
||||
Cicero, written in 45 BC. This book is a treatise on the theory of
|
||||
ethics, very popular during the Renaissance. The first line of Lorem
|
||||
Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in
|
||||
section 1.10.32.
|
||||
</Text>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
content: {
|
||||
paddingVertical: 16,
|
||||
},
|
||||
author: {
|
||||
flexDirection: 'row',
|
||||
marginVertical: 8,
|
||||
marginHorizontal: 16,
|
||||
},
|
||||
meta: {
|
||||
marginHorizontal: 8,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
name: {
|
||||
color: '#000',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 16,
|
||||
lineHeight: 24,
|
||||
},
|
||||
timestamp: {
|
||||
color: '#999',
|
||||
fontSize: 14,
|
||||
lineHeight: 21,
|
||||
},
|
||||
avatar: {
|
||||
height: 48,
|
||||
width: 48,
|
||||
borderRadius: 24,
|
||||
},
|
||||
title: {
|
||||
color: '#000',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 36,
|
||||
marginVertical: 8,
|
||||
marginHorizontal: 16,
|
||||
},
|
||||
paragraph: {
|
||||
color: '#000',
|
||||
fontSize: 16,
|
||||
lineHeight: 24,
|
||||
marginVertical: 8,
|
||||
marginHorizontal: 16,
|
||||
},
|
||||
image: {
|
||||
width: '100%',
|
||||
height: 200,
|
||||
resizeMode: 'cover',
|
||||
marginVertical: 8,
|
||||
},
|
||||
});
|
||||
156
example/src/Shared/Button.tsx
Normal file
@@ -0,0 +1,156 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
AccessibilityStates,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
export interface ButtonProps {
|
||||
/**
|
||||
* Text to display inside the button
|
||||
*/
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* Handler to be called when the user taps the button
|
||||
*/
|
||||
onPress: (event?: any) => void;
|
||||
|
||||
/**
|
||||
* Color of the text (iOS), or background color of the button (Android)
|
||||
*/
|
||||
color?: string;
|
||||
|
||||
/**
|
||||
* TV preferred focus (see documentation for the View component).
|
||||
*/
|
||||
hasTVPreferredFocus?: boolean;
|
||||
|
||||
/**
|
||||
* Text to display for blindness accessibility features
|
||||
*/
|
||||
accessibilityLabel?: string;
|
||||
|
||||
/**
|
||||
* If true, disable all interactions for this component.
|
||||
*/
|
||||
disabled?: boolean;
|
||||
|
||||
/**
|
||||
* Used to locate this view in end-to-end tests.
|
||||
*/
|
||||
testID?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic button component that should render nicely on any platform. Supports
|
||||
* a minimal level of customization.
|
||||
*
|
||||
* <center><img src="img/buttonExample.png"></img></center>
|
||||
*
|
||||
* This button is built using TouchableOpacity
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* ```
|
||||
* import { Button } from 'react-native';
|
||||
* ...
|
||||
*
|
||||
* <Button
|
||||
* onPress={onPressLearnMore}
|
||||
* title="Learn More"
|
||||
* color="#841584"
|
||||
* accessibilityLabel="Learn more about this purple button"
|
||||
* />
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
|
||||
export default class Button extends React.Component<ButtonProps> {
|
||||
render() {
|
||||
const {
|
||||
accessibilityLabel,
|
||||
color,
|
||||
onPress,
|
||||
title,
|
||||
disabled,
|
||||
testID,
|
||||
} = this.props;
|
||||
const buttonStyles: any = [styles.button];
|
||||
const textStyles: any = [styles.text];
|
||||
if (color) {
|
||||
if (Platform.OS === 'ios') {
|
||||
textStyles.push({ color });
|
||||
} else {
|
||||
buttonStyles.push({ backgroundColor: color });
|
||||
}
|
||||
}
|
||||
const accessibilityStates: AccessibilityStates[] = [];
|
||||
if (disabled) {
|
||||
buttonStyles.push(styles.buttonDisabled);
|
||||
textStyles.push(styles.textDisabled);
|
||||
accessibilityStates.push('disabled');
|
||||
}
|
||||
const formattedTitle =
|
||||
Platform.OS === 'android' ? title.toUpperCase() : title;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
accessibilityRole="button"
|
||||
accessibilityStates={accessibilityStates}
|
||||
testID={testID}
|
||||
disabled={disabled}
|
||||
onPress={onPress}
|
||||
>
|
||||
<View style={buttonStyles}>
|
||||
<Text style={textStyles}>{formattedTitle}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: Platform.select({
|
||||
default: {
|
||||
backgroundColor: '#2196F3',
|
||||
borderRadius: 2,
|
||||
elevation: 4,
|
||||
// Material design blue from https://material.google.com/style/color.html#color-color-palette
|
||||
},
|
||||
ios: {},
|
||||
}),
|
||||
buttonDisabled: Platform.select({
|
||||
default: {
|
||||
backgroundColor: '#dfdfdf',
|
||||
elevation: 0,
|
||||
},
|
||||
ios: {},
|
||||
}),
|
||||
text: {
|
||||
padding: 8,
|
||||
textAlign: 'center',
|
||||
...Platform.select({
|
||||
default: {
|
||||
color: 'white',
|
||||
fontWeight: '500',
|
||||
},
|
||||
ios: {
|
||||
// iOS blue from https://developer.apple.com/ios/human-interface-guidelines/visual-design/color/
|
||||
color: '#007AFF',
|
||||
fontSize: 18,
|
||||
},
|
||||
}),
|
||||
},
|
||||
textDisabled: Platform.select({
|
||||
default: {
|
||||
color: '#a1a1a1',
|
||||
},
|
||||
ios: {
|
||||
color: '#cdcdcd',
|
||||
},
|
||||
}),
|
||||
});
|
||||
19
example/src/Shared/ButtonWithMargin.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import { Platform, StyleSheet, View } from 'react-native';
|
||||
import BaseButton, { ButtonProps } from './Button';
|
||||
|
||||
export const Button = (props: ButtonProps) => (
|
||||
<View style={styles.margin}>
|
||||
<BaseButton {...props} />
|
||||
</View>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
margin: {
|
||||
...Platform.select({
|
||||
android: {
|
||||
margin: 10,
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
97
example/src/Shared/Chat.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import * as React from 'react';
|
||||
import { View, Image, Text, ScrollView, StyleSheet } from 'react-native';
|
||||
|
||||
const MESSAGES = [
|
||||
'okay',
|
||||
'sudo make me a sandwich',
|
||||
'what? make it yourself',
|
||||
'make me a sandwich',
|
||||
];
|
||||
|
||||
export default class Albums extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ScrollView
|
||||
style={styles.inverted}
|
||||
contentContainerStyle={styles.content}
|
||||
>
|
||||
{MESSAGES.map((text, i) => {
|
||||
const odd = i % 2;
|
||||
|
||||
return (
|
||||
<View
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
key={i}
|
||||
style={[odd ? styles.odd : styles.even, styles.inverted]}
|
||||
>
|
||||
<Image
|
||||
style={styles.avatar}
|
||||
source={
|
||||
odd
|
||||
? require('../assets/avatar-2.png')
|
||||
: require('../assets/avatar-1.png')
|
||||
}
|
||||
/>
|
||||
<View
|
||||
style={[styles.bubble, odd ? styles.received : styles.sent]}
|
||||
>
|
||||
<Text style={odd ? styles.receivedText : styles.sentText}>
|
||||
{text}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#eceff1',
|
||||
},
|
||||
inverted: {
|
||||
transform: [{ scaleY: -1 }],
|
||||
},
|
||||
content: {
|
||||
padding: 16,
|
||||
},
|
||||
even: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
odd: {
|
||||
flexDirection: 'row-reverse',
|
||||
},
|
||||
avatar: {
|
||||
marginVertical: 8,
|
||||
marginHorizontal: 6,
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 20,
|
||||
borderColor: 'rgba(0, 0, 0, .16)',
|
||||
borderWidth: StyleSheet.hairlineWidth,
|
||||
},
|
||||
bubble: {
|
||||
marginVertical: 8,
|
||||
marginHorizontal: 6,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 16,
|
||||
borderRadius: 20,
|
||||
},
|
||||
sent: {
|
||||
backgroundColor: '#cfd8dc',
|
||||
},
|
||||
received: {
|
||||
backgroundColor: '#2196F3',
|
||||
},
|
||||
sentText: {
|
||||
color: 'black',
|
||||
},
|
||||
receivedText: {
|
||||
color: 'white',
|
||||
},
|
||||
});
|
||||
133
example/src/Shared/Contacts.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import * as React from 'react';
|
||||
import { View, Text, StyleSheet, FlatList } from 'react-native';
|
||||
|
||||
type Item = { name: string; number: number };
|
||||
|
||||
const CONTACTS: Item[] = [
|
||||
{ name: 'Marissa Castillo', number: 7766398169 },
|
||||
{ name: 'Denzel Curry', number: 9394378449 },
|
||||
{ name: 'Miles Ferguson', number: 8966872888 },
|
||||
{ name: 'Desiree Webster', number: 6818656371 },
|
||||
{ name: 'Samantha Young', number: 6538288534 },
|
||||
{ name: 'Irene Hunter', number: 2932176249 },
|
||||
{ name: 'Annie Ryan', number: 4718456627 },
|
||||
{ name: 'Sasha Oliver', number: 9743195919 },
|
||||
{ name: 'Jarrod Avila', number: 8339212305 },
|
||||
{ name: 'Griffin Weaver', number: 6059349721 },
|
||||
{ name: 'Emilee Moss', number: 7382905180 },
|
||||
{ name: 'Angelique Oliver', number: 9689298436 },
|
||||
{ name: 'Emanuel Little', number: 6673376805 },
|
||||
{ name: 'Wayne Day', number: 6918839582 },
|
||||
{ name: 'Lauren Reese', number: 4652613201 },
|
||||
{ name: 'Kailey Ward', number: 2232609512 },
|
||||
{ name: 'Gabrielle Newman', number: 2837997127 },
|
||||
{ name: 'Luke Strickland', number: 8404732322 },
|
||||
{ name: 'Payton Garza', number: 7916140875 },
|
||||
{ name: 'Anna Moss', number: 3504954657 },
|
||||
{ name: 'Kailey Vazquez', number: 3002136330 },
|
||||
{ name: 'Jennifer Coleman', number: 5469629753 },
|
||||
{ name: 'Cindy Casey', number: 8446175026 },
|
||||
{ name: 'Dillon Doyle', number: 5614510703 },
|
||||
{ name: 'Savannah Garcia', number: 5634775094 },
|
||||
{ name: 'Kailey Hudson', number: 3289239675 },
|
||||
{ name: 'Ariel Green', number: 2103492196 },
|
||||
{ name: 'Weston Perez', number: 2984221823 },
|
||||
{ name: 'Kari Juarez', number: 9502125065 },
|
||||
{ name: 'Sara Sanders', number: 7696668206 },
|
||||
{ name: 'Griffin Le', number: 3396937040 },
|
||||
{ name: 'Fernando Valdez', number: 9124257306 },
|
||||
{ name: 'Taylor Marshall', number: 9656072372 },
|
||||
{ name: 'Elias Dunn', number: 9738536473 },
|
||||
{ name: 'Diane Barrett', number: 6886824829 },
|
||||
{ name: 'Samuel Freeman', number: 5523948094 },
|
||||
{ name: 'Irene Garza', number: 2077694008 },
|
||||
{ name: 'Devante Alvarez', number: 9897002645 },
|
||||
{ name: 'Sydney Floyd', number: 6462897254 },
|
||||
{ name: 'Toni Dixon', number: 3775448213 },
|
||||
{ name: 'Anastasia Spencer', number: 4548212752 },
|
||||
{ name: 'Reid Cortez', number: 6668056507 },
|
||||
{ name: 'Ramon Duncan', number: 8889157751 },
|
||||
{ name: 'Kenny Moreno', number: 5748219540 },
|
||||
{ name: 'Shelby Craig', number: 9473708675 },
|
||||
{ name: 'Jordyn Brewer', number: 7552277991 },
|
||||
{ name: 'Tanya Walker', number: 4308189657 },
|
||||
{ name: 'Nolan Figueroa', number: 9173443776 },
|
||||
{ name: 'Sophia Gibbs', number: 6435942770 },
|
||||
{ name: 'Vincent Sandoval', number: 2606111495 },
|
||||
];
|
||||
|
||||
class ContactItem extends React.PureComponent<{
|
||||
item: Item;
|
||||
}> {
|
||||
render() {
|
||||
const { item } = this.props;
|
||||
|
||||
return (
|
||||
<View style={styles.item}>
|
||||
<View style={styles.avatar}>
|
||||
<Text style={styles.letter}>
|
||||
{item.name.slice(0, 1).toUpperCase()}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.details}>
|
||||
<Text style={styles.name}>{item.name}</Text>
|
||||
<Text style={styles.number}>{item.number}</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default class Contacts extends React.Component {
|
||||
_renderItem = ({ item }: { item: Item }) => <ContactItem item={item} />;
|
||||
|
||||
_ItemSeparator = () => <View style={styles.separator} />;
|
||||
|
||||
render() {
|
||||
return (
|
||||
<FlatList
|
||||
data={CONTACTS}
|
||||
keyExtractor={(_, i) => String(i)}
|
||||
renderItem={this._renderItem}
|
||||
ItemSeparatorComponent={this._ItemSeparator}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
item: {
|
||||
backgroundColor: 'white',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
padding: 8,
|
||||
},
|
||||
avatar: {
|
||||
height: 36,
|
||||
width: 36,
|
||||
borderRadius: 18,
|
||||
backgroundColor: '#e91e63',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
letter: {
|
||||
color: 'white',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
details: {
|
||||
margin: 8,
|
||||
},
|
||||
name: {
|
||||
fontWeight: 'bold',
|
||||
fontSize: 14,
|
||||
color: 'black',
|
||||
},
|
||||
number: {
|
||||
fontSize: 12,
|
||||
color: '#999',
|
||||
},
|
||||
separator: {
|
||||
height: StyleSheet.hairlineWidth,
|
||||
backgroundColor: 'rgba(0, 0, 0, .08)',
|
||||
},
|
||||
});
|
||||
18
example/src/Shared/HeaderButtons.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
HeaderButtons as DefaultHeaderButtons,
|
||||
Item,
|
||||
} from 'react-navigation-header-buttons';
|
||||
|
||||
export class HeaderButtons extends React.PureComponent {
|
||||
static Item = Item;
|
||||
|
||||
render() {
|
||||
return (
|
||||
<DefaultHeaderButtons
|
||||
// color={Platform.OS === 'ios' ? '#037aff' : 'black'}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
86
example/src/Shared/PhotoGrid.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
View,
|
||||
Image,
|
||||
ScrollView,
|
||||
Dimensions,
|
||||
StyleSheet,
|
||||
StyleProp,
|
||||
ViewStyle,
|
||||
ScrollViewProperties,
|
||||
} from 'react-native';
|
||||
import {
|
||||
withNavigation,
|
||||
NavigationScreenProp,
|
||||
NavigationRoute,
|
||||
NavigationEventSubscription,
|
||||
} from 'react-navigation';
|
||||
|
||||
class NavigationAwareScrollViewBase extends React.Component<{
|
||||
navigation: NavigationScreenProp<NavigationRoute>;
|
||||
contentContainerStyle: StyleProp<ViewStyle>;
|
||||
}> {
|
||||
componentDidMount() {
|
||||
this.subscription = this.props.navigation.addListener('refocus', () => {
|
||||
if (this.props.navigation.isFocused()) {
|
||||
this.root.current && this.root.current.scrollTo({ x: 0, y: 0 });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.subscription && this.subscription.remove();
|
||||
}
|
||||
|
||||
setNativeProps(props: ScrollViewProperties) {
|
||||
// @ts-ignore
|
||||
this.root.current.setNativeProps(props);
|
||||
}
|
||||
|
||||
getNode() {
|
||||
return this.root.current;
|
||||
}
|
||||
|
||||
private subscription: NavigationEventSubscription | undefined;
|
||||
|
||||
private root = React.createRef<ScrollView>();
|
||||
|
||||
render() {
|
||||
return <ScrollView {...this.props} ref={this.root} />;
|
||||
}
|
||||
}
|
||||
|
||||
const NavigationAwareScrollView = withNavigation(NavigationAwareScrollViewBase);
|
||||
|
||||
export default function PhotoGrid({ id }: { id: string }) {
|
||||
const PHOTOS = Array.from({ length: 24 }).map(
|
||||
(_, i) => `https://unsplash.it/300/300/?random&__id=${id}${i}`
|
||||
);
|
||||
|
||||
return (
|
||||
<NavigationAwareScrollView contentContainerStyle={styles.content}>
|
||||
{PHOTOS.map(uri => (
|
||||
<View key={uri} style={styles.item}>
|
||||
<Image source={{ uri }} style={styles.photo} />
|
||||
</View>
|
||||
))}
|
||||
</NavigationAwareScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
content: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
padding: 4,
|
||||
},
|
||||
item: {
|
||||
height: Dimensions.get('window').width / 2,
|
||||
width: '50%',
|
||||
padding: 4,
|
||||
},
|
||||
photo: {
|
||||
flex: 1,
|
||||
resizeMode: 'cover',
|
||||
},
|
||||
});
|
||||
19
example/src/Shared/SampleText.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { Themed } from 'react-navigation';
|
||||
|
||||
/**
|
||||
* Used across examples as a screen placeholder.
|
||||
*/
|
||||
|
||||
const SampleText = ({ children }: { children?: ReactNode }) => (
|
||||
<Themed.Text style={styles.sampleText}>{children}</Themed.Text>
|
||||
);
|
||||
|
||||
export default SampleText;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
sampleText: {
|
||||
margin: 14,
|
||||
},
|
||||
});
|
||||
13
example/src/Shared/tabBarIcon.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
|
||||
const tabBarIcon = (name: string) => ({ tintColor }: { tintColor: string }) => (
|
||||
<MaterialIcons style={styles.icon} name={name} color={tintColor} size={24} />
|
||||
);
|
||||
|
||||
export default tabBarIcon;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
icon: { backgroundColor: 'transparent' },
|
||||
});
|
||||
64
example/src/ShiftingTabs.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
createMaterialBottomTabNavigator,
|
||||
NavigationMaterialBottomTabOptions,
|
||||
} from 'react-navigation-material-bottom-tabs';
|
||||
import PhotoGrid from './Shared/PhotoGrid';
|
||||
import tabBarIcon from './Shared/tabBarIcon';
|
||||
|
||||
class Album extends React.Component {
|
||||
static navigationOptions: NavigationMaterialBottomTabOptions = {
|
||||
tabBarColor: '#6200ee',
|
||||
tabBarIcon: tabBarIcon('photo-album'),
|
||||
};
|
||||
|
||||
render() {
|
||||
return <PhotoGrid id="album" />;
|
||||
}
|
||||
}
|
||||
|
||||
class Library extends React.Component {
|
||||
static navigationOptions: NavigationMaterialBottomTabOptions = {
|
||||
tabBarColor: '#2962ff',
|
||||
tabBarIcon: tabBarIcon('inbox'),
|
||||
};
|
||||
|
||||
render() {
|
||||
return <PhotoGrid id="library" />;
|
||||
}
|
||||
}
|
||||
|
||||
class Favorites extends React.Component {
|
||||
static navigationOptions: NavigationMaterialBottomTabOptions = {
|
||||
tabBarColor: '#00796b',
|
||||
tabBarIcon: tabBarIcon('favorite'),
|
||||
};
|
||||
|
||||
render() {
|
||||
return <PhotoGrid id="favorites" />;
|
||||
}
|
||||
}
|
||||
|
||||
class Purchased extends React.Component {
|
||||
static navigationOptions: NavigationMaterialBottomTabOptions = {
|
||||
tabBarColor: '#c51162',
|
||||
tabBarIcon: tabBarIcon('shop'),
|
||||
tabBarBadge: 'test',
|
||||
};
|
||||
|
||||
render() {
|
||||
return <PhotoGrid id="purchased" />;
|
||||
}
|
||||
}
|
||||
|
||||
export default createMaterialBottomTabNavigator(
|
||||
{
|
||||
Album,
|
||||
Library,
|
||||
Favorites,
|
||||
Purchased,
|
||||
},
|
||||
{
|
||||
shifting: true,
|
||||
}
|
||||
);
|
||||
201
example/src/SimpleDrawer.tsx
Normal file
@@ -0,0 +1,201 @@
|
||||
import React from 'react';
|
||||
import { Button, ScrollView, StyleSheet, View } from 'react-native';
|
||||
import {
|
||||
ThemeColors,
|
||||
useTheme,
|
||||
Themed,
|
||||
SafeAreaView,
|
||||
NavigationRoute,
|
||||
} from 'react-navigation';
|
||||
import {
|
||||
createDrawerNavigator,
|
||||
NavigationDrawerOptions,
|
||||
NavigationDrawerScreenProps,
|
||||
NavigationDrawerProp,
|
||||
NavigationDrawerScreenComponent,
|
||||
} from 'react-navigation-drawer';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
|
||||
const SampleText = ({ children }: { children: React.ReactNode }) => (
|
||||
<Themed.Text>{children}</Themed.Text>
|
||||
);
|
||||
|
||||
type Params = { drawerLockMode: 'unlocked' | 'locked-open' | 'locked-closed' };
|
||||
|
||||
const MyNavScreen = ({
|
||||
navigation,
|
||||
banner,
|
||||
}: {
|
||||
navigation: NavigationDrawerProp<NavigationRoute, Params>;
|
||||
banner: string;
|
||||
}) => {
|
||||
let theme = useTheme();
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
<SafeAreaView forceInset={{ top: 'always' }}>
|
||||
<View
|
||||
style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}
|
||||
>
|
||||
<SampleText>{banner}</SampleText>
|
||||
</View>
|
||||
<Themed.TextInput
|
||||
style={{
|
||||
flex: 1,
|
||||
height: 35,
|
||||
marginHorizontal: 10,
|
||||
marginVertical: 10,
|
||||
borderWidth: StyleSheet.hairlineWidth,
|
||||
borderColor: ThemeColors[theme].bodyBorder,
|
||||
textAlign: 'center',
|
||||
}}
|
||||
placeholder="Focus this TextInput then drag the drawer!"
|
||||
/>
|
||||
<Button onPress={() => navigation.openDrawer()} title="Open drawer" />
|
||||
<Button
|
||||
onPress={() => navigation.toggleDrawer()}
|
||||
title="Toggle drawer"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => {
|
||||
navigation.openDrawer();
|
||||
navigation.closeDrawer();
|
||||
}}
|
||||
title="Open and immediately close"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => {
|
||||
navigation.closeDrawer();
|
||||
navigation.openDrawer();
|
||||
}}
|
||||
title="Close and immediately open"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => {
|
||||
navigation.openDrawer();
|
||||
setTimeout(() => {
|
||||
navigation.closeDrawer();
|
||||
}, 150);
|
||||
}}
|
||||
title="Open then close drawer shortly after"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Email')}
|
||||
title="Open other screen"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Index')}
|
||||
title="Go back to list"
|
||||
/>
|
||||
{
|
||||
{
|
||||
'locked-open': (
|
||||
<Button
|
||||
onPress={() =>
|
||||
navigation.setParams({ drawerLockMode: 'locked-closed' })
|
||||
}
|
||||
title="Set locked-closed"
|
||||
/>
|
||||
),
|
||||
'locked-closed': (
|
||||
<Button
|
||||
onPress={() =>
|
||||
navigation.setParams({ drawerLockMode: 'unlocked' })
|
||||
}
|
||||
title="Set unlocked"
|
||||
/>
|
||||
),
|
||||
unlocked: (
|
||||
<Button
|
||||
onPress={() =>
|
||||
navigation.setParams({ drawerLockMode: 'locked-open' })
|
||||
}
|
||||
title="Set locked-open"
|
||||
/>
|
||||
),
|
||||
}[navigation.getParam('drawerLockMode', 'unlocked')]
|
||||
}
|
||||
</SafeAreaView>
|
||||
<Themed.StatusBar />
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
const InboxScreen: NavigationDrawerScreenComponent<Params> = ({
|
||||
navigation,
|
||||
}) => <MyNavScreen banner="Inbox Screen" navigation={navigation} />;
|
||||
|
||||
const EmailScreen: NavigationDrawerScreenComponent<Params> = ({
|
||||
navigation,
|
||||
}) => <MyNavScreen banner="Email Screen" navigation={navigation} />;
|
||||
|
||||
const DraftsScreen: NavigationDrawerScreenComponent<Params> = ({
|
||||
navigation,
|
||||
}) => <MyNavScreen banner="Drafts Screen" navigation={navigation} />;
|
||||
|
||||
function createDrawerExample(options = {}) {
|
||||
let DrawerExample = createDrawerNavigator(
|
||||
{
|
||||
Inbox: {
|
||||
path: '/',
|
||||
screen: InboxScreen,
|
||||
navigationOptions: ({ navigation }: NavigationDrawerScreenProps) => {
|
||||
const options: NavigationDrawerOptions = {
|
||||
drawerLabel: 'Inbox',
|
||||
drawerLockMode: navigation.state.params?.drawerLockMode,
|
||||
drawerIcon: ({ tintColor }) => (
|
||||
<MaterialIcons
|
||||
name="move-to-inbox"
|
||||
size={24}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
return options;
|
||||
},
|
||||
},
|
||||
Drafts: {
|
||||
path: '/sent',
|
||||
screen: DraftsScreen,
|
||||
navigationOptions: ({ navigation }: NavigationDrawerScreenProps) => {
|
||||
const options: NavigationDrawerOptions = {
|
||||
drawerLabel: 'Drafts',
|
||||
drawerLockMode: navigation.state.params?.drawerLockMode,
|
||||
drawerIcon: ({ tintColor }) => (
|
||||
<MaterialIcons
|
||||
name="drafts"
|
||||
size={24}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
return options;
|
||||
},
|
||||
},
|
||||
Email: {
|
||||
screen: EmailScreen,
|
||||
},
|
||||
},
|
||||
{
|
||||
initialRouteName: 'Drafts',
|
||||
drawerWidth: 210,
|
||||
navigationOptions: {
|
||||
headerShown: false,
|
||||
},
|
||||
contentOptions: {
|
||||
activeTintColor: '#e91e63',
|
||||
},
|
||||
...options,
|
||||
}
|
||||
);
|
||||
|
||||
return DrawerExample;
|
||||
}
|
||||
|
||||
export const SimpleDrawer = createDrawerExample();
|
||||
export const SimpleDrawerUnmountInactive = createDrawerExample({
|
||||
unmountInactiveRoutes: true,
|
||||
});
|
||||
141
example/src/SimpleStack.tsx
Normal file
@@ -0,0 +1,141 @@
|
||||
import * as React from 'react';
|
||||
import { Dimensions, Button, View, Text } from 'react-native';
|
||||
import { withNavigation } from 'react-navigation';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackScreenProps,
|
||||
NavigationStackProp,
|
||||
} from 'react-navigation-stack';
|
||||
|
||||
const Buttons = withNavigation((props: { navigation: NavigationStackProp }) => (
|
||||
<React.Fragment>
|
||||
<Button
|
||||
title="Push Details"
|
||||
onPress={() => props.navigation.push('Details')}
|
||||
/>
|
||||
<Button title="PopToTop" onPress={() => props.navigation.popToTop()} />
|
||||
<Button
|
||||
title="Go to Details"
|
||||
onPress={() => props.navigation.navigate('Details')}
|
||||
/>
|
||||
<Button
|
||||
title="Replace with List"
|
||||
onPress={() => props.navigation.replace('List')}
|
||||
/>
|
||||
<Button
|
||||
title="Go and then go to details quick"
|
||||
onPress={() => {
|
||||
props.navigation.pop();
|
||||
setTimeout(() => {
|
||||
props.navigation.navigate('Details');
|
||||
}, 100);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
title="Go to Headerless"
|
||||
onPress={() => props.navigation.navigate('Headerless')}
|
||||
/>
|
||||
<Button title="Go back" onPress={() => props.navigation.goBack()} />
|
||||
<Button
|
||||
title="Go back quick"
|
||||
onPress={() => {
|
||||
props.navigation.pop();
|
||||
setTimeout(() => {
|
||||
props.navigation.pop();
|
||||
}, 100);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
title="Go back to all examples"
|
||||
onPress={() => props.navigation.navigate('Index')}
|
||||
/>
|
||||
</React.Fragment>
|
||||
));
|
||||
|
||||
class ListScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
title: 'List',
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#fff',
|
||||
}}
|
||||
>
|
||||
<Text>List Screen</Text>
|
||||
<Text>A list may go here</Text>
|
||||
<Buttons />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DetailsScreen extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Details',
|
||||
gestureResponseDistance: {
|
||||
horizontal: Dimensions.get('window').width,
|
||||
},
|
||||
};
|
||||
|
||||
_goBackInTwoSeconds = () => {
|
||||
setTimeout(() => {
|
||||
this.props.navigation.goBack();
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#fff',
|
||||
}}
|
||||
>
|
||||
<Text>Details Screen</Text>
|
||||
<Button title="Go back in 2s" onPress={this._goBackInTwoSeconds} />
|
||||
<Buttons />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HeaderlessScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
headerShown: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#fff',
|
||||
}}
|
||||
>
|
||||
<Text>Headerless Screen</Text>
|
||||
<Buttons />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default createStackNavigator(
|
||||
{
|
||||
List: ListScreen,
|
||||
Details: DetailsScreen,
|
||||
Headerless: HeaderlessScreen,
|
||||
},
|
||||
{
|
||||
initialRouteName: 'List',
|
||||
}
|
||||
);
|
||||
69
example/src/SimpleTabs.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import {
|
||||
createMaterialBottomTabNavigator,
|
||||
NavigationMaterialBottomTabOptions,
|
||||
} from 'react-navigation-material-bottom-tabs';
|
||||
import PhotoGrid from './Shared/PhotoGrid';
|
||||
import tabBarIcon from './Shared/tabBarIcon';
|
||||
|
||||
class Album extends React.Component {
|
||||
static navigationOptions: NavigationMaterialBottomTabOptions = {
|
||||
tabBarIcon: tabBarIcon('photo-album'),
|
||||
};
|
||||
|
||||
render() {
|
||||
return <PhotoGrid id="album" />;
|
||||
}
|
||||
}
|
||||
|
||||
class Library extends React.Component {
|
||||
static navigationOptions: NavigationMaterialBottomTabOptions = {
|
||||
tabBarIcon: tabBarIcon('inbox'),
|
||||
};
|
||||
|
||||
render() {
|
||||
return <PhotoGrid id="library" />;
|
||||
}
|
||||
}
|
||||
|
||||
class Favorites extends React.Component {
|
||||
static navigationOptions: NavigationMaterialBottomTabOptions = {
|
||||
tabBarIcon: tabBarIcon('favorite'),
|
||||
};
|
||||
|
||||
render() {
|
||||
return <PhotoGrid id="favorites" />;
|
||||
}
|
||||
}
|
||||
|
||||
class Purchased extends React.Component {
|
||||
static navigationOptions: NavigationMaterialBottomTabOptions = {
|
||||
tabBarIcon: tabBarIcon('shop'),
|
||||
tabBarBadge: 12,
|
||||
};
|
||||
|
||||
render() {
|
||||
return <PhotoGrid id="purchased" />;
|
||||
}
|
||||
}
|
||||
|
||||
export default createMaterialBottomTabNavigator(
|
||||
{
|
||||
Album,
|
||||
Library,
|
||||
Favorites,
|
||||
Purchased,
|
||||
},
|
||||
{
|
||||
shifting: false,
|
||||
activeColor: '#6200ee',
|
||||
inactiveColor: '#828792',
|
||||
barStyle: {
|
||||
backgroundColor: '#f8f7f9',
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderStyle: 'solid',
|
||||
borderColor: '#d0cfd0',
|
||||
},
|
||||
}
|
||||
);
|
||||
113
example/src/StackAnimationConsumerStack.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import * as React from 'react';
|
||||
import { Animated, Button, View } from 'react-native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackScreenProps,
|
||||
CardAnimationContext,
|
||||
} from 'react-navigation-stack';
|
||||
|
||||
const ListScreen = (props: NavigationStackScreenProps) => (
|
||||
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
|
||||
<Button
|
||||
title="Push to another screen"
|
||||
onPress={() => props.navigation.push('Another')}
|
||||
/>
|
||||
<Button
|
||||
title="Push to yet another screen"
|
||||
onPress={() => props.navigation.push('YetAnother')}
|
||||
/>
|
||||
<Button
|
||||
title="Go back to all examples"
|
||||
onPress={() => props.navigation.navigate('Index')}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
||||
const AnotherScreen = () => (
|
||||
<CardAnimationContext.Consumer>
|
||||
{value => {
|
||||
const scale = value
|
||||
? value.current.progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0.25, 1],
|
||||
})
|
||||
: 1;
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'honeydew',
|
||||
}}
|
||||
>
|
||||
<Animated.Text
|
||||
style={{
|
||||
fontSize: 32,
|
||||
opacity: value ? value.current.progress : 1,
|
||||
transform: [{ scale }],
|
||||
}}
|
||||
>
|
||||
Animates on progress
|
||||
</Animated.Text>
|
||||
</View>
|
||||
);
|
||||
}}
|
||||
</CardAnimationContext.Consumer>
|
||||
);
|
||||
|
||||
const YetAnotherScreen = () => (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'papayawhip',
|
||||
}}
|
||||
>
|
||||
<CardAnimationContext.Consumer>
|
||||
{value => (
|
||||
<Animated.Text
|
||||
style={{
|
||||
fontSize: 24,
|
||||
opacity: value
|
||||
? value.swiping.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [1, 0],
|
||||
})
|
||||
: 1,
|
||||
}}
|
||||
>
|
||||
Disappears when swiping
|
||||
</Animated.Text>
|
||||
)}
|
||||
</CardAnimationContext.Consumer>
|
||||
<CardAnimationContext.Consumer>
|
||||
{value => (
|
||||
<Animated.Text
|
||||
style={{
|
||||
fontSize: 24,
|
||||
opacity: value
|
||||
? value.closing.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [1, 0],
|
||||
})
|
||||
: 1,
|
||||
}}
|
||||
>
|
||||
Disappears only when closing
|
||||
</Animated.Text>
|
||||
)}
|
||||
</CardAnimationContext.Consumer>
|
||||
</View>
|
||||
);
|
||||
|
||||
export default createStackNavigator(
|
||||
{
|
||||
List: ListScreen,
|
||||
Another: AnotherScreen,
|
||||
YetAnother: YetAnotherScreen,
|
||||
},
|
||||
{ initialRouteName: 'List' }
|
||||
);
|
||||
158
example/src/StackWithCustomHeaderBackImage.tsx
Normal file
@@ -0,0 +1,158 @@
|
||||
import * as React from 'react';
|
||||
import { Button, Image, StyleSheet } from 'react-native';
|
||||
import {
|
||||
NavigationScreenProp,
|
||||
NavigationState,
|
||||
Themed,
|
||||
SafeAreaView,
|
||||
} from 'react-navigation';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackScreenProps,
|
||||
} from 'react-navigation-stack';
|
||||
import SampleText from './Shared/SampleText';
|
||||
|
||||
interface MyNavScreenProps {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
banner: React.ReactNode;
|
||||
}
|
||||
|
||||
class MyCustomHeaderBackImage extends React.Component<any, any> {
|
||||
render() {
|
||||
const source = require('./assets/back.png');
|
||||
return (
|
||||
<Image
|
||||
source={source}
|
||||
style={[styles.myCustomHeaderBackImage, this.props.style]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyNavScreen extends React.Component<MyNavScreenProps> {
|
||||
render() {
|
||||
const { navigation, banner } = this.props;
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SampleText>{banner}</SampleText>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Photos', { name: 'Jane' })}
|
||||
title="Navigate to a photos screen"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<Themed.StatusBar />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface MyHomeScreenProps {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
}
|
||||
|
||||
class MyHomeScreen extends React.Component<MyHomeScreenProps> {
|
||||
static navigationOptions = {
|
||||
headerBackTitleVisible: false,
|
||||
title: 'Welcome',
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
return <MyNavScreen banner="Home Screen" navigation={navigation} />;
|
||||
}
|
||||
}
|
||||
|
||||
interface MyPhotosScreenProps {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
}
|
||||
|
||||
class MyPhotosScreen extends React.Component<MyPhotosScreenProps> {
|
||||
static navigationOptions = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
}) => ({
|
||||
headerBackTitleVisible: false,
|
||||
title: `${navigation.state.params!.name}'s photos`,
|
||||
});
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SampleText>{`${navigation.state.params!.name}'s Photos`}</SampleText>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Profile', { name: 'Jane' })}
|
||||
title="Navigate to a profile screen"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<Themed.StatusBar />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface MyProfileScreenProps {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
}
|
||||
|
||||
class MyProfileScreen extends React.Component<MyProfileScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Profile',
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SampleText>{`${navigation.state.params!.name}'s Profile`}</SampleText>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<Themed.StatusBar />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const StackWithCustomHeaderBackImage = createStackNavigator(
|
||||
{
|
||||
Home: {
|
||||
screen: MyHomeScreen,
|
||||
},
|
||||
Photos: {
|
||||
path: 'photos/:name',
|
||||
screen: MyPhotosScreen,
|
||||
},
|
||||
Profile: {
|
||||
path: 'profile/:name',
|
||||
screen: MyProfileScreen,
|
||||
},
|
||||
},
|
||||
{
|
||||
defaultNavigationOptions: ({ theme }: NavigationStackScreenProps) => ({
|
||||
headerBackImage: () => (
|
||||
<MyCustomHeaderBackImage
|
||||
style={[
|
||||
styles.myCustomHeaderBackImageAlt,
|
||||
{
|
||||
tintColor: theme === 'light' ? '#000' : '#fff',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
export default StackWithCustomHeaderBackImage;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
myCustomHeaderBackImage: {
|
||||
height: 14.5,
|
||||
marginLeft: 9,
|
||||
marginRight: 12,
|
||||
marginVertical: 12,
|
||||
resizeMode: 'contain',
|
||||
width: 24,
|
||||
},
|
||||
myCustomHeaderBackImageAlt: {},
|
||||
});
|
||||
72
example/src/StackWithDrawer.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import * as React from 'react';
|
||||
import { Button, Text, View } from 'react-native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackScreenProps,
|
||||
} from 'react-navigation-stack';
|
||||
import {
|
||||
createDrawerNavigator,
|
||||
DrawerContentComponentProps,
|
||||
} from 'react-navigation-drawer';
|
||||
import {
|
||||
createBottomTabNavigator,
|
||||
NavigationTabScreenProps,
|
||||
} from 'react-navigation-tabs';
|
||||
|
||||
function Menu({ navigation }: DrawerContentComponentProps) {
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<Button
|
||||
title="Open on top"
|
||||
onPress={() => {
|
||||
// @ts-ignore
|
||||
navigation.navigate('Top');
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
class Fake extends React.Component<
|
||||
NavigationTabScreenProps | NavigationStackScreenProps
|
||||
> {
|
||||
static navigationOptions = ({
|
||||
navigation,
|
||||
}: NavigationTabScreenProps | NavigationStackScreenProps) => ({
|
||||
title: navigation.getParam('title'),
|
||||
});
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Text style={{ fontSize: 20 }}>
|
||||
{this.props.navigation.getParam('title')}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const Tab = createBottomTabNavigator({
|
||||
Home: { screen: Fake, params: { title: 'Home' } },
|
||||
Other: { screen: Fake, params: { title: 'Other' } },
|
||||
});
|
||||
|
||||
const Drawer = createDrawerNavigator(
|
||||
{
|
||||
TabScreen: {
|
||||
screen: Tab,
|
||||
},
|
||||
},
|
||||
{
|
||||
contentComponent: props => <Menu {...props} />,
|
||||
navigationOptions: { title: 'Example' },
|
||||
}
|
||||
);
|
||||
|
||||
const App = createStackNavigator({
|
||||
Drawer: { screen: Drawer },
|
||||
Top: { screen: Fake, params: { title: 'Top' } },
|
||||
});
|
||||
|
||||
export default App;
|
||||
117
example/src/StackWithHeaderPreset.tsx
Normal file
@@ -0,0 +1,117 @@
|
||||
import * as React from 'react';
|
||||
import { SafeAreaView, Themed } from 'react-navigation';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackScreenProps,
|
||||
TransitionPresets,
|
||||
HeaderStyleInterpolators,
|
||||
} from 'react-navigation-stack';
|
||||
|
||||
import { Button } from './Shared/ButtonWithMargin';
|
||||
|
||||
class HomeScreen extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Welcome',
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
const { push } = navigation;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ paddingTop: 30 }}>
|
||||
<Button onPress={() => push('Other')} title="Push another screen" />
|
||||
<Button
|
||||
onPress={() => push('ScreenWithNoHeader')}
|
||||
title="Push screen with no header"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go Home" />
|
||||
<Themed.StatusBar />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OtherScreen extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Your title here',
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
const { push, pop } = navigation;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ paddingTop: 30 }}>
|
||||
<Button
|
||||
onPress={() => push('ScreenWithLongTitle')}
|
||||
title="Push another screen"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => push('ScreenWithNoHeader')}
|
||||
title="Push screen with no header"
|
||||
/>
|
||||
<Button onPress={() => pop()} title="Pop" />
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<Themed.StatusBar />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ScreenWithLongTitle extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: "Another title that's kind of long",
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
const { pop } = navigation;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ paddingTop: 30 }}>
|
||||
<Button onPress={() => pop()} title="Pop" />
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<Themed.StatusBar />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ScreenWithNoHeader extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
headerShown: false,
|
||||
title: 'No Header',
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
const { push, pop } = navigation;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ paddingTop: 30 }}>
|
||||
<Button onPress={() => push('Other')} title="Push another screen" />
|
||||
<Button onPress={() => pop()} title="Pop" />
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<Themed.StatusBar />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const StackWithHeaderPreset = createStackNavigator(
|
||||
{
|
||||
Home: HomeScreen,
|
||||
Other: OtherScreen,
|
||||
ScreenWithLongTitle,
|
||||
ScreenWithNoHeader,
|
||||
},
|
||||
{
|
||||
defaultNavigationOptions: {
|
||||
...TransitionPresets.SlideFromRightIOS,
|
||||
headerStyleInterpolator: HeaderStyleInterpolators.forUIKit,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default StackWithHeaderPreset;
|
||||
61
example/src/StackWithInput.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import * as React from 'react';
|
||||
import { Button, TextInput, View } from 'react-native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
CardStyleInterpolators,
|
||||
NavigationStackScreenProps,
|
||||
} from 'react-navigation-stack';
|
||||
|
||||
class Input extends React.Component {
|
||||
static navigationOptions = {
|
||||
title: 'Input screen',
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TextInput
|
||||
placeholder="Type something"
|
||||
style={{
|
||||
backgroundColor: 'white',
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 16,
|
||||
margin: 24,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Home extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Home',
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Button
|
||||
onPress={() => this.props.navigation.push('Input')}
|
||||
title="Push screen with input"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const App = createStackNavigator(
|
||||
{
|
||||
Home: { screen: Home },
|
||||
Input: { screen: Input },
|
||||
},
|
||||
{
|
||||
headerMode: 'screen',
|
||||
defaultNavigationOptions: {
|
||||
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
|
||||
gestureDirection: 'horizontal',
|
||||
gestureEnabled: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default App;
|
||||
246
example/src/StackWithTranslucentHeader.tsx
Normal file
@@ -0,0 +1,246 @@
|
||||
import * as React from 'react';
|
||||
import { Platform, ScrollView, StyleSheet, View } from 'react-native';
|
||||
import { BlurView } from 'expo-blur';
|
||||
import { getStatusBarHeight } from 'react-native-iphone-x-helper';
|
||||
import {
|
||||
NavigationEventPayload,
|
||||
NavigationEventSubscription,
|
||||
Themed,
|
||||
SupportedThemes,
|
||||
} from 'react-navigation';
|
||||
import {
|
||||
createStackNavigator,
|
||||
HeaderStyleInterpolators,
|
||||
NavigationStackScreenProps,
|
||||
NavigationStackProp,
|
||||
TransitionPresets,
|
||||
} from 'react-navigation-stack';
|
||||
import { Button } from './Shared/ButtonWithMargin';
|
||||
import { HeaderButtons } from './Shared/HeaderButtons';
|
||||
import SampleText from './Shared/SampleText';
|
||||
|
||||
interface MyNavScreenProps {
|
||||
navigation: NavigationStackProp;
|
||||
banner: React.ReactNode;
|
||||
}
|
||||
|
||||
class MyNavScreen extends React.Component<MyNavScreenProps> {
|
||||
// Inset to compensate for navigation bar being transparent.
|
||||
// And improved abstraction for this will be built in to react-navigation
|
||||
// at some point.
|
||||
|
||||
getHeaderInset(): any {
|
||||
const HEADER_HEIGHT =
|
||||
getStatusBarHeight() + Platform.select({ ios: 44, default: 56 });
|
||||
|
||||
return Platform.select({
|
||||
android: {
|
||||
contentContainerStyle: {
|
||||
paddingTop: HEADER_HEIGHT,
|
||||
},
|
||||
},
|
||||
ios: {
|
||||
contentInset: { top: HEADER_HEIGHT },
|
||||
contentOffset: { y: -HEADER_HEIGHT },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { navigation, banner } = this.props;
|
||||
const { push, replace, popToTop, pop } = navigation;
|
||||
return (
|
||||
<ScrollView style={{ flex: 1 }} {...this.getHeaderInset()}>
|
||||
<SampleText>{banner}</SampleText>
|
||||
<Button
|
||||
onPress={() => push('Profile', { name: 'Jane' })}
|
||||
title="Push a profile screen"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Photos', { name: 'Jane' })}
|
||||
title="Navigate to a photos screen"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => replace('Profile', { name: 'Lucy' })}
|
||||
title="Replace with profile"
|
||||
/>
|
||||
<Button onPress={() => popToTop()} title="Pop to top" />
|
||||
<Button onPress={() => pop()} title="Pop" />
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<Themed.StatusBar />
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface MyHomeScreenProps {
|
||||
navigation: NavigationStackProp;
|
||||
}
|
||||
|
||||
class MyHomeScreen extends React.Component<MyHomeScreenProps> {
|
||||
// eslint-disable-next-line react/sort-comp
|
||||
static navigationOptions = {
|
||||
title: 'Welcome',
|
||||
};
|
||||
s0: NavigationEventSubscription | null = null;
|
||||
s1: NavigationEventSubscription | null = null;
|
||||
s2: NavigationEventSubscription | null = null;
|
||||
s3: NavigationEventSubscription | null = null;
|
||||
|
||||
componentDidMount() {
|
||||
this.s0 = this.props.navigation.addListener('willFocus', this.onWF);
|
||||
this.s1 = this.props.navigation.addListener('didFocus', this.onDF);
|
||||
this.s2 = this.props.navigation.addListener('willBlur', this.onWB);
|
||||
this.s3 = this.props.navigation.addListener('didBlur', this.onDB);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this.s0!.remove();
|
||||
this.s1!.remove();
|
||||
this.s2!.remove();
|
||||
this.s3!.remove();
|
||||
}
|
||||
onWF = (a: NavigationEventPayload) => {
|
||||
console.log('willFocus HomeScreen', a);
|
||||
};
|
||||
onDF = (a: NavigationEventPayload) => {
|
||||
console.log('didFocus HomeScreen', a);
|
||||
};
|
||||
onWB = (a: NavigationEventPayload) => {
|
||||
console.log('willBlur HomeScreen', a);
|
||||
};
|
||||
onDB = (a: NavigationEventPayload) => {
|
||||
console.log('didBlur HomeScreen', a);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
return <MyNavScreen banner="Home Screen" navigation={navigation} />;
|
||||
}
|
||||
}
|
||||
|
||||
interface MyPhotosScreenProps {
|
||||
navigation: NavigationStackProp;
|
||||
}
|
||||
class MyPhotosScreen extends React.Component<MyPhotosScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Photos',
|
||||
};
|
||||
s0: NavigationEventSubscription | null = null;
|
||||
s1: NavigationEventSubscription | null = null;
|
||||
s2: NavigationEventSubscription | null = null;
|
||||
s3: NavigationEventSubscription | null = null;
|
||||
|
||||
componentDidMount() {
|
||||
this.s0 = this.props.navigation.addListener('willFocus', this.onWF);
|
||||
this.s1 = this.props.navigation.addListener('didFocus', this.onDF);
|
||||
this.s2 = this.props.navigation.addListener('willBlur', this.onWB);
|
||||
this.s3 = this.props.navigation.addListener('didBlur', this.onDB);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this.s0!.remove();
|
||||
this.s1!.remove();
|
||||
this.s2!.remove();
|
||||
this.s3!.remove();
|
||||
}
|
||||
onWF = (a: NavigationEventPayload) => {
|
||||
console.log('willFocus PhotosScreen', a);
|
||||
};
|
||||
onDF = (a: NavigationEventPayload) => {
|
||||
console.log('didFocus PhotosScreen', a);
|
||||
};
|
||||
onWB = (a: NavigationEventPayload) => {
|
||||
console.log('willBlur PhotosScreen', a);
|
||||
};
|
||||
onDB = (a: NavigationEventPayload) => {
|
||||
console.log('didBlur PhotosScreen', a);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
return (
|
||||
<MyNavScreen
|
||||
banner={`${navigation.state.params!.name}'s Photos`}
|
||||
navigation={navigation}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const MyProfileScreen = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NavigationStackProp;
|
||||
}) => (
|
||||
<MyNavScreen
|
||||
banner={`${navigation.state.params!.mode === 'edit' ? 'Now Editing ' : ''}${
|
||||
navigation.state.params!.name
|
||||
}'s Profile`}
|
||||
navigation={navigation}
|
||||
/>
|
||||
);
|
||||
|
||||
MyProfileScreen.navigationOptions = (props: {
|
||||
navigation: NavigationStackProp;
|
||||
theme: SupportedThemes;
|
||||
}) => {
|
||||
const { navigation, theme } = props;
|
||||
const { state, setParams } = navigation;
|
||||
const { params } = state;
|
||||
return {
|
||||
headerBackImage: params!.headerBackImage
|
||||
? () => params!.headerBackImage
|
||||
: undefined,
|
||||
// Render a button on the right side of the header.
|
||||
// When pressed switches the screen to edit mode.
|
||||
headerRight: () => (
|
||||
<HeaderButtons>
|
||||
<HeaderButtons.Item
|
||||
color={theme === 'light' ? '#000' : '#fff'}
|
||||
title={params!.mode === 'edit' ? 'Done' : 'Edit'}
|
||||
onPress={() =>
|
||||
setParams({ mode: params!.mode === 'edit' ? '' : 'edit' })
|
||||
}
|
||||
/>
|
||||
</HeaderButtons>
|
||||
),
|
||||
headerTitle: `${params!.name}'s Profile!`,
|
||||
};
|
||||
};
|
||||
|
||||
const StackWithTranslucentHeader = createStackNavigator(
|
||||
{
|
||||
Home: {
|
||||
screen: MyHomeScreen,
|
||||
},
|
||||
Photos: {
|
||||
path: 'photos/:name',
|
||||
screen: MyPhotosScreen,
|
||||
},
|
||||
Profile: {
|
||||
path: 'people/:name',
|
||||
screen: MyProfileScreen,
|
||||
},
|
||||
},
|
||||
{
|
||||
defaultNavigationOptions: ({ theme }: NavigationStackScreenProps) => ({
|
||||
...TransitionPresets.SlideFromRightIOS,
|
||||
headerBackground: () =>
|
||||
Platform.OS === 'ios' ? (
|
||||
<BlurView
|
||||
style={{ flex: 1 }}
|
||||
blurType={theme === 'light' ? 'light' : 'dark'}
|
||||
/>
|
||||
) : (
|
||||
<View style={{ flex: 1, backgroundColor: 'rgba(255,255,255,0.7)' }} />
|
||||
),
|
||||
headerStyle: {
|
||||
borderBottomColor: theme === 'light' ? '#A7A7AA' : 'transparent',
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
},
|
||||
headerTransparent: true,
|
||||
headerStyleInterpolator: HeaderStyleInterpolators.forUIKit,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
export default StackWithTranslucentHeader;
|
||||
109
example/src/StacksAndKeys.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { Themed } from 'react-navigation';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackScreenProps,
|
||||
} from 'react-navigation-stack';
|
||||
import { Button } from './Shared/ButtonWithMargin';
|
||||
|
||||
class HomeScreen extends React.Component<NavigationStackScreenProps> {
|
||||
render() {
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Text>Home</Text>
|
||||
<Button
|
||||
title="Navigate to 'Profile' with key 'A'"
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate({
|
||||
key: 'A',
|
||||
params: { homeKey: this.props.navigation.state.key },
|
||||
routeName: 'Profile',
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
title="Go back to other examples"
|
||||
onPress={() => this.props.navigation.goBack(null)}
|
||||
/>
|
||||
<Themed.StatusBar />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ProfileScreen extends React.Component<
|
||||
NavigationStackScreenProps<{ homeKey: string }>
|
||||
> {
|
||||
render() {
|
||||
const homeKey = this.props.navigation.getParam('homeKey');
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Text>Profile</Text>
|
||||
<Button
|
||||
title="Navigate to 'Settings' with key 'B'"
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate({
|
||||
key: 'B',
|
||||
params: { homeKey },
|
||||
routeName: 'Settings',
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
title={`Navigate back to 'Home' with key ${homeKey}`}
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate({ routeName: 'Home', key: homeKey })
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsScreen extends React.Component<NavigationStackScreenProps> {
|
||||
render() {
|
||||
const { homeKey } = this.props.navigation.state.params!;
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Text>Settings</Text>
|
||||
<Button
|
||||
title={`Navigate back to 'Home' with key ${homeKey}`}
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate({ routeName: 'Home', key: homeKey })
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
title="Navigate back to 'Profile' with key 'A'"
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate({
|
||||
key: 'A',
|
||||
routeName: 'Profile',
|
||||
})
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const Stack = createStackNavigator(
|
||||
{
|
||||
Home: {
|
||||
screen: HomeScreen,
|
||||
},
|
||||
Profile: {
|
||||
screen: ProfileScreen,
|
||||
},
|
||||
Settings: {
|
||||
screen: SettingsScreen,
|
||||
},
|
||||
},
|
||||
{
|
||||
headerMode: 'none',
|
||||
}
|
||||
);
|
||||
|
||||
export default Stack;
|
||||
164
example/src/StacksInTabs.tsx
Normal file
@@ -0,0 +1,164 @@
|
||||
import React from 'react';
|
||||
import { Text } from 'react-native';
|
||||
import {
|
||||
Themed,
|
||||
NavigationScreenProp,
|
||||
NavigationState,
|
||||
SafeAreaView,
|
||||
ScrollView,
|
||||
} from 'react-navigation';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackScreenProps,
|
||||
NavigationStackProp,
|
||||
} from 'react-navigation-stack';
|
||||
import { createBottomTabNavigator } from 'react-navigation-tabs';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { Button } from './Shared/ButtonWithMargin';
|
||||
import SampleText from './Shared/SampleText';
|
||||
|
||||
const TEXT = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla a hendrerit dui, id consectetur nulla. Curabitur mattis sapien nunc, quis dignissim eros venenatis sit amet. Praesent rutrum dapibus diam quis eleifend. Donec vulputate quis purus sed vulputate. Fusce ipsum felis, cursus at congue vel, consectetur tincidunt purus. Pellentesque et fringilla lorem. In at augue malesuada, sollicitudin ex ut, convallis elit. Curabitur metus nibh, consequat vel libero sit amet, iaculis congue nisl. Maecenas eleifend sodales sapien, fringilla sagittis nisi ornare volutpat. Integer tellus enim, volutpat vitae nisl et, dignissim pharetra leo. Sed sit amet efficitur sapien, at tristique sapien. Aenean dignissim semper sagittis. Nullam sit amet volutpat mi.
|
||||
Curabitur auctor orci et justo molestie iaculis. Integer elementum tortor ac ipsum egestas pharetra. Etiam ultrices elementum pharetra. Maecenas lobortis ultrices risus dignissim luctus. Nunc malesuada cursus posuere. Vestibulum tristique lectus pretium pellentesque pellentesque. Nunc ac nisi lacus. Duis ultrices dui ac viverra ullamcorper. Morbi placerat laoreet lacus sit amet ullamcorper.
|
||||
Nulla convallis pulvinar hendrerit. Nulla mattis sem et aliquam ultrices. Nam egestas magna leo, nec luctus turpis sollicitudin ac. Sed id leo luctus, lobortis tortor ut, rhoncus ex. Aliquam gravida enim ac dapibus ultricies. Vestibulum at interdum est, et vehicula nibh. Phasellus dignissim iaculis rhoncus. Vestibulum tempus leo lectus, quis euismod metus ullamcorper quis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut id ipsum at enim eleifend porttitor id quis metus. Proin bibendum ornare iaculis. Duis elementum lacus vel cursus efficitur. Nunc eu tortor sed risus lacinia scelerisque.
|
||||
Praesent lobortis elit sit amet mauris pulvinar, viverra condimentum massa pellentesque. Curabitur massa ex, dignissim eget neque at, fringilla consectetur justo. Cras sollicitudin vel ligula sed cursus. Aliquam porta sem hendrerit diam porta ultricies. Sed eu mi erat. Curabitur id justo vel tortor hendrerit vestibulum id eget est. Morbi eros magna, placerat id diam ut, varius sollicitudin mi. Curabitur pretium finibus accumsan.`;
|
||||
|
||||
interface Props {
|
||||
navigation: NavigationStackProp;
|
||||
banner: string;
|
||||
}
|
||||
|
||||
class MyNavScreen extends React.Component<Props> {
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
const banner = navigation.getParam('banner');
|
||||
|
||||
return (
|
||||
<ScrollView style={{ flex: 1 }}>
|
||||
<SafeAreaView forceInset={{ horizontal: 'always' }}>
|
||||
<SampleText>{banner}</SampleText>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Profile', { name: 'Jordan' })}
|
||||
title="Open profile screen"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('NotifSettings')}
|
||||
title="Open notifications screen"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('SettingsTab')}
|
||||
title="Go to settings tab"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
|
||||
{TEXT.split('\n').map((p, n) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<Text key={n} style={{ marginVertical: 10, marginHorizontal: 8 }}>
|
||||
{p}
|
||||
</Text>
|
||||
))}
|
||||
</SafeAreaView>
|
||||
|
||||
<Themed.StatusBar />
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const MyProfileScreen = ({ navigation }: NavigationStackScreenProps) => (
|
||||
<MyNavScreen
|
||||
banner={`${navigation.state.params!.name}s Profile`}
|
||||
navigation={navigation}
|
||||
/>
|
||||
);
|
||||
|
||||
const MainTab = createStackNavigator({
|
||||
Home: {
|
||||
navigationOptions: {
|
||||
title: 'Welcome',
|
||||
},
|
||||
params: { banner: 'Home Screen' },
|
||||
path: '/',
|
||||
screen: MyNavScreen,
|
||||
},
|
||||
Profile: {
|
||||
navigationOptions: ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
}) => ({
|
||||
title: `${navigation.state.params!.name}'s Profile!`,
|
||||
}),
|
||||
path: '/people/:name',
|
||||
screen: MyProfileScreen,
|
||||
},
|
||||
});
|
||||
|
||||
const SettingsTab = createStackNavigator({
|
||||
NotifSettings: {
|
||||
navigationOptions: {
|
||||
title: 'Notifications',
|
||||
},
|
||||
params: { banner: 'Notifications Screen' },
|
||||
screen: MyNavScreen,
|
||||
},
|
||||
Settings: {
|
||||
navigationOptions: () => ({
|
||||
title: 'Settings',
|
||||
}),
|
||||
params: { banner: 'Settings Screen' },
|
||||
path: '/',
|
||||
screen: MyNavScreen,
|
||||
},
|
||||
});
|
||||
|
||||
const StacksInTabs = createBottomTabNavigator(
|
||||
{
|
||||
MainTab: {
|
||||
navigationOptions: {
|
||||
tabBarIcon: ({
|
||||
tintColor,
|
||||
focused,
|
||||
}: {
|
||||
tintColor: string;
|
||||
focused: boolean;
|
||||
}) => (
|
||||
<Ionicons
|
||||
name={focused ? 'ios-home' : 'ios-home'}
|
||||
size={26}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
),
|
||||
tabBarLabel: 'Home',
|
||||
},
|
||||
path: '/',
|
||||
screen: MainTab,
|
||||
},
|
||||
SettingsTab: {
|
||||
screen: SettingsTab,
|
||||
path: '/settings',
|
||||
navigationOptions: {
|
||||
tabBarLabel: 'Settings',
|
||||
tabBarIcon: ({
|
||||
tintColor,
|
||||
focused,
|
||||
}: {
|
||||
tintColor: string;
|
||||
focused: boolean;
|
||||
}) => (
|
||||
<Ionicons
|
||||
name={focused ? 'ios-settings' : 'ios-settings'}
|
||||
size={26}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
tabBarOptions: {
|
||||
showLabel: false,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default StacksInTabs;
|
||||
151
example/src/StacksOverTabs.tsx
Normal file
@@ -0,0 +1,151 @@
|
||||
import React from 'react';
|
||||
import { ScrollView, StatusBar } from 'react-native';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import {
|
||||
getActiveChildNavigationOptions,
|
||||
NavigationScreenProp,
|
||||
NavigationState,
|
||||
SafeAreaView,
|
||||
} from 'react-navigation';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackScreenProps,
|
||||
} from 'react-navigation-stack';
|
||||
import { createBottomTabNavigator } from 'react-navigation-tabs';
|
||||
import { Button } from './Shared/ButtonWithMargin';
|
||||
import SampleText from './Shared/SampleText';
|
||||
|
||||
const MyNavScreen = ({
|
||||
navigation,
|
||||
banner,
|
||||
}: {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
banner: string;
|
||||
}) => (
|
||||
<ScrollView>
|
||||
<SafeAreaView forceInset={{ horizontal: 'always', vertical: 'never' }}>
|
||||
<SampleText>{banner}</SampleText>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Profile', { name: 'Jordan' })}
|
||||
title="Open profile screen"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('NotifSettings')}
|
||||
title="Open notifications screen"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('SettingsTab')}
|
||||
title="Go to settings tab"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
</SafeAreaView>
|
||||
<StatusBar barStyle="default" />
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
const MyHomeScreen = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
}) => <MyNavScreen banner="Home Screen" navigation={navigation} />;
|
||||
|
||||
const MyProfileScreen = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
}) => (
|
||||
<MyNavScreen
|
||||
banner={`${navigation.state.params!.name}s Profile`}
|
||||
navigation={navigation}
|
||||
/>
|
||||
);
|
||||
|
||||
const MyNotificationsSettingsScreen = ({
|
||||
navigation,
|
||||
}: NavigationStackScreenProps) => (
|
||||
<MyNavScreen banner="Notifications Screen" navigation={navigation} />
|
||||
);
|
||||
|
||||
const MySettingsScreen = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
}) => <MyNavScreen banner="Settings Screen" navigation={navigation} />;
|
||||
|
||||
const TabNav = createBottomTabNavigator({
|
||||
MainTab: {
|
||||
navigationOptions: {
|
||||
tabBarIcon: ({
|
||||
tintColor,
|
||||
focused,
|
||||
}: {
|
||||
tintColor: string;
|
||||
focused: boolean;
|
||||
}) => (
|
||||
<Ionicons
|
||||
name={focused ? 'ios-home' : 'ios-home'}
|
||||
size={26}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
),
|
||||
tabBarLabel: 'Home',
|
||||
title: 'Welcome',
|
||||
},
|
||||
path: '/',
|
||||
screen: MyHomeScreen,
|
||||
},
|
||||
SettingsTab: {
|
||||
navigationOptions: {
|
||||
tabBarIcon: ({
|
||||
tintColor,
|
||||
focused,
|
||||
}: {
|
||||
tintColor: string;
|
||||
focused: boolean;
|
||||
}) => (
|
||||
<Ionicons
|
||||
name={focused ? 'ios-settings' : 'ios-settings'}
|
||||
size={26}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
),
|
||||
title: 'Settings',
|
||||
},
|
||||
path: '/settings',
|
||||
screen: MySettingsScreen,
|
||||
},
|
||||
});
|
||||
|
||||
TabNav.navigationOptions = ({
|
||||
navigation,
|
||||
screenProps,
|
||||
}: {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
screenProps: { [key: string]: any };
|
||||
}) => {
|
||||
const childOptions = getActiveChildNavigationOptions(navigation, screenProps);
|
||||
return {
|
||||
title: childOptions.title,
|
||||
};
|
||||
};
|
||||
|
||||
const StacksOverTabs = createStackNavigator({
|
||||
NotifSettings: {
|
||||
navigationOptions: {
|
||||
title: 'Notifications',
|
||||
},
|
||||
screen: MyNotificationsSettingsScreen,
|
||||
},
|
||||
Profile: {
|
||||
navigationOptions: ({ navigation }: NavigationStackScreenProps) => ({
|
||||
title: `${navigation.state.params!.name}'s Profile!`,
|
||||
}),
|
||||
path: '/people/:name',
|
||||
screen: MyProfileScreen,
|
||||
},
|
||||
Root: {
|
||||
screen: TabNav,
|
||||
},
|
||||
});
|
||||
|
||||
export default StacksOverTabs;
|
||||
173
example/src/StacksOverTopTabs.tsx
Normal file
@@ -0,0 +1,173 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
ScrollView,
|
||||
StatusBar,
|
||||
StatusBarStyle,
|
||||
StyleSheet,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import {
|
||||
NavigationScreenProp,
|
||||
NavigationState,
|
||||
SafeAreaView,
|
||||
} from 'react-navigation';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackScreenProps,
|
||||
} from 'react-navigation-stack';
|
||||
import {
|
||||
createMaterialTopTabNavigator,
|
||||
MaterialTopTabBar,
|
||||
} from 'react-navigation-tabs';
|
||||
|
||||
import { Button } from './Shared/ButtonWithMargin';
|
||||
import SampleText from './Shared/SampleText';
|
||||
|
||||
const HEADER_HEIGHT = 64;
|
||||
|
||||
const MyNavScreen = ({
|
||||
navigation,
|
||||
banner,
|
||||
statusBarStyle,
|
||||
}: {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
banner: string;
|
||||
statusBarStyle?: StatusBarStyle;
|
||||
}) => (
|
||||
<ScrollView>
|
||||
<SafeAreaView forceInset={{ horizontal: 'always' }}>
|
||||
<SampleText>{banner}</SampleText>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Profile', { name: 'Jordan' })}
|
||||
title="Open profile screen"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('NotifSettings')}
|
||||
title="Open notifications screen"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('SettingsTab')}
|
||||
title="Go to settings tab"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
</SafeAreaView>
|
||||
<StatusBar barStyle={statusBarStyle || 'default'} />
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
const MyHomeScreen = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
}) => (
|
||||
<MyNavScreen
|
||||
banner="Home Screen"
|
||||
navigation={navigation}
|
||||
statusBarStyle="light-content"
|
||||
/>
|
||||
);
|
||||
|
||||
const MyProfileScreen = ({ navigation }: NavigationStackScreenProps) => (
|
||||
<MyNavScreen
|
||||
banner={`${navigation.state.params!.name}s Profile`}
|
||||
navigation={navigation}
|
||||
/>
|
||||
);
|
||||
|
||||
const MyNotificationsSettingsScreen = ({
|
||||
navigation,
|
||||
}: NavigationStackScreenProps) => (
|
||||
<MyNavScreen banner="Notifications Screen" navigation={navigation} />
|
||||
);
|
||||
|
||||
const MySettingsScreen = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NavigationScreenProp<NavigationState>;
|
||||
}) => (
|
||||
<MyNavScreen
|
||||
banner="Settings Screen"
|
||||
navigation={navigation}
|
||||
statusBarStyle="light-content"
|
||||
/>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
stackHeader: {
|
||||
height: HEADER_HEIGHT,
|
||||
},
|
||||
tab: {
|
||||
height: HEADER_HEIGHT,
|
||||
},
|
||||
});
|
||||
|
||||
function MaterialTopTabBarWithStatusBar(props: any) {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingTop: 20,
|
||||
backgroundColor: '#2196f3',
|
||||
}}
|
||||
>
|
||||
<MaterialTopTabBar
|
||||
{...props}
|
||||
jumpToIndex={() => {
|
||||
//
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const TabNavigator = createMaterialTopTabNavigator(
|
||||
{
|
||||
MainTab: {
|
||||
screen: MyHomeScreen,
|
||||
navigationOptions: {
|
||||
title: 'Welcome',
|
||||
},
|
||||
},
|
||||
SettingsTab: {
|
||||
screen: MySettingsScreen,
|
||||
navigationOptions: {
|
||||
title: 'Settings',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
tabBarComponent: MaterialTopTabBarWithStatusBar,
|
||||
tabBarOptions: {
|
||||
tabStyle: styles.tab,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const StackNavigator = createStackNavigator(
|
||||
{
|
||||
Root: {
|
||||
screen: TabNavigator,
|
||||
navigationOptions: {
|
||||
headerShown: false,
|
||||
},
|
||||
},
|
||||
NotifSettings: {
|
||||
screen: MyNotificationsSettingsScreen,
|
||||
navigationOptions: {
|
||||
title: 'Notifications',
|
||||
},
|
||||
},
|
||||
Profile: {
|
||||
screen: MyProfileScreen,
|
||||
navigationOptions: ({ navigation }: NavigationStackScreenProps) => ({
|
||||
title: `${navigation.state.params!.name}'s Profile!`,
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
defaultNavigationOptions: {
|
||||
headerStyle: styles.stackHeader,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default StackNavigator;
|
||||
98
example/src/StyledDrawer.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import React from 'react';
|
||||
import { Button, ScrollView } from 'react-native';
|
||||
import { Themed, SafeAreaView } from 'react-navigation';
|
||||
import {
|
||||
createDrawerNavigator,
|
||||
NavigationDrawerScreenComponent,
|
||||
NavigationDrawerProp,
|
||||
} from 'react-navigation-drawer';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
|
||||
const SampleText = ({ children }: { children: React.ReactNode }) => (
|
||||
<Themed.Text>{children}</Themed.Text>
|
||||
);
|
||||
|
||||
const MyNavScreen = ({
|
||||
navigation,
|
||||
banner,
|
||||
}: {
|
||||
navigation: NavigationDrawerProp;
|
||||
banner: string;
|
||||
}) => (
|
||||
<ScrollView style={{ backgroundColor: '#eee' }}>
|
||||
<SafeAreaView forceInset={{ top: 'always' }}>
|
||||
<SampleText>{banner}</SampleText>
|
||||
<Button onPress={() => navigation.openDrawer()} title="Open drawer" />
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Email')}
|
||||
title="Open other screen"
|
||||
/>
|
||||
<Button onPress={() => navigation.navigate('Index')} title="Go back" />
|
||||
</SafeAreaView>
|
||||
<Themed.StatusBar />
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
const InboxScreen: NavigationDrawerScreenComponent = ({ navigation }) => (
|
||||
<MyNavScreen banner="Inbox Screen" navigation={navigation} />
|
||||
);
|
||||
|
||||
const EmailScreen: NavigationDrawerScreenComponent = ({ navigation }) => (
|
||||
<MyNavScreen banner="Email Screen" navigation={navigation} />
|
||||
);
|
||||
|
||||
const DraftsScreen: NavigationDrawerScreenComponent = ({ navigation }) => (
|
||||
<MyNavScreen banner="Drafts Screen" navigation={navigation} />
|
||||
);
|
||||
|
||||
const DrawerExample = createDrawerNavigator(
|
||||
{
|
||||
Inbox: {
|
||||
path: '/',
|
||||
screen: InboxScreen,
|
||||
navigationOptions: {
|
||||
drawerLabel: 'Inbox',
|
||||
drawerIcon: ({ tintColor }) => (
|
||||
<MaterialIcons
|
||||
name="move-to-inbox"
|
||||
size={24}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
),
|
||||
},
|
||||
},
|
||||
Drafts: {
|
||||
path: '/sent',
|
||||
screen: DraftsScreen,
|
||||
navigationOptions: {
|
||||
drawerLabel: 'Drafts',
|
||||
drawerIcon: ({ tintColor }) => (
|
||||
<MaterialIcons name="drafts" size={24} style={{ color: tintColor }} />
|
||||
),
|
||||
},
|
||||
},
|
||||
Email: {
|
||||
path: '/sent',
|
||||
screen: EmailScreen,
|
||||
},
|
||||
},
|
||||
{
|
||||
drawerBackgroundColor: {
|
||||
light: '#eee',
|
||||
dark: 'rgba(40,40,40,1)',
|
||||
},
|
||||
initialRouteName: 'Drafts',
|
||||
contentOptions: {
|
||||
activeTintColor: '#e91e63',
|
||||
},
|
||||
drawerType: 'back',
|
||||
overlayColor: '#00000000',
|
||||
hideStatusBar: true,
|
||||
}
|
||||
);
|
||||
|
||||
DrawerExample.navigationOptions = {
|
||||
headerShown: false,
|
||||
};
|
||||
|
||||
export default DrawerExample;
|
||||
121
example/src/SwitchWithStacks.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Button,
|
||||
AsyncStorage,
|
||||
StatusBar,
|
||||
StyleSheet,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { createSwitchNavigator } from 'react-navigation';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackScreenProps,
|
||||
} from 'react-navigation-stack';
|
||||
|
||||
class SignInScreen extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Please sign in',
|
||||
};
|
||||
|
||||
_signInAsync = async () => {
|
||||
await AsyncStorage.setItem('userToken', 'abc');
|
||||
this.props.navigation.navigate('Index');
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Button title="Sign in!" onPress={this._signInAsync} />
|
||||
<Button
|
||||
title="Go back to other examples"
|
||||
onPress={() => this.props.navigation.goBack(null)}
|
||||
/>
|
||||
<StatusBar barStyle="default" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HomeScreen extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Welcome to the app!',
|
||||
};
|
||||
|
||||
_showMoreApp = () => {
|
||||
this.props.navigation.navigate('Other');
|
||||
};
|
||||
|
||||
_signOutAsync = async () => {
|
||||
await AsyncStorage.clear();
|
||||
this.props.navigation.navigate('Auth');
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Button title="Show me more of the app" onPress={this._showMoreApp} />
|
||||
<Button title="Actually, sign me out :)" onPress={this._signOutAsync} />
|
||||
<StatusBar barStyle="default" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OtherScreen extends React.Component<NavigationStackScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Lots of features here',
|
||||
};
|
||||
|
||||
_signOutAsync = async () => {
|
||||
await AsyncStorage.clear();
|
||||
this.props.navigation.navigate('Auth');
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Button title="I'm done, sign me out" onPress={this._signOutAsync} />
|
||||
<StatusBar barStyle="default" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LoadingScreen extends React.Component<NavigationStackScreenProps> {
|
||||
componentDidMount() {
|
||||
this._bootstrapAsync();
|
||||
}
|
||||
|
||||
_bootstrapAsync = async () => {
|
||||
const userToken = await AsyncStorage.getItem('userToken');
|
||||
let initialRouteName = userToken ? 'App' : 'Auth';
|
||||
this.props.navigation.navigate(initialRouteName);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ActivityIndicator />
|
||||
<StatusBar barStyle="default" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
const AppStack = createStackNavigator({ Home: HomeScreen, Other: OtherScreen });
|
||||
const AuthStack = createStackNavigator({ SignIn: SignInScreen });
|
||||
|
||||
export default createSwitchNavigator({
|
||||
Loading: LoadingScreen,
|
||||
App: AppStack,
|
||||
Auth: AuthStack,
|
||||
});
|
||||
28
example/src/TabsInDrawer.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { createDrawerNavigator } from 'react-navigation-drawer';
|
||||
import SimpleTabs from './SimpleTabs';
|
||||
import StacksOverTabs from './StacksOverTabs';
|
||||
|
||||
const TabsInDrawer = createDrawerNavigator({
|
||||
SimpleTabs: {
|
||||
navigationOptions: {
|
||||
drawerIcon: ({ tintColor }: { tintColor: string }) => (
|
||||
<MaterialIcons name="filter-1" size={24} style={{ color: tintColor }} />
|
||||
),
|
||||
drawerLabel: 'Simple tabs',
|
||||
},
|
||||
screen: SimpleTabs,
|
||||
},
|
||||
StacksOverTabs: {
|
||||
navigationOptions: {
|
||||
drawerIcon: ({ tintColor }: { tintColor: string }) => (
|
||||
<MaterialIcons name="filter-2" size={24} style={{ color: tintColor }} />
|
||||
),
|
||||
drawerLabel: 'Stacks Over Tabs',
|
||||
},
|
||||
screen: StacksOverTabs,
|
||||
},
|
||||
});
|
||||
|
||||
export default TabsInDrawer;
|
||||
90
example/src/TransparentStack.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import * as React from 'react';
|
||||
import { Button, View, Text } from 'react-native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackScreenComponent,
|
||||
} from 'react-navigation-stack';
|
||||
|
||||
const ListScreen: NavigationStackScreenComponent = function(props) {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'white',
|
||||
}}
|
||||
>
|
||||
<Text>List Screen</Text>
|
||||
<Text>A list may go here</Text>
|
||||
<Button
|
||||
title="Open Dialog"
|
||||
onPress={() => props.navigation.navigate('ModalDialog')}
|
||||
/>
|
||||
<Button
|
||||
title="Go back to all examples"
|
||||
onPress={() => props.navigation.navigate('Index')}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const ModalDialogScreen: NavigationStackScreenComponent = function(props) {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: 'white',
|
||||
padding: 16,
|
||||
width: '90%',
|
||||
maxWidth: 500,
|
||||
minHeight: 300,
|
||||
borderRadius: 6,
|
||||
elevation: 6,
|
||||
shadowColor: 'black',
|
||||
shadowOpacity: 0.15,
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowRadius: 10,
|
||||
}}
|
||||
>
|
||||
<Text style={{ flex: 1, fontSize: 16 }}>Dialog</Text>
|
||||
<Button title="Close" onPress={() => props.navigation.goBack()} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default createStackNavigator(
|
||||
{
|
||||
List: ListScreen,
|
||||
ModalDialog: ModalDialogScreen,
|
||||
},
|
||||
{
|
||||
initialRouteName: 'List',
|
||||
mode: 'modal',
|
||||
headerMode: 'none',
|
||||
defaultNavigationOptions: {
|
||||
cardStyle: { backgroundColor: 'transparent' },
|
||||
gestureEnabled: false,
|
||||
cardStyleInterpolator: ({ current: { progress } }) => {
|
||||
const opacity = progress.interpolate({
|
||||
inputRange: [0, 0.5, 0.9, 1],
|
||||
outputRange: [0, 0.25, 0.7, 1],
|
||||
});
|
||||
|
||||
return {
|
||||
cardStyle: {
|
||||
opacity,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
BIN
example/src/assets/NavLogo.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
example/src/assets/album-art-1.jpg
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
example/src/assets/album-art-2.jpg
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
example/src/assets/album-art-3.jpg
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
example/src/assets/album-art-4.jpg
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
example/src/assets/album-art-5.jpg
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
example/src/assets/album-art-6.jpg
Normal file
|
After Width: | Height: | Size: 119 KiB |
BIN
example/src/assets/album-art-7.jpg
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
example/src/assets/album-art-8.jpg
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
example/src/assets/avatar-1.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
example/src/assets/avatar-2.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
example/src/assets/back.png
Normal file
|
After Width: | Height: | Size: 930 B |
BIN
example/src/assets/book.jpg
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
example/src/assets/dog-back.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
example/src/assets/icon.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
example/src/assets/splash.png
Normal file
|
After Width: | Height: | Size: 75 KiB |