Compare commits
42 Commits
@react-nav
...
@react-nav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c514542305 | ||
|
|
dcc5f99ecd | ||
|
|
adbeb292f5 | ||
|
|
543679f185 | ||
|
|
cbe240eae6 | ||
|
|
7f963a74bb | ||
|
|
572beae41b | ||
|
|
15fe3ebb51 | ||
|
|
31565d5576 | ||
|
|
2c31d1705c | ||
|
|
8f9a250958 | ||
|
|
87d28ca430 | ||
|
|
fa4411a14d | ||
|
|
77b757091c | ||
|
|
6b9b999c5b | ||
|
|
8c5f84094f | ||
|
|
21709f7674 | ||
|
|
22742e4a7f | ||
|
|
3cd1aedcf4 | ||
|
|
797d3a798e | ||
|
|
59803f54d6 | ||
|
|
935659899f | ||
|
|
ef0f5d6567 | ||
|
|
499f66dba4 | ||
|
|
2ef2f1a86f | ||
|
|
0252bdc222 | ||
|
|
282f62c258 | ||
|
|
f462d67270 | ||
|
|
873afec9fe | ||
|
|
878297e52f | ||
|
|
1ea9b4524d | ||
|
|
c52a8c46a8 | ||
|
|
0635365483 | ||
|
|
9843b92e05 | ||
|
|
ebd145a09d | ||
|
|
9fc1af02c2 | ||
|
|
68a334cc93 | ||
|
|
c110570d4c | ||
|
|
d57226fd8b | ||
|
|
c3d3748143 | ||
|
|
8002d51795 | ||
|
|
31cf19912b |
@@ -13,5 +13,8 @@ module.exports = {
|
||||
'@babel/preset-react',
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
plugins: ['@babel/plugin-proposal-class-properties'],
|
||||
plugins: [
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
],
|
||||
};
|
||||
|
||||
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 900 B |
@@ -28,24 +28,25 @@
|
||||
"react-dom": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-gesture-handler": "~1.5.0",
|
||||
"react-native-paper": "^3.2.1",
|
||||
"react-native-iphone-x-helper": "^1.2.1",
|
||||
"react-native-paper": "^3.3.0",
|
||||
"react-native-reanimated": "^1.4.0",
|
||||
"react-native-safe-area-context": "^0.6.0",
|
||||
"react-native-screens": "^2.0.0-alpha.12",
|
||||
"react-native-screens": "^2.0.0-alpha.19",
|
||||
"react-native-tab-view": "2.11.0",
|
||||
"react-native-unimodules": "^0.6.0",
|
||||
"react-native-unimodules": "^0.7.0",
|
||||
"react-native-web": "^0.11.7",
|
||||
"scheduler": "^0.18.0",
|
||||
"shortid": "^2.2.15",
|
||||
"use-subscription": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.7.2",
|
||||
"@expo/webpack-config": "^0.10.1",
|
||||
"@types/react": "^16.9.11",
|
||||
"@types/react-native": "^0.60.22",
|
||||
"babel-preset-expo": "^7.1.0",
|
||||
"expo-cli": "^3.8.0",
|
||||
"typescript": "^3.7.2"
|
||||
"@babel/core": "^7.7.5",
|
||||
"@expo/webpack-config": "^0.10.6",
|
||||
"@types/react": "^16.9.16",
|
||||
"@types/react-native": "^0.60.25",
|
||||
"babel-preset-expo": "^8.0.0",
|
||||
"expo-cli": "^3.11.1",
|
||||
"typescript": "^3.7.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ const getTabBarIcon = (name: string) => ({
|
||||
}) => <MaterialCommunityIcons name={name} color={color} size={size} />;
|
||||
|
||||
type BottomTabParams = {
|
||||
article: undefined;
|
||||
albums: undefined;
|
||||
contacts: undefined;
|
||||
chat: undefined;
|
||||
Article: undefined;
|
||||
Albums: undefined;
|
||||
Contacts: undefined;
|
||||
Chat: undefined;
|
||||
};
|
||||
|
||||
const BottomTabs = createBottomTabNavigator<BottomTabParams>();
|
||||
@@ -32,7 +32,7 @@ export default function BottomTabsScreen() {
|
||||
}}
|
||||
>
|
||||
<BottomTabs.Screen
|
||||
name="article"
|
||||
name="Article"
|
||||
options={{
|
||||
title: 'Article',
|
||||
tabBarIcon: getTabBarIcon('file-document-box'),
|
||||
@@ -41,7 +41,7 @@ export default function BottomTabsScreen() {
|
||||
{props => <SimpleStackScreen {...props} headerMode="none" />}
|
||||
</BottomTabs.Screen>
|
||||
<BottomTabs.Screen
|
||||
name="chat"
|
||||
name="Chat"
|
||||
component={Chat}
|
||||
options={{
|
||||
tabBarLabel: 'Chat',
|
||||
@@ -49,7 +49,7 @@ export default function BottomTabsScreen() {
|
||||
}}
|
||||
/>
|
||||
<BottomTabs.Screen
|
||||
name="contacts"
|
||||
name="Contacts"
|
||||
component={Contacts}
|
||||
options={{
|
||||
title: 'Contacts',
|
||||
@@ -57,7 +57,7 @@ export default function BottomTabsScreen() {
|
||||
}}
|
||||
/>
|
||||
<BottomTabs.Screen
|
||||
name="albums"
|
||||
name="Albums"
|
||||
component={Albums}
|
||||
options={{
|
||||
title: 'Albums',
|
||||
|
||||
@@ -7,10 +7,10 @@ import Chat from '../Shared/Chat';
|
||||
import SimpleStackScreen from './SimpleStack';
|
||||
|
||||
type MaterialBottomTabParams = {
|
||||
article: undefined;
|
||||
albums: undefined;
|
||||
contacts: undefined;
|
||||
chat: undefined;
|
||||
Article: undefined;
|
||||
Albums: undefined;
|
||||
Contacts: undefined;
|
||||
Chat: undefined;
|
||||
};
|
||||
|
||||
const MaterialBottomTabs = createMaterialBottomTabNavigator<
|
||||
@@ -21,7 +21,7 @@ export default function MaterialBottomTabsScreen() {
|
||||
return (
|
||||
<MaterialBottomTabs.Navigator barStyle={styles.tabBar}>
|
||||
<MaterialBottomTabs.Screen
|
||||
name="article"
|
||||
name="Article"
|
||||
options={{
|
||||
tabBarLabel: 'Article',
|
||||
tabBarIcon: 'file-document-box',
|
||||
@@ -31,7 +31,7 @@ export default function MaterialBottomTabsScreen() {
|
||||
{props => <SimpleStackScreen {...props} headerMode="none" />}
|
||||
</MaterialBottomTabs.Screen>
|
||||
<MaterialBottomTabs.Screen
|
||||
name="chat"
|
||||
name="Chat"
|
||||
component={Chat}
|
||||
options={{
|
||||
tabBarLabel: 'Chat',
|
||||
@@ -41,7 +41,7 @@ export default function MaterialBottomTabsScreen() {
|
||||
}}
|
||||
/>
|
||||
<MaterialBottomTabs.Screen
|
||||
name="contacts"
|
||||
name="Contacts"
|
||||
component={Contacts}
|
||||
options={{
|
||||
tabBarLabel: 'Contacts',
|
||||
@@ -50,7 +50,7 @@ export default function MaterialBottomTabsScreen() {
|
||||
}}
|
||||
/>
|
||||
<MaterialBottomTabs.Screen
|
||||
name="albums"
|
||||
name="Albums"
|
||||
component={Albums}
|
||||
options={{
|
||||
tabBarLabel: 'Albums',
|
||||
|
||||
@@ -5,9 +5,9 @@ import Contacts from '../Shared/Contacts';
|
||||
import Chat from '../Shared/Chat';
|
||||
|
||||
type MaterialTopTabParams = {
|
||||
albums: undefined;
|
||||
contacts: undefined;
|
||||
chat: undefined;
|
||||
Albums: undefined;
|
||||
Contacts: undefined;
|
||||
Chat: undefined;
|
||||
};
|
||||
|
||||
const MaterialTopTabs = createMaterialTopTabNavigator<MaterialTopTabParams>();
|
||||
@@ -16,17 +16,17 @@ export default function MaterialTopTabsScreen() {
|
||||
return (
|
||||
<MaterialTopTabs.Navigator>
|
||||
<MaterialTopTabs.Screen
|
||||
name="chat"
|
||||
name="Chat"
|
||||
component={Chat}
|
||||
options={{ title: 'Chat' }}
|
||||
/>
|
||||
<MaterialTopTabs.Screen
|
||||
name="contacts"
|
||||
name="Contacts"
|
||||
component={Contacts}
|
||||
options={{ title: 'Contacts' }}
|
||||
/>
|
||||
<MaterialTopTabs.Screen
|
||||
name="albums"
|
||||
name="Albums"
|
||||
component={Albums}
|
||||
options={{ title: 'Albums' }}
|
||||
/>
|
||||
|
||||
@@ -11,19 +11,19 @@ import {
|
||||
import Article from '../Shared/Article';
|
||||
import Albums from '../Shared/Albums';
|
||||
|
||||
type SimpleStackParams = {
|
||||
article: { author: string };
|
||||
album: undefined;
|
||||
type ModalStackParams = {
|
||||
Article: { author: string };
|
||||
Album: undefined;
|
||||
};
|
||||
|
||||
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
|
||||
type ModalStackNavigation = StackNavigationProp<ModalStackParams>;
|
||||
|
||||
const ArticleScreen = ({
|
||||
navigation,
|
||||
route,
|
||||
}: {
|
||||
navigation: SimpleStackNavigation;
|
||||
route: RouteProp<SimpleStackParams, 'article'>;
|
||||
navigation: ModalStackNavigation;
|
||||
route: RouteProp<ModalStackParams, 'Article'>;
|
||||
}) => {
|
||||
const insets = useSafeArea();
|
||||
|
||||
@@ -32,7 +32,7 @@ const ArticleScreen = ({
|
||||
<View style={[styles.buttons, { marginTop: insets.top }]}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('album')}
|
||||
onPress={() => navigation.push('Album')}
|
||||
style={styles.button}
|
||||
>
|
||||
Push album
|
||||
@@ -50,11 +50,7 @@ const ArticleScreen = ({
|
||||
);
|
||||
};
|
||||
|
||||
const AlbumsScreen = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: SimpleStackNavigation;
|
||||
}) => {
|
||||
const AlbumsScreen = ({ navigation }: { navigation: ModalStackNavigation }) => {
|
||||
const insets = useSafeArea();
|
||||
|
||||
return (
|
||||
@@ -62,7 +58,7 @@ const AlbumsScreen = ({
|
||||
<View style={[styles.buttons, { marginTop: insets.top }]}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('article', { author: 'Babel fish' })}
|
||||
onPress={() => navigation.push('Article', { author: 'Babel fish' })}
|
||||
style={styles.button}
|
||||
>
|
||||
Push article
|
||||
@@ -80,7 +76,7 @@ const AlbumsScreen = ({
|
||||
);
|
||||
};
|
||||
|
||||
const ModalPresentationStack = createStackNavigator<SimpleStackParams>();
|
||||
const ModalPresentationStack = createStackNavigator<ModalStackParams>();
|
||||
|
||||
type Props = {
|
||||
options?: React.ComponentProps<typeof ModalPresentationStack.Navigator>;
|
||||
@@ -104,7 +100,7 @@ export default function SimpleStackScreen({ navigation, options }: Props) {
|
||||
{...options}
|
||||
>
|
||||
<ModalPresentationStack.Screen
|
||||
name="article"
|
||||
name="Article"
|
||||
component={ArticleScreen}
|
||||
options={({ route }) => ({
|
||||
title: `Article by ${route.params.author}`,
|
||||
@@ -112,7 +108,7 @@ export default function SimpleStackScreen({ navigation, options }: Props) {
|
||||
initialParams={{ author: 'Gandalf' }}
|
||||
/>
|
||||
<ModalPresentationStack.Screen
|
||||
name="album"
|
||||
name="Album"
|
||||
component={AlbumsScreen}
|
||||
options={{ title: 'Album' }}
|
||||
/>
|
||||
|
||||
@@ -18,8 +18,8 @@ import {
|
||||
import Albums from '../Shared/Albums';
|
||||
|
||||
type NativeStackParams = {
|
||||
article: { author: string };
|
||||
album: undefined;
|
||||
Article: { author: string };
|
||||
Album: undefined;
|
||||
};
|
||||
|
||||
type NativeStackNavigation = NativeStackNavigationProp<NativeStackParams>;
|
||||
@@ -42,7 +42,7 @@ const ArticleScreen = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NativeStackNavigation;
|
||||
route: RouteProp<NativeStackParams, 'article'>;
|
||||
route: RouteProp<NativeStackParams, 'Article'>;
|
||||
}) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
@@ -54,7 +54,7 @@ const ArticleScreen = ({
|
||||
<View style={styles.buttons}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('album')}
|
||||
onPress={() => navigation.push('Album')}
|
||||
style={styles.button}
|
||||
>
|
||||
Push album
|
||||
@@ -141,7 +141,7 @@ const AlbumsScreen = ({
|
||||
<View style={styles.buttons}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('article', { author: 'Babel fish' })}
|
||||
onPress={() => navigation.push('Article', { author: 'Babel fish' })}
|
||||
style={styles.button}
|
||||
>
|
||||
Push article
|
||||
@@ -188,7 +188,7 @@ export default function NativeStackScreen({ navigation }: Props) {
|
||||
return (
|
||||
<NativeStack.Navigator>
|
||||
<NativeStack.Screen
|
||||
name="article"
|
||||
name="Article"
|
||||
component={ArticleScreen}
|
||||
options={{
|
||||
title: 'Lorem Ipsum',
|
||||
@@ -197,7 +197,7 @@ export default function NativeStackScreen({ navigation }: Props) {
|
||||
}}
|
||||
/>
|
||||
<NativeStack.Screen
|
||||
name="album"
|
||||
name="Album"
|
||||
component={AlbumsScreen}
|
||||
options={{ title: 'Album' }}
|
||||
/>
|
||||
|
||||
@@ -10,8 +10,8 @@ import Article from '../Shared/Article';
|
||||
import Albums from '../Shared/Albums';
|
||||
|
||||
type SimpleStackParams = {
|
||||
article: { author: string };
|
||||
album: undefined;
|
||||
Article: { author: string };
|
||||
Album: undefined;
|
||||
};
|
||||
|
||||
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
|
||||
@@ -21,14 +21,14 @@ const ArticleScreen = ({
|
||||
route,
|
||||
}: {
|
||||
navigation: SimpleStackNavigation;
|
||||
route: RouteProp<SimpleStackParams, 'article'>;
|
||||
route: RouteProp<SimpleStackParams, 'Article'>;
|
||||
}) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<View style={styles.buttons}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('album')}
|
||||
onPress={() => navigation.push('Album')}
|
||||
style={styles.button}
|
||||
>
|
||||
Push album
|
||||
@@ -56,7 +56,7 @@ const AlbumsScreen = ({
|
||||
<View style={styles.buttons}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('article', { author: 'Babel fish' })}
|
||||
onPress={() => navigation.push('Article', { author: 'Babel fish' })}
|
||||
style={styles.button}
|
||||
>
|
||||
Push article
|
||||
@@ -88,7 +88,7 @@ export default function SimpleStackScreen({ navigation, ...rest }: Props) {
|
||||
return (
|
||||
<SimpleStack.Navigator {...rest}>
|
||||
<SimpleStack.Screen
|
||||
name="article"
|
||||
name="Article"
|
||||
component={ArticleScreen}
|
||||
options={({ route }) => ({
|
||||
title: `Article by ${route.params.author}`,
|
||||
@@ -96,7 +96,7 @@ export default function SimpleStackScreen({ navigation, ...rest }: Props) {
|
||||
initialParams={{ author: 'Gandalf' }}
|
||||
/>
|
||||
<SimpleStack.Screen
|
||||
name="album"
|
||||
name="Album"
|
||||
component={AlbumsScreen}
|
||||
options={{ title: 'Album' }}
|
||||
/>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable import/no-commonjs */
|
||||
|
||||
import * as React from 'react';
|
||||
import { Image, Dimensions, ScrollView, StyleSheet } from 'react-native';
|
||||
import { useScrollToTop } from '@react-navigation/native';
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
import { Asset } from 'expo-asset';
|
||||
import {
|
||||
InitialState,
|
||||
getStateFromPath,
|
||||
useLinking,
|
||||
NavigationContainerRef,
|
||||
NavigationNativeContainer,
|
||||
@@ -104,20 +103,19 @@ export default function App() {
|
||||
// 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);
|
||||
config: {
|
||||
Root: Object.keys(SCREENS).reduce<{ [key: string]: string }>(
|
||||
(acc, name) => {
|
||||
// Convert screen names such as SimpleStack to kebab case (simple-stack)
|
||||
acc[name] = name
|
||||
.replace(/([A-Z]+)/g, '-$1')
|
||||
.replace(/^-/, '')
|
||||
.toLowerCase();
|
||||
|
||||
return {
|
||||
routes: [
|
||||
{
|
||||
name: 'root',
|
||||
state: {
|
||||
...state,
|
||||
routes: [{ name: 'home' }, ...(state ? state.routes : [])],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -252,19 +250,19 @@ export default function App() {
|
||||
/>
|
||||
</View>
|
||||
<Divider />
|
||||
{(Object.keys(SCREENS) as Array<
|
||||
keyof typeof SCREENS
|
||||
>).map(name => (
|
||||
<List.Item
|
||||
key={name}
|
||||
title={SCREENS[name].title}
|
||||
onPress={() => navigation.push(name)}
|
||||
/>
|
||||
))}
|
||||
{(Object.keys(SCREENS) as (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(
|
||||
{(Object.keys(SCREENS) as (keyof typeof SCREENS)[]).map(
|
||||
name => (
|
||||
<Stack.Screen
|
||||
key={name}
|
||||
|
||||
@@ -4,6 +4,9 @@ const createExpoWebpackConfigAsync = require('@expo/webpack-config');
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||
|
||||
const node_modules = path.resolve(__dirname, 'node_modules');
|
||||
const packages = path.resolve(__dirname, '..', 'packages');
|
||||
|
||||
module.exports = async function(env, argv) {
|
||||
const config = await createExpoWebpackConfigAsync(env, argv);
|
||||
|
||||
@@ -19,28 +22,15 @@ module.exports = async function(env, argv) {
|
||||
);
|
||||
|
||||
Object.assign(config.resolve.alias, {
|
||||
react: path.resolve(__dirname, 'node_modules', 'react'),
|
||||
'react-native': path.resolve(__dirname, 'node_modules', 'react-native-web'),
|
||||
'react-native-web': path.resolve(
|
||||
__dirname,
|
||||
'node_modules',
|
||||
'react-native-web'
|
||||
),
|
||||
'react-native-reanimated': path.resolve(
|
||||
__dirname,
|
||||
'node_modules',
|
||||
'react-native-reanimated-web'
|
||||
),
|
||||
'@expo/vector-icons/MaterialCommunityIcons': require.resolve(
|
||||
'@expo/vector-icons/MaterialCommunityIcons'
|
||||
),
|
||||
react: path.resolve(node_modules, 'react'),
|
||||
'react-native': path.resolve(node_modules, 'react-native-web'),
|
||||
'react-native-web': path.resolve(node_modules, 'react-native-web'),
|
||||
'@expo/vector-icons': path.resolve(node_modules, '@expo/vector-icons'),
|
||||
});
|
||||
|
||||
fs.readdirSync(path.join(__dirname, '..')).forEach(name => {
|
||||
fs.readdirSync(packages).forEach(name => {
|
||||
config.resolve.alias[`@react-navigation/${name}`] = path.resolve(
|
||||
__dirname,
|
||||
'..',
|
||||
'packages',
|
||||
packages,
|
||||
name,
|
||||
'src'
|
||||
);
|
||||
|
||||
@@ -2,7 +2,7 @@ const error = console.error;
|
||||
|
||||
console.error = (...args) =>
|
||||
// Supress error messages regarding error boundary in tests
|
||||
/Consider adding an error boundary to your tree to customize error handling behavior/m.test(
|
||||
/(Consider adding an error boundary to your tree to customize error handling behavior|React will try to recreate this component tree from scratch using the error boundary you provided|Error boundaries should implement getDerivedStateFromError)/m.test(
|
||||
args[0]
|
||||
)
|
||||
? void 0
|
||||
|
||||
13
package.json
@@ -24,22 +24,23 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-class-properties": "^7.7.0",
|
||||
"@babel/preset-env": "^7.7.1",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.7.5",
|
||||
"@babel/preset-env": "^7.7.6",
|
||||
"@babel/preset-react": "^7.7.0",
|
||||
"@babel/preset-typescript": "^7.7.2",
|
||||
"@babel/runtime": "^7.7.2",
|
||||
"@babel/runtime": "^7.7.6",
|
||||
"@commitlint/config-conventional": "^8.2.0",
|
||||
"@types/jest": "^24.0.23",
|
||||
"codecov": "^3.6.1",
|
||||
"commitlint": "^8.2.0",
|
||||
"core-js": "^3.4.1",
|
||||
"eslint": "^6.6.0",
|
||||
"eslint-config-satya164": "^3.1.2",
|
||||
"core-js": "^3.5.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-config-satya164": "^3.1.5",
|
||||
"husky": "^3.0.9",
|
||||
"jest": "^24.8.0",
|
||||
"lerna": "^3.18.4",
|
||||
"prettier": "^1.19.1",
|
||||
"typescript": "^3.7.2"
|
||||
"typescript": "^3.7.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"react": "~16.9.0",
|
||||
|
||||
@@ -3,6 +3,57 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.0.0-alpha.32](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.31...@react-navigation/bottom-tabs@5.0.0-alpha.32) (2020-01-05)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.31](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.30...@react-navigation/bottom-tabs@5.0.0-alpha.31) (2020-01-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* provide initial values for safe area to prevent blank screen ([#238](https://github.com/react-navigation/navigation-ex/issues/238)) ([77b7570](https://github.com/react-navigation/navigation-ex/commit/77b757091c0451e20bca01138629669c7da544a8))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.30](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.29...@react-navigation/bottom-tabs@5.0.0-alpha.30) (2020-01-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.29](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.28...@react-navigation/bottom-tabs@5.0.0-alpha.29) (2020-01-01)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.28](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.27...@react-navigation/bottom-tabs@5.0.0-alpha.28) (2019-12-19)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.27](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.26...@react-navigation/bottom-tabs@5.0.0-alpha.27) (2019-12-16)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.26](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.25...@react-navigation/bottom-tabs@5.0.0-alpha.26) (2019-12-14)
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"android",
|
||||
"tab"
|
||||
],
|
||||
"version": "5.0.0-alpha.26",
|
||||
"version": "5.0.0-alpha.32",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -33,25 +33,26 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.0.0-alpha.16",
|
||||
"color": "^3.1.2"
|
||||
"@react-navigation/routers": "^5.0.0-alpha.20",
|
||||
"color": "^3.1.2",
|
||||
"react-native-iphone-x-helper": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.7.0",
|
||||
"@types/color": "^3.0.0",
|
||||
"@types/react": "^16.9.11",
|
||||
"@types/react-native": "^0.60.22",
|
||||
"@types/react": "^16.9.16",
|
||||
"@types/react-native": "^0.60.25",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-safe-area-context": "^0.6.0",
|
||||
"typescript": "^3.7.2"
|
||||
"typescript": "^3.7.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.0-alpha.0",
|
||||
"react": "*",
|
||||
"react-native": "*",
|
||||
"react-native-safe-area-context": "^0.3.6"
|
||||
"react-native-safe-area-context": "^0.6.0"
|
||||
},
|
||||
"@react-native-community/bob": {
|
||||
"source": "src",
|
||||
|
||||
@@ -14,7 +14,7 @@ const FAR_FAR_AWAY = 3000; // this should be big enough to move the whole view o
|
||||
|
||||
export default class ResourceSavingScene extends React.Component<Props> {
|
||||
render() {
|
||||
if (screensEnabled && screensEnabled()) {
|
||||
if (screensEnabled?.()) {
|
||||
const { isVisible, ...rest } = this.props;
|
||||
// @ts-ignore
|
||||
return <Screen active={isVisible ? 1 : 0} {...rest} />;
|
||||
|
||||
@@ -3,6 +3,17 @@ import {
|
||||
SafeAreaProvider,
|
||||
SafeAreaConsumer,
|
||||
} from 'react-native-safe-area-context';
|
||||
import {
|
||||
getStatusBarHeight,
|
||||
getBottomSpace,
|
||||
} from 'react-native-iphone-x-helper';
|
||||
|
||||
const initialSafeAreaInsets = {
|
||||
top: getStatusBarHeight(true),
|
||||
bottom: getBottomSpace(),
|
||||
right: 0,
|
||||
left: 0,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode;
|
||||
@@ -19,7 +30,11 @@ export default function SafeAreaProviderCompat({ children }: Props) {
|
||||
return children;
|
||||
}
|
||||
|
||||
return <SafeAreaProvider>{children}</SafeAreaProvider>;
|
||||
return (
|
||||
<SafeAreaProvider initialSafeAreaInsets={initialSafeAreaInsets}>
|
||||
{children}
|
||||
</SafeAreaProvider>
|
||||
);
|
||||
}}
|
||||
</SafeAreaConsumer>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.0.0-alpha.21](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/compat@5.0.0-alpha.20...@react-navigation/compat@5.0.0-alpha.21) (2020-01-05)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.20](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/compat@5.0.0-alpha.19...@react-navigation/compat@5.0.0-alpha.20) (2020-01-01)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.19](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/compat@5.0.0-alpha.18...@react-navigation/compat@5.0.0-alpha.19) (2019-12-19)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.18](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/compat@5.0.0-alpha.17...@react-navigation/compat@5.0.0-alpha.18) (2019-12-16)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.17](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/compat@5.0.0-alpha.16...@react-navigation/compat@5.0.0-alpha.17) (2019-12-11)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/compat",
|
||||
"description": "Compatibility layer to write navigator definitions in static configuration format",
|
||||
"version": "5.0.0-alpha.17",
|
||||
"version": "5.0.0-alpha.21",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -24,12 +24,12 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.0.0-alpha.16"
|
||||
"@react-navigation/routers": "^5.0.0-alpha.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^16.9.11",
|
||||
"@types/react": "^16.9.16",
|
||||
"react": "~16.9.0",
|
||||
"typescript": "^3.7.2"
|
||||
"typescript": "^3.7.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.0-alpha.0",
|
||||
|
||||
@@ -25,7 +25,7 @@ export function navigate({
|
||||
}
|
||||
|
||||
export function back(options?: { key?: null | string }) {
|
||||
return options && options.key != null
|
||||
return options?.key != null
|
||||
? (state: NavigationState) => ({
|
||||
...CommonActions.goBack(),
|
||||
source: options.key,
|
||||
|
||||
@@ -109,17 +109,17 @@ export default function createCompatNavigationProp<
|
||||
break;
|
||||
case 'didFocus': {
|
||||
const unsubscribe = focusSubscriptions.get(callback);
|
||||
unsubscribe && unsubscribe();
|
||||
unsubscribe?.();
|
||||
break;
|
||||
}
|
||||
case 'didBlur': {
|
||||
const unsubscribe = blurSubscriptions.get(callback);
|
||||
unsubscribe && unsubscribe();
|
||||
unsubscribe?.();
|
||||
break;
|
||||
}
|
||||
case 'refocus': {
|
||||
const unsubscribe = refocusSubscriptions.get(callback);
|
||||
unsubscribe && unsubscribe();
|
||||
unsubscribe?.();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -47,7 +47,7 @@ export default function createCompatNavigatorFactory<
|
||||
>(
|
||||
routeConfig: CompatRouteConfig<NavigationPropType>,
|
||||
navigationConfig: Partial<Omit<NavigationConfig, 'screenOptions'>> & {
|
||||
order?: Array<Extract<keyof ParamList, string>>;
|
||||
order?: Extract<keyof ParamList, string>[];
|
||||
defaultNavigationOptions?: ScreenOptions;
|
||||
navigationOptions?: Record<string, any>;
|
||||
} = {}
|
||||
|
||||
@@ -3,6 +3,42 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.0.0-alpha.30](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.29...@react-navigation/core@5.0.0-alpha.30) (2020-01-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* cleanup transaction even if action wasn't handled ([f462d67](https://github.com/react-navigation/navigation-ex/commit/f462d672708cabfb0477c3a48505bd194ea626fd))
|
||||
* show error if an action was not handled ([0252bdc](https://github.com/react-navigation/navigation-ex/commit/0252bdc2222ebe7410a0ed593bf03b2bdf5dc7ca))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.29](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.28...@react-navigation/core@5.0.0-alpha.29) (2019-12-19)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.28](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.27...@react-navigation/core@5.0.0-alpha.28) (2019-12-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use Partial type for initialParam ([#206](https://github.com/react-navigation/navigation-ex/issues/206)) ([c3d3748](https://github.com/react-navigation/navigation-ex/commit/c3d374814308b0bd6d259099444f0f24593f4d7e))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add nested config in deep linking ([#210](https://github.com/react-navigation/navigation-ex/issues/210)) ([8002d51](https://github.com/react-navigation/navigation-ex/commit/8002d5179524a7211c37760a4ed45e8c12af4358)), closes [#154](https://github.com/react-navigation/navigation-ex/issues/154)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.27](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.26...@react-navigation/core@5.0.0-alpha.27) (2019-12-10)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/core
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"react-native",
|
||||
"react-navigation"
|
||||
],
|
||||
"version": "5.0.0-alpha.27",
|
||||
"version": "5.0.0-alpha.30",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -35,15 +35,15 @@
|
||||
"use-subscription": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.7.2",
|
||||
"@babel/core": "^7.7.5",
|
||||
"@react-native-community/bob": "^0.7.0",
|
||||
"@types/react": "^16.9.11",
|
||||
"@types/react": "^16.9.16",
|
||||
"@types/shortid": "^0.0.29",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
"react-native-testing-library": "^1.9.1",
|
||||
"react-native-testing-library": "^1.12.0",
|
||||
"react-test-renderer": "~16.9.0",
|
||||
"typescript": "^3.7.2"
|
||||
"typescript": "^3.7.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "~16.9.0"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import { NavigationState, PartialState } from './types';
|
||||
|
||||
export type Action =
|
||||
|
||||
@@ -123,9 +123,11 @@ const Container = React.forwardRef(function NavigationContainer(
|
||||
isTransactionActiveRef.current = true;
|
||||
transactionStateRef.current = navigationState;
|
||||
|
||||
callback();
|
||||
|
||||
isTransactionActiveRef.current = false;
|
||||
try {
|
||||
callback();
|
||||
} finally {
|
||||
isTransactionActiveRef.current = false;
|
||||
}
|
||||
|
||||
return transactionStateRef.current;
|
||||
});
|
||||
@@ -202,7 +204,7 @@ const Container = React.forwardRef(function NavigationContainer(
|
||||
}, [getStateForRoute]);
|
||||
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
...(Object.keys(CommonActions) as Array<keyof typeof CommonActions>).reduce<
|
||||
...(Object.keys(CommonActions) as (keyof typeof CommonActions)[]).reduce<
|
||||
any
|
||||
>((acc, name) => {
|
||||
acc[name] = (...args: any[]) =>
|
||||
|
||||
75
packages/core/src/__tests__/getActionFromState.test.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import getActionFromState from '../getActionFromState';
|
||||
|
||||
it('gets navigate action from state', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'bar',
|
||||
params: { answer: 42 },
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getActionFromState(state)).toEqual({
|
||||
payload: {
|
||||
name: 'foo',
|
||||
params: {
|
||||
params: {
|
||||
answer: 42,
|
||||
params: {
|
||||
author: 'jane',
|
||||
},
|
||||
screen: 'qux',
|
||||
},
|
||||
screen: 'bar',
|
||||
},
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets reset action from state', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'bar',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
{ name: 'quz' },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getActionFromState(state)).toEqual({
|
||||
payload: state,
|
||||
type: 'RESET_ROOT',
|
||||
});
|
||||
});
|
||||
@@ -127,3 +127,238 @@ it('handles route without param', () => {
|
||||
it('returns undefined for invalid path', () => {
|
||||
expect(getStateFromPath('//')).toBe(undefined);
|
||||
});
|
||||
|
||||
it('converts path string to initial state with config with nested screens', () => {
|
||||
expect(
|
||||
getStateFromPath(
|
||||
'/few/bar/sweet/apple/baz/jane?count=10&answer=42&valid=true',
|
||||
{
|
||||
Foo: {
|
||||
Foe: 'few',
|
||||
},
|
||||
Bar: 'bar/:type/:fruit',
|
||||
Baz: {
|
||||
path: 'baz/:author',
|
||||
parse: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
).toEqual({
|
||||
routes: [
|
||||
{
|
||||
name: 'Foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Foe',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Bar',
|
||||
params: { fruit: 'apple', type: 'sweet' },
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Baz',
|
||||
params: {
|
||||
author: 'Jane',
|
||||
count: 10,
|
||||
answer: '42',
|
||||
valid: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('converts path string to initial state with config with nested screens and unused configs', () => {
|
||||
expect(
|
||||
getStateFromPath('/few/baz/jane?count=10&answer=42&valid=true', {
|
||||
Foo: {
|
||||
Foe: 'few',
|
||||
},
|
||||
Baz: {
|
||||
path: 'baz/:author',
|
||||
parse: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
routes: [
|
||||
{
|
||||
name: 'Foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Foe',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Baz',
|
||||
params: {
|
||||
author: 'Jane',
|
||||
count: 10,
|
||||
answer: '42',
|
||||
valid: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('handles parse in nested object with parse in it', () => {
|
||||
expect(
|
||||
getStateFromPath(
|
||||
'/bar/sweet/apple/few/bis/jane?count=10&answer=42&valid=true',
|
||||
{
|
||||
Foo: {
|
||||
Foe: 'few',
|
||||
},
|
||||
Bar: 'bar/:type/:fruit',
|
||||
Baz: {
|
||||
Bis: {
|
||||
path: 'bis/:author',
|
||||
parse: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
).toEqual({
|
||||
routes: [
|
||||
{
|
||||
name: 'Bar',
|
||||
params: { fruit: 'apple', type: 'sweet' },
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Foe',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Baz',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Bis',
|
||||
params: {
|
||||
author: 'Jane',
|
||||
count: 10,
|
||||
answer: '42',
|
||||
valid: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('handles parse in nested object for second route depth', () => {
|
||||
expect(
|
||||
getStateFromPath('/baz', {
|
||||
Foo: {
|
||||
path: 'foo',
|
||||
Foe: 'foe',
|
||||
Bar: {
|
||||
Baz: 'baz',
|
||||
},
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
routes: [
|
||||
{
|
||||
name: 'Foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Bar',
|
||||
state: {
|
||||
routes: [{ name: 'Baz' }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('handles parse in nested object for second route depth and and path and parse in roots', () => {
|
||||
expect(
|
||||
getStateFromPath('/baz', {
|
||||
Foo: {
|
||||
path: 'foo/:id',
|
||||
parse: {
|
||||
id: Number,
|
||||
},
|
||||
Foe: 'foe',
|
||||
Bar: {
|
||||
path: 'bar/:id',
|
||||
parse: {
|
||||
id: Number,
|
||||
},
|
||||
Baz: 'baz',
|
||||
},
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
routes: [
|
||||
{
|
||||
name: 'Foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Bar',
|
||||
state: {
|
||||
routes: [{ name: 'Baz' }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -357,6 +357,8 @@ it("doesn't update state if action wasn't handled", () => {
|
||||
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const spy = jest.spyOn(console, 'error').mockImplementation();
|
||||
|
||||
render(
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator initialRouteName="foo">
|
||||
@@ -367,6 +369,12 @@ it("doesn't update state if action wasn't handled", () => {
|
||||
);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(0);
|
||||
|
||||
expect(spy.mock.calls[0][0]).toMatch(
|
||||
"The action 'INVALID' with payload 'undefined' was not handled by any navigator."
|
||||
);
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('cleans up state when the navigator unmounts', () => {
|
||||
|
||||
@@ -305,8 +305,7 @@ it("action doesn't bubble if target is specified", () => {
|
||||
expect(onStateChange).not.toBeCalled();
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it("doesn't crash if no navigator handled the action", () => {
|
||||
it('logs error if no navigator handled the action', () => {
|
||||
const TestRouter = MockRouter;
|
||||
|
||||
const TestNavigator = (props: any) => {
|
||||
@@ -366,5 +365,13 @@ it("doesn't crash if no navigator handled the action", () => {
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
||||
const spy = jest.spyOn(console, 'error').mockImplementation();
|
||||
|
||||
render(element).update(element);
|
||||
|
||||
expect(spy.mock.calls[0][0]).toMatch(
|
||||
"The action 'UNKNOWN' with payload 'undefined' was not handled by any navigator."
|
||||
);
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
@@ -19,7 +19,7 @@ it('gets route prop from context', () => {
|
||||
const Test = () => {
|
||||
const route = useRoute<RouteProp<{ sample: { x: string } }, 'sample'>>();
|
||||
|
||||
expect(route && route.params && route.params.x).toEqual(1);
|
||||
expect(route?.params?.x).toEqual(1);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
67
packages/core/src/getActionFromState.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import { PartialState, NavigationState } from './types';
|
||||
|
||||
type NavigateParams = {
|
||||
screen?: string;
|
||||
params?: NavigateParams;
|
||||
};
|
||||
|
||||
type Action =
|
||||
| {
|
||||
type: 'NAVIGATE';
|
||||
payload: { name: string; params: NavigateParams };
|
||||
}
|
||||
| {
|
||||
type: 'RESET_ROOT';
|
||||
payload: PartialState<NavigationState>;
|
||||
};
|
||||
|
||||
export default function getActionFromState(
|
||||
state: PartialState<NavigationState>
|
||||
): Action {
|
||||
let payload: { name: string; params: NavigateParams } | undefined;
|
||||
|
||||
if (state.routes.length === 1) {
|
||||
// Try to construct payload for a `NAVIGATE` action from the state
|
||||
// This lets us preserve the navigation state and not lose it
|
||||
let route = state.routes[0];
|
||||
|
||||
payload = {
|
||||
name: route.name,
|
||||
params: { ...route.params },
|
||||
};
|
||||
|
||||
let current = state.routes[0].state;
|
||||
let params = payload.params;
|
||||
|
||||
while (current) {
|
||||
if (current.routes.length === 1) {
|
||||
route = current.routes[0];
|
||||
params.screen = route.name;
|
||||
|
||||
if (route.state) {
|
||||
params.params = { ...route.params };
|
||||
params = params.params;
|
||||
} else {
|
||||
params.params = route.params;
|
||||
}
|
||||
|
||||
current = route.state;
|
||||
} else {
|
||||
payload = undefined;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (payload) {
|
||||
return {
|
||||
type: 'NAVIGATE',
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'RESET_ROOT',
|
||||
payload: state,
|
||||
};
|
||||
}
|
||||
@@ -60,7 +60,7 @@ export default function getPathFromState(
|
||||
Object.entries(route.params).reduce<{
|
||||
[key: string]: string;
|
||||
}>((acc, [key, value]) => {
|
||||
acc[key] = config && config[key] ? config[key](value) : String(value);
|
||||
acc[key] = config?.[key] ? config[key](value) : String(value);
|
||||
return acc;
|
||||
}, {})
|
||||
: undefined;
|
||||
@@ -80,6 +80,7 @@ export default function getPathFromState(
|
||||
if (params && name in params && p.startsWith(':')) {
|
||||
const value = params[name];
|
||||
// Remove the used value from the params object since we'll use the rest for query string
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete params[name];
|
||||
return encodeURIComponent(value);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
import escape from 'escape-string-regexp';
|
||||
import queryString from 'query-string';
|
||||
import { NavigationState, PartialState } from './types';
|
||||
import { NavigationState, PartialState, InitialState } from './types';
|
||||
|
||||
type ParseConfig = Record<string, (value: string) => any>;
|
||||
|
||||
type Options = {
|
||||
[routeName: string]: string | { path: string; parse?: ParseConfig };
|
||||
[routeName: string]: string | { path: string; parse?: ParseConfig } | Options;
|
||||
};
|
||||
|
||||
type RouteConfig = {
|
||||
match: RegExp;
|
||||
pattern: string;
|
||||
routeNames: string[];
|
||||
parse: Record<string, (value: string) => any> | undefined;
|
||||
};
|
||||
|
||||
type ResultState = PartialState<NavigationState> & {
|
||||
state?: ResultState;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -30,28 +41,11 @@ type Options = {
|
||||
export default function getStateFromPath(
|
||||
path: string,
|
||||
options: Options = {}
|
||||
): PartialState<NavigationState> | undefined {
|
||||
// Create a normalized config array which will be easier to use
|
||||
const routeConfig = Object.keys(options).map(key => {
|
||||
const pattern =
|
||||
typeof options[key] === 'string'
|
||||
? (options[key] as string)
|
||||
: (options[key] as { path: string }).path;
|
||||
|
||||
// Create a regex from the provided path pattern
|
||||
// With the pattern, we can match segements containing params and extract them
|
||||
const match = new RegExp(
|
||||
'^' + escape(pattern).replace(/:[a-z0-9]+/gi, '([^/]+)') + '/?'
|
||||
);
|
||||
|
||||
return {
|
||||
match,
|
||||
pattern,
|
||||
routeName: key,
|
||||
// @ts-ignore
|
||||
parse: options[key].parse,
|
||||
};
|
||||
});
|
||||
): ResultState | undefined {
|
||||
// Create a normalized configs array which will be easier to use
|
||||
const configs = ([] as RouteConfig[]).concat(
|
||||
...Object.keys(options).map(key => createNormalizedConfigs(key, options))
|
||||
);
|
||||
|
||||
let result: PartialState<NavigationState> | undefined;
|
||||
let current: PartialState<NavigationState> | undefined;
|
||||
@@ -62,16 +56,16 @@ export default function getStateFromPath(
|
||||
.replace(/\?.*/, ''); // Remove query params which we will handle later
|
||||
|
||||
while (remaining) {
|
||||
let routeName;
|
||||
let routeNames;
|
||||
let params;
|
||||
|
||||
// Go through all configs, and see if the next path segment matches our regex
|
||||
for (const config of routeConfig) {
|
||||
for (const config of configs) {
|
||||
const match = remaining.match(config.match);
|
||||
|
||||
// If our regex matches, we need to extract params from the path
|
||||
if (match) {
|
||||
routeName = config.routeName;
|
||||
routeNames = config.routeNames;
|
||||
|
||||
const paramPatterns = config.pattern
|
||||
.split('/')
|
||||
@@ -99,20 +93,54 @@ export default function getStateFromPath(
|
||||
}
|
||||
|
||||
// If we hadn't matched any segments earlier, use the path as route name
|
||||
if (routeName === undefined) {
|
||||
if (routeNames === undefined) {
|
||||
const segments = remaining.split('/');
|
||||
|
||||
routeName = decodeURIComponent(segments[0]);
|
||||
routeNames = [decodeURIComponent(segments[0])];
|
||||
segments.shift();
|
||||
remaining = segments.join('/');
|
||||
}
|
||||
|
||||
const state = {
|
||||
routes: [{ name: routeName, params }],
|
||||
};
|
||||
let state: InitialState;
|
||||
|
||||
if (routeNames.length === 1) {
|
||||
state = {
|
||||
routes: [
|
||||
{ name: routeNames.shift() as string, ...(params && { params }) },
|
||||
],
|
||||
};
|
||||
} else {
|
||||
state = {
|
||||
routes: [{ name: routeNames.shift() as string, state: { routes: [] } }],
|
||||
};
|
||||
|
||||
let helper = state.routes[0].state as InitialState;
|
||||
let routeName;
|
||||
|
||||
while ((routeName = routeNames.shift())) {
|
||||
if (routeNames.length === 0) {
|
||||
helper.routes.push({
|
||||
name: routeName,
|
||||
...(params && { params }),
|
||||
});
|
||||
} else {
|
||||
helper.routes[0] = {
|
||||
name: routeName,
|
||||
state: {
|
||||
routes: [],
|
||||
},
|
||||
};
|
||||
helper = helper.routes[0].state as InitialState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current) {
|
||||
// The state should be nested inside the route we parsed before
|
||||
// The state should be nested inside the deepest route we parsed before
|
||||
while (current.routes[0].state) {
|
||||
current = current.routes[0].state;
|
||||
}
|
||||
|
||||
current.routes[0].state = state;
|
||||
} else {
|
||||
result = state;
|
||||
@@ -128,17 +156,20 @@ export default function getStateFromPath(
|
||||
const query = path.split('?')[1];
|
||||
|
||||
if (query) {
|
||||
while (current.routes[0].state) {
|
||||
// The query params apply to the deepest route
|
||||
current = current.routes[0].state;
|
||||
}
|
||||
|
||||
const route = current.routes[0];
|
||||
|
||||
const params = queryString.parse(query);
|
||||
const config = options[route.name]
|
||||
? (options[route.name] as { parse?: ParseConfig }).parse
|
||||
: undefined;
|
||||
const parseFunction = findParseConfigForRoute(route.name, options);
|
||||
|
||||
if (config) {
|
||||
if (parseFunction) {
|
||||
Object.keys(params).forEach(name => {
|
||||
if (config[name] && typeof params[name] === 'string') {
|
||||
params[name] = config[name](params[name] as string);
|
||||
if (parseFunction[name] && typeof params[name] === 'string') {
|
||||
params[name] = parseFunction[name](params[name] as string);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -148,3 +179,89 @@ export default function getStateFromPath(
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function createNormalizedConfigs(
|
||||
key: string,
|
||||
routeConfig: Options,
|
||||
routeNames: string[] = []
|
||||
): RouteConfig[] {
|
||||
const configs = [];
|
||||
|
||||
routeNames.push(key);
|
||||
|
||||
const value = routeConfig[key];
|
||||
|
||||
if (typeof value === 'string') {
|
||||
// If a string is specified as the value of the key(e.g. Foo: '/path'), use it as the pattern
|
||||
configs.push(createConfigItem(routeNames, value));
|
||||
} else if (typeof value === 'object') {
|
||||
// if an object is specified as the value (e.g. Foo: { ... }),
|
||||
// it could have config object and optionally nested config
|
||||
Object.keys(value).forEach(nestedKey => {
|
||||
if (nestedKey === 'path') {
|
||||
configs.push(
|
||||
createConfigItem(
|
||||
routeNames,
|
||||
value[nestedKey] as string,
|
||||
value.parse ? (value.parse as ParseConfig) : undefined
|
||||
)
|
||||
);
|
||||
} else if (nestedKey === 'parse') {
|
||||
// We handle custom parse function when a `path` is specified (in nestedKey === path)
|
||||
} else {
|
||||
// If the name of the key is not `path` or `parse`, it's a nested config for route
|
||||
// So we need to traverse into it and collect the configs
|
||||
const result = createNormalizedConfigs(
|
||||
nestedKey,
|
||||
routeConfig[key] as Options,
|
||||
routeNames
|
||||
);
|
||||
|
||||
configs.push(...result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
routeNames.pop();
|
||||
|
||||
return configs;
|
||||
}
|
||||
|
||||
function createConfigItem(
|
||||
routeNames: string[],
|
||||
pattern: string,
|
||||
parse?: ParseConfig
|
||||
): RouteConfig {
|
||||
const match = new RegExp(
|
||||
'^' + escape(pattern).replace(/:[a-z0-9]+/gi, '([^/]+)') + '/?'
|
||||
);
|
||||
|
||||
return {
|
||||
match,
|
||||
pattern,
|
||||
// The routeNames array is mutated, so copy it to keep the current state
|
||||
routeNames: [...routeNames],
|
||||
parse,
|
||||
};
|
||||
}
|
||||
|
||||
function findParseConfigForRoute(
|
||||
routeName: string,
|
||||
config: Options
|
||||
): ParseConfig | undefined {
|
||||
if (config[routeName]) {
|
||||
return (config[routeName] as { parse?: ParseConfig }).parse;
|
||||
}
|
||||
|
||||
for (const name in config) {
|
||||
if (typeof config[name] === 'object') {
|
||||
const parse = findParseConfigForRoute(routeName, config[name] as Options);
|
||||
|
||||
if (parse) {
|
||||
return parse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -17,5 +17,6 @@ export { default as useIsFocused } from './useIsFocused';
|
||||
|
||||
export { default as getStateFromPath } from './getStateFromPath';
|
||||
export { default as getPathFromState } from './getPathFromState';
|
||||
export { default as getActionFromState } from './getActionFromState';
|
||||
|
||||
export * from './types';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import * as CommonActions from './CommonActions';
|
||||
import * as React from 'react';
|
||||
|
||||
@@ -20,9 +19,9 @@ export type NavigationState = {
|
||||
/**
|
||||
* List of rendered routes.
|
||||
*/
|
||||
routes: Array<
|
||||
Route<string> & { state?: NavigationState | PartialState<NavigationState> }
|
||||
>;
|
||||
routes: (Route<string> & {
|
||||
state?: NavigationState | PartialState<NavigationState>;
|
||||
})[];
|
||||
/**
|
||||
* Custom type for the state, whether it's for tab, stack, drawer etc.
|
||||
* During rehydration, the state will be discarded if type doesn't match with router type.
|
||||
@@ -38,7 +37,7 @@ export type NavigationState = {
|
||||
export type InitialState = Partial<
|
||||
Omit<NavigationState, 'stale' | 'routes'>
|
||||
> & {
|
||||
routes: Array<Omit<Route<string>, 'key'> & { state?: InitialState }>;
|
||||
routes: (Omit<Route<string>, 'key'> & { state?: InitialState })[];
|
||||
};
|
||||
|
||||
export type PartialState<State extends NavigationState> = Partial<
|
||||
@@ -46,9 +45,10 @@ export type PartialState<State extends NavigationState> = Partial<
|
||||
> & {
|
||||
stale?: true;
|
||||
type?: string;
|
||||
routes: Array<
|
||||
Omit<Route<string>, 'key'> & { key?: string; state?: InitialState }
|
||||
>;
|
||||
routes: (Omit<Route<string>, 'key'> & {
|
||||
key?: string;
|
||||
state?: InitialState;
|
||||
})[];
|
||||
};
|
||||
|
||||
export type Route<RouteName extends string> = {
|
||||
@@ -71,6 +71,10 @@ export type NavigationAction = {
|
||||
* Type of the action (e.g. `NAVIGATE`)
|
||||
*/
|
||||
type: string;
|
||||
/**
|
||||
* Additional data for the action
|
||||
*/
|
||||
payload?: object;
|
||||
/**
|
||||
* Key of the route which dispatched this action.
|
||||
*/
|
||||
@@ -533,7 +537,7 @@ export type RouteConfig<
|
||||
/**
|
||||
* Initial params object for the route.
|
||||
*/
|
||||
initialParams?: ParamList[RouteName];
|
||||
initialParams?: Partial<ParamList[RouteName]>;
|
||||
} & (
|
||||
| {
|
||||
/**
|
||||
|
||||
@@ -44,16 +44,15 @@ export default function useDevTools({ name, reset, state }: Options) {
|
||||
|
||||
const devTools = devToolsRef.current;
|
||||
const lastStateRef = React.useRef<State>(state);
|
||||
const actions = React.useRef<Array<NavigationAction | string>>([]);
|
||||
const actions = React.useRef<(NavigationAction | string)[]>([]);
|
||||
|
||||
React.useEffect(() => {
|
||||
devTools && devTools.init(lastStateRef.current);
|
||||
devTools?.init(lastStateRef.current);
|
||||
}, [devTools]);
|
||||
|
||||
React.useEffect(
|
||||
() =>
|
||||
devTools &&
|
||||
devTools.subscribe(message => {
|
||||
devTools?.subscribe(message => {
|
||||
if (message.type === 'DISPATCH' && message.state) {
|
||||
reset(JSON.parse(message.state));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ export type NavigationEventEmitter = EventEmitter<Record<string, any>> & {
|
||||
create: (target: string) => EventConsumer<Record<string, any>>;
|
||||
};
|
||||
|
||||
type Listeners = Array<(data: any) => void>;
|
||||
type Listeners = ((data: any) => void)[];
|
||||
|
||||
/**
|
||||
* Hook to manage the event system used by the navigator to notify screens of various events.
|
||||
@@ -69,7 +69,7 @@ export default function useEventEmitter(): NavigationEventEmitter {
|
||||
},
|
||||
};
|
||||
|
||||
callbacks && callbacks.forEach(cb => cb(event));
|
||||
callbacks?.forEach(cb => cb(event));
|
||||
|
||||
return event;
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import useNavigation from './useNavigation';
|
||||
|
||||
type EffectCallback = (() => void) | (() => () => void);
|
||||
type EffectCallback = (() => undefined) | (() => () => void);
|
||||
|
||||
/**
|
||||
* Hook to run an effect in a focused screen, similar to `React.useEffect`.
|
||||
@@ -15,7 +15,7 @@ export default function useFocusEffect(callback: EffectCallback) {
|
||||
|
||||
React.useEffect(() => {
|
||||
let isFocused = false;
|
||||
let cleanup: (() => void) | void;
|
||||
let cleanup: (() => void) | undefined;
|
||||
|
||||
// We need to run the effect on intial render/dep changes if the screen is focused
|
||||
if (navigation.isFocused()) {
|
||||
@@ -30,19 +30,19 @@ export default function useFocusEffect(callback: EffectCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
cleanup && cleanup();
|
||||
cleanup?.();
|
||||
cleanup = callback();
|
||||
isFocused = true;
|
||||
});
|
||||
|
||||
const unsubscribeBlur = navigation.addListener('blur', () => {
|
||||
cleanup && cleanup();
|
||||
cleanup?.();
|
||||
cleanup = undefined;
|
||||
isFocused = false;
|
||||
});
|
||||
|
||||
return () => {
|
||||
cleanup && cleanup();
|
||||
cleanup?.();
|
||||
unsubscribeFocus();
|
||||
unsubscribeBlur();
|
||||
};
|
||||
|
||||
@@ -21,8 +21,7 @@ export default function useFocusEvents({ state, emitter }: Options) {
|
||||
// Coz the child screen can't be focused if the parent screen is out of fcous
|
||||
React.useEffect(
|
||||
() =>
|
||||
navigation &&
|
||||
navigation.addListener('focus', () =>
|
||||
navigation?.addListener('focus', () =>
|
||||
emitter.emit({ type: 'focus', target: currentFocusedKey })
|
||||
),
|
||||
[currentFocusedKey, emitter, navigation]
|
||||
@@ -30,8 +29,7 @@ export default function useFocusEvents({ state, emitter }: Options) {
|
||||
|
||||
React.useEffect(
|
||||
() =>
|
||||
navigation &&
|
||||
navigation.addListener('blur', () =>
|
||||
navigation?.addListener('blur', () =>
|
||||
emitter.emit({ type: 'blur', target: currentFocusedKey })
|
||||
),
|
||||
[currentFocusedKey, emitter, navigation]
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function useFocusedListenersChildrenAdapter({
|
||||
[focusedListeners, navigation]
|
||||
);
|
||||
|
||||
React.useEffect(() => addFocusedListener && addFocusedListener(listener), [
|
||||
React.useEffect(() => addFocusedListener?.(listener), [
|
||||
addFocusedListener,
|
||||
listener,
|
||||
]);
|
||||
|
||||
@@ -117,7 +117,7 @@ export default function useNavigationBuilder<
|
||||
const { current: router } = React.useRef<Router<State, any>>(
|
||||
createRouter({
|
||||
...((rest as unknown) as RouterOptions),
|
||||
...(route && route.params && typeof route.params.screen === 'string'
|
||||
...(route?.params && typeof route.params.screen === 'string'
|
||||
? { initialRouteName: route.params.screen }
|
||||
: null),
|
||||
})
|
||||
@@ -141,7 +141,7 @@ export default function useNavigationBuilder<
|
||||
(acc, curr) => {
|
||||
const { initialParams } = screens[curr];
|
||||
const initialParamsFromParams =
|
||||
route && route.params && route.params.screen === curr
|
||||
route?.params && route.params.screen === curr
|
||||
? route.params.params
|
||||
: undefined;
|
||||
|
||||
|
||||
@@ -42,13 +42,22 @@ export default function useNavigationHelpers<
|
||||
const { performTransaction } = React.useContext(NavigationStateContext);
|
||||
|
||||
return React.useMemo(() => {
|
||||
const dispatch = (action: Action | ((state: State) => Action)) =>
|
||||
const dispatch = (action: Action | ((state: State) => Action)) => {
|
||||
performTransaction(() => {
|
||||
const payload =
|
||||
typeof action === 'function' ? action(getState()) : action;
|
||||
|
||||
onAction(payload);
|
||||
const handled = onAction(payload);
|
||||
|
||||
if (!handled && process.env.NODE_ENV !== 'production') {
|
||||
console.error(
|
||||
`The action '${payload.type}' with payload '${JSON.stringify(
|
||||
payload.payload
|
||||
)}' was not handled by any navigator.`
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const actions = {
|
||||
...router.actionCreators,
|
||||
@@ -81,7 +90,7 @@ export default function useNavigationHelpers<
|
||||
routeNames: state.routeNames,
|
||||
routeParamList: {},
|
||||
}) !== null ||
|
||||
(parentNavigationHelpers && parentNavigationHelpers.canGoBack()) ||
|
||||
parentNavigationHelpers?.canGoBack() ||
|
||||
false
|
||||
);
|
||||
},
|
||||
|
||||
@@ -130,10 +130,10 @@ export default function useOnAction({
|
||||
]
|
||||
);
|
||||
|
||||
React.useEffect(
|
||||
() => addActionListenerParent && addActionListenerParent(onAction),
|
||||
[addActionListenerParent, onAction]
|
||||
);
|
||||
React.useEffect(() => addActionListenerParent?.(onAction), [
|
||||
addActionListenerParent,
|
||||
onAction,
|
||||
]);
|
||||
|
||||
return onAction;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,6 @@ export default function useOnGetState({
|
||||
}, [getState, getStateForRoute]);
|
||||
|
||||
React.useEffect(() => {
|
||||
return addStateGetter && addStateGetter(key, getRehydratedState);
|
||||
return addStateGetter?.(key, getRehydratedState);
|
||||
}, [addStateGetter, getRehydratedState, key]);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,68 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.0.0-alpha.34](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.33...@react-navigation/drawer@5.0.0-alpha.34) (2020-01-05)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.33](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.32...@react-navigation/drawer@5.0.0-alpha.33) (2020-01-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* provide initial values for safe area to prevent blank screen ([#238](https://github.com/react-navigation/navigation-ex/issues/238)) ([77b7570](https://github.com/react-navigation/navigation-ex/commit/77b757091c0451e20bca01138629669c7da544a8))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add interaction handle to drawer ([#239](https://github.com/react-navigation/navigation-ex/issues/239)) ([fa4411a](https://github.com/react-navigation/navigation-ex/commit/fa4411a14dc4aae568794e4b884088e3276a2876))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.32](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.31...@react-navigation/drawer@5.0.0-alpha.32) (2020-01-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* pass backBehavior to the router in drawer. fixes [#230](https://github.com/react-navigation/navigation-ex/issues/230) ([3cd1aed](https://github.com/react-navigation/navigation-ex/commit/3cd1aedcf490a4c7962b2d36873d714637f3b9b0))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.31](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.30...@react-navigation/drawer@5.0.0-alpha.31) (2020-01-01)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.30](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.29...@react-navigation/drawer@5.0.0-alpha.30) (2019-12-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* set screen background in drawer from theme ([0635365](https://github.com/react-navigation/navigation-ex/commit/0635365483bf5ac38e75191b4ba8f52cf6d73896))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.29](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.28...@react-navigation/drawer@5.0.0-alpha.29) (2019-12-16)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.28](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.27...@react-navigation/drawer@5.0.0-alpha.28) (2019-12-14)
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"material",
|
||||
"drawer"
|
||||
],
|
||||
"version": "5.0.0-alpha.28",
|
||||
"version": "5.0.0-alpha.34",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -34,21 +34,22 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.0.0-alpha.16",
|
||||
"color": "^3.1.2"
|
||||
"@react-navigation/routers": "^5.0.0-alpha.20",
|
||||
"color": "^3.1.2",
|
||||
"react-native-iphone-x-helper": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.7.0",
|
||||
"@types/react": "^16.9.11",
|
||||
"@types/react-native": "^0.60.22",
|
||||
"@types/react": "^16.9.16",
|
||||
"@types/react-native": "^0.60.25",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-gesture-handler": "^1.5.0",
|
||||
"react-native-reanimated": "^1.4.0",
|
||||
"react-native-safe-area-context": "^0.6.0",
|
||||
"react-native-screens": "^2.0.0-alpha.11",
|
||||
"typescript": "^3.7.2"
|
||||
"react-native-screens": "^2.0.0-alpha.19",
|
||||
"typescript": "^3.7.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.0-alpha.0",
|
||||
@@ -56,7 +57,7 @@
|
||||
"react-native": "*",
|
||||
"react-native-gesture-handler": "^1.0.0",
|
||||
"react-native-reanimated": "^1.0.0",
|
||||
"react-native-safe-area-context": "^0.3.6",
|
||||
"react-native-safe-area-context": "^0.6.0",
|
||||
"react-native-screens": "^1.0.0-alpha.0 || ^2.0.0-alpha.0"
|
||||
},
|
||||
"@react-native-community/bob": {
|
||||
|
||||
@@ -19,10 +19,11 @@ import {
|
||||
|
||||
type Props = DefaultNavigatorOptions<DrawerNavigationOptions> &
|
||||
DrawerRouterOptions &
|
||||
Partial<DrawerNavigationConfig>;
|
||||
DrawerNavigationConfig;
|
||||
|
||||
function DrawerNavigator({
|
||||
initialRouteName,
|
||||
backBehavior,
|
||||
children,
|
||||
screenOptions,
|
||||
...rest
|
||||
@@ -34,6 +35,7 @@ function DrawerNavigator({
|
||||
DrawerNavigationEventMap
|
||||
>(DrawerRouter, {
|
||||
initialRouteName,
|
||||
backBehavior,
|
||||
children,
|
||||
screenOptions,
|
||||
});
|
||||
|
||||
@@ -20,14 +20,14 @@ export type DrawerNavigationConfig<T = DrawerContentOptions> = {
|
||||
/**
|
||||
* Position of the drawer on the screen. Defaults to `left`.
|
||||
*/
|
||||
drawerPosition: 'left' | 'right';
|
||||
drawerPosition?: 'left' | 'right';
|
||||
/**
|
||||
* Type of the drawer. It determines how the drawer looks and animates.
|
||||
* - `front`: Traditional drawer which covers the screen with a overlay behind it.
|
||||
* - `back`: The drawer is revealed behind the screen on swipe.
|
||||
* - `slide`: Both the screen and the drawer slide on swipe to reveal the drawer.
|
||||
*/
|
||||
drawerType: 'front' | 'back' | 'slide';
|
||||
drawerType?: 'front' | 'back' | 'slide';
|
||||
/**
|
||||
* How far from the edge of the screen the swipe gesture should activate.
|
||||
*/
|
||||
@@ -35,12 +35,12 @@ export type DrawerNavigationConfig<T = DrawerContentOptions> = {
|
||||
/**
|
||||
* Whether the statusbar should be hidden when the drawer is pulled or opens,
|
||||
*/
|
||||
hideStatusBar: boolean;
|
||||
hideStatusBar?: boolean;
|
||||
/**
|
||||
* Whether the keyboard should be dismissed when the swipe gesture begins.
|
||||
* Defaults to `'on-drag'`. Set to `'none'` to disable keyboard handling.
|
||||
*/
|
||||
keyboardDismissMode: 'on-drag' | 'none';
|
||||
keyboardDismissMode?: 'on-drag' | 'none';
|
||||
/**
|
||||
* Minimum swipe distance threshold that should activate opening the drawer.
|
||||
*/
|
||||
@@ -53,7 +53,7 @@ export type DrawerNavigationConfig<T = DrawerContentOptions> = {
|
||||
/**
|
||||
* Animation of the statusbar when hiding it. use in combination with `hideStatusBar`.
|
||||
*/
|
||||
statusBarAnimation: 'slide' | 'none' | 'fade';
|
||||
statusBarAnimation?: 'slide' | 'none' | 'fade';
|
||||
/**
|
||||
* Props to pass to the underlying pan gesture handler.
|
||||
*/
|
||||
@@ -62,7 +62,7 @@ export type DrawerNavigationConfig<T = DrawerContentOptions> = {
|
||||
* Whether the screens should render the first time they are accessed. Defaults to `true`.
|
||||
* Set it to `false` if you want to render all screens on initial render.
|
||||
*/
|
||||
lazy: boolean;
|
||||
lazy?: boolean;
|
||||
/**
|
||||
* Whether a screen should be unmounted when navigating away from it.
|
||||
* Defaults to `false`.
|
||||
@@ -72,7 +72,7 @@ export type DrawerNavigationConfig<T = DrawerContentOptions> = {
|
||||
* Function that returns React element to render as the content of the drawer, for example, navigation items.
|
||||
* Defaults to `DrawerContent`.
|
||||
*/
|
||||
drawerContent: (props: DrawerContentComponentProps<T>) => React.ReactNode;
|
||||
drawerContent?: (props: DrawerContentComponentProps<T>) => React.ReactNode;
|
||||
/**
|
||||
* Options for the content component which will be passed as props.
|
||||
*/
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
StatusBar,
|
||||
StyleProp,
|
||||
View,
|
||||
InteractionManager,
|
||||
} from 'react-native';
|
||||
import {
|
||||
PanGestureHandler,
|
||||
@@ -161,9 +162,24 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
|
||||
componentWillUnmount() {
|
||||
this.toggleStatusBar(false);
|
||||
this.handleEndInteraction();
|
||||
}
|
||||
|
||||
private handleEndInteraction = () => {
|
||||
if (this.interactionHandle !== undefined) {
|
||||
InteractionManager.clearInteractionHandle(this.interactionHandle);
|
||||
this.interactionHandle = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
private handleStartInteraction = () => {
|
||||
if (this.interactionHandle === undefined) {
|
||||
this.interactionHandle = InteractionManager.createInteractionHandle();
|
||||
}
|
||||
};
|
||||
|
||||
private clock = new Clock();
|
||||
private interactionHandle: number | undefined;
|
||||
|
||||
private isDrawerTypeFront = new Value<Binary>(
|
||||
this.props.drawerType === 'front' ? TRUE : FALSE
|
||||
@@ -277,6 +293,7 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
set(state.velocity, this.velocityX),
|
||||
set(this.isOpen, isOpen),
|
||||
startClock(this.clock),
|
||||
call([], this.handleStartInteraction),
|
||||
set(this.manuallyTriggerSpring, FALSE),
|
||||
]),
|
||||
spring(this.clock, state, { ...SPRING_CONFIG, toValue }),
|
||||
@@ -288,8 +305,9 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
set(this.offsetX, 0),
|
||||
// When the animation finishes, stop the clock
|
||||
stopClock(this.clock),
|
||||
call([this.isOpen], ([value]: ReadonlyArray<Binary>) => {
|
||||
call([this.isOpen], ([value]: readonly Binary[]) => {
|
||||
const open = Boolean(value);
|
||||
this.handleEndInteraction();
|
||||
|
||||
if (open !== this.props.open) {
|
||||
// Sync drawer's state after animation finished
|
||||
@@ -304,7 +322,7 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
private dragX = block([
|
||||
onChange(
|
||||
this.isOpen,
|
||||
call([this.isOpen], ([value]: ReadonlyArray<Binary>) => {
|
||||
call([this.isOpen], ([value]: readonly Binary[]) => {
|
||||
const open = Boolean(value);
|
||||
|
||||
this.currentOpenValue = open;
|
||||
@@ -344,7 +362,7 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
// Listen to updates for this value only when it changes
|
||||
// Without `onChange`, this will fire even if the value didn't change
|
||||
// We don't want to call the listeners if the value didn't change
|
||||
call([this.isSwiping], ([value]: ReadonlyArray<Binary>) => {
|
||||
call([this.isSwiping], ([value]: readonly Binary[]) => {
|
||||
const { keyboardDismissMode } = this.props;
|
||||
|
||||
if (value === TRUE) {
|
||||
@@ -358,6 +376,13 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
}
|
||||
})
|
||||
),
|
||||
onChange(
|
||||
this.gestureState,
|
||||
cond(
|
||||
eq(this.gestureState, State.ACTIVE),
|
||||
call([], this.handleStartInteraction)
|
||||
)
|
||||
),
|
||||
cond(
|
||||
eq(this.gestureState, State.ACTIVE),
|
||||
[
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { ScrollView, StyleSheet, ScrollViewProps } from 'react-native';
|
||||
import { useSafeArea } from 'react-native-safe-area-context';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
|
||||
type Props = ScrollViewProps & {
|
||||
drawerPosition: 'left' | 'right';
|
||||
@@ -16,7 +15,6 @@ export default function DrawerContentScrollView({
|
||||
...rest
|
||||
}: Props) {
|
||||
const insets = useSafeArea();
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
@@ -29,7 +27,7 @@ export default function DrawerContentScrollView({
|
||||
},
|
||||
contentContainerStyle,
|
||||
]}
|
||||
style={[styles.container, { backgroundColor: colors.card }, style]}
|
||||
style={[styles.container, style]}
|
||||
>
|
||||
{children}
|
||||
</ScrollView>
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
DrawerNavigationState,
|
||||
DrawerActions,
|
||||
} from '@react-navigation/routers';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
|
||||
import DrawerGestureContext from '../utils/DrawerGestureContext';
|
||||
import SafeAreaProviderCompat from './SafeAreaProviderCompat';
|
||||
@@ -26,16 +27,10 @@ import {
|
||||
DrawerContentComponentProps,
|
||||
} from '../types';
|
||||
|
||||
type Props = Omit<DrawerNavigationConfig, 'overlayColor'> & {
|
||||
type Props = DrawerNavigationConfig & {
|
||||
state: DrawerNavigationState;
|
||||
navigation: DrawerNavigationHelpers;
|
||||
descriptors: DrawerDescriptorMap;
|
||||
overlayColor: string;
|
||||
};
|
||||
|
||||
type State = {
|
||||
loaded: number[];
|
||||
drawerWidth: number;
|
||||
};
|
||||
|
||||
const getDefaultDrawerWidth = ({
|
||||
@@ -62,49 +57,52 @@ const getDefaultDrawerWidth = ({
|
||||
/**
|
||||
* Component that renders the drawer.
|
||||
*/
|
||||
export default class DrawerView extends React.PureComponent<Props, State> {
|
||||
static defaultProps = {
|
||||
lazy: true,
|
||||
drawerContent: (props: DrawerContentComponentProps) => (
|
||||
<DrawerContent {...props} />
|
||||
),
|
||||
drawerPosition: I18nManager.isRTL ? 'right' : 'left',
|
||||
keyboardDismissMode: 'on-drag',
|
||||
overlayColor: 'rgba(0, 0, 0, 0.5)',
|
||||
drawerType: 'front',
|
||||
hideStatusBar: false,
|
||||
statusBarAnimation: 'slide',
|
||||
};
|
||||
export default function DrawerView({
|
||||
state,
|
||||
navigation,
|
||||
descriptors,
|
||||
lazy = true,
|
||||
drawerContent = (props: DrawerContentComponentProps) => (
|
||||
<DrawerContent {...props} />
|
||||
),
|
||||
drawerPosition = I18nManager.isRTL ? 'right' : 'left',
|
||||
keyboardDismissMode = 'on-drag',
|
||||
overlayColor = 'rgba(0, 0, 0, 0.5)',
|
||||
drawerType = 'front',
|
||||
hideStatusBar = false,
|
||||
statusBarAnimation = 'slide',
|
||||
drawerContentOptions,
|
||||
drawerStyle,
|
||||
edgeWidth,
|
||||
gestureHandlerProps,
|
||||
minSwipeDistance,
|
||||
sceneContainerStyle,
|
||||
unmountInactiveScreens,
|
||||
}: Props) {
|
||||
const [loaded, setLoaded] = React.useState([state.index]);
|
||||
const [drawerWidth, setDrawerWidth] = React.useState(() =>
|
||||
getDefaultDrawerWidth(Dimensions.get('window'))
|
||||
);
|
||||
|
||||
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
|
||||
const { index } = nextProps.state;
|
||||
const drawerGestureRef = React.useRef<PanGestureHandler>(null);
|
||||
|
||||
return {
|
||||
// Set the current tab to be loaded if it was not loaded before
|
||||
loaded: prevState.loaded.includes(index)
|
||||
? prevState.loaded
|
||||
: [...prevState.loaded, index],
|
||||
const { colors } = useTheme();
|
||||
|
||||
React.useEffect(() => {
|
||||
const updateWidth = ({ window }: { window: ScaledSize }) => {
|
||||
setDrawerWidth(getDefaultDrawerWidth(window));
|
||||
};
|
||||
|
||||
Dimensions.addEventListener('change', updateWidth);
|
||||
|
||||
return () => Dimensions.removeEventListener('change', updateWidth);
|
||||
}, []);
|
||||
|
||||
if (!loaded.includes(state.index)) {
|
||||
setLoaded([...loaded, state.index]);
|
||||
}
|
||||
|
||||
state: State = {
|
||||
loaded: [this.props.state.index],
|
||||
drawerWidth: getDefaultDrawerWidth(Dimensions.get('window')),
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
Dimensions.addEventListener('change', this.updateWidth);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
Dimensions.removeEventListener('change', this.updateWidth);
|
||||
}
|
||||
|
||||
private drawerGestureRef = React.createRef<PanGestureHandler>();
|
||||
|
||||
private handleDrawerOpen = () => {
|
||||
const { state, navigation } = this.props;
|
||||
|
||||
const handleDrawerOpen = () => {
|
||||
navigation.dispatch({
|
||||
...DrawerActions.openDrawer(),
|
||||
target: state.key,
|
||||
@@ -113,9 +111,7 @@ export default class DrawerView extends React.PureComponent<Props, State> {
|
||||
navigation.emit({ type: 'drawerOpen' });
|
||||
};
|
||||
|
||||
private handleDrawerClose = () => {
|
||||
const { state, navigation } = this.props;
|
||||
|
||||
const handleDrawerClose = () => {
|
||||
navigation.dispatch({
|
||||
...DrawerActions.closeDrawer(),
|
||||
target: state.key,
|
||||
@@ -124,24 +120,7 @@ export default class DrawerView extends React.PureComponent<Props, State> {
|
||||
navigation.emit({ type: 'drawerClose' });
|
||||
};
|
||||
|
||||
private updateWidth = ({ window }: { window: ScaledSize }) => {
|
||||
const drawerWidth = getDefaultDrawerWidth(window);
|
||||
|
||||
if (this.state.drawerWidth !== drawerWidth) {
|
||||
this.setState({ drawerWidth });
|
||||
}
|
||||
};
|
||||
|
||||
private renderNavigationView = ({ progress }: any) => {
|
||||
const {
|
||||
state,
|
||||
navigation,
|
||||
descriptors,
|
||||
drawerPosition,
|
||||
drawerContent,
|
||||
drawerContentOptions,
|
||||
} = this.props;
|
||||
|
||||
const renderNavigationView = ({ progress }: any) => {
|
||||
return drawerContent({
|
||||
...drawerContentOptions,
|
||||
progress: progress,
|
||||
@@ -152,11 +131,7 @@ export default class DrawerView extends React.PureComponent<Props, State> {
|
||||
});
|
||||
};
|
||||
|
||||
private renderContent = () => {
|
||||
let { lazy, state, descriptors, unmountInactiveScreens } = this.props;
|
||||
|
||||
const { loaded } = this.state;
|
||||
|
||||
const renderContent = () => {
|
||||
return (
|
||||
<ScreenContainer style={styles.content}>
|
||||
{state.routes.map((route, index) => {
|
||||
@@ -164,7 +139,7 @@ export default class DrawerView extends React.PureComponent<Props, State> {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (lazy && !loaded.includes(index)) {
|
||||
if (lazy && !loaded.includes(index) && index !== state.index) {
|
||||
// Don't render a screen if we've never navigated to it
|
||||
return null;
|
||||
}
|
||||
@@ -186,58 +161,45 @@ export default class DrawerView extends React.PureComponent<Props, State> {
|
||||
);
|
||||
};
|
||||
|
||||
private setDrawerGestureRef = (ref: PanGestureHandler | null) => {
|
||||
// @ts-ignore
|
||||
this.drawerGestureRef.current = ref;
|
||||
};
|
||||
const activeKey = state.routes[state.index].key;
|
||||
const { gestureEnabled } = descriptors[activeKey].options;
|
||||
|
||||
render() {
|
||||
const {
|
||||
state,
|
||||
descriptors,
|
||||
drawerType,
|
||||
drawerPosition,
|
||||
overlayColor,
|
||||
sceneContainerStyle,
|
||||
drawerStyle,
|
||||
edgeWidth,
|
||||
minSwipeDistance,
|
||||
hideStatusBar,
|
||||
statusBarAnimation,
|
||||
gestureHandlerProps,
|
||||
} = this.props;
|
||||
|
||||
const { drawerWidth } = this.state;
|
||||
|
||||
const activeKey = state.routes[state.index].key;
|
||||
const { gestureEnabled } = descriptors[activeKey].options;
|
||||
|
||||
return (
|
||||
<SafeAreaProviderCompat>
|
||||
<DrawerGestureContext.Provider value={this.drawerGestureRef}>
|
||||
<Drawer
|
||||
open={state.isDrawerOpen}
|
||||
gestureEnabled={gestureEnabled !== false}
|
||||
onOpen={this.handleDrawerOpen}
|
||||
onClose={this.handleDrawerClose}
|
||||
onGestureRef={this.setDrawerGestureRef}
|
||||
gestureHandlerProps={gestureHandlerProps}
|
||||
drawerType={drawerType}
|
||||
drawerPosition={drawerPosition}
|
||||
sceneContainerStyle={sceneContainerStyle}
|
||||
drawerStyle={[{ width: drawerWidth }, drawerStyle]}
|
||||
overlayStyle={{ backgroundColor: overlayColor }}
|
||||
swipeEdgeWidth={edgeWidth}
|
||||
swipeDistanceThreshold={minSwipeDistance}
|
||||
hideStatusBar={hideStatusBar}
|
||||
statusBarAnimation={statusBarAnimation}
|
||||
renderDrawerContent={this.renderNavigationView}
|
||||
renderSceneContent={this.renderContent}
|
||||
/>
|
||||
</DrawerGestureContext.Provider>
|
||||
</SafeAreaProviderCompat>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<SafeAreaProviderCompat>
|
||||
<DrawerGestureContext.Provider value={drawerGestureRef}>
|
||||
<Drawer
|
||||
open={state.isDrawerOpen}
|
||||
gestureEnabled={gestureEnabled !== false}
|
||||
onOpen={handleDrawerOpen}
|
||||
onClose={handleDrawerClose}
|
||||
onGestureRef={ref => {
|
||||
// @ts-ignore
|
||||
drawerGestureRef.current = ref;
|
||||
}}
|
||||
gestureHandlerProps={gestureHandlerProps}
|
||||
drawerType={drawerType}
|
||||
drawerPosition={drawerPosition}
|
||||
sceneContainerStyle={[
|
||||
{ backgroundColor: colors.background },
|
||||
sceneContainerStyle,
|
||||
]}
|
||||
drawerStyle={[
|
||||
{ width: drawerWidth, backgroundColor: colors.card },
|
||||
drawerStyle,
|
||||
]}
|
||||
overlayStyle={{ backgroundColor: overlayColor }}
|
||||
swipeEdgeWidth={edgeWidth}
|
||||
swipeDistanceThreshold={minSwipeDistance}
|
||||
hideStatusBar={hideStatusBar}
|
||||
statusBarAnimation={statusBarAnimation}
|
||||
renderDrawerContent={renderNavigationView}
|
||||
renderSceneContent={renderContent}
|
||||
keyboardDismissMode={keyboardDismissMode}
|
||||
drawerPostion={drawerPosition}
|
||||
/>
|
||||
</DrawerGestureContext.Provider>
|
||||
</SafeAreaProviderCompat>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
||||
@@ -13,7 +13,7 @@ const FAR_FAR_AWAY = 3000; // this should be big enough to move the whole view o
|
||||
|
||||
export default class ResourceSavingScene extends React.Component<Props> {
|
||||
render() {
|
||||
if (screensEnabled && screensEnabled()) {
|
||||
if (screensEnabled?.()) {
|
||||
const { isVisible, ...rest } = this.props;
|
||||
|
||||
// @ts-ignore
|
||||
|
||||
@@ -3,6 +3,17 @@ import {
|
||||
SafeAreaProvider,
|
||||
SafeAreaConsumer,
|
||||
} from 'react-native-safe-area-context';
|
||||
import {
|
||||
getStatusBarHeight,
|
||||
getBottomSpace,
|
||||
} from 'react-native-iphone-x-helper';
|
||||
|
||||
const initialSafeAreaInsets = {
|
||||
top: getStatusBarHeight(true),
|
||||
bottom: getBottomSpace(),
|
||||
right: 0,
|
||||
left: 0,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode;
|
||||
@@ -19,7 +30,11 @@ export default function SafeAreaProviderCompat({ children }: Props) {
|
||||
return children;
|
||||
}
|
||||
|
||||
return <SafeAreaProvider>{children}</SafeAreaProvider>;
|
||||
return (
|
||||
<SafeAreaProvider initialSafeAreaInsets={initialSafeAreaInsets}>
|
||||
{children}
|
||||
</SafeAreaProvider>
|
||||
);
|
||||
}}
|
||||
</SafeAreaConsumer>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.0.0-alpha.29](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.28...@react-navigation/material-bottom-tabs@5.0.0-alpha.29) (2020-01-05)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.28](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.27...@react-navigation/material-bottom-tabs@5.0.0-alpha.28) (2020-01-01)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.27](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.26...@react-navigation/material-bottom-tabs@5.0.0-alpha.27) (2019-12-19)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.26](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.25...@react-navigation/material-bottom-tabs@5.0.0-alpha.26) (2019-12-16)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.25](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.24...@react-navigation/material-bottom-tabs@5.0.0-alpha.25) (2019-12-14)
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"material",
|
||||
"tab"
|
||||
],
|
||||
"version": "5.0.0-alpha.25",
|
||||
"version": "5.0.0-alpha.29",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -34,19 +34,19 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.0.0-alpha.16"
|
||||
"@react-navigation/routers": "^5.0.0-alpha.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.7.0",
|
||||
"@types/react": "^16.9.11",
|
||||
"@types/react-native": "^0.60.22",
|
||||
"@types/react": "^16.9.16",
|
||||
"@types/react-native": "^0.60.25",
|
||||
"@types/react-native-vector-icons": "^6.4.4",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-paper": "^3.2.1",
|
||||
"react-native-paper": "^3.3.0",
|
||||
"react-native-vector-icons": "^6.6.0",
|
||||
"typescript": "^3.7.2"
|
||||
"typescript": "^3.7.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.0-alpha.0",
|
||||
|
||||
@@ -3,6 +3,44 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.0.0-alpha.27](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-top-tabs@5.0.0-alpha.26...@react-navigation/material-top-tabs@5.0.0-alpha.27) (2020-01-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add support for pager component ([dcc5f99](https://github.com/react-navigation/navigation-ex/commit/dcc5f99ecd495ad1903c9e99884e0d4e9b3994f1))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.26](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-top-tabs@5.0.0-alpha.25...@react-navigation/material-top-tabs@5.0.0-alpha.26) (2020-01-01)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.25](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-top-tabs@5.0.0-alpha.24...@react-navigation/material-top-tabs@5.0.0-alpha.25) (2019-12-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix backgroundColor in sceneContainerStyle overriden by theme ([ebd145a](https://github.com/react-navigation/navigation-ex/commit/ebd145a09d80f119070a14a8d4940b5757b5e7fb)), closes [#215](https://github.com/react-navigation/navigation-ex/issues/215)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.24](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-top-tabs@5.0.0-alpha.23...@react-navigation/material-top-tabs@5.0.0-alpha.24) (2019-12-16)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.23](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-top-tabs@5.0.0-alpha.22...@react-navigation/material-top-tabs@5.0.0-alpha.23) (2019-12-14)
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"material",
|
||||
"tab"
|
||||
],
|
||||
"version": "5.0.0-alpha.23",
|
||||
"version": "5.0.0-alpha.27",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -34,20 +34,20 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.0.0-alpha.16",
|
||||
"@react-navigation/routers": "^5.0.0-alpha.20",
|
||||
"color": "^3.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.7.0",
|
||||
"@types/react": "^16.9.11",
|
||||
"@types/react-native": "^0.60.22",
|
||||
"@types/react": "^16.9.16",
|
||||
"@types/react-native": "^0.60.25",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-gesture-handler": "^1.5.0",
|
||||
"react-native-reanimated": "^1.4.0",
|
||||
"react-native-tab-view": "^2.11.0",
|
||||
"typescript": "^3.7.2"
|
||||
"typescript": "^3.7.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.0-alpha.0",
|
||||
|
||||
@@ -113,9 +113,15 @@ export type MaterialTopTabNavigationConfig = Partial<
|
||||
| 'onSwipeEnd'
|
||||
| 'renderScene'
|
||||
| 'renderTabBar'
|
||||
| 'renderPager'
|
||||
| 'renderLazyPlaceholder'
|
||||
>
|
||||
> & {
|
||||
/**
|
||||
* Function that returns a React element to use as the pager.
|
||||
* The pager handles swipe gestures and page switching.
|
||||
*/
|
||||
pager?: React.ComponentProps<typeof TabView>['renderPager'];
|
||||
/**
|
||||
* Function that returns a React element to render for routes that haven't been rendered yet.
|
||||
* Receives an object containing the route as the prop.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { TabView, SceneRendererProps } from 'react-native-tab-view';
|
||||
import { Route, useTheme } from '@react-navigation/native';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
import { TabNavigationState, TabActions } from '@react-navigation/routers';
|
||||
|
||||
import MaterialTopTabBar from './MaterialTopTabBar';
|
||||
@@ -16,49 +15,28 @@ type Props = MaterialTopTabNavigationConfig & {
|
||||
state: TabNavigationState;
|
||||
navigation: MaterialTopTabNavigationHelpers;
|
||||
descriptors: MaterialTopTabDescriptorMap;
|
||||
tabBarPosition: 'top' | 'bottom';
|
||||
tabBarPosition?: 'top' | 'bottom';
|
||||
};
|
||||
|
||||
function SceneContent({ children }: { children: React.ReactNode }) {
|
||||
export default function MaterialTopTabView({
|
||||
pager,
|
||||
lazyPlaceholder,
|
||||
tabBar = (props: MaterialTopTabBarProps) => <MaterialTopTabBar {...props} />,
|
||||
tabBarOptions,
|
||||
state,
|
||||
navigation,
|
||||
descriptors,
|
||||
sceneContainerStyle,
|
||||
...rest
|
||||
}: Props) {
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default class MaterialTopTabView extends React.PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
tabBarPosition: 'top',
|
||||
};
|
||||
|
||||
private renderLazyPlaceholder = (props: { route: Route<string> }) => {
|
||||
const { lazyPlaceholder } = this.props;
|
||||
|
||||
if (lazyPlaceholder != null) {
|
||||
return lazyPlaceholder(props);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
private renderTabBar = (props: SceneRendererProps) => {
|
||||
const { state, descriptors } = this.props;
|
||||
const renderTabBar = (props: SceneRendererProps) => {
|
||||
const route = state.routes[state.index];
|
||||
const options = descriptors[route.key].options;
|
||||
|
||||
const tabBarVisible = options.tabBarVisible !== false;
|
||||
|
||||
const {
|
||||
navigation,
|
||||
tabBar = (props: MaterialTopTabBarProps) => (
|
||||
<MaterialTopTabBar {...props} />
|
||||
),
|
||||
tabBarOptions,
|
||||
} = this.props;
|
||||
|
||||
if (tabBarVisible === false) {
|
||||
return null;
|
||||
}
|
||||
@@ -72,47 +50,26 @@ export default class MaterialTopTabView extends React.PureComponent<Props> {
|
||||
});
|
||||
};
|
||||
|
||||
private handleSwipeStart = () =>
|
||||
this.props.navigation.emit({
|
||||
type: 'swipeStart',
|
||||
});
|
||||
|
||||
private handleSwipeEnd = () =>
|
||||
this.props.navigation.emit({
|
||||
type: 'swipeEnd',
|
||||
});
|
||||
|
||||
render() {
|
||||
const {
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
lazyPlaceholder,
|
||||
tabBar,
|
||||
tabBarOptions,
|
||||
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||||
state,
|
||||
navigation,
|
||||
descriptors,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<TabView
|
||||
{...rest}
|
||||
onIndexChange={index =>
|
||||
navigation.dispatch({
|
||||
...TabActions.jumpTo(state.routes[index].name),
|
||||
target: state.key,
|
||||
})
|
||||
}
|
||||
renderScene={({ route }) => (
|
||||
<SceneContent>{descriptors[route.key].render()}</SceneContent>
|
||||
)}
|
||||
navigationState={state}
|
||||
renderTabBar={this.renderTabBar}
|
||||
renderLazyPlaceholder={this.renderLazyPlaceholder}
|
||||
onSwipeStart={this.handleSwipeStart}
|
||||
onSwipeEnd={this.handleSwipeEnd}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<TabView
|
||||
{...rest}
|
||||
onIndexChange={index =>
|
||||
navigation.dispatch({
|
||||
...TabActions.jumpTo(state.routes[index].name),
|
||||
target: state.key,
|
||||
})
|
||||
}
|
||||
renderScene={({ route }) => descriptors[route.key].render()}
|
||||
navigationState={state}
|
||||
renderTabBar={renderTabBar}
|
||||
renderPager={pager}
|
||||
renderLazyPlaceholder={lazyPlaceholder}
|
||||
onSwipeStart={() => navigation.emit({ type: 'swipeStart' })}
|
||||
onSwipeEnd={() => navigation.emit({ type: 'swipeEnd' })}
|
||||
sceneContainerStyle={[
|
||||
{ backgroundColor: colors.background },
|
||||
sceneContainerStyle,
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,46 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.0.0-alpha.22](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native-stack@5.0.0-alpha.21...@react-navigation/native-stack@5.0.0-alpha.22) (2020-01-05)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native-stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.21](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native-stack@5.0.0-alpha.20...@react-navigation/native-stack@5.0.0-alpha.21) (2020-01-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native-stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.20](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native-stack@5.0.0-alpha.19...@react-navigation/native-stack@5.0.0-alpha.20) (2020-01-01)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native-stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.19](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native-stack@5.0.0-alpha.18...@react-navigation/native-stack@5.0.0-alpha.19) (2019-12-19)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native-stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.18](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native-stack@5.0.0-alpha.17...@react-navigation/native-stack@5.0.0-alpha.18) (2019-12-16)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native-stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.17](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native-stack@5.0.0-alpha.16...@react-navigation/native-stack@5.0.0-alpha.17) (2019-12-14)
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Stack navigator for React Native using native primitives for navigation. Uses [`react-native-screens`](https://github.com/kmagiera/react-native-screens) under the hood.
|
||||
|
||||
Expo is currently not supported as it includes an older version of `react-native-screens`.
|
||||
Expo SDK 35 and lower is not supported as it includes an older version of `react-native-screens`.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -11,12 +11,30 @@ Open a Terminal in your project's folder and run,
|
||||
```sh
|
||||
yarn add @react-navigation/native @react-navigation/native-stack
|
||||
```
|
||||
Or with npm
|
||||
|
||||
Now we need to install [`react-native-screens`](https://github.com/kmagiera/react-native-screens).
|
||||
```sh
|
||||
npm install --save @react-navigation/native @react-navigation/native-stack
|
||||
```
|
||||
|
||||
If you are using Expo, to ensure that you get the compatible versions of the libraries, run:
|
||||
|
||||
```sh
|
||||
expo install react-native-screens
|
||||
```
|
||||
|
||||
If you are not using Expo, run the following:
|
||||
|
||||
```sh
|
||||
yarn add react-native-screens
|
||||
```
|
||||
Or with npm
|
||||
|
||||
```sh
|
||||
npm install --save react-native-screens
|
||||
```
|
||||
|
||||
If you are using Expo, you are done. Otherwise, continue to the next steps.
|
||||
|
||||
To complete the linking on iOS, make sure you have [Cocoapods](https://cocoapods.org/) installed. Then run:
|
||||
|
||||
@@ -36,9 +54,9 @@ implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02'
|
||||
Make sure to enable `react-native-screens`. This needs to be done before our app renders. To do it, add the following code in your entry file (e.g. `App.js`):
|
||||
|
||||
```js
|
||||
import { useScreens } from 'react-native-screens';
|
||||
import { enableScreens } from 'react-native-screens';
|
||||
|
||||
useScreens();
|
||||
enableScreens();
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"react-native",
|
||||
"react-navigation"
|
||||
],
|
||||
"version": "5.0.0-alpha.17",
|
||||
"version": "5.0.0-alpha.22",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -29,13 +29,13 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.0.0-alpha.16"
|
||||
"@react-navigation/routers": "^5.0.0-alpha.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.7.0",
|
||||
"del-cli": "^3.0.0",
|
||||
"react-native-screens": "^2.0.0-alpha.11",
|
||||
"typescript": "^3.7.2"
|
||||
"react-native-screens": "^2.0.0-alpha.19",
|
||||
"typescript": "^3.7.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.0-alpha.0",
|
||||
|
||||
@@ -57,7 +57,7 @@ export default function HeaderConfig(props: Props) {
|
||||
? headerTintColor
|
||||
: colors.text
|
||||
}
|
||||
backTitle={headerBackTitleVisible ? headerBackTitle : ''}
|
||||
backTitle={headerBackTitleVisible ? headerBackTitle : ' '}
|
||||
backTitleFontFamily={headerBackTitleStyle.fontFamily}
|
||||
backTitleFontSize={headerBackTitleStyle.fontSize}
|
||||
color={headerTintColor !== undefined ? headerTintColor : colors.primary}
|
||||
|
||||
@@ -3,6 +3,33 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.0.0-alpha.22](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native@5.0.0-alpha.21...@react-navigation/native@5.0.0-alpha.22) (2020-01-01)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.21](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native@5.0.0-alpha.20...@react-navigation/native@5.0.0-alpha.21) (2019-12-19)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.20](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native@5.0.0-alpha.19...@react-navigation/native@5.0.0-alpha.20) (2019-12-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add nested config in deep linking ([#210](https://github.com/react-navigation/navigation-ex/issues/210)) ([8002d51](https://github.com/react-navigation/navigation-ex/commit/8002d5179524a7211c37760a4ed45e8c12af4358)), closes [#154](https://github.com/react-navigation/navigation-ex/issues/154)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.19](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native@5.0.0-alpha.18...@react-navigation/native@5.0.0-alpha.19) (2019-12-14)
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"ios",
|
||||
"android"
|
||||
],
|
||||
"version": "5.0.0-alpha.19",
|
||||
"version": "5.0.0-alpha.22",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -30,16 +30,16 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/core": "^5.0.0-alpha.27"
|
||||
"@react-navigation/core": "^5.0.0-alpha.30"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.7.0",
|
||||
"@types/react": "^16.9.11",
|
||||
"@types/react-native": "^0.60.22",
|
||||
"@types/react": "^16.9.16",
|
||||
"@types/react-native": "^0.60.25",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"typescript": "^3.7.2"
|
||||
"typescript": "^3.7.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import { Linking } from 'react-native';
|
||||
import {
|
||||
getActionFromState,
|
||||
getStateFromPath as getStateFromPathDefault,
|
||||
NavigationContainerRef,
|
||||
NavigationState,
|
||||
PartialState,
|
||||
} from '@react-navigation/core';
|
||||
|
||||
type Config = {
|
||||
[routeName: string]:
|
||||
| string
|
||||
| { path: string; parse?: Record<string, (value: string) => any> };
|
||||
};
|
||||
type GetStateFromPath = typeof getStateFromPathDefault;
|
||||
|
||||
type Config = Parameters<GetStateFromPath>[1];
|
||||
|
||||
type Options = {
|
||||
/**
|
||||
@@ -36,10 +33,7 @@ type Options = {
|
||||
/**
|
||||
* Custom function to parse the URL object to a valid navigation state (advanced).
|
||||
*/
|
||||
getStateFromPath?: (
|
||||
path: string,
|
||||
options?: Config
|
||||
) => PartialState<NavigationState> | undefined;
|
||||
getStateFromPath?: GetStateFromPath;
|
||||
};
|
||||
|
||||
export default function useLinking(
|
||||
@@ -89,7 +83,13 @@ export default function useLinking(
|
||||
const state = getStateFromPathRef.current(path, configRef.current);
|
||||
|
||||
if (state) {
|
||||
navigation.resetRoot(state);
|
||||
const action = getActionFromState(state);
|
||||
|
||||
if (action.type === 'RESET_ROOT') {
|
||||
navigation.resetRoot(action.payload);
|
||||
} else {
|
||||
navigation.dispatch(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,6 +3,41 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.0.0-alpha.20](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/routers@5.0.0-alpha.19...@react-navigation/routers@5.0.0-alpha.20) (2020-01-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* preserve focused route in tab on changing screens list ([adbeb29](https://github.com/react-navigation/navigation-ex/commit/adbeb292f522be8d7a58dd3f84e560a6d83d01a8))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.19](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/routers@5.0.0-alpha.18...@react-navigation/routers@5.0.0-alpha.19) (2020-01-01)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/routers
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.18](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/routers@5.0.0-alpha.17...@react-navigation/routers@5.0.0-alpha.18) (2019-12-19)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/routers
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.17](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/routers@5.0.0-alpha.16...@react-navigation/routers@5.0.0-alpha.17) (2019-12-16)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/routers
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.16](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/routers@5.0.0-alpha.15...@react-navigation/routers@5.0.0-alpha.16) (2019-12-11)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/routers
|
||||
|
||||
@@ -250,6 +250,89 @@ it('gets state on route names change', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves focused route on route names change', () => {
|
||||
const router = TabRouter({});
|
||||
|
||||
expect(
|
||||
router.getStateForRouteNamesChange(
|
||||
{
|
||||
index: 1,
|
||||
key: 'tab-test',
|
||||
routeKeyHistory: [],
|
||||
routeNames: ['bar', 'baz', 'qux'],
|
||||
routes: [
|
||||
{ key: 'bar-test', name: 'bar' },
|
||||
{ key: 'baz-test', name: 'baz', params: { answer: 42 } },
|
||||
{ key: 'qux-test', name: 'qux', params: { name: 'Jane' } },
|
||||
],
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
},
|
||||
{
|
||||
routeNames: ['qux', 'foo', 'fiz', 'baz'],
|
||||
routeParamList: {
|
||||
qux: { name: 'John' },
|
||||
fiz: { fruit: 'apple' },
|
||||
},
|
||||
}
|
||||
)
|
||||
).toEqual({
|
||||
index: 3,
|
||||
key: 'tab-test',
|
||||
routeKeyHistory: [],
|
||||
routeNames: ['qux', 'foo', 'fiz', 'baz'],
|
||||
routes: [
|
||||
{ key: 'qux-test', name: 'qux', params: { name: 'Jane' } },
|
||||
{ key: 'foo-test', name: 'foo' },
|
||||
{ key: 'fiz-test', name: 'fiz', params: { fruit: 'apple' } },
|
||||
{ key: 'baz-test', name: 'baz', params: { answer: 42 } },
|
||||
],
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
});
|
||||
});
|
||||
|
||||
it('falls back to first route if route is removed on route names change', () => {
|
||||
const router = TabRouter({});
|
||||
|
||||
expect(
|
||||
router.getStateForRouteNamesChange(
|
||||
{
|
||||
index: 1,
|
||||
key: 'tab-test',
|
||||
routeKeyHistory: [],
|
||||
routeNames: ['bar', 'baz', 'qux'],
|
||||
routes: [
|
||||
{ key: 'bar-test', name: 'bar' },
|
||||
{ key: 'baz-test', name: 'baz', params: { answer: 42 } },
|
||||
{ key: 'qux-test', name: 'qux', params: { name: 'Jane' } },
|
||||
],
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
},
|
||||
{
|
||||
routeNames: ['qux', 'foo', 'fiz'],
|
||||
routeParamList: {
|
||||
qux: { name: 'John' },
|
||||
fiz: { fruit: 'apple' },
|
||||
},
|
||||
}
|
||||
)
|
||||
).toEqual({
|
||||
index: 0,
|
||||
key: 'tab-test',
|
||||
routeKeyHistory: [],
|
||||
routeNames: ['qux', 'foo', 'fiz'],
|
||||
routes: [
|
||||
{ key: 'qux-test', name: 'qux', params: { name: 'Jane' } },
|
||||
{ key: 'foo-test', name: 'foo' },
|
||||
{ key: 'fiz-test', name: 'fiz', params: { fruit: 'apple' } },
|
||||
],
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
});
|
||||
});
|
||||
|
||||
it('handles navigate action', () => {
|
||||
const router = TabRouter({});
|
||||
const options = {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"react-native",
|
||||
"react-navigation"
|
||||
],
|
||||
"version": "5.0.0-alpha.16",
|
||||
"version": "5.0.0-alpha.20",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -29,13 +29,13 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/core": "^5.0.0-alpha.27",
|
||||
"@react-navigation/core": "^5.0.0-alpha.30",
|
||||
"shortid": "^2.2.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.7.0",
|
||||
"del-cli": "^3.0.0",
|
||||
"typescript": "^3.7.2"
|
||||
"typescript": "^3.7.3"
|
||||
},
|
||||
"@react-native-community/bob": {
|
||||
"source": "src",
|
||||
|
||||
@@ -147,11 +147,16 @@ export default function TabRouter({
|
||||
}
|
||||
);
|
||||
|
||||
const index = Math.max(
|
||||
0,
|
||||
routeNames.indexOf(state.routes[state.index].name)
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
routeNames,
|
||||
routes,
|
||||
index: Math.min(state.index, routes.length - 1),
|
||||
index,
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -3,6 +3,81 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.0.0-alpha.51](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.50...@react-navigation/stack@5.0.0-alpha.51) (2020-01-05)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.50](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.49...@react-navigation/stack@5.0.0-alpha.50) (2020-01-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* keep screens for replace when animation is enabled ([7f963a7](https://github.com/react-navigation/navigation-ex/commit/7f963a74bb4d4b04134e917fe47e38e4d982afed))
|
||||
* use gesture direction when using next screen's animation ([572beae](https://github.com/react-navigation/navigation-ex/commit/572beae41b326f3ef80bc2b790badf123e0071bc))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.49](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.48...@react-navigation/stack@5.0.0-alpha.49) (2020-01-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* dismiss keyboard on page change ([2c31d17](https://github.com/react-navigation/navigation-ex/commit/2c31d1705c4e5827b19b9cc7f3e5b05207c3238a))
|
||||
* interaction manager in stack ([#237](https://github.com/react-navigation/navigation-ex/issues/237)) ([6b9b999](https://github.com/react-navigation/navigation-ex/commit/6b9b999c5b60a67ed683b84484928700d4260585))
|
||||
* provide initial values for safe area to prevent blank screen ([#238](https://github.com/react-navigation/navigation-ex/issues/238)) ([77b7570](https://github.com/react-navigation/navigation-ex/commit/77b757091c0451e20bca01138629669c7da544a8))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.48](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.47...@react-navigation/stack@5.0.0-alpha.48) (2020-01-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve gesture performance ([59803f5](https://github.com/react-navigation/navigation-ex/commit/59803f54d64f85c8e46c1ebc70613a70a812f53a))
|
||||
* use native driver for gestures ([9356598](https://github.com/react-navigation/navigation-ex/commit/935659899f1d4084c601fbefea4a935f9b6ce087))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.47](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.46...@react-navigation/stack@5.0.0-alpha.47) (2020-01-01)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.46](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.45...@react-navigation/stack@5.0.0-alpha.46) (2019-12-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix typescript issues ([c52a8c4](https://github.com/react-navigation/navigation-ex/commit/c52a8c46a8906812651e5259a850207fc448590e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.45](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.44...@react-navigation/stack@5.0.0-alpha.45) (2019-12-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* disable style interpolation for card when animation is disabled ([c110570](https://github.com/react-navigation/navigation-ex/commit/c110570d4c89a38336f19403e6f2d0870868620e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.44](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.43...@react-navigation/stack@5.0.0-alpha.44) (2019-12-14)
|
||||
|
||||
|
||||
|
||||
@@ -10,18 +10,18 @@ Open a Terminal in your project's folder and run,
|
||||
yarn add @react-navigation/native @react-navigation/stack @react-native-community/masked-view
|
||||
```
|
||||
|
||||
Now we need to install [`react-native-gesture-handler`](https://github.com/kmagiera/react-native-gesture-handler), [`react-native-reanimated`](https://github.com/kmagiera/react-native-reanimated), [`react-native-screens`](https://github.com/kmagiera/react-native-screens) and [`react-native-safe-area-context`](https://github.com/th3rdwave/react-native-safe-area-context).
|
||||
Now we need to install [`react-native-gesture-handler`](https://github.com/kmagiera/react-native-gesture-handler), [`react-native-screens`](https://github.com/kmagiera/react-native-screens) and [`react-native-safe-area-context`](https://github.com/th3rdwave/react-native-safe-area-context).
|
||||
|
||||
If you are using Expo, to ensure that you get the compatible versions of the libraries, run:
|
||||
|
||||
```sh
|
||||
expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context
|
||||
expo install react-native-gesture-handler react-native-screens react-native-safe-area-context
|
||||
```
|
||||
|
||||
If you are not using Expo, run the following:
|
||||
|
||||
```sh
|
||||
yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context
|
||||
yarn add react-native-gesture-handler react-native-screens react-native-safe-area-context
|
||||
```
|
||||
|
||||
If you are using Expo, you are done. Otherwise, continue to the next steps.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"android",
|
||||
"stack"
|
||||
],
|
||||
"version": "5.0.0-alpha.44",
|
||||
"version": "5.0.0-alpha.51",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -33,21 +33,23 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.0.0-alpha.16"
|
||||
"@react-navigation/routers": "^5.0.0-alpha.20",
|
||||
"color": "^3.1.2",
|
||||
"react-native-iphone-x-helper": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.7.0",
|
||||
"@react-native-community/masked-view": "^0.1.5",
|
||||
"@types/react": "^16.9.11",
|
||||
"@types/react-native": "^0.60.22",
|
||||
"@types/color": "^3.0.0",
|
||||
"@types/react": "^16.9.16",
|
||||
"@types/react-native": "^0.60.25",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-gesture-handler": "^1.5.0",
|
||||
"react-native-reanimated": "^1.4.0",
|
||||
"react-native-safe-area-context": "^0.6.0",
|
||||
"react-native-screens": "^2.0.0-alpha.11",
|
||||
"typescript": "^3.7.2"
|
||||
"react-native-screens": "^2.0.0-alpha.19",
|
||||
"typescript": "^3.7.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-native-community/masked-view": "^0.1.1",
|
||||
@@ -55,8 +57,7 @@
|
||||
"react": "*",
|
||||
"react-native": "*",
|
||||
"react-native-gesture-handler": "^1.0.0",
|
||||
"react-native-reanimated": "^1.0.0",
|
||||
"react-native-safe-area-context": "^0.3.6",
|
||||
"react-native-safe-area-context": "^0.6.0",
|
||||
"react-native-screens": "^1.0.0-alpha.0 || ^2.0.0-alpha.0"
|
||||
},
|
||||
"@react-native-community/bob": {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import Animated from 'react-native-reanimated';
|
||||
import { Animated } from 'react-native';
|
||||
import conditional from '../utils/conditional';
|
||||
import {
|
||||
StackCardInterpolationProps,
|
||||
StackCardInterpolatedStyle,
|
||||
} from '../types';
|
||||
|
||||
const { cond, add, multiply, interpolate } = Animated;
|
||||
const { add, multiply } = Animated;
|
||||
|
||||
/**
|
||||
* Standard iOS-style slide in from the right.
|
||||
@@ -16,7 +17,7 @@ export function forHorizontalIOS({
|
||||
layouts: { screen },
|
||||
}: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
||||
const translateFocused = multiply(
|
||||
interpolate(current.progress, {
|
||||
current.progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [screen.width, 0],
|
||||
}),
|
||||
@@ -25,20 +26,20 @@ export function forHorizontalIOS({
|
||||
|
||||
const translateUnfocused = next
|
||||
? multiply(
|
||||
interpolate(next.progress, {
|
||||
next.progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, multiply(screen.width, -0.3)],
|
||||
outputRange: [0, screen.width * -0.3],
|
||||
}),
|
||||
inverted
|
||||
)
|
||||
: 0;
|
||||
|
||||
const overlayOpacity = interpolate(current.progress, {
|
||||
const overlayOpacity = current.progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 0.07],
|
||||
});
|
||||
|
||||
const shadowOpacity = interpolate(current.progress, {
|
||||
const shadowOpacity = current.progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 0.3],
|
||||
});
|
||||
@@ -66,7 +67,7 @@ export function forVerticalIOS({
|
||||
layouts: { screen },
|
||||
}: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
||||
const translateY = multiply(
|
||||
interpolate(current.progress, {
|
||||
current.progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [screen.height, 0],
|
||||
}),
|
||||
@@ -102,7 +103,7 @@ export function forModalPresentationIOS({
|
||||
const progress = add(current.progress, next ? next.progress : 0);
|
||||
|
||||
const translateY = multiply(
|
||||
interpolate(progress, {
|
||||
progress.interpolate({
|
||||
inputRange: [0, 1, 2],
|
||||
outputRange: [
|
||||
screen.height,
|
||||
@@ -113,14 +114,14 @@ export function forModalPresentationIOS({
|
||||
inverted
|
||||
);
|
||||
|
||||
const overlayOpacity = interpolate(progress, {
|
||||
const overlayOpacity = progress.interpolate({
|
||||
inputRange: [0, 1, 1.0001, 2],
|
||||
outputRange: [0, 0.3, 1, 1],
|
||||
});
|
||||
|
||||
const scale = isLandscape
|
||||
? 1
|
||||
: interpolate(progress, {
|
||||
: progress.interpolate({
|
||||
inputRange: [0, 1, 2],
|
||||
outputRange: [
|
||||
1,
|
||||
@@ -132,7 +133,7 @@ export function forModalPresentationIOS({
|
||||
const borderRadius = isLandscape
|
||||
? 0
|
||||
: index === 0
|
||||
? interpolate(progress, {
|
||||
? progress.interpolate({
|
||||
inputRange: [0, 1, 2],
|
||||
outputRange: [0, 0, 10],
|
||||
})
|
||||
@@ -160,17 +161,17 @@ export function forFadeFromBottomAndroid({
|
||||
closing,
|
||||
}: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
||||
const translateY = multiply(
|
||||
interpolate(current.progress, {
|
||||
current.progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [multiply(screen.height, 0.08), 0],
|
||||
outputRange: [screen.height * 0.08, 0],
|
||||
}),
|
||||
inverted
|
||||
);
|
||||
|
||||
const opacity = cond(
|
||||
const opacity = conditional(
|
||||
closing,
|
||||
current.progress,
|
||||
interpolate(current.progress, {
|
||||
current.progress.interpolate({
|
||||
inputRange: [0, 0.5, 0.9, 1],
|
||||
outputRange: [0, 0.25, 0.7, 1],
|
||||
})
|
||||
@@ -194,7 +195,7 @@ export function forRevealFromBottomAndroid({
|
||||
layouts: { screen },
|
||||
}: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
||||
const containerTranslateY = multiply(
|
||||
interpolate(current.progress, {
|
||||
current.progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [screen.height, 0],
|
||||
}),
|
||||
@@ -202,24 +203,24 @@ export function forRevealFromBottomAndroid({
|
||||
);
|
||||
|
||||
const cardTranslateYFocused = multiply(
|
||||
interpolate(current.progress, {
|
||||
current.progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [multiply(screen.height, 95.9 / 100, -1), 0],
|
||||
outputRange: [screen.height * (95.9 / 100) * -1, 0],
|
||||
}),
|
||||
inverted
|
||||
);
|
||||
|
||||
const cardTranslateYUnfocused = next
|
||||
? multiply(
|
||||
interpolate(next.progress, {
|
||||
next.progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, multiply(screen.height, 2 / 100, -1)],
|
||||
outputRange: [0, screen.height * (2 / 100) * -1],
|
||||
}),
|
||||
inverted
|
||||
)
|
||||
: 0;
|
||||
|
||||
const overlayOpacity = interpolate(current.progress, {
|
||||
const overlayOpacity = current.progress.interpolate({
|
||||
inputRange: [0, 0.36, 1],
|
||||
outputRange: [0, 0.1, 0.1],
|
||||
});
|
||||
@@ -249,18 +250,18 @@ export function forScaleFromCenterAndroid({
|
||||
}: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
||||
const progress = add(current.progress, next ? next.progress : 0);
|
||||
|
||||
const opacity = interpolate(progress, {
|
||||
const opacity = progress.interpolate({
|
||||
inputRange: [0, 0.8, 1, 1.2, 2],
|
||||
outputRange: [0, 0.5, 1, 0.33, 0],
|
||||
});
|
||||
|
||||
const scale = cond(
|
||||
const scale = conditional(
|
||||
closing,
|
||||
interpolate(current.progress, {
|
||||
current.progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0.9, 1],
|
||||
}),
|
||||
interpolate(progress, {
|
||||
progress.interpolate({
|
||||
inputRange: [0, 1, 2],
|
||||
outputRange: [0.85, 1, 1.1],
|
||||
})
|
||||
@@ -273,3 +274,7 @@ export function forScaleFromCenterAndroid({
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function forNoAnimation(): StackCardInterpolatedStyle {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { I18nManager } from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import { Animated, I18nManager } from 'react-native';
|
||||
import {
|
||||
StackHeaderInterpolationProps,
|
||||
StackHeaderInterpolatedStyle,
|
||||
} from '../types';
|
||||
|
||||
const { interpolate, add } = Animated;
|
||||
const { add } = Animated;
|
||||
|
||||
/**
|
||||
* Standard UIKit style animation for the header where the title fades into the back button label.
|
||||
@@ -38,7 +37,7 @@ export function forUIKit({
|
||||
|
||||
return {
|
||||
leftButtonStyle: {
|
||||
opacity: interpolate(progress, {
|
||||
opacity: progress.interpolate({
|
||||
inputRange: [0.3, 1, 1.5],
|
||||
outputRange: [0, 1, 0],
|
||||
}),
|
||||
@@ -46,7 +45,7 @@ export function forUIKit({
|
||||
leftLabelStyle: {
|
||||
transform: [
|
||||
{
|
||||
translateX: interpolate(progress, {
|
||||
translateX: progress.interpolate({
|
||||
inputRange: [0, 1, 2],
|
||||
outputRange: I18nManager.isRTL
|
||||
? [-rightOffset, 0, leftLabelOffset]
|
||||
@@ -56,19 +55,19 @@ export function forUIKit({
|
||||
],
|
||||
},
|
||||
rightButtonStyle: {
|
||||
opacity: interpolate(progress, {
|
||||
opacity: progress.interpolate({
|
||||
inputRange: [0.3, 1, 1.5],
|
||||
outputRange: [0, 1, 0],
|
||||
}),
|
||||
},
|
||||
titleStyle: {
|
||||
opacity: interpolate(progress, {
|
||||
opacity: progress.interpolate({
|
||||
inputRange: [0, 0.4, 1, 1.5],
|
||||
outputRange: [0, 0.1, 1, 0],
|
||||
}),
|
||||
transform: [
|
||||
{
|
||||
translateX: interpolate(progress, {
|
||||
translateX: progress.interpolate({
|
||||
inputRange: [0.5, 1, 2],
|
||||
outputRange: I18nManager.isRTL
|
||||
? [-titleLeftOffset, 0, rightOffset]
|
||||
@@ -80,7 +79,7 @@ export function forUIKit({
|
||||
backgroundStyle: {
|
||||
transform: [
|
||||
{
|
||||
translateX: interpolate(progress, {
|
||||
translateX: progress.interpolate({
|
||||
inputRange: [0, 1, 2],
|
||||
outputRange: I18nManager.isRTL
|
||||
? [-layouts.screen.width, 0, layouts.screen.width]
|
||||
@@ -100,7 +99,7 @@ export function forFade({
|
||||
next,
|
||||
}: StackHeaderInterpolationProps): StackHeaderInterpolatedStyle {
|
||||
const progress = add(current.progress, next ? next.progress : 0);
|
||||
const opacity = interpolate(progress, {
|
||||
const opacity = progress.interpolate({
|
||||
inputRange: [0, 1, 2],
|
||||
outputRange: [0, 1, 0],
|
||||
});
|
||||
@@ -110,7 +109,7 @@ export function forFade({
|
||||
rightButtonStyle: { opacity },
|
||||
titleStyle: { opacity },
|
||||
backgroundStyle: {
|
||||
opacity: interpolate(progress, {
|
||||
opacity: progress.interpolate({
|
||||
inputRange: [0, 1, 1.9, 2],
|
||||
outputRange: [0, 1, 1, 0],
|
||||
}),
|
||||
@@ -127,7 +126,7 @@ export function forStatic({
|
||||
layouts: { screen },
|
||||
}: StackHeaderInterpolationProps): StackHeaderInterpolatedStyle {
|
||||
const progress = add(current.progress, next ? next.progress : 0);
|
||||
const translateX = interpolate(progress, {
|
||||
const translateX = progress.interpolate({
|
||||
inputRange: [0, 1, 2],
|
||||
outputRange: I18nManager.isRTL
|
||||
? [-screen.width, 0, screen.width]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Easing } from 'react-native-reanimated';
|
||||
import { Easing } from 'react-native';
|
||||
import { TransitionSpec } from '../types';
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,9 @@ import * as TransitionPresets from './TransitionConfigs/TransitionPresets';
|
||||
export { default as createStackNavigator } from './navigators/createStackNavigator';
|
||||
|
||||
export const Assets = [
|
||||
// eslint-disable-next-line import/no-commonjs
|
||||
require('./views/assets/back-icon.png'),
|
||||
// eslint-disable-next-line import/no-commonjs
|
||||
require('./views/assets/back-icon-mask.png'),
|
||||
];
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import {
|
||||
Animated,
|
||||
EasingFunction,
|
||||
StyleProp,
|
||||
TextStyle,
|
||||
ViewStyle,
|
||||
LayoutChangeEvent,
|
||||
} from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import {
|
||||
NavigationProp,
|
||||
@@ -88,22 +89,24 @@ export type Scene<T> = {
|
||||
/**
|
||||
* Progress value of the current screen.
|
||||
*/
|
||||
current: Animated.Node<number>;
|
||||
current: Animated.AnimatedInterpolation;
|
||||
/**
|
||||
* Progress value for the screen after this one in the stack.
|
||||
* This can be `undefined` in case the screen animating is the last one.
|
||||
*/
|
||||
next?: Animated.Node<number>;
|
||||
next?: Animated.AnimatedInterpolation;
|
||||
/**
|
||||
* Progress value for the screen before this one in the stack.
|
||||
* This can be `undefined` in case the screen animating is the first one.
|
||||
*/
|
||||
previous?: Animated.Node<number>;
|
||||
previous?: Animated.AnimatedInterpolation;
|
||||
};
|
||||
};
|
||||
|
||||
export type StackHeaderMode = 'float' | 'screen' | 'none';
|
||||
|
||||
export type StackCardMode = 'card' | 'modal';
|
||||
|
||||
export type StackHeaderOptions = {
|
||||
/**
|
||||
* String or a function that returns a React Element to be used by the header.
|
||||
@@ -271,18 +274,14 @@ export type StackNavigationOptions = StackHeaderOptions &
|
||||
* Defaults to `true` on Android and `false` on iOS.
|
||||
*/
|
||||
cardOverlayEnabled?: boolean;
|
||||
/**
|
||||
* Whether to use a transparent background for the card instead of a white one.
|
||||
* This is useful to implement things like modal dialogs where the previous scene should still be visible underneath the current one.
|
||||
* Defaults to `false`.
|
||||
*
|
||||
* If you use [`react-native-screens`](https://github.com/kmagiera/react-native-screens),
|
||||
* you should also specify `mode: 'modal'` in the stack view config so previous screens aren't detached.
|
||||
*/
|
||||
cardTransparent?: boolean;
|
||||
/**
|
||||
* Style object for the card in stack.
|
||||
* You can provide a custom background color to use instead of the default background here.
|
||||
*
|
||||
* You can also specify `{ backgroundColor: 'transparent' }` to make the previous screen visible underneath.
|
||||
* This is useful to implement things like modal dialogs.
|
||||
* If you use [`react-native-screens`](https://github.com/kmagiera/react-native-screens), you should also specify `mode: 'modal'`
|
||||
* in the stack view config when using a transparent background so previous screens aren't detached.
|
||||
*/
|
||||
cardStyle?: StyleProp<ViewStyle>;
|
||||
/**
|
||||
@@ -326,7 +325,7 @@ export type StackNavigationOptions = StackHeaderOptions &
|
||||
};
|
||||
|
||||
export type StackNavigationConfig = {
|
||||
mode?: 'card' | 'modal';
|
||||
mode?: StackCardMode;
|
||||
headerMode?: StackHeaderMode;
|
||||
/**
|
||||
* If `false`, the keyboard will NOT automatically dismiss when navigating to a new screen.
|
||||
@@ -437,7 +436,7 @@ export type SpringConfig = {
|
||||
|
||||
export type TimingConfig = {
|
||||
duration: number;
|
||||
easing: Animated.EasingFunction;
|
||||
easing: EasingFunction;
|
||||
};
|
||||
|
||||
export type TransitionSpec =
|
||||
@@ -452,7 +451,7 @@ export type StackCardInterpolationProps = {
|
||||
/**
|
||||
* Animated node representing the progress value of the current screen.
|
||||
*/
|
||||
progress: Animated.Node<number>;
|
||||
progress: Animated.AnimatedInterpolation;
|
||||
};
|
||||
/**
|
||||
* Values for the current screen the screen after this one in the stack.
|
||||
@@ -462,24 +461,24 @@ export type StackCardInterpolationProps = {
|
||||
/**
|
||||
* Animated node representing the progress value of the next screen.
|
||||
*/
|
||||
progress: Animated.Node<number>;
|
||||
progress: Animated.AnimatedInterpolation;
|
||||
};
|
||||
/**
|
||||
* The index of the card in the stack.
|
||||
*/
|
||||
index: number;
|
||||
/**
|
||||
* Animated node representing whether the card is closing.
|
||||
* Animated node representing whether the card is closing (1 - closing, 0 - not closing).
|
||||
*/
|
||||
closing: Animated.Node<0 | 1>;
|
||||
closing: Animated.AnimatedInterpolation;
|
||||
/**
|
||||
* Animated node representing whether the card is being swiped.
|
||||
* Animated node representing whether the card is being swiped (1 - swiping, 0 - not swiping).
|
||||
*/
|
||||
swiping: Animated.Node<0 | 1>;
|
||||
swiping: Animated.AnimatedInterpolation;
|
||||
/**
|
||||
* Animated node representing multiplier when direction is inverted.
|
||||
* Animated node representing multiplier when direction is inverted (-1 - inverted, 1 - normal).
|
||||
*/
|
||||
inverted: Animated.Node<-1 | 1>;
|
||||
inverted: Animated.AnimatedInterpolation;
|
||||
/**
|
||||
* Layout measurements for various items we use for animation.
|
||||
*/
|
||||
@@ -531,7 +530,7 @@ export type StackHeaderInterpolationProps = {
|
||||
/**
|
||||
* Animated node representing the progress value of the current screen.
|
||||
*/
|
||||
progress: Animated.Node<number>;
|
||||
progress: Animated.AnimatedInterpolation;
|
||||
};
|
||||
/**
|
||||
* Values for the current screen the screen after this one in the stack.
|
||||
@@ -541,7 +540,7 @@ export type StackHeaderInterpolationProps = {
|
||||
/**
|
||||
* Animated node representing the progress value of the next screen.
|
||||
*/
|
||||
progress: Animated.Node<number>;
|
||||
progress: Animated.AnimatedInterpolation;
|
||||
};
|
||||
/**
|
||||
* Layout measurements for various items we use for animation.
|
||||
|
||||
33
packages/stack/src/utils/conditional.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Animated } from 'react-native';
|
||||
|
||||
const { add, multiply } = Animated;
|
||||
|
||||
/**
|
||||
* Use an Animated Node based on a condition. Similar to Reanimated's `cond`.
|
||||
*
|
||||
* @param condition Animated Node representing the condition, must be 0 or 1, 1 means `true`, 0 means `false`
|
||||
* @param main Animated Node to use if the condition is `true`
|
||||
* @param fallback Animated Node to use if the condition is `false`
|
||||
*/
|
||||
export default function conditional(
|
||||
condition: Animated.AnimatedInterpolation,
|
||||
main: Animated.AnimatedInterpolation,
|
||||
fallback: Animated.AnimatedInterpolation
|
||||
) {
|
||||
// To implement this behavior, we multiply the main node with the condition.
|
||||
// So if condition is 0, result will be 0, and if condition is 1, result will be main node.
|
||||
// Then we multiple reverse of the condition (0 if condition is 1) with the fallback.
|
||||
// So if condition is 0, result will be fallback node, and if condition is 1, result will be 0,
|
||||
// This way, one of them will always be 0, and other one will be the value we need.
|
||||
// In the end we add them both together, 0 + value we need = value we need
|
||||
return add(
|
||||
multiply(condition, main),
|
||||
multiply(
|
||||
condition.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [1, 0],
|
||||
}),
|
||||
fallback
|
||||
)
|
||||
);
|
||||
}
|
||||
18
packages/stack/src/utils/getDistanceForDirection.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import getInvertedMultiplier from './getInvertedMultiplier';
|
||||
import { GestureDirection, Layout } from '../types';
|
||||
|
||||
export default function getDistanceForDirection(
|
||||
layout: Layout,
|
||||
gestureDirection: GestureDirection
|
||||
): number {
|
||||
const multiplier = getInvertedMultiplier(gestureDirection);
|
||||
|
||||
switch (gestureDirection) {
|
||||
case 'vertical':
|
||||
case 'vertical-inverted':
|
||||
return layout.height * multiplier;
|
||||
case 'horizontal':
|
||||
case 'horizontal-inverted':
|
||||
return layout.width * multiplier;
|
||||
}
|
||||
}
|
||||
17
packages/stack/src/utils/getInvertedMultiplier.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { I18nManager } from 'react-native';
|
||||
import { GestureDirection } from '../types';
|
||||
|
||||
export default function getInvertedMultiplier(
|
||||
gestureDirection: GestureDirection
|
||||
): 1 | -1 {
|
||||
switch (gestureDirection) {
|
||||
case 'vertical':
|
||||
return 1;
|
||||
case 'vertical-inverted':
|
||||
return -1;
|
||||
case 'horizontal':
|
||||
return I18nManager.isRTL ? -1 : 1;
|
||||
case 'horizontal-inverted':
|
||||
return I18nManager.isRTL ? 1 : -1;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
export default function memoize<Result, Deps extends ReadonlyArray<any>>(
|
||||
export default function memoize<Result, Deps extends readonly any[]>(
|
||||
callback: (...deps: Deps) => Result
|
||||
) {
|
||||
let previous: Deps | undefined;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Animated,
|
||||
I18nManager,
|
||||
Image,
|
||||
View,
|
||||
@@ -7,7 +8,6 @@ import {
|
||||
StyleSheet,
|
||||
LayoutChangeEvent,
|
||||
} from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
import MaskedView from '../MaskedView';
|
||||
import TouchableItem from '../TouchableItem';
|
||||
@@ -52,7 +52,7 @@ export default function HeaderBackButton({
|
||||
: 'rgba(0, 0, 0, .32)';
|
||||
|
||||
const handleLabelLayout = (e: LayoutChangeEvent) => {
|
||||
onLabelLayout && onLabelLayout(e);
|
||||
onLabelLayout?.(e);
|
||||
|
||||
setInitialLabelWidth(e.nativeEvent.layout.x + e.nativeEvent.layout.width);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet, Platform, ViewProps } from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import { Animated, StyleSheet, Platform, ViewProps } from 'react-native';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
|
||||
export default function HeaderBackground({ style, ...rest }: ViewProps) {
|
||||
|
||||
@@ -21,7 +21,7 @@ export type Props = {
|
||||
mode: 'float' | 'screen';
|
||||
layout: Layout;
|
||||
insets: EdgeInsets;
|
||||
scenes: Array<Scene<Route<string>> | undefined>;
|
||||
scenes: (Scene<Route<string>> | undefined)[];
|
||||
state: StackNavigationState;
|
||||
getPreviousRoute: (props: {
|
||||
route: Route<string>;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Animated,
|
||||
View,
|
||||
StyleSheet,
|
||||
LayoutChangeEvent,
|
||||
Platform,
|
||||
ViewStyle,
|
||||
} from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import { Route } from '@react-navigation/native';
|
||||
import HeaderBackButton from './HeaderBackButton';
|
||||
@@ -114,8 +114,8 @@ export default class HeaderSegment extends React.Component<Props, State> {
|
||||
(
|
||||
styleInterpolator: StackHeaderStyleInterpolator,
|
||||
layout: Layout,
|
||||
current: Animated.Node<number>,
|
||||
next: Animated.Node<number> | undefined,
|
||||
current: Animated.AnimatedInterpolation,
|
||||
next: Animated.AnimatedInterpolation | undefined,
|
||||
titleLayout: Layout | undefined,
|
||||
leftLabelLayout: Layout | undefined
|
||||
) =>
|
||||
@@ -272,6 +272,7 @@ export default class HeaderSegment extends React.Component<Props, State> {
|
||||
// @ts-ignore
|
||||
if (safeStyles[styleProp] === undefined) {
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete safeStyles[styleProp];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet, Platform, TextProps } from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import { Animated, StyleSheet, Platform, TextProps } from 'react-native';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
|
||||
type Props = TextProps & {
|
||||
|
||||
@@ -11,14 +11,15 @@ type Props = {
|
||||
};
|
||||
|
||||
export default class KeyboardManager extends React.Component<Props> {
|
||||
componentWillUnmount = () => {
|
||||
componentWillUnmount() {
|
||||
this.clearKeyboardTimeout();
|
||||
};
|
||||
}
|
||||
|
||||
// Numeric id of the previously focused text input
|
||||
// When a gesture didn't change the tab, we can restore the focused input with this
|
||||
private previouslyFocusedTextInput: number | null = null;
|
||||
private startTimestamp: number = 0;
|
||||
private keyboardTimeout: NodeJS.Timeout | undefined;
|
||||
private keyboardTimeout: any;
|
||||
|
||||
private clearKeyboardTimeout = () => {
|
||||
if (this.keyboardTimeout !== undefined) {
|
||||
@@ -31,6 +32,7 @@ export default class KeyboardManager extends React.Component<Props> {
|
||||
if (!this.props.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.clearKeyboardTimeout();
|
||||
|
||||
const input = TextInput.State.currentlyFocusedField();
|
||||
@@ -49,6 +51,7 @@ export default class KeyboardManager extends React.Component<Props> {
|
||||
if (!this.props.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.clearKeyboardTimeout();
|
||||
|
||||
Keyboard.dismiss();
|
||||
@@ -61,6 +64,7 @@ export default class KeyboardManager extends React.Component<Props> {
|
||||
if (!this.props.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.clearKeyboardTimeout();
|
||||
|
||||
// The page didn't change, we should restore the focus of text input
|
||||
|
||||