mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-13 09:39:18 +08:00
chore: move example folder to root
This commit is contained in:
2
example/src/LinkingPrefixes.expo.ts
Normal file
2
example/src/LinkingPrefixes.expo.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import { Linking } from 'expo';
|
||||
export default [Linking.makeUrl('/')];
|
||||
1
example/src/LinkingPrefixes.ts
Normal file
1
example/src/LinkingPrefixes.ts
Normal file
@@ -0,0 +1 @@
|
||||
export default [];
|
||||
162
example/src/Screens/AuthFlow.tsx
Normal file
162
example/src/Screens/AuthFlow.tsx
Normal file
@@ -0,0 +1,162 @@
|
||||
import * as React from 'react';
|
||||
import { View, TextInput, ActivityIndicator, StyleSheet } from 'react-native';
|
||||
import { Title, Button } from 'react-native-paper';
|
||||
import { ParamListBase } from '@react-navigation/core';
|
||||
import {
|
||||
createStackNavigator,
|
||||
HeaderBackButton,
|
||||
StackNavigationProp,
|
||||
} from '@react-navigation/stack';
|
||||
|
||||
type AuthStackParams = {
|
||||
splash: undefined;
|
||||
home: undefined;
|
||||
'sign-in': undefined;
|
||||
};
|
||||
|
||||
const SplashScreen = () => {
|
||||
return (
|
||||
<View style={styles.content}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const SignInScreen = ({ onSignIn }: { onSignIn: (token: string) => void }) => {
|
||||
return (
|
||||
<View style={styles.content}>
|
||||
<TextInput placeholder="Username" style={styles.input} />
|
||||
<TextInput placeholder="Password" secureTextEntry style={styles.input} />
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => onSignIn('token')}
|
||||
style={styles.button}
|
||||
>
|
||||
Sign in
|
||||
</Button>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const HomeScreen = ({ onSignOut }: { onSignOut: () => void }) => {
|
||||
return (
|
||||
<View style={styles.content}>
|
||||
<Title style={styles.text}>Signed in successfully 🎉</Title>
|
||||
<Button onPress={onSignOut} style={styles.button}>
|
||||
Sign out
|
||||
</Button>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const SimpleStack = createStackNavigator<AuthStackParams>();
|
||||
|
||||
type Props = {
|
||||
navigation: StackNavigationProp<ParamListBase>;
|
||||
};
|
||||
|
||||
type State = {
|
||||
isLoading: boolean;
|
||||
userToken: undefined | string;
|
||||
};
|
||||
|
||||
type Action =
|
||||
| { type: 'RESTORE_TOKEN'; token: undefined | string }
|
||||
| { type: 'SIGN_IN'; token: string }
|
||||
| { type: 'SIGN_OUT' };
|
||||
|
||||
export default function SimpleStackScreen({ navigation }: Props) {
|
||||
const [state, dispatch] = React.useReducer<React.Reducer<State, Action>>(
|
||||
(prevState, action) => {
|
||||
switch (action.type) {
|
||||
case 'RESTORE_TOKEN':
|
||||
return {
|
||||
...prevState,
|
||||
userToken: action.token,
|
||||
isLoading: false,
|
||||
};
|
||||
case 'SIGN_IN':
|
||||
return {
|
||||
...prevState,
|
||||
userToken: action.token,
|
||||
};
|
||||
case 'SIGN_OUT':
|
||||
return {
|
||||
...prevState,
|
||||
userToken: undefined,
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
isLoading: true,
|
||||
userToken: undefined,
|
||||
}
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
dispatch({ type: 'RESTORE_TOKEN', token: undefined });
|
||||
}, 1000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
navigation.setOptions({
|
||||
headerShown: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<SimpleStack.Navigator
|
||||
screenOptions={{
|
||||
headerLeft: () => (
|
||||
<HeaderBackButton onPress={() => navigation.goBack()} />
|
||||
),
|
||||
}}
|
||||
>
|
||||
{state.isLoading ? (
|
||||
<SimpleStack.Screen
|
||||
name="splash"
|
||||
component={SplashScreen}
|
||||
options={{ title: `Auth Flow` }}
|
||||
/>
|
||||
) : state.userToken === undefined ? (
|
||||
<SimpleStack.Screen name="sign-in" options={{ title: `Sign in` }}>
|
||||
{() => (
|
||||
<SignInScreen
|
||||
onSignIn={token => dispatch({ type: 'SIGN_IN', token })}
|
||||
/>
|
||||
)}
|
||||
</SimpleStack.Screen>
|
||||
) : (
|
||||
<SimpleStack.Screen name="home" options={{ title: 'Home' }}>
|
||||
{() => (
|
||||
<HomeScreen onSignOut={() => dispatch({ type: 'SIGN_OUT' })} />
|
||||
)}
|
||||
</SimpleStack.Screen>
|
||||
)}
|
||||
</SimpleStack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
content: {
|
||||
flex: 1,
|
||||
padding: 16,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
input: {
|
||||
margin: 8,
|
||||
padding: 10,
|
||||
backgroundColor: 'white',
|
||||
borderRadius: 3,
|
||||
borderWidth: StyleSheet.hairlineWidth,
|
||||
borderColor: 'rgba(0, 0, 0, 0.08)',
|
||||
},
|
||||
button: {
|
||||
margin: 8,
|
||||
},
|
||||
text: {
|
||||
textAlign: 'center',
|
||||
margin: 8,
|
||||
},
|
||||
});
|
||||
69
example/src/Screens/BottomTabs.tsx
Normal file
69
example/src/Screens/BottomTabs.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import * as React from 'react';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||
import TouchableBounce from '../Shared/TouchableBounce';
|
||||
import Albums from '../Shared/Albums';
|
||||
import Contacts from '../Shared/Contacts';
|
||||
import Chat from '../Shared/Chat';
|
||||
import SimpleStackScreen from './SimpleStack';
|
||||
|
||||
const getTabBarIcon = (name: string) => ({
|
||||
color,
|
||||
size,
|
||||
}: {
|
||||
color: string;
|
||||
size: number;
|
||||
}) => <MaterialCommunityIcons name={name} color={color} size={size} />;
|
||||
|
||||
type BottomTabParams = {
|
||||
article: undefined;
|
||||
albums: undefined;
|
||||
contacts: undefined;
|
||||
chat: undefined;
|
||||
};
|
||||
|
||||
const BottomTabs = createBottomTabNavigator<BottomTabParams>();
|
||||
|
||||
export default function BottomTabsScreen() {
|
||||
return (
|
||||
<BottomTabs.Navigator
|
||||
screenOptions={{
|
||||
tabBarButton: props => <TouchableBounce {...props} />,
|
||||
}}
|
||||
>
|
||||
<BottomTabs.Screen
|
||||
name="article"
|
||||
options={{
|
||||
title: 'Article',
|
||||
tabBarIcon: getTabBarIcon('file-document-box'),
|
||||
}}
|
||||
>
|
||||
{props => <SimpleStackScreen {...props} headerMode="none" />}
|
||||
</BottomTabs.Screen>
|
||||
<BottomTabs.Screen
|
||||
name="chat"
|
||||
component={Chat}
|
||||
options={{
|
||||
tabBarLabel: 'Chat',
|
||||
tabBarIcon: getTabBarIcon('message-reply'),
|
||||
}}
|
||||
/>
|
||||
<BottomTabs.Screen
|
||||
name="contacts"
|
||||
component={Contacts}
|
||||
options={{
|
||||
title: 'Contacts',
|
||||
tabBarIcon: getTabBarIcon('contacts'),
|
||||
}}
|
||||
/>
|
||||
<BottomTabs.Screen
|
||||
name="albums"
|
||||
component={Albums}
|
||||
options={{
|
||||
title: 'Albums',
|
||||
tabBarIcon: getTabBarIcon('image-album'),
|
||||
}}
|
||||
/>
|
||||
</BottomTabs.Navigator>
|
||||
);
|
||||
}
|
||||
114
example/src/Screens/CompatAPI.tsx
Normal file
114
example/src/Screens/CompatAPI.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import * as React from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import { Button } from 'react-native-paper';
|
||||
import {
|
||||
createCompatNavigatorFactory,
|
||||
CompatScreenType,
|
||||
} from '@react-navigation/compat';
|
||||
import {
|
||||
createStackNavigator,
|
||||
StackNavigationProp,
|
||||
} from '@react-navigation/stack';
|
||||
import Article from '../Shared/Article';
|
||||
import Albums from '../Shared/Albums';
|
||||
|
||||
type CompatStackParams = {
|
||||
Article: { author: string };
|
||||
Album: undefined;
|
||||
};
|
||||
|
||||
const ArticleScreen: CompatScreenType<StackNavigationProp<
|
||||
CompatStackParams,
|
||||
'Article'
|
||||
>> = ({ navigation }) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<View style={styles.buttons}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('Album')}
|
||||
style={styles.button}
|
||||
>
|
||||
Push album
|
||||
</Button>
|
||||
<Button
|
||||
mode="outlined"
|
||||
onPress={() => navigation.goBack()}
|
||||
style={styles.button}
|
||||
>
|
||||
Go back
|
||||
</Button>
|
||||
</View>
|
||||
<Article author={{ name: navigation.getParam('author') }} />
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
ArticleScreen.navigationOptions = ({ navigation }) => ({
|
||||
title: `Article by ${navigation.getParam('author')}`,
|
||||
});
|
||||
|
||||
const AlbumsScreen: CompatScreenType<StackNavigationProp<
|
||||
CompatStackParams
|
||||
>> = ({ navigation }) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<View style={styles.buttons}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('Article', { author: 'Babel fish' })}
|
||||
style={styles.button}
|
||||
>
|
||||
Push article
|
||||
</Button>
|
||||
<Button
|
||||
mode="outlined"
|
||||
onPress={() => navigation.goBack()}
|
||||
style={styles.button}
|
||||
>
|
||||
Go back
|
||||
</Button>
|
||||
</View>
|
||||
<Albums />
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const CompatStack = createCompatNavigatorFactory(createStackNavigator)<
|
||||
StackNavigationProp<CompatStackParams>
|
||||
>(
|
||||
{
|
||||
Article: {
|
||||
screen: ArticleScreen,
|
||||
params: {
|
||||
author: 'Gandalf',
|
||||
},
|
||||
},
|
||||
Album: AlbumsScreen,
|
||||
},
|
||||
{
|
||||
mode: 'modal',
|
||||
}
|
||||
);
|
||||
|
||||
export default function CompatStackScreen({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: StackNavigationProp<{}>;
|
||||
}) {
|
||||
navigation.setOptions({
|
||||
headerShown: false,
|
||||
});
|
||||
|
||||
return <CompatStack />;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
buttons: {
|
||||
flexDirection: 'row',
|
||||
padding: 8,
|
||||
},
|
||||
button: {
|
||||
margin: 8,
|
||||
},
|
||||
});
|
||||
69
example/src/Screens/MaterialBottomTabs.tsx
Normal file
69
example/src/Screens/MaterialBottomTabs.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { createMaterialBottomTabNavigator } from '@react-navigation/material-bottom-tabs';
|
||||
import Albums from '../Shared/Albums';
|
||||
import Contacts from '../Shared/Contacts';
|
||||
import Chat from '../Shared/Chat';
|
||||
import SimpleStackScreen from './SimpleStack';
|
||||
|
||||
type MaterialBottomTabParams = {
|
||||
article: undefined;
|
||||
albums: undefined;
|
||||
contacts: undefined;
|
||||
chat: undefined;
|
||||
};
|
||||
|
||||
const MaterialBottomTabs = createMaterialBottomTabNavigator<
|
||||
MaterialBottomTabParams
|
||||
>();
|
||||
|
||||
export default function MaterialBottomTabsScreen() {
|
||||
return (
|
||||
<MaterialBottomTabs.Navigator barStyle={styles.tabBar}>
|
||||
<MaterialBottomTabs.Screen
|
||||
name="article"
|
||||
options={{
|
||||
tabBarLabel: 'Article',
|
||||
tabBarIcon: 'file-document-box',
|
||||
tabBarColor: '#C9E7F8',
|
||||
}}
|
||||
>
|
||||
{props => <SimpleStackScreen {...props} headerMode="none" />}
|
||||
</MaterialBottomTabs.Screen>
|
||||
<MaterialBottomTabs.Screen
|
||||
name="chat"
|
||||
component={Chat}
|
||||
options={{
|
||||
tabBarLabel: 'Chat',
|
||||
tabBarIcon: 'message-reply',
|
||||
tabBarColor: '#9FD5C9',
|
||||
tabBarBadge: true,
|
||||
}}
|
||||
/>
|
||||
<MaterialBottomTabs.Screen
|
||||
name="contacts"
|
||||
component={Contacts}
|
||||
options={{
|
||||
tabBarLabel: 'Contacts',
|
||||
tabBarIcon: 'contacts',
|
||||
tabBarColor: '#F7EAA2',
|
||||
}}
|
||||
/>
|
||||
<MaterialBottomTabs.Screen
|
||||
name="albums"
|
||||
component={Albums}
|
||||
options={{
|
||||
tabBarLabel: 'Albums',
|
||||
tabBarIcon: 'image-album',
|
||||
tabBarColor: '#FAD4D6',
|
||||
}}
|
||||
/>
|
||||
</MaterialBottomTabs.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
tabBar: {
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
});
|
||||
54
example/src/Screens/MaterialTopTabs.tsx
Normal file
54
example/src/Screens/MaterialTopTabs.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
|
||||
import Albums from '../Shared/Albums';
|
||||
import Contacts from '../Shared/Contacts';
|
||||
import Chat from '../Shared/Chat';
|
||||
|
||||
type MaterialTopTabParams = {
|
||||
albums: undefined;
|
||||
contacts: undefined;
|
||||
chat: undefined;
|
||||
};
|
||||
|
||||
const MaterialTopTabs = createMaterialTopTabNavigator<MaterialTopTabParams>();
|
||||
|
||||
export default function MaterialTopTabsScreen() {
|
||||
return (
|
||||
<MaterialTopTabs.Navigator
|
||||
tabBarOptions={{
|
||||
style: styles.tabBar,
|
||||
labelStyle: styles.tabLabel,
|
||||
indicatorStyle: styles.tabIndicator,
|
||||
}}
|
||||
>
|
||||
<MaterialTopTabs.Screen
|
||||
name="chat"
|
||||
component={Chat}
|
||||
options={{ title: 'Chat' }}
|
||||
/>
|
||||
<MaterialTopTabs.Screen
|
||||
name="contacts"
|
||||
component={Contacts}
|
||||
options={{ title: 'Contacts' }}
|
||||
/>
|
||||
<MaterialTopTabs.Screen
|
||||
name="albums"
|
||||
component={Albums}
|
||||
options={{ title: 'Albums' }}
|
||||
/>
|
||||
</MaterialTopTabs.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
tabBar: {
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
tabLabel: {
|
||||
color: 'black',
|
||||
},
|
||||
tabIndicator: {
|
||||
backgroundColor: 'tomato',
|
||||
},
|
||||
});
|
||||
131
example/src/Screens/ModalPresentationStack.tsx
Normal file
131
example/src/Screens/ModalPresentationStack.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import * as React from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import { Button } from 'react-native-paper';
|
||||
import { useSafeArea } from 'react-native-safe-area-context';
|
||||
import { RouteProp, ParamListBase } from '@react-navigation/core';
|
||||
import {
|
||||
createStackNavigator,
|
||||
StackNavigationProp,
|
||||
TransitionPresets,
|
||||
} from '@react-navigation/stack';
|
||||
import Article from '../Shared/Article';
|
||||
import Albums from '../Shared/Albums';
|
||||
|
||||
type SimpleStackParams = {
|
||||
article: { author: string };
|
||||
album: undefined;
|
||||
};
|
||||
|
||||
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
|
||||
|
||||
const ArticleScreen = ({
|
||||
navigation,
|
||||
route,
|
||||
}: {
|
||||
navigation: SimpleStackNavigation;
|
||||
route: RouteProp<SimpleStackParams, 'article'>;
|
||||
}) => {
|
||||
const insets = useSafeArea();
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<View style={[styles.buttons, { marginTop: insets.top }]}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('album')}
|
||||
style={styles.button}
|
||||
>
|
||||
Push album
|
||||
</Button>
|
||||
<Button
|
||||
mode="outlined"
|
||||
onPress={() => navigation.goBack()}
|
||||
style={styles.button}
|
||||
>
|
||||
Go back
|
||||
</Button>
|
||||
</View>
|
||||
<Article author={{ name: route.params.author }} />
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const AlbumsScreen = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: SimpleStackNavigation;
|
||||
}) => {
|
||||
const insets = useSafeArea();
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<View style={[styles.buttons, { marginTop: insets.top }]}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('article', { author: 'Babel fish' })}
|
||||
style={styles.button}
|
||||
>
|
||||
Push article
|
||||
</Button>
|
||||
<Button
|
||||
mode="outlined"
|
||||
onPress={() => navigation.goBack()}
|
||||
style={styles.button}
|
||||
>
|
||||
Go back
|
||||
</Button>
|
||||
</View>
|
||||
<Albums />
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const ModalPresentationStack = createStackNavigator<SimpleStackParams>();
|
||||
|
||||
type Props = {
|
||||
options?: React.ComponentProps<typeof ModalPresentationStack.Navigator>;
|
||||
navigation: StackNavigationProp<ParamListBase>;
|
||||
};
|
||||
|
||||
export default function SimpleStackScreen({ navigation, options }: Props) {
|
||||
navigation.setOptions({
|
||||
headerShown: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<ModalPresentationStack.Navigator
|
||||
mode="modal"
|
||||
headerMode="none"
|
||||
screenOptions={{
|
||||
...TransitionPresets.ModalPresentationIOS,
|
||||
cardOverlayEnabled: true,
|
||||
gestureEnabled: true,
|
||||
}}
|
||||
{...options}
|
||||
>
|
||||
<ModalPresentationStack.Screen
|
||||
name="article"
|
||||
component={ArticleScreen}
|
||||
options={({ route }) => ({
|
||||
title: `Article by ${route.params.author}`,
|
||||
})}
|
||||
initialParams={{ author: 'Gandalf' }}
|
||||
/>
|
||||
<ModalPresentationStack.Screen
|
||||
name="album"
|
||||
component={AlbumsScreen}
|
||||
options={{ title: 'Album' }}
|
||||
/>
|
||||
</ModalPresentationStack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
buttons: {
|
||||
flexDirection: 'row',
|
||||
padding: 8,
|
||||
},
|
||||
button: {
|
||||
margin: 8,
|
||||
},
|
||||
});
|
||||
23
example/src/Screens/NativeStack.expo.tsx
Normal file
23
example/src/Screens/NativeStack.expo.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import * as React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
|
||||
export default function NativeStack() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>Currently not supported on Expo :(</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#eceff1',
|
||||
},
|
||||
text: {
|
||||
fontSize: 16,
|
||||
color: '#999',
|
||||
},
|
||||
});
|
||||
214
example/src/Screens/NativeStack.tsx
Normal file
214
example/src/Screens/NativeStack.tsx
Normal file
@@ -0,0 +1,214 @@
|
||||
import * as React from 'react';
|
||||
import { View, Text, ScrollView, StyleSheet } from 'react-native';
|
||||
import { Button } from 'react-native-paper';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { enableScreens } from 'react-native-screens';
|
||||
import {
|
||||
RouteProp,
|
||||
ParamListBase,
|
||||
useFocusEffect,
|
||||
} from '@react-navigation/core';
|
||||
import { DrawerNavigationProp } from '@react-navigation/drawer';
|
||||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
import {
|
||||
createNativeStackNavigator,
|
||||
NativeStackNavigationProp,
|
||||
} from '@react-navigation/native-stack';
|
||||
import Albums from '../Shared/Albums';
|
||||
|
||||
type NativeStackParams = {
|
||||
article: { author: string };
|
||||
album: undefined;
|
||||
};
|
||||
|
||||
type NativeStackNavigation = NativeStackNavigationProp<NativeStackParams>;
|
||||
|
||||
const ArticleScreen = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NativeStackNavigation;
|
||||
route: RouteProp<NativeStackParams, 'article'>;
|
||||
}) => (
|
||||
<ScrollView style={styles.container} contentContainerStyle={styles.content}>
|
||||
<View style={styles.buttons}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('album')}
|
||||
style={styles.button}
|
||||
>
|
||||
Push album
|
||||
</Button>
|
||||
<Button
|
||||
mode="outlined"
|
||||
onPress={() => navigation.goBack()}
|
||||
style={styles.button}
|
||||
>
|
||||
Go back
|
||||
</Button>
|
||||
</View>
|
||||
<Text style={styles.title}>What is Lorem Ipsum?</Text>
|
||||
<Text style={styles.paragraph}>
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
|
||||
Lorem Ipsum has been the industry's standard dummy text ever since
|
||||
the 1500s, when an unknown printer took a galley of type and scrambled it
|
||||
to make a type specimen book. It has survived not only five centuries, but
|
||||
also the leap into electronic typesetting, remaining essentially
|
||||
unchanged. It was popularised in the 1960s with the release of Letraset
|
||||
sheets containing Lorem Ipsum passages, and more recently with desktop
|
||||
publishing software like Aldus PageMaker including versions of Lorem
|
||||
Ipsum.
|
||||
</Text>
|
||||
<Text style={styles.title}>Where does it come from?</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. 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. 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>
|
||||
<Text style={styles.paragraph}>
|
||||
The standard chunk of Lorem Ipsum used since the 1500s is reproduced below
|
||||
for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus
|
||||
Bonorum et Malorum" by Cicero are also reproduced in their exact
|
||||
original form, accompanied by English versions from the 1914 translation
|
||||
by H. Rackham.
|
||||
</Text>
|
||||
<Text style={styles.title}>Why do we use it?</Text>
|
||||
<Text style={styles.paragraph}>
|
||||
It is a long established fact that a reader will be distracted by the
|
||||
readable content of a page when looking at its layout. The point of using
|
||||
Lorem Ipsum is that it has a more-or-less normal distribution of letters,
|
||||
as opposed to using "Content here, content here", making it look
|
||||
like readable English. Many desktop publishing packages and web page
|
||||
editors now use Lorem Ipsum as their default model text, and a search for
|
||||
"lorem ipsum" will uncover many web sites still in their
|
||||
infancy. Various versions have evolved over the years, sometimes by
|
||||
accident, sometimes on purpose (injected humour and the like).
|
||||
</Text>
|
||||
<Text style={styles.title}>Where can I get some?</Text>
|
||||
<Text style={styles.paragraph}>
|
||||
There are many variations of passages of Lorem Ipsum available, but the
|
||||
majority have suffered alteration in some form, by injected humour, or
|
||||
randomised words which don't look even slightly believable. If you
|
||||
are going to use a passage of Lorem Ipsum, you need to be sure there
|
||||
isn't anything embarrassing hidden in the middle of text. All the
|
||||
Lorem Ipsum generators on the Internet tend to repeat predefined chunks as
|
||||
necessary, making this the first true generator on the Internet. It uses a
|
||||
dictionary of over 200 Latin words, combined with a handful of model
|
||||
sentence structures, to generate Lorem Ipsum which looks reasonable. The
|
||||
generated Lorem Ipsum is therefore always free from repetition, injected
|
||||
humour, or non-characteristic words etc.
|
||||
</Text>
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
const AlbumsScreen = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NativeStackNavigation;
|
||||
}) => (
|
||||
<React.Fragment>
|
||||
<View style={styles.buttons}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('article', { author: 'Babel fish' })}
|
||||
style={styles.button}
|
||||
>
|
||||
Push article
|
||||
</Button>
|
||||
<Button
|
||||
mode="outlined"
|
||||
onPress={() => navigation.goBack()}
|
||||
style={styles.button}
|
||||
>
|
||||
Go back
|
||||
</Button>
|
||||
</View>
|
||||
<Albums />
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
const NativeStack = createNativeStackNavigator<NativeStackParams>();
|
||||
|
||||
type Props = {
|
||||
navigation: StackNavigationProp<ParamListBase>;
|
||||
};
|
||||
|
||||
export default function NativeStackScreen({ navigation }: Props) {
|
||||
navigation.setOptions({
|
||||
headerShown: false,
|
||||
});
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
const drawer = navigation.dangerouslyGetParent() as DrawerNavigationProp<
|
||||
ParamListBase
|
||||
>;
|
||||
|
||||
navigation.setOptions({ gestureEnabled: false });
|
||||
drawer.setOptions({ gestureEnabled: false });
|
||||
|
||||
return () => {
|
||||
navigation.setOptions({ gestureEnabled: true });
|
||||
drawer.setOptions({ gestureEnabled: true });
|
||||
};
|
||||
}, [navigation])
|
||||
);
|
||||
|
||||
return (
|
||||
<NativeStack.Navigator>
|
||||
<NativeStack.Screen
|
||||
name="article"
|
||||
component={ArticleScreen}
|
||||
options={{
|
||||
title: 'Lorem Ipsum',
|
||||
headerLargeTitle: true,
|
||||
headerHideShadow: true,
|
||||
}}
|
||||
/>
|
||||
<NativeStack.Screen
|
||||
name="album"
|
||||
component={AlbumsScreen}
|
||||
options={{ title: 'Album' }}
|
||||
/>
|
||||
</NativeStack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
enableScreens(true);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
buttons: {
|
||||
flexDirection: 'row',
|
||||
padding: 8,
|
||||
},
|
||||
button: {
|
||||
margin: 8,
|
||||
},
|
||||
container: {
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
content: {
|
||||
paddingVertical: 16,
|
||||
},
|
||||
title: {
|
||||
color: '#000',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 24,
|
||||
marginVertical: 8,
|
||||
marginHorizontal: 16,
|
||||
},
|
||||
paragraph: {
|
||||
color: '#000',
|
||||
fontSize: 16,
|
||||
lineHeight: 24,
|
||||
marginVertical: 8,
|
||||
marginHorizontal: 16,
|
||||
},
|
||||
});
|
||||
23
example/src/Screens/NativeStack.web.tsx
Normal file
23
example/src/Screens/NativeStack.web.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import * as React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
|
||||
export default function NativeStack() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>Not supported on Web :(</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#eceff1',
|
||||
},
|
||||
text: {
|
||||
fontSize: 16,
|
||||
color: '#999',
|
||||
},
|
||||
});
|
||||
115
example/src/Screens/SimpleStack.tsx
Normal file
115
example/src/Screens/SimpleStack.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import * as React from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import { Button } from 'react-native-paper';
|
||||
import { RouteProp, ParamListBase } from '@react-navigation/core';
|
||||
import {
|
||||
createStackNavigator,
|
||||
StackNavigationProp,
|
||||
} from '@react-navigation/stack';
|
||||
import Article from '../Shared/Article';
|
||||
import Albums from '../Shared/Albums';
|
||||
|
||||
type SimpleStackParams = {
|
||||
article: { author: string };
|
||||
album: undefined;
|
||||
};
|
||||
|
||||
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
|
||||
|
||||
const ArticleScreen = ({
|
||||
navigation,
|
||||
route,
|
||||
}: {
|
||||
navigation: SimpleStackNavigation;
|
||||
route: RouteProp<SimpleStackParams, 'article'>;
|
||||
}) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<View style={styles.buttons}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('album')}
|
||||
style={styles.button}
|
||||
>
|
||||
Push album
|
||||
</Button>
|
||||
<Button
|
||||
mode="outlined"
|
||||
onPress={() => navigation.goBack()}
|
||||
style={styles.button}
|
||||
>
|
||||
Go back
|
||||
</Button>
|
||||
</View>
|
||||
<Article author={{ name: route.params.author }} />
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const AlbumsScreen = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: SimpleStackNavigation;
|
||||
}) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<View style={styles.buttons}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('article', { author: 'Babel fish' })}
|
||||
style={styles.button}
|
||||
>
|
||||
Push article
|
||||
</Button>
|
||||
<Button
|
||||
mode="outlined"
|
||||
onPress={() => navigation.goBack()}
|
||||
style={styles.button}
|
||||
>
|
||||
Go back
|
||||
</Button>
|
||||
</View>
|
||||
<Albums />
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const SimpleStack = createStackNavigator<SimpleStackParams>();
|
||||
|
||||
type Props = Partial<React.ComponentProps<typeof SimpleStack.Navigator>> & {
|
||||
navigation: StackNavigationProp<ParamListBase>;
|
||||
};
|
||||
|
||||
export default function SimpleStackScreen({ navigation, ...rest }: Props) {
|
||||
navigation.setOptions({
|
||||
headerShown: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<SimpleStack.Navigator {...rest}>
|
||||
<SimpleStack.Screen
|
||||
name="article"
|
||||
component={ArticleScreen}
|
||||
options={({ route }) => ({
|
||||
title: `Article by ${route.params.author}`,
|
||||
})}
|
||||
initialParams={{ author: 'Gandalf' }}
|
||||
/>
|
||||
<SimpleStack.Screen
|
||||
name="album"
|
||||
component={AlbumsScreen}
|
||||
options={{ title: 'Album' }}
|
||||
/>
|
||||
</SimpleStack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
buttons: {
|
||||
flexDirection: 'row',
|
||||
padding: 8,
|
||||
},
|
||||
button: {
|
||||
margin: 8,
|
||||
},
|
||||
});
|
||||
51
example/src/Shared/Albums.tsx
Normal file
51
example/src/Shared/Albums.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import * as React from 'react';
|
||||
import { Image, Dimensions, ScrollView, StyleSheet } from 'react-native';
|
||||
import { useScrollToTop } from '@react-navigation/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 function Albums() {
|
||||
const ref = React.useRef<ScrollView>(null);
|
||||
|
||||
useScrollToTop(ref);
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
ref={ref}
|
||||
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} />
|
||||
))}
|
||||
{COVERS.map((source, i) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<Image key={i + 'F'} 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,
|
||||
},
|
||||
});
|
||||
115
example/src/Shared/Article.tsx
Normal file
115
example/src/Shared/Article.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import * as React from 'react';
|
||||
import { View, Text, Image, ScrollView, StyleSheet } from 'react-native';
|
||||
import { useScrollToTop } from '@react-navigation/native';
|
||||
|
||||
type Props = {
|
||||
date?: string;
|
||||
author?: {
|
||||
name: string;
|
||||
};
|
||||
};
|
||||
|
||||
export default function Article({
|
||||
date = '1st Jan 2025',
|
||||
author = {
|
||||
name: 'Knowledge Bot',
|
||||
},
|
||||
}: Props) {
|
||||
const ref = React.useRef<ScrollView>(null);
|
||||
|
||||
useScrollToTop(ref);
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
ref={ref}
|
||||
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}>{author.name}</Text>
|
||||
<Text style={styles.timestamp}>{date}</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,
|
||||
},
|
||||
});
|
||||
118
example/src/Shared/Chat.tsx
Normal file
118
example/src/Shared/Chat.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
View,
|
||||
Image,
|
||||
Text,
|
||||
TextInput,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
} from 'react-native';
|
||||
import { useScrollToTop } from '@react-navigation/native';
|
||||
|
||||
const MESSAGES = [
|
||||
'okay',
|
||||
'sudo make me a sandwich',
|
||||
'what? make it yourself',
|
||||
'make me a sandwich',
|
||||
];
|
||||
|
||||
export default function Chat() {
|
||||
const ref = React.useRef<ScrollView>(null);
|
||||
|
||||
useScrollToTop(ref);
|
||||
|
||||
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>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="Write a message"
|
||||
underlineColorAndroid="transparent"
|
||||
/>
|
||||
</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',
|
||||
},
|
||||
input: {
|
||||
height: 48,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 24,
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
});
|
||||
137
example/src/Shared/Contacts.tsx
Normal file
137
example/src/Shared/Contacts.tsx
Normal file
@@ -0,0 +1,137 @@
|
||||
import * as React from 'react';
|
||||
import { View, Text, FlatList, StyleSheet } from 'react-native';
|
||||
import { useScrollToTop } from '@react-navigation/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: { name: string; number: number };
|
||||
}> {
|
||||
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 function Contacts() {
|
||||
const ref = React.useRef<FlatList<Item>>(null);
|
||||
|
||||
useScrollToTop(ref);
|
||||
|
||||
const renderItem = ({ item }: { item: Item }) => <ContactItem item={item} />;
|
||||
|
||||
const ItemSeparator = () => <View style={styles.separator} />;
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
ref={ref}
|
||||
data={CONTACTS}
|
||||
keyExtractor={(_, i) => String(i)}
|
||||
renderItem={renderItem}
|
||||
ItemSeparatorComponent={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)',
|
||||
},
|
||||
});
|
||||
4
example/src/Shared/TouchableBounce.native.tsx
Normal file
4
example/src/Shared/TouchableBounce.native.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
// @ts-ignore
|
||||
import TouchableBounce from 'react-native/Libraries/Components/Touchable/TouchableBounce';
|
||||
|
||||
export default TouchableBounce;
|
||||
3
example/src/Shared/TouchableBounce.tsx
Normal file
3
example/src/Shared/TouchableBounce.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
import { TouchableOpacity } from 'react-native';
|
||||
|
||||
export default TouchableOpacity;
|
||||
207
example/src/index.tsx
Normal file
207
example/src/index.tsx
Normal file
@@ -0,0 +1,207 @@
|
||||
import * as React from 'react';
|
||||
import { ScrollView, AsyncStorage, YellowBox } from 'react-native';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { Appbar, List } from 'react-native-paper';
|
||||
import { Asset } from 'expo-asset';
|
||||
import {
|
||||
InitialState,
|
||||
getStateFromPath,
|
||||
NavigationContainerRef,
|
||||
} from '@react-navigation/core';
|
||||
import {
|
||||
useLinking,
|
||||
NavigationNativeContainer,
|
||||
} from '@react-navigation/native';
|
||||
import {
|
||||
createDrawerNavigator,
|
||||
DrawerNavigationProp,
|
||||
} from '@react-navigation/drawer';
|
||||
import {
|
||||
createStackNavigator,
|
||||
Assets as StackAssets,
|
||||
StackNavigationProp,
|
||||
} from '@react-navigation/stack';
|
||||
|
||||
import LinkingPrefixes from './LinkingPrefixes';
|
||||
import SimpleStack from './Screens/SimpleStack';
|
||||
import NativeStack from './Screens/NativeStack';
|
||||
import ModalPresentationStack from './Screens/ModalPresentationStack';
|
||||
import BottomTabs from './Screens/BottomTabs';
|
||||
import MaterialTopTabsScreen from './Screens/MaterialTopTabs';
|
||||
import MaterialBottomTabs from './Screens/MaterialBottomTabs';
|
||||
import AuthFlow from './Screens/AuthFlow';
|
||||
import CompatAPI from './Screens/CompatAPI';
|
||||
|
||||
YellowBox.ignoreWarnings(['Require cycle:', 'Warning: Async Storage']);
|
||||
|
||||
type RootDrawerParamList = {
|
||||
Root: undefined;
|
||||
Another: undefined;
|
||||
};
|
||||
|
||||
type RootStackParamList = {
|
||||
Home: undefined;
|
||||
} & {
|
||||
[P in keyof typeof SCREENS]: undefined;
|
||||
};
|
||||
|
||||
const SCREENS = {
|
||||
SimpleStack: { title: 'Simple Stack', component: SimpleStack },
|
||||
NativeStack: { title: 'Native Stack', component: NativeStack },
|
||||
ModalPresentationStack: {
|
||||
title: 'Modal Presentation Stack',
|
||||
component: ModalPresentationStack,
|
||||
},
|
||||
BottomTabs: { title: 'Bottom Tabs', component: BottomTabs },
|
||||
MaterialTopTabs: {
|
||||
title: 'Material Top Tabs',
|
||||
component: MaterialTopTabsScreen,
|
||||
},
|
||||
MaterialBottomTabs: {
|
||||
title: 'Material Bottom Tabs',
|
||||
component: MaterialBottomTabs,
|
||||
},
|
||||
AuthFlow: {
|
||||
title: 'Auth Flow',
|
||||
component: AuthFlow,
|
||||
},
|
||||
CompatAPI: {
|
||||
title: 'Compat Layer',
|
||||
component: CompatAPI,
|
||||
},
|
||||
};
|
||||
|
||||
const Drawer = createDrawerNavigator<RootDrawerParamList>();
|
||||
const Stack = createStackNavigator<RootStackParamList>();
|
||||
|
||||
const PERSISTENCE_KEY = 'NAVIGATION_STATE';
|
||||
|
||||
Asset.loadAsync(StackAssets);
|
||||
|
||||
export default function App() {
|
||||
const containerRef = React.useRef<NavigationContainerRef>();
|
||||
|
||||
// To test deep linking on, run the following in the Terminal:
|
||||
// Android: adb shell am start -a android.intent.action.VIEW -d "exp://127.0.0.1:19000/--/simple-stack"
|
||||
// iOS: xcrun simctl openurl booted exp://127.0.0.1:19000/--/simple-stack
|
||||
// The first segment of the link is the the scheme + host (returned by `Linking.makeUrl`)
|
||||
const { getInitialState } = useLinking(containerRef, {
|
||||
prefixes: LinkingPrefixes,
|
||||
getStateFromPath: path => {
|
||||
const state = getStateFromPath(path);
|
||||
|
||||
return {
|
||||
routes: [
|
||||
{
|
||||
name: 'root',
|
||||
state: {
|
||||
...state,
|
||||
routes: [{ name: 'home' }, ...(state ? state.routes : [])],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const [isReady, setIsReady] = React.useState(false);
|
||||
const [initialState, setInitialState] = React.useState<
|
||||
InitialState | undefined
|
||||
>();
|
||||
|
||||
React.useEffect(() => {
|
||||
const restoreState = async () => {
|
||||
try {
|
||||
let state = await getInitialState();
|
||||
|
||||
if (state === undefined) {
|
||||
const savedState = await AsyncStorage.getItem(PERSISTENCE_KEY);
|
||||
state = savedState ? JSON.parse(savedState) : undefined;
|
||||
}
|
||||
|
||||
if (state !== undefined) {
|
||||
setInitialState(state);
|
||||
}
|
||||
} finally {
|
||||
setIsReady(true);
|
||||
}
|
||||
};
|
||||
|
||||
restoreState();
|
||||
}, [getInitialState]);
|
||||
|
||||
if (!isReady) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<NavigationNativeContainer
|
||||
ref={containerRef}
|
||||
initialState={initialState}
|
||||
onStateChange={state =>
|
||||
AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state))
|
||||
}
|
||||
>
|
||||
<Drawer.Navigator>
|
||||
<Drawer.Screen
|
||||
name="Root"
|
||||
options={{
|
||||
title: 'Examples',
|
||||
drawerIcon: ({ size, color }) => (
|
||||
<MaterialIcons size={size} color={color} name="folder" />
|
||||
),
|
||||
}}
|
||||
>
|
||||
{({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: DrawerNavigationProp<RootDrawerParamList>;
|
||||
}) => (
|
||||
<Stack.Navigator>
|
||||
<Stack.Screen
|
||||
name="Home"
|
||||
options={{
|
||||
title: 'Examples',
|
||||
headerLeft: () => (
|
||||
<Appbar.Action
|
||||
icon="menu"
|
||||
onPress={() => navigation.toggleDrawer()}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: StackNavigationProp<RootStackParamList>;
|
||||
}) => (
|
||||
<ScrollView>
|
||||
{(Object.keys(SCREENS) as Array<keyof typeof SCREENS>).map(
|
||||
name => (
|
||||
<List.Item
|
||||
key={name}
|
||||
title={SCREENS[name].title}
|
||||
onPress={() => navigation.push(name)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</ScrollView>
|
||||
)}
|
||||
</Stack.Screen>
|
||||
{(Object.keys(SCREENS) as Array<keyof typeof SCREENS>).map(
|
||||
name => (
|
||||
<Stack.Screen
|
||||
key={name}
|
||||
name={name}
|
||||
component={SCREENS[name].component}
|
||||
options={{ title: SCREENS[name].title }}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</Stack.Navigator>
|
||||
)}
|
||||
</Drawer.Screen>
|
||||
</Drawer.Navigator>
|
||||
</NavigationNativeContainer>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user