mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-13 17:32:55 +08:00
Compare commits
51 Commits
@react-nav
...
@react-nav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2697355ab2 | ||
|
|
a695cf9c05 | ||
|
|
c9c825bee6 | ||
|
|
b172b51f17 | ||
|
|
9c05af50b4 | ||
|
|
24febf6ea9 | ||
|
|
8cbb201f1a | ||
|
|
2467ce4ff7 | ||
|
|
5683bebfd6 | ||
|
|
78485cea69 | ||
|
|
1613915669 | ||
|
|
335a04edc1 | ||
|
|
5e0069a896 | ||
|
|
249248e741 | ||
|
|
821343fed3 | ||
|
|
82edb2581b | ||
|
|
cb67530dc5 | ||
|
|
36689e24c2 | ||
|
|
6e51f596fa | ||
|
|
402df73aa2 | ||
|
|
187aefe9c4 | ||
|
|
2613a62874 | ||
|
|
6bdf6ae4ed | ||
|
|
e2bcf5168c | ||
|
|
dfdba8d741 | ||
|
|
a3f7a5feba | ||
|
|
004c7d7ab1 | ||
|
|
49f658fbc0 | ||
|
|
cb2f157a56 | ||
|
|
c4acdaa703 | ||
|
|
f1a8bceba5 | ||
|
|
44081172d4 | ||
|
|
de5d985f3b | ||
|
|
b71de6cc79 | ||
|
|
303f0b78a5 | ||
|
|
ce3994c82c | ||
|
|
ba1f405129 | ||
|
|
d4fd906915 | ||
|
|
b7fa90bf8d | ||
|
|
9556aa9eff | ||
|
|
9a8fea8f2c | ||
|
|
9973db86f0 | ||
|
|
8432e5ab25 | ||
|
|
9bb5cfded3 | ||
|
|
4ac40b5c5d | ||
|
|
cd47915861 | ||
|
|
d649fbc669 | ||
|
|
105da6ab2f | ||
|
|
ac7f972e92 | ||
|
|
babb5027f9 | ||
|
|
78d7a66b2b |
4
.github/workflows/triage.yml
vendored
4
.github/workflows/triage.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
args: comment "Hey! Thanks for opening the issue. Can you provide more information about the issue? Please fill the issue template when opening the issue without deleting any section. We need all the information we can to be able to help. Make sure to at least provide - Current behaviour, Expected behaviour, A way to reproduce the issue with minimal code (link to [snack.expo.io](https://snack.expo.io)) or a repo on GitHub, and the information about your environment (such as the platform of the device, exact versions of all the packages mentioned in the template etc.)."
|
||||
args: comment "Hey! Thanks for opening the issue. Can you provide more information about the issue? Please fill the issue template when opening the issue without deleting any section. We need all the information we can to be able to help. Make sure to at least provide - Current behaviour, Expected behaviour, A way to [reproduce the issue with minimal code](https://stackoverflow.com/help/minimal-reproducible-example) (link to [snack.expo.io](https://snack.expo.io)) or a repo on GitHub, and the information about your environment (such as the platform of the device, exact versions of all the packages mentioned in the template etc.)."
|
||||
|
||||
needs-repro:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
args: comment "Hey! Thanks for opening the issue. Can you provide a minimal repro which demonstrates the issue? Posting a snippet of your code in the issue is useful, but it's not usually straightforward to run. A repro will help us debug the issue faster. Please try to keep the repro as small as possible. The easiest way to provide a repro is on [snack.expo.io](https://snack.expo.io). If it's not possible to repro it on [snack.expo.io](https://snack.expo.io), then you can also provide the repro in a GitHub repository."
|
||||
args: comment "Hey! Thanks for opening the issue. Can you provide a [minimal repro](https://stackoverflow.com/help/minimal-reproducible-example) which demonstrates the issue? Posting a snippet of your code in the issue is useful, but it's not usually straightforward to run. A repro will help us debug the issue faster. Please try to keep the repro as small as possible. The easiest way to provide a repro is on [snack.expo.io](https://snack.expo.io). If it's not possible to repro it on [snack.expo.io](https://snack.expo.io), then you can also provide the repro in a GitHub repository."
|
||||
|
||||
question:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
27
.github/workflows/versions.yml
vendored
Normal file
27
.github/workflows/versions.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Check versions
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
check-versions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: react-navigation/check-versions-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
packages: |
|
||||
@react-navigation/bottom-tabs
|
||||
@react-navigation/compat
|
||||
@react-navigation/core
|
||||
@react-navigation/drawer
|
||||
@react-navigation/material-bottom-tabs
|
||||
@react-navigation/material-top-tabs
|
||||
@react-navigation/native
|
||||
@react-navigation/routers
|
||||
@react-navigation/stack
|
||||
react-navigation-animated-switch
|
||||
react-navigation-drawer
|
||||
react-navigation-material-bottom-tabs
|
||||
react-navigation-stack
|
||||
react-navigation-tabs
|
||||
@@ -7,7 +7,7 @@
|
||||
"slug": "react-navigation-example",
|
||||
"description": "Demo app to showcase various functionality of React Navigation",
|
||||
"privacy": "public",
|
||||
"sdkVersion": "36.0.0",
|
||||
"sdkVersion": "37.0.0",
|
||||
"platforms": [
|
||||
"ios",
|
||||
"android",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable jest/no-jasmine-globals, import/no-commonjs */
|
||||
/* eslint-disable import/no-commonjs */
|
||||
|
||||
const detox = require('detox');
|
||||
const config = require('../../package.json').detox;
|
||||
|
||||
@@ -12,31 +12,30 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/vector-icons": "^10.0.0",
|
||||
"@react-native-community/masked-view": "0.1.7",
|
||||
"@types/react-native-restart": "^0.0.0",
|
||||
"@react-native-community/masked-view": "^0.1.7",
|
||||
"color": "^3.1.2",
|
||||
"expo": "^36.0.2",
|
||||
"expo-asset": "~8.0.0",
|
||||
"expo-blur": "^8.0.0",
|
||||
"expo": "^37.0.0",
|
||||
"expo-asset": "~8.1.3",
|
||||
"expo-blur": "~8.1.0",
|
||||
"react": "~16.9.0",
|
||||
"react-dom": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-gesture-handler": "^1.6.0",
|
||||
"react-native-paper": "^3.6.0",
|
||||
"react-native-paper": "^3.7.0",
|
||||
"react-native-reanimated": "^1.7.0",
|
||||
"react-native-restart": "^0.0.14",
|
||||
"react-native-safe-area-context": "^0.7.3",
|
||||
"react-native-screens": "^2.3.0",
|
||||
"react-native-tab-view": "2.13.0",
|
||||
"react-native-unimodules": "^0.7.0",
|
||||
"react-native-tab-view": "2.14.0",
|
||||
"react-native-unimodules": "~0.8.1",
|
||||
"react-native-web": "^0.11.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@expo/webpack-config": "^0.11.7",
|
||||
"@expo/webpack-config": "^0.11.19",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-native": "^0.61.22",
|
||||
"babel-preset-expo": "^8.0.0",
|
||||
"expo-cli": "^3.13.8",
|
||||
"typescript": "^3.7.5"
|
||||
"@types/react-native": "^0.60.22",
|
||||
"babel-preset-expo": "^8.1.0",
|
||||
"expo-cli": "^3.17.18",
|
||||
"typescript": "^3.8.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import { View, ScrollView, StyleSheet } from 'react-native';
|
||||
import { Button } from 'react-native-paper';
|
||||
import {
|
||||
createCompatNavigatorFactory,
|
||||
@@ -11,25 +11,30 @@ import {
|
||||
} from '@react-navigation/stack';
|
||||
import Article from '../Shared/Article';
|
||||
import Albums from '../Shared/Albums';
|
||||
import NewsFeed from '../Shared/NewsFeed';
|
||||
|
||||
type CompatStackParams = {
|
||||
Article: { author: string };
|
||||
Album: undefined;
|
||||
Albums: undefined;
|
||||
Nested: { author: string };
|
||||
};
|
||||
|
||||
const ArticleScreen: CompatScreenType<StackNavigationProp<
|
||||
CompatStackParams,
|
||||
'Article'
|
||||
type NestedStackParams = {
|
||||
Feed: undefined;
|
||||
Article: { author: string };
|
||||
};
|
||||
|
||||
const AlbumsScreen: CompatScreenType<StackNavigationProp<
|
||||
CompatStackParams
|
||||
>> = ({ navigation }) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ScrollView>
|
||||
<View style={styles.buttons}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('Album')}
|
||||
onPress={() => navigation.push('Nested', { author: 'Babel fish' })}
|
||||
style={styles.button}
|
||||
>
|
||||
Push album
|
||||
Push nested
|
||||
</Button>
|
||||
<Button
|
||||
mode="outlined"
|
||||
@@ -39,24 +44,20 @@ const ArticleScreen: CompatScreenType<StackNavigationProp<
|
||||
Go back
|
||||
</Button>
|
||||
</View>
|
||||
<Article author={{ name: navigation.getParam('author') }} />
|
||||
</React.Fragment>
|
||||
<Albums scrollEnabled={false} />
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
ArticleScreen.navigationOptions = ({ navigation }) => ({
|
||||
title: `Article by ${navigation.getParam('author')}`,
|
||||
});
|
||||
|
||||
const AlbumsScreen: CompatScreenType<StackNavigationProp<
|
||||
CompatStackParams
|
||||
>> = ({ navigation }) => {
|
||||
const FeedScreen: CompatScreenType<StackNavigationProp<NestedStackParams>> = ({
|
||||
navigation,
|
||||
}) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ScrollView>
|
||||
<View style={styles.buttons}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('Article', { author: 'Babel fish' })}
|
||||
onPress={() => navigation.push('Article')}
|
||||
style={styles.button}
|
||||
>
|
||||
Push article
|
||||
@@ -69,22 +70,69 @@ const AlbumsScreen: CompatScreenType<StackNavigationProp<
|
||||
Go back
|
||||
</Button>
|
||||
</View>
|
||||
<Albums />
|
||||
</React.Fragment>
|
||||
<NewsFeed scrollEnabled={false} />
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
const CompatStack = createCompatNavigatorFactory(createStackNavigator)<
|
||||
const ArticleScreen: CompatScreenType<StackNavigationProp<
|
||||
NestedStackParams,
|
||||
'Article'
|
||||
>> = ({ navigation }) => {
|
||||
navigation.dangerouslyGetParent();
|
||||
return (
|
||||
<ScrollView>
|
||||
<View style={styles.buttons}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('Albums')}
|
||||
style={styles.button}
|
||||
>
|
||||
Push albums
|
||||
</Button>
|
||||
<Button
|
||||
mode="outlined"
|
||||
onPress={() => navigation.goBack()}
|
||||
style={styles.button}
|
||||
>
|
||||
Go back
|
||||
</Button>
|
||||
</View>
|
||||
<Article
|
||||
author={{ name: navigation.getParam('author') }}
|
||||
scrollEnabled={false}
|
||||
/>
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
ArticleScreen.navigationOptions = ({ navigation }) => ({
|
||||
title: `Article by ${navigation.getParam('author')}`,
|
||||
});
|
||||
|
||||
const createCompatStackNavigator = createCompatNavigatorFactory(
|
||||
createStackNavigator
|
||||
);
|
||||
|
||||
const CompatStack = createCompatStackNavigator<
|
||||
StackNavigationProp<CompatStackParams>
|
||||
>(
|
||||
{
|
||||
Article: {
|
||||
screen: ArticleScreen,
|
||||
Albums: AlbumsScreen,
|
||||
Nested: {
|
||||
screen: createCompatStackNavigator<
|
||||
StackNavigationProp<NestedStackParams>
|
||||
>(
|
||||
{
|
||||
Feed: FeedScreen,
|
||||
Article: ArticleScreen,
|
||||
},
|
||||
{ navigationOptions: { headerShown: false } }
|
||||
),
|
||||
params: {
|
||||
author: 'Gandalf',
|
||||
},
|
||||
},
|
||||
Album: AlbumsScreen,
|
||||
},
|
||||
{
|
||||
mode: 'modal',
|
||||
|
||||
127
example/src/Screens/MasterDetail.tsx
Normal file
127
example/src/Screens/MasterDetail.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import * as React from 'react';
|
||||
import { Dimensions, ScaledSize } from 'react-native';
|
||||
import { Appbar } from 'react-native-paper';
|
||||
import { ParamListBase } from '@react-navigation/native';
|
||||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
import {
|
||||
createDrawerNavigator,
|
||||
DrawerNavigationProp,
|
||||
DrawerContent,
|
||||
} from '@react-navigation/drawer';
|
||||
import Article from '../Shared/Article';
|
||||
import Albums from '../Shared/Albums';
|
||||
import NewsFeed from '../Shared/NewsFeed';
|
||||
|
||||
type DrawerParams = {
|
||||
Article: undefined;
|
||||
NewsFeed: undefined;
|
||||
Album: undefined;
|
||||
};
|
||||
|
||||
type DrawerNavigation = DrawerNavigationProp<DrawerParams>;
|
||||
|
||||
const useIsLargeScreen = () => {
|
||||
const [dimensions, setDimensions] = React.useState(Dimensions.get('window'));
|
||||
|
||||
React.useEffect(() => {
|
||||
const onDimensionsChange = ({ window }: { window: ScaledSize }) => {
|
||||
setDimensions(window);
|
||||
};
|
||||
|
||||
Dimensions.addEventListener('change', onDimensionsChange);
|
||||
|
||||
return () => Dimensions.removeEventListener('change', onDimensionsChange);
|
||||
}, []);
|
||||
|
||||
return dimensions.width > 414;
|
||||
};
|
||||
|
||||
const Header = ({
|
||||
onGoBack,
|
||||
title,
|
||||
}: {
|
||||
onGoBack: () => void;
|
||||
title: string;
|
||||
}) => {
|
||||
const isLargeScreen = useIsLargeScreen();
|
||||
|
||||
return (
|
||||
<Appbar.Header>
|
||||
{isLargeScreen ? null : <Appbar.BackAction onPress={onGoBack} />}
|
||||
<Appbar.Content title={title} />
|
||||
</Appbar.Header>
|
||||
);
|
||||
};
|
||||
|
||||
const ArticleScreen = ({ navigation }: { navigation: DrawerNavigation }) => {
|
||||
return (
|
||||
<>
|
||||
<Header title="Article" onGoBack={() => navigation.toggleDrawer()} />
|
||||
<Article />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const NewsFeedScreen = ({ navigation }: { navigation: DrawerNavigation }) => {
|
||||
return (
|
||||
<>
|
||||
<Header title="Feed" onGoBack={() => navigation.toggleDrawer()} />
|
||||
<NewsFeed />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const AlbumsScreen = ({ navigation }: { navigation: DrawerNavigation }) => {
|
||||
return (
|
||||
<>
|
||||
<Header title="Albums" onGoBack={() => navigation.toggleDrawer()} />
|
||||
<Albums />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Drawer = createDrawerNavigator<DrawerParams>();
|
||||
|
||||
type Props = Partial<React.ComponentProps<typeof Drawer.Navigator>> & {
|
||||
navigation: StackNavigationProp<ParamListBase>;
|
||||
};
|
||||
|
||||
export default function DrawerScreen({ navigation, ...rest }: Props) {
|
||||
navigation.setOptions({
|
||||
headerShown: false,
|
||||
gestureEnabled: false,
|
||||
});
|
||||
|
||||
const isLargeScreen = useIsLargeScreen();
|
||||
|
||||
return (
|
||||
<Drawer.Navigator
|
||||
openByDefault
|
||||
drawerType={isLargeScreen ? 'permanent' : 'back'}
|
||||
drawerStyle={isLargeScreen ? null : { width: '100%' }}
|
||||
overlayColor="transparent"
|
||||
drawerContent={(props) => (
|
||||
<>
|
||||
<Appbar.Header>
|
||||
<Appbar.Action icon="close" onPress={() => navigation.goBack()} />
|
||||
<Appbar.Content title="Pages" />
|
||||
</Appbar.Header>
|
||||
<DrawerContent {...props} />
|
||||
</>
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
<Drawer.Screen name="Article" component={ArticleScreen} />
|
||||
<Drawer.Screen
|
||||
name="NewsFeed"
|
||||
component={NewsFeedScreen}
|
||||
options={{ title: 'Feed' }}
|
||||
/>
|
||||
<Drawer.Screen
|
||||
name="Album"
|
||||
component={AlbumsScreen}
|
||||
options={{ title: 'Album' }}
|
||||
/>
|
||||
</Drawer.Navigator>
|
||||
);
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ScrollViewProps,
|
||||
Dimensions,
|
||||
Platform,
|
||||
ScaledSize,
|
||||
} from 'react-native';
|
||||
import { useScrollToTop } from '@react-navigation/native';
|
||||
|
||||
@@ -40,15 +41,38 @@ const COVERS = [
|
||||
];
|
||||
|
||||
export default function Albums(props: Partial<ScrollViewProps>) {
|
||||
const [dimensions, setDimensions] = React.useState(Dimensions.get('window'));
|
||||
|
||||
React.useEffect(() => {
|
||||
const onDimensionsChange = ({ window }: { window: ScaledSize }) => {
|
||||
setDimensions(window);
|
||||
};
|
||||
|
||||
Dimensions.addEventListener('change', onDimensionsChange);
|
||||
|
||||
return () => Dimensions.removeEventListener('change', onDimensionsChange);
|
||||
}, []);
|
||||
|
||||
const ref = React.useRef<ScrollView>(null);
|
||||
|
||||
useScrollToTop(ref);
|
||||
|
||||
const itemSize = dimensions.width / Math.floor(dimensions.width / 150);
|
||||
|
||||
return (
|
||||
<ScrollView ref={ref} contentContainerStyle={styles.content} {...props}>
|
||||
{COVERS.map((source, i) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<View key={i} style={styles.item}>
|
||||
<View
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
key={i}
|
||||
style={[
|
||||
styles.item,
|
||||
Platform.OS !== 'web' && {
|
||||
height: itemSize,
|
||||
width: itemSize,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Image source={source} style={styles.photo} />
|
||||
</View>
|
||||
))}
|
||||
@@ -76,10 +100,6 @@ const styles = StyleSheet.create({
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
item: {
|
||||
height: Dimensions.get('window').width / 2,
|
||||
width: '50%',
|
||||
},
|
||||
},
|
||||
}),
|
||||
photo: {
|
||||
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
} from '@react-navigation/stack';
|
||||
|
||||
import LinkingPrefixes from './LinkingPrefixes';
|
||||
import SettingsItem from './Shared/SettingsItem';
|
||||
import SimpleStack from './Screens/SimpleStack';
|
||||
import ModalPresentationStack from './Screens/ModalPresentationStack';
|
||||
import StackTransparent from './Screens/StackTransparent';
|
||||
@@ -53,12 +54,15 @@ import MaterialBottomTabs from './Screens/MaterialBottomTabs';
|
||||
import DynamicTabs from './Screens/DynamicTabs';
|
||||
import AuthFlow from './Screens/AuthFlow';
|
||||
import CompatAPI from './Screens/CompatAPI';
|
||||
import SettingsItem from './Shared/SettingsItem';
|
||||
import MasterDetail from './Screens/MasterDetail';
|
||||
|
||||
YellowBox.ignoreWarnings(['Require cycle:', 'Warning: Async Storage']);
|
||||
|
||||
enableScreens();
|
||||
|
||||
// @ts-ignore
|
||||
global.REACT_NAVIGATION_REDUX_DEVTOOLS_EXTENSION_INTEGRATION_ENABLED = true;
|
||||
|
||||
type RootDrawerParamList = {
|
||||
Root: undefined;
|
||||
Another: undefined;
|
||||
@@ -97,6 +101,10 @@ const SCREENS = {
|
||||
title: 'Dynamic Tabs',
|
||||
component: DynamicTabs,
|
||||
},
|
||||
MasterDetail: {
|
||||
title: 'Master Detail',
|
||||
component: MasterDetail,
|
||||
},
|
||||
AuthFlow: {
|
||||
title: 'Auth Flow',
|
||||
component: AuthFlow,
|
||||
@@ -116,7 +124,7 @@ const THEME_PERSISTENCE_KEY = 'THEME_TYPE';
|
||||
Asset.loadAsync(StackAssets);
|
||||
|
||||
export default function App() {
|
||||
const containerRef = React.useRef<NavigationContainerRef>();
|
||||
const containerRef = React.useRef<NavigationContainerRef>(null);
|
||||
|
||||
// To test deep linking on, run the following in the Terminal:
|
||||
// Android: adb shell am start -a android.intent.action.VIEW -d "exp://127.0.0.1:19000/--/simple-stack"
|
||||
@@ -214,7 +222,7 @@ export default function App() {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isLargeScreen = dimensions.width > 900;
|
||||
const isLargeScreen = dimensions.width >= 1024;
|
||||
|
||||
return (
|
||||
<PaperProvider theme={paperTheme}>
|
||||
|
||||
5
netlify.toml
Normal file
5
netlify.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
[build]
|
||||
base = "/"
|
||||
publish = "example/web-build"
|
||||
command = "yarn example expo build:web"
|
||||
26
package.json
26
package.json
@@ -18,7 +18,7 @@
|
||||
"author": "Satyajit Sahoo <satyajit.happy@gmail.com> (https://github.com/satya164/), Michał Osadnik <micosa97@gmail.com> (https://github.com/osdnk/)",
|
||||
"scripts": {
|
||||
"lint": "eslint --ext '.js,.ts,.tsx' .",
|
||||
"typescript": "tsc --noEmit",
|
||||
"typescript": "tsc --noEmit --composite false",
|
||||
"test": "jest",
|
||||
"prerelease": "lerna run clean",
|
||||
"release": "lerna publish",
|
||||
@@ -26,25 +26,25 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.8.3",
|
||||
"@babel/preset-env": "^7.8.7",
|
||||
"@babel/preset-flow": "^7.8.3",
|
||||
"@babel/preset-react": "^7.8.3",
|
||||
"@babel/preset-typescript": "^7.8.3",
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.9.0",
|
||||
"@babel/preset-env": "^7.9.0",
|
||||
"@babel/preset-flow": "^7.9.0",
|
||||
"@babel/preset-react": "^7.9.4",
|
||||
"@babel/preset-typescript": "^7.9.0",
|
||||
"@babel/runtime": "^7.9.2",
|
||||
"@commitlint/config-conventional": "^8.3.4",
|
||||
"@types/jest": "^25.1.4",
|
||||
"@types/jest": "^25.2.1",
|
||||
"babel-jest": "^25.2.6",
|
||||
"codecov": "^3.6.5",
|
||||
"commitlint": "^8.3.5",
|
||||
"core-js": "^3.6.4",
|
||||
"detox": "^16.0.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-satya164": "^3.1.5",
|
||||
"eslint-config-satya164": "^3.1.6",
|
||||
"husky": "^4.2.3",
|
||||
"jest": "^25.1.0",
|
||||
"jest": "^25.2.7",
|
||||
"lerna": "^3.20.2",
|
||||
"prettier": "^2.0.1",
|
||||
"typescript": "^3.7.5"
|
||||
"prettier": "^2.0.4",
|
||||
"typescript": "^3.8.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"react": "~16.9.0",
|
||||
|
||||
@@ -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.2.8](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.7...@react-navigation/bottom-tabs@5.2.8) (2020-04-27)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.7](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.6...@react-navigation/bottom-tabs@5.2.7) (2020-04-17)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.6](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.5...@react-navigation/bottom-tabs@5.2.6) (2020-04-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* mark type exports for all packages ([b71de6c](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/b71de6cc799143f1d0e8a0cfcc34f0a2381f9840))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.5](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.4...@react-navigation/bottom-tabs@5.2.5) (2020-03-30)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.4](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.3...@react-navigation/bottom-tabs@5.2.4) (2020-03-23)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/bottom-tabs",
|
||||
"description": "Bottom tab navigator following iOS design guidelines",
|
||||
"version": "5.2.4",
|
||||
"version": "5.2.8",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -35,7 +35,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@react-navigation/native": "^5.1.3",
|
||||
"@react-navigation/native": "^5.1.7",
|
||||
"@types/color": "^3.0.1",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-native": "^0.61.22",
|
||||
@@ -44,7 +44,7 @@
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-safe-area-context": "^0.7.3",
|
||||
"react-native-screens": "^2.3.0",
|
||||
"typescript": "^3.7.5"
|
||||
"typescript": "^3.8.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.5",
|
||||
|
||||
@@ -12,7 +12,7 @@ export { default as BottomTabBar } from './views/BottomTabBar';
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
export {
|
||||
export type {
|
||||
BottomTabNavigationOptions,
|
||||
BottomTabNavigationProp,
|
||||
BottomTabBarProps,
|
||||
|
||||
@@ -138,7 +138,7 @@ export type BottomTabBarOptions = {
|
||||
*/
|
||||
inactiveTintColor?: string;
|
||||
/**
|
||||
* Background olor for the active tab.
|
||||
* Background color for the active tab.
|
||||
*/
|
||||
activeBackgroundColor?: string;
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,45 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.1.10](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.9...@react-navigation/compat@5.1.10) (2020-04-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix typo in navigationOptions ([8cbb201](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/commit/8cbb201f1a7fb90e45a078df6bc42ce4771cc6a6))
|
||||
* spread parent params to children in compat navigator ([24febf6](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/commit/24febf6ea99be2e5f22005fdd2a82136d647255c)), closes [#6785](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/issues/6785)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.9](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.8...@react-navigation/compat@5.1.9) (2020-04-17)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.8](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.7...@react-navigation/compat@5.1.8) (2020-04-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use 1 as default in compatibility pop action ([4408117](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/commit/44081172d440c713ad3543a2d5e1e18ebc8f72a4))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.7](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.6...@react-navigation/compat@5.1.7) (2020-03-30)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.6](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.5...@react-navigation/compat@5.1.6) (2020-03-23)
|
||||
|
||||
**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.1.6",
|
||||
"version": "5.1.10",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/compat",
|
||||
"bugs": {
|
||||
@@ -26,10 +26,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@react-navigation/native": "^5.1.3",
|
||||
"@react-navigation/native": "^5.1.7",
|
||||
"@types/react": "^16.9.23",
|
||||
"react": "~16.9.0",
|
||||
"typescript": "^3.7.5"
|
||||
"typescript": "^3.8.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.5",
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
NavigationProp,
|
||||
RouteProp,
|
||||
EventMapBase,
|
||||
NavigationRouteContext,
|
||||
} from '@react-navigation/native';
|
||||
import CompatScreen from './CompatScreen';
|
||||
import ScreenPropsContext from './ScreenPropsContext';
|
||||
@@ -67,6 +68,9 @@ export default function createCompatNavigatorFactory<
|
||||
const routeNames = order !== undefined ? order : Object.keys(routeConfig);
|
||||
|
||||
function Navigator({ screenProps }: { screenProps?: unknown }) {
|
||||
const parentRouteParams = React.useContext(NavigationRouteContext)
|
||||
?.params;
|
||||
|
||||
const screens = React.useMemo(
|
||||
() =>
|
||||
routeNames.map((name) => {
|
||||
@@ -135,7 +139,7 @@ export default function createCompatNavigatorFactory<
|
||||
<Pair.Screen
|
||||
key={name}
|
||||
name={name}
|
||||
initialParams={initialParams}
|
||||
initialParams={{ ...parentRouteParams, ...initialParams }}
|
||||
options={screenOptions}
|
||||
>
|
||||
{({ navigation, route }) => (
|
||||
@@ -148,7 +152,7 @@ export default function createCompatNavigatorFactory<
|
||||
</Pair.Screen>
|
||||
);
|
||||
}),
|
||||
[screenProps]
|
||||
[parentRouteParams, screenProps]
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -163,7 +167,7 @@ export default function createCompatNavigatorFactory<
|
||||
);
|
||||
}
|
||||
|
||||
Navigator.navigationOtions = parentNavigationOptions;
|
||||
Navigator.navigationOptions = parentNavigationOptions;
|
||||
|
||||
return Navigator;
|
||||
};
|
||||
|
||||
@@ -57,7 +57,7 @@ export function push(routeName: string, params?: object, action?: never) {
|
||||
});
|
||||
}
|
||||
|
||||
export function pop(n: number) {
|
||||
export function pop(n: number = 1) {
|
||||
return StackActions.pop(typeof n === 'number' ? { n } : n);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,53 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.3.5](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.3.4...@react-navigation/core@5.3.5) (2020-04-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add config to enable redux devtools integration ([c9c825b](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/c9c825bee61426635a28ee149eeeff3d628171cd))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.4](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.3.3...@react-navigation/core@5.3.4) (2020-04-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add initial option for navigating to nested navigators ([004c7d7](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/004c7d7ab1f80faf04b2a1836ec6b79a5419e45f))
|
||||
* add initial param for actions from deep link ([a3f7a5f](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/a3f7a5feba2e6aa2158aeaea6cde73ae1603173e))
|
||||
* handle initial: false for nested route after first initialization ([187aefe](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/187aefe9c400b499f920c212bf856414e25c5aaf))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.3](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.3.2...@react-navigation/core@5.3.3) (2020-04-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* switch order of focus and blur events. closes [#7963](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/7963) ([ce3994c](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/ce3994c82c28669d5742017eb7627e9adf996933))
|
||||
* workaround warning about setState in another component in render ([d4fd906](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/d4fd906915cc20d6fb21508384c05a540d8644d8))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.2](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.3.1...@react-navigation/core@5.3.2) (2020-03-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* handle no path property and undefined query params ([#7911](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/7911)) ([cd47915](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/cd47915861a56cd7eaa9ac79f5139cde56ca95a7))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.1](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.3.0...@react-navigation/core@5.3.1) (2020-03-23)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/core",
|
||||
"description": "Core utilities for building navigators",
|
||||
"version": "5.3.1",
|
||||
"version": "5.3.5",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-native",
|
||||
@@ -29,24 +29,23 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.2.0",
|
||||
"@react-navigation/routers": "^5.4.1",
|
||||
"escape-string-regexp": "^2.0.0",
|
||||
"query-string": "^6.11.1",
|
||||
"nanoid": "^3.0.2",
|
||||
"query-string": "^6.12.0",
|
||||
"react-is": "^16.13.0",
|
||||
"shortid": "^2.2.15",
|
||||
"use-subscription": "^1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-is": "^16.7.1",
|
||||
"@types/shortid": "^0.0.29",
|
||||
"@types/use-subscription": "^1.0.0",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
"react-native-testing-library": "^1.12.0",
|
||||
"react-test-renderer": "~16.9.0",
|
||||
"typescript": "^3.7.5"
|
||||
"react-test-renderer": "~16.13.1",
|
||||
"typescript": "^3.8.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
|
||||
@@ -9,17 +9,21 @@ import {
|
||||
} from '@react-navigation/routers';
|
||||
import EnsureSingleNavigator from './EnsureSingleNavigator';
|
||||
import NavigationBuilderContext from './NavigationBuilderContext';
|
||||
import { ScheduleUpdateContext } from './useScheduleUpdate';
|
||||
import useFocusedListeners from './useFocusedListeners';
|
||||
import useDevTools from './useDevTools';
|
||||
import useStateGetters from './useStateGetters';
|
||||
import useEventEmitter from './useEventEmitter';
|
||||
import useSyncState from './useSyncState';
|
||||
import isSerializable from './isSerializable';
|
||||
|
||||
import { NavigationContainerRef, NavigationContainerProps } from './types';
|
||||
import useEventEmitter from './useEventEmitter';
|
||||
import useSyncState from './useSyncState';
|
||||
|
||||
type State = NavigationState | PartialState<NavigationState> | undefined;
|
||||
|
||||
const DEVTOOLS_CONFIG_KEY =
|
||||
'REACT_NAVIGATION_REDUX_DEVTOOLS_EXTENSION_INTEGRATION_ENABLED';
|
||||
|
||||
const MISSING_CONTEXT_ERROR =
|
||||
"Couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'? See https://reactnavigation.org/docs/getting-started for setup instructions.";
|
||||
|
||||
@@ -102,7 +106,7 @@ const BaseNavigationContainer = React.forwardRef(
|
||||
independent,
|
||||
children,
|
||||
}: NavigationContainerProps,
|
||||
ref: React.Ref<NavigationContainerRef>
|
||||
ref?: React.Ref<NavigationContainerRef>
|
||||
) {
|
||||
const parent = React.useContext(NavigationStateContext);
|
||||
|
||||
@@ -112,7 +116,13 @@ const BaseNavigationContainer = React.forwardRef(
|
||||
);
|
||||
}
|
||||
|
||||
const [state, getState, setState] = useSyncState<State>(() =>
|
||||
const [
|
||||
state,
|
||||
getState,
|
||||
setState,
|
||||
scheduleUpdate,
|
||||
flushUpdates,
|
||||
] = useSyncState<State>(() =>
|
||||
getPartialState(initialState == null ? undefined : initialState)
|
||||
);
|
||||
|
||||
@@ -136,7 +146,9 @@ const BaseNavigationContainer = React.forwardRef(
|
||||
);
|
||||
|
||||
const { trackState, trackAction } = useDevTools({
|
||||
enabled: false,
|
||||
enabled:
|
||||
// @ts-ignore
|
||||
DEVTOOLS_CONFIG_KEY in global ? global[DEVTOOLS_CONFIG_KEY] : false,
|
||||
name: '@react-navigation',
|
||||
reset,
|
||||
state,
|
||||
@@ -218,6 +230,11 @@ const BaseNavigationContainer = React.forwardRef(
|
||||
[addFocusedListener, trackAction, addStateGetter]
|
||||
);
|
||||
|
||||
const scheduleContext = React.useMemo(
|
||||
() => ({ scheduleUpdate, flushUpdates }),
|
||||
[scheduleUpdate, flushUpdates]
|
||||
);
|
||||
|
||||
const context = React.useMemo(
|
||||
() => ({
|
||||
state,
|
||||
@@ -263,11 +280,13 @@ const BaseNavigationContainer = React.forwardRef(
|
||||
}, [onStateChange, trackState, getRootState, emitter, state]);
|
||||
|
||||
return (
|
||||
<NavigationBuilderContext.Provider value={builderContext}>
|
||||
<NavigationStateContext.Provider value={context}>
|
||||
<EnsureSingleNavigator>{children}</EnsureSingleNavigator>
|
||||
</NavigationStateContext.Provider>
|
||||
</NavigationBuilderContext.Provider>
|
||||
<ScheduleUpdateContext.Provider value={scheduleContext}>
|
||||
<NavigationBuilderContext.Provider value={builderContext}>
|
||||
<NavigationStateContext.Provider value={context}>
|
||||
<EnsureSingleNavigator>{children}</EnsureSingleNavigator>
|
||||
</NavigationStateContext.Provider>
|
||||
</NavigationBuilderContext.Provider>
|
||||
</ScheduleUpdateContext.Provider>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -35,8 +35,10 @@ it('gets navigate action from state', () => {
|
||||
author: 'jane',
|
||||
},
|
||||
screen: 'qux',
|
||||
initial: true,
|
||||
},
|
||||
screen: 'bar',
|
||||
initial: true,
|
||||
},
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
@@ -70,9 +72,11 @@ it('gets navigate action from state', () => {
|
||||
payload: {
|
||||
name: 'foo',
|
||||
params: {
|
||||
initial: true,
|
||||
screen: 'bar',
|
||||
params: {
|
||||
screen: 'quz',
|
||||
initial: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -521,3 +521,209 @@ it('returns "/" for empty path', () => {
|
||||
|
||||
expect(getPathFromState(state, config)).toBe('/');
|
||||
});
|
||||
|
||||
it('parses no path specified', () => {
|
||||
const path = '/Foo/bar';
|
||||
const config = {
|
||||
Foo: {
|
||||
screens: {
|
||||
Foe: {},
|
||||
},
|
||||
},
|
||||
Bar: 'bar',
|
||||
};
|
||||
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'Foo',
|
||||
state: {
|
||||
routes: [{ name: 'Bar' }],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||
});
|
||||
|
||||
it('parses no path specified in nested config', () => {
|
||||
const path = '/Foo/Foe/bar';
|
||||
const config = {
|
||||
Foo: {
|
||||
path: 'foo',
|
||||
screens: {
|
||||
Foe: {},
|
||||
},
|
||||
},
|
||||
Bar: 'bar',
|
||||
};
|
||||
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'Foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Foe',
|
||||
state: {
|
||||
routes: [{ name: 'Bar' }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||
});
|
||||
|
||||
it('strips undefined query params', () => {
|
||||
const path = '/bar/sweet/apple/foo/bis/jane?count=10&valid=true';
|
||||
const config = {
|
||||
Foo: {
|
||||
path: 'foo',
|
||||
screens: {
|
||||
Foe: {
|
||||
path: 'foe',
|
||||
},
|
||||
},
|
||||
},
|
||||
Bar: 'bar/:type/:fruit',
|
||||
Baz: {
|
||||
path: 'baz',
|
||||
screens: {
|
||||
Bos: 'bos',
|
||||
Bis: {
|
||||
path: 'bis/:author',
|
||||
stringify: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, (c) => c.toLowerCase()),
|
||||
},
|
||||
parse: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, (c) => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'Bar',
|
||||
params: { fruit: 'apple', type: 'sweet' },
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Baz',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Bis',
|
||||
params: {
|
||||
author: 'Jane',
|
||||
count: 10,
|
||||
answer: undefined,
|
||||
valid: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||
});
|
||||
|
||||
it('handles stripping all query params', () => {
|
||||
const path = '/bar/sweet/apple/foo/bis/jane';
|
||||
const config = {
|
||||
Foo: {
|
||||
path: 'foo',
|
||||
screens: {
|
||||
Foe: {
|
||||
path: 'foe',
|
||||
},
|
||||
},
|
||||
},
|
||||
Bar: 'bar/:type/:fruit',
|
||||
Baz: {
|
||||
path: 'baz',
|
||||
screens: {
|
||||
Bos: 'bos',
|
||||
Bis: {
|
||||
path: 'bis/:author',
|
||||
stringify: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, (c) => c.toLowerCase()),
|
||||
},
|
||||
parse: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, (c) => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'Bar',
|
||||
params: { fruit: 'apple', type: 'sweet' },
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Baz',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Bis',
|
||||
params: {
|
||||
author: 'Jane',
|
||||
count: undefined,
|
||||
answer: undefined,
|
||||
valid: undefined,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||
});
|
||||
|
||||
@@ -626,7 +626,7 @@ it('updates route params with setParams applied to parent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('handles change in route names', () => {
|
||||
it('handles change in route names', async () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
useNavigationBuilder(MockRouter, props);
|
||||
return null;
|
||||
@@ -635,7 +635,7 @@ it('handles change in route names', () => {
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const root = render(
|
||||
<BaseNavigationContainer onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator initialRouteName="bar">
|
||||
<Screen name="foo" component={jest.fn()} />
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
@@ -737,6 +737,366 @@ it('navigates to nested child in a navigator', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('navigates to nested child in a navigator with initial: false', () => {
|
||||
const TestRouter: typeof MockRouter = (options) => {
|
||||
const router = MockRouter(options);
|
||||
|
||||
return {
|
||||
...router,
|
||||
|
||||
getStateForAction(state, action, options) {
|
||||
switch (action.type) {
|
||||
case 'NAVIGATE': {
|
||||
if (!options.routeNames.includes(action.payload.name as any)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const routes = [
|
||||
...state.routes,
|
||||
{
|
||||
key: String(MockRouterKey.current++),
|
||||
name: action.payload.name,
|
||||
params: action.payload.params,
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
...state,
|
||||
index: routes.length - 1,
|
||||
routes,
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
return router.getStateForAction(state, action, options);
|
||||
}
|
||||
},
|
||||
} as typeof router;
|
||||
};
|
||||
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(TestRouter, props);
|
||||
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const TestComponent = ({ route }: any): any =>
|
||||
`[${route.name}, ${JSON.stringify(route.params)}]`;
|
||||
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const navigation = React.createRef<NavigationContainerRef>();
|
||||
|
||||
const first = render(
|
||||
<BaseNavigationContainer ref={navigation} onStateChange={onStateChange}>
|
||||
<TestNavigator>
|
||||
<Screen name="foo">
|
||||
{() => (
|
||||
<TestNavigator>
|
||||
<Screen name="foo-a" component={TestComponent} />
|
||||
<Screen name="foo-b" component={TestComponent} />
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
<Screen name="bar">
|
||||
{() => (
|
||||
<TestNavigator initialRouteName="bar-a">
|
||||
<Screen
|
||||
name="bar-a"
|
||||
component={TestComponent}
|
||||
initialParams={{ lol: 'why' }}
|
||||
/>
|
||||
<Screen
|
||||
name="bar-b"
|
||||
component={TestComponent}
|
||||
initialParams={{ some: 'stuff' }}
|
||||
/>
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(first).toMatchInlineSnapshot(`"[foo-a, undefined]"`);
|
||||
expect(navigation.current?.getRootState()).toEqual({
|
||||
index: 0,
|
||||
key: '0',
|
||||
routeNames: ['foo', 'bar'],
|
||||
routes: [
|
||||
{
|
||||
key: 'foo',
|
||||
name: 'foo',
|
||||
state: {
|
||||
index: 0,
|
||||
key: '1',
|
||||
routeNames: ['foo-a', 'foo-b'],
|
||||
routes: [
|
||||
{
|
||||
key: 'foo-a',
|
||||
name: 'foo-a',
|
||||
},
|
||||
{
|
||||
key: 'foo-b',
|
||||
name: 'foo-b',
|
||||
},
|
||||
],
|
||||
stale: false,
|
||||
type: 'test',
|
||||
},
|
||||
},
|
||||
{ key: 'bar', name: 'bar' },
|
||||
],
|
||||
stale: false,
|
||||
type: 'test',
|
||||
});
|
||||
|
||||
act(
|
||||
() =>
|
||||
navigation.current &&
|
||||
navigation.current.navigate('bar', {
|
||||
screen: 'bar-b',
|
||||
params: { test: 42 },
|
||||
})
|
||||
);
|
||||
|
||||
expect(first).toMatchInlineSnapshot(
|
||||
`"[bar-b, {\\"some\\":\\"stuff\\",\\"test\\":42}]"`
|
||||
);
|
||||
|
||||
expect(navigation.current?.getRootState()).toEqual({
|
||||
index: 2,
|
||||
key: '0',
|
||||
routeNames: ['foo', 'bar'],
|
||||
routes: [
|
||||
{ key: 'foo', name: 'foo' },
|
||||
{ key: 'bar', name: 'bar' },
|
||||
{
|
||||
key: '2',
|
||||
name: 'bar',
|
||||
params: { params: { test: 42 }, screen: 'bar-b' },
|
||||
state: {
|
||||
index: 1,
|
||||
key: '3',
|
||||
routeNames: ['bar-a', 'bar-b'],
|
||||
routes: [
|
||||
{
|
||||
key: 'bar-a',
|
||||
name: 'bar-a',
|
||||
params: { lol: 'why' },
|
||||
},
|
||||
{
|
||||
key: 'bar-b',
|
||||
name: 'bar-b',
|
||||
params: { some: 'stuff', test: 42 },
|
||||
},
|
||||
],
|
||||
stale: false,
|
||||
type: 'test',
|
||||
},
|
||||
},
|
||||
],
|
||||
stale: false,
|
||||
type: 'test',
|
||||
});
|
||||
|
||||
const second = render(
|
||||
<BaseNavigationContainer ref={navigation} onStateChange={onStateChange}>
|
||||
<TestNavigator>
|
||||
<Screen name="foo">
|
||||
{() => (
|
||||
<TestNavigator>
|
||||
<Screen name="foo-a" component={TestComponent} />
|
||||
<Screen name="foo-b" component={TestComponent} />
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
<Screen name="bar">
|
||||
{() => (
|
||||
<TestNavigator initialRouteName="bar-a">
|
||||
<Screen
|
||||
name="bar-a"
|
||||
component={TestComponent}
|
||||
initialParams={{ lol: 'why' }}
|
||||
/>
|
||||
<Screen
|
||||
name="bar-b"
|
||||
component={TestComponent}
|
||||
initialParams={{ some: 'stuff' }}
|
||||
/>
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(second).toMatchInlineSnapshot(`"[foo-a, undefined]"`);
|
||||
expect(navigation.current?.getRootState()).toEqual({
|
||||
index: 0,
|
||||
key: '4',
|
||||
routeNames: ['foo', 'bar'],
|
||||
routes: [
|
||||
{
|
||||
key: 'foo',
|
||||
name: 'foo',
|
||||
state: {
|
||||
index: 0,
|
||||
key: '5',
|
||||
routeNames: ['foo-a', 'foo-b'],
|
||||
routes: [
|
||||
{ key: 'foo-a', name: 'foo-a' },
|
||||
{ key: 'foo-b', name: 'foo-b' },
|
||||
],
|
||||
stale: false,
|
||||
type: 'test',
|
||||
},
|
||||
},
|
||||
{ key: 'bar', name: 'bar' },
|
||||
],
|
||||
stale: false,
|
||||
type: 'test',
|
||||
});
|
||||
|
||||
act(
|
||||
() =>
|
||||
navigation.current &&
|
||||
navigation.current.navigate('bar', {
|
||||
screen: 'bar-b',
|
||||
params: { test: 42 },
|
||||
initial: false,
|
||||
})
|
||||
);
|
||||
|
||||
expect(second).toMatchInlineSnapshot(`"[bar-b, {\\"test\\":42}]"`);
|
||||
|
||||
expect(navigation.current?.getRootState()).toEqual({
|
||||
index: 2,
|
||||
key: '4',
|
||||
routeNames: ['foo', 'bar'],
|
||||
routes: [
|
||||
{ key: 'foo', name: 'foo' },
|
||||
{ key: 'bar', name: 'bar' },
|
||||
{
|
||||
key: '6',
|
||||
name: 'bar',
|
||||
params: { initial: false, params: { test: 42 }, screen: 'bar-b' },
|
||||
state: {
|
||||
index: 2,
|
||||
key: '7',
|
||||
routeNames: ['bar-a', 'bar-b'],
|
||||
routes: [
|
||||
{
|
||||
key: 'bar-a',
|
||||
name: 'bar-a',
|
||||
params: { lol: 'why' },
|
||||
},
|
||||
{
|
||||
key: 'bar-b',
|
||||
name: 'bar-b',
|
||||
params: { some: 'stuff' },
|
||||
},
|
||||
{ key: '8', name: 'bar-b', params: { test: 42 } },
|
||||
],
|
||||
stale: false,
|
||||
type: 'test',
|
||||
},
|
||||
},
|
||||
],
|
||||
stale: false,
|
||||
type: 'test',
|
||||
});
|
||||
|
||||
const third = render(
|
||||
<BaseNavigationContainer
|
||||
ref={navigation}
|
||||
initialState={{
|
||||
index: 1,
|
||||
routes: [
|
||||
{ name: 'foo' },
|
||||
{
|
||||
name: 'bar',
|
||||
params: { initial: false, params: { test: 42 }, screen: 'bar-b' },
|
||||
state: {
|
||||
index: 1,
|
||||
key: '7',
|
||||
routes: [
|
||||
{
|
||||
name: 'bar-a',
|
||||
params: { lol: 'why' },
|
||||
},
|
||||
{
|
||||
name: 'bar-b',
|
||||
params: { some: 'stuff' },
|
||||
},
|
||||
],
|
||||
type: 'test',
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'test',
|
||||
}}
|
||||
>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={TestComponent} />
|
||||
<Screen name="bar">
|
||||
{() => (
|
||||
<TestNavigator initialRouteName="bar-a">
|
||||
<Screen
|
||||
name="bar-a"
|
||||
component={TestComponent}
|
||||
initialParams={{ lol: 'why' }}
|
||||
/>
|
||||
<Screen
|
||||
name="bar-b"
|
||||
component={TestComponent}
|
||||
initialParams={{ some: 'stuff' }}
|
||||
/>
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(third).toMatchInlineSnapshot(`"[bar-b, {\\"some\\":\\"stuff\\"}]"`);
|
||||
|
||||
expect(navigation.current?.getRootState()).toEqual({
|
||||
index: 1,
|
||||
key: '11',
|
||||
routeNames: ['foo', 'bar'],
|
||||
routes: [
|
||||
{ key: 'foo-9', name: 'foo' },
|
||||
{
|
||||
key: 'bar-10',
|
||||
name: 'bar',
|
||||
params: { initial: false, params: { test: 42 }, screen: 'bar-b' },
|
||||
state: {
|
||||
index: 1,
|
||||
key: '14',
|
||||
routeNames: ['bar-a', 'bar-b'],
|
||||
routes: [
|
||||
{
|
||||
key: 'bar-a-12',
|
||||
name: 'bar-a',
|
||||
params: { lol: 'why' },
|
||||
},
|
||||
{
|
||||
key: 'bar-b-13',
|
||||
name: 'bar-b',
|
||||
params: { some: 'stuff' },
|
||||
},
|
||||
],
|
||||
stale: false,
|
||||
type: 'test',
|
||||
},
|
||||
},
|
||||
],
|
||||
stale: false,
|
||||
type: 'test',
|
||||
});
|
||||
});
|
||||
|
||||
it('gives access to internal state', () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
@@ -262,8 +262,10 @@ it('sets initial options with setOptions', () => {
|
||||
};
|
||||
|
||||
const TestScreen = ({ navigation }: any): any => {
|
||||
navigation.setOptions({
|
||||
title: 'Hello world',
|
||||
React.useEffect(() => {
|
||||
navigation.setOptions({
|
||||
title: 'Hello world',
|
||||
});
|
||||
});
|
||||
|
||||
return 'Test screen';
|
||||
@@ -315,12 +317,12 @@ it('updates options with setOptions', () => {
|
||||
};
|
||||
|
||||
const TestScreen = ({ navigation }: any): any => {
|
||||
navigation.setOptions({
|
||||
title: 'Hello world',
|
||||
description: 'Something here',
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
navigation.setOptions({
|
||||
title: 'Hello world',
|
||||
description: 'Something here',
|
||||
});
|
||||
|
||||
const timer = setTimeout(() =>
|
||||
navigation.setOptions({
|
||||
title: 'Hello again',
|
||||
|
||||
@@ -97,6 +97,69 @@ it('fires focus and blur events in root navigator', () => {
|
||||
expect(fourthBlurCallback).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
it('fires focus event after blur', () => {
|
||||
const TestNavigator = React.forwardRef((props: any, ref: any): any => {
|
||||
const { state, navigation, descriptors } = useNavigationBuilder(
|
||||
MockRouter,
|
||||
props
|
||||
);
|
||||
|
||||
React.useImperativeHandle(ref, () => navigation, [navigation]);
|
||||
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
});
|
||||
|
||||
const callback = jest.fn();
|
||||
|
||||
const Test = ({ route, navigation }: any) => {
|
||||
React.useEffect(
|
||||
() =>
|
||||
navigation.addListener('focus', () => callback(route.name, 'focus')),
|
||||
[navigation, route.name]
|
||||
);
|
||||
|
||||
React.useEffect(
|
||||
() => navigation.addListener('blur', () => callback(route.name, 'blur')),
|
||||
[navigation, route.name]
|
||||
);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const navigation = React.createRef<any>();
|
||||
|
||||
const element = (
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator ref={navigation}>
|
||||
<Screen name="first" component={Test} />
|
||||
<Screen name="second" component={Test} />
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element);
|
||||
|
||||
expect(callback.mock.calls).toEqual([['first', 'focus']]);
|
||||
|
||||
act(() => navigation.current.navigate('second'));
|
||||
|
||||
expect(callback.mock.calls).toEqual([
|
||||
['first', 'focus'],
|
||||
['first', 'blur'],
|
||||
['second', 'focus'],
|
||||
]);
|
||||
|
||||
act(() => navigation.current.navigate('first'));
|
||||
|
||||
expect(callback.mock.calls).toEqual([
|
||||
['first', 'focus'],
|
||||
['first', 'blur'],
|
||||
['second', 'focus'],
|
||||
['second', 'blur'],
|
||||
['first', 'focus'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('fires focus and blur events in nested navigator', () => {
|
||||
const TestNavigator = React.forwardRef((props: any, ref: any): any => {
|
||||
const { state, navigation, descriptors } = useNavigationBuilder(
|
||||
|
||||
@@ -3,6 +3,7 @@ import { PartialState, NavigationState } from '@react-navigation/routers';
|
||||
type NavigateParams = {
|
||||
screen?: string;
|
||||
params?: NavigateParams;
|
||||
initial?: boolean;
|
||||
};
|
||||
|
||||
type NavigateAction = {
|
||||
@@ -35,6 +36,7 @@ export default function getActionFromState(
|
||||
}
|
||||
|
||||
route = current.routes[current.routes.length - 1];
|
||||
params.initial = current.routes.length === 1;
|
||||
params.screen = route.name;
|
||||
|
||||
if (route.state) {
|
||||
|
||||
@@ -64,6 +64,8 @@ export default function getPathFromState(
|
||||
};
|
||||
let currentOptions = options;
|
||||
let pattern = route.name;
|
||||
// we keep all the route names that appeared during going deeper in config in case the pattern is resolved to undefined
|
||||
let nestedRouteNames = '';
|
||||
|
||||
while (route.name in currentOptions) {
|
||||
if (typeof currentOptions[route.name] === 'string') {
|
||||
@@ -77,11 +79,13 @@ export default function getPathFromState(
|
||||
}).screens
|
||||
) {
|
||||
pattern = (currentOptions[route.name] as { path: string }).path;
|
||||
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
|
||||
break;
|
||||
} else {
|
||||
// if it is the end of state, we return pattern
|
||||
if (route.state === undefined) {
|
||||
pattern = (currentOptions[route.name] as { path: string }).path;
|
||||
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
|
||||
break;
|
||||
} else {
|
||||
index =
|
||||
@@ -92,11 +96,13 @@ export default function getPathFromState(
|
||||
}).screens;
|
||||
// if there is config for next route name, we go deeper
|
||||
if (nextRoute.name in deeperConfig) {
|
||||
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
|
||||
route = nextRoute as Route<string> & { state?: State };
|
||||
currentOptions = deeperConfig;
|
||||
} else {
|
||||
// if not, there is no sense in going deeper in config
|
||||
pattern = (currentOptions[route.name] as { path: string }).path;
|
||||
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -104,6 +110,11 @@ export default function getPathFromState(
|
||||
}
|
||||
}
|
||||
|
||||
if (pattern === undefined) {
|
||||
// cut the first `/`
|
||||
pattern = nestedRouteNames.substring(1);
|
||||
}
|
||||
|
||||
// we don't add empty path strings to path
|
||||
if (pattern !== '') {
|
||||
const config =
|
||||
@@ -147,6 +158,12 @@ export default function getPathFromState(
|
||||
if (route.state) {
|
||||
path += '/';
|
||||
} else if (params) {
|
||||
for (let param in params) {
|
||||
if (params[param] === 'undefined') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete params[param];
|
||||
}
|
||||
}
|
||||
const query = queryString.stringify(params);
|
||||
|
||||
if (query) {
|
||||
|
||||
@@ -410,24 +410,19 @@ export type RouteConfig<
|
||||
}
|
||||
);
|
||||
|
||||
export type NavigationContainerRef =
|
||||
| (NavigationHelpers<ParamListBase> &
|
||||
EventConsumer<{ state: { data: { state: NavigationState } } }> & {
|
||||
/**
|
||||
* Reset the navigation state of the root navigator to the provided state.
|
||||
*
|
||||
* @param state Navigation state object.
|
||||
*/
|
||||
resetRoot(
|
||||
state?: PartialState<NavigationState> | NavigationState
|
||||
): void;
|
||||
/**
|
||||
* Get the rehydrated navigation state of the navigation tree.
|
||||
*/
|
||||
getRootState(): NavigationState;
|
||||
})
|
||||
| undefined
|
||||
| null;
|
||||
export type NavigationContainerRef = NavigationHelpers<ParamListBase> &
|
||||
EventConsumer<{ state: { data: { state: NavigationState } } }> & {
|
||||
/**
|
||||
* Reset the navigation state of the root navigator to the provided state.
|
||||
*
|
||||
* @param state Navigation state object.
|
||||
*/
|
||||
resetRoot(state?: PartialState<NavigationState> | NavigationState): void;
|
||||
/**
|
||||
* Get the rehydrated navigation state of the navigation tree.
|
||||
*/
|
||||
getRootState(): NavigationState;
|
||||
};
|
||||
|
||||
export type TypedNavigator<
|
||||
ParamList extends ParamListBase,
|
||||
|
||||
@@ -48,7 +48,7 @@ export default function useFocusEvents({ state, emitter }: Options) {
|
||||
emitter.emit({ type: 'focus', target: currentFocusedKey });
|
||||
}
|
||||
|
||||
// We should only dispatch events when the focused key changed and navigator is focused
|
||||
// We should only emit events when the focused key changed and navigator is focused
|
||||
// When navigator is not focused, screens inside shouldn't receive focused status either
|
||||
if (
|
||||
lastFocusedKey === currentFocusedKey ||
|
||||
@@ -62,7 +62,7 @@ export default function useFocusEvents({ state, emitter }: Options) {
|
||||
return;
|
||||
}
|
||||
|
||||
emitter.emit({ type: 'focus', target: currentFocusedKey });
|
||||
emitter.emit({ type: 'blur', target: lastFocusedKey });
|
||||
emitter.emit({ type: 'focus', target: currentFocusedKey });
|
||||
}, [currentFocusedKey, emitter, navigation]);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
} from './types';
|
||||
import useStateGetters from './useStateGetters';
|
||||
import useOnGetState from './useOnGetState';
|
||||
import useScheduleUpdate from './useScheduleUpdate';
|
||||
|
||||
// This is to make TypeScript compiler happy
|
||||
// eslint-disable-next-line babel/no-unused-expressions
|
||||
@@ -42,6 +43,7 @@ type NavigatorRoute = {
|
||||
params?: {
|
||||
screen?: string;
|
||||
params?: object;
|
||||
initial?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -175,17 +177,19 @@ export default function useNavigationBuilder<
|
||||
| NavigatorRoute
|
||||
| undefined;
|
||||
|
||||
const previousRouteRef = React.useRef(route);
|
||||
const previousNestedParamsRef = React.useRef(route?.params);
|
||||
|
||||
React.useEffect(() => {
|
||||
previousRouteRef.current = route;
|
||||
previousNestedParamsRef.current = route?.params;
|
||||
}, [route]);
|
||||
|
||||
const { children, ...rest } = options;
|
||||
const { current: router } = React.useRef<Router<State, any>>(
|
||||
createRouter({
|
||||
...((rest as unknown) as RouterOptions),
|
||||
...(route?.params && typeof route.params.screen === 'string'
|
||||
...(route?.params &&
|
||||
route.params.initial !== false &&
|
||||
typeof route.params.screen === 'string'
|
||||
? { initialRouteName: route.params.screen }
|
||||
: null),
|
||||
})
|
||||
@@ -218,7 +222,7 @@ export default function useNavigationBuilder<
|
||||
(acc, curr) => {
|
||||
const { initialParams } = screens[curr];
|
||||
const initialParamsFromParams =
|
||||
route?.params && route.params.screen === curr
|
||||
route?.params?.initial !== false && route?.params?.screen === curr
|
||||
? route.params.params
|
||||
: undefined;
|
||||
|
||||
@@ -265,6 +269,8 @@ export default function useNavigationBuilder<
|
||||
>();
|
||||
const initializedStateRef = React.useRef<State>();
|
||||
|
||||
let isFirstStateInitialization = false;
|
||||
|
||||
if (
|
||||
initializedStateRef.current === undefined ||
|
||||
currentState !== previousStateRef.current
|
||||
@@ -273,16 +279,21 @@ export default function useNavigationBuilder<
|
||||
// We also need to re-initialize it if the state passed from parent was changed (maybe due to reset)
|
||||
// Otherwise assume that the state was provided as initial state
|
||||
// So we need to rehydrate it to make it usable
|
||||
initializedStateRef.current =
|
||||
currentState === undefined || !isStateValid(currentState)
|
||||
? router.getInitialState({
|
||||
routeNames,
|
||||
routeParamList,
|
||||
})
|
||||
: router.getRehydratedState(currentState as PartialState<State>, {
|
||||
routeNames,
|
||||
routeParamList,
|
||||
});
|
||||
if (currentState === undefined || !isStateValid(currentState)) {
|
||||
isFirstStateInitialization = true;
|
||||
initializedStateRef.current = router.getInitialState({
|
||||
routeNames,
|
||||
routeParamList,
|
||||
});
|
||||
} else {
|
||||
initializedStateRef.current = router.getRehydratedState(
|
||||
currentState as PartialState<State>,
|
||||
{
|
||||
routeNames,
|
||||
routeParamList,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
@@ -308,16 +319,14 @@ export default function useNavigationBuilder<
|
||||
}
|
||||
|
||||
if (
|
||||
previousRouteRef.current &&
|
||||
route &&
|
||||
route.params &&
|
||||
typeof route.params.screen === 'string' &&
|
||||
route.params !== previousRouteRef.current.params
|
||||
typeof route?.params?.screen === 'string' &&
|
||||
(route.params !== previousNestedParamsRef.current ||
|
||||
(route.params.initial === false && isFirstStateInitialization))
|
||||
) {
|
||||
// If the route was updated with new name and/or params, we should navigate there
|
||||
// The update should be limited to current navigator only, so we call the router manually
|
||||
const updatedState = router.getStateForAction(
|
||||
state,
|
||||
nextState,
|
||||
CommonActions.navigate(route.params.screen, route.params.params),
|
||||
{
|
||||
routeNames,
|
||||
@@ -331,17 +340,17 @@ export default function useNavigationBuilder<
|
||||
routeNames,
|
||||
routeParamList,
|
||||
})
|
||||
: state;
|
||||
: nextState;
|
||||
}
|
||||
|
||||
const shouldUpdate = state !== nextState;
|
||||
|
||||
React.useEffect(() => {
|
||||
useScheduleUpdate(() => {
|
||||
if (shouldUpdate) {
|
||||
// If the state needs to be updated, we'll schedule an update with React
|
||||
// If the state needs to be updated, we'll schedule an update
|
||||
setState(nextState);
|
||||
}
|
||||
}, [nextState, setState, shouldUpdate]);
|
||||
});
|
||||
|
||||
// The up-to-date state will come in next render, but we don't need to wait for it
|
||||
// We can't use the outdated state since the screens have changed, which will cause error due to mismatched config
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import shortid from 'shortid';
|
||||
import { nanoid } from 'nanoid/non-secure';
|
||||
import { SingleNavigatorContext } from './EnsureSingleNavigator';
|
||||
|
||||
/**
|
||||
@@ -7,7 +7,7 @@ import { SingleNavigatorContext } from './EnsureSingleNavigator';
|
||||
* This is used to prevent multiple navigators under a single container or screen.
|
||||
*/
|
||||
export default function useRegisterNavigator() {
|
||||
const [key] = React.useState(() => shortid());
|
||||
const [key] = React.useState(() => nanoid());
|
||||
const container = React.useContext(SingleNavigatorContext);
|
||||
|
||||
if (container === undefined) {
|
||||
|
||||
32
packages/core/src/useScheduleUpdate.tsx
Normal file
32
packages/core/src/useScheduleUpdate.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import * as React from 'react';
|
||||
|
||||
const MISSING_CONTEXT_ERROR = "Couldn't find a schedule context.";
|
||||
|
||||
export const ScheduleUpdateContext = React.createContext<{
|
||||
scheduleUpdate: (callback: () => void) => void;
|
||||
flushUpdates: () => void;
|
||||
}>({
|
||||
scheduleUpdate() {
|
||||
throw new Error(MISSING_CONTEXT_ERROR);
|
||||
},
|
||||
flushUpdates() {
|
||||
throw new Error(MISSING_CONTEXT_ERROR);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* When screen config changes, we want to update the navigator in the same update phase.
|
||||
* However, navigation state is in the root component and React won't let us update it from a child.
|
||||
* This is a workaround for that, the scheduled update is stored in the ref without actually calling setState.
|
||||
* It lets all subsequent updates access the latest state so it stays correct.
|
||||
* Then we call setState during after the component updates.
|
||||
*/
|
||||
export default function useScheduleUpdate(callback: () => void) {
|
||||
const { scheduleUpdate, flushUpdates } = React.useContext(
|
||||
ScheduleUpdateContext
|
||||
);
|
||||
|
||||
scheduleUpdate(callback);
|
||||
|
||||
React.useEffect(flushUpdates);
|
||||
}
|
||||
@@ -2,8 +2,12 @@ import * as React from 'react';
|
||||
|
||||
const UNINTIALIZED_STATE = {};
|
||||
|
||||
/**
|
||||
* This is definitely not compatible with concurrent mode, but we don't have a solution for sync state yet.
|
||||
*/
|
||||
export default function useSyncState<T>(initialState?: (() => T) | T) {
|
||||
const stateRef = React.useRef<T>(UNINTIALIZED_STATE as any);
|
||||
const isSchedulingRef = React.useRef(false);
|
||||
|
||||
if (stateRef.current === UNINTIALIZED_STATE) {
|
||||
stateRef.current =
|
||||
@@ -11,7 +15,7 @@ export default function useSyncState<T>(initialState?: (() => T) | T) {
|
||||
typeof initialState === 'function' ? initialState() : initialState;
|
||||
}
|
||||
|
||||
const [state, setTrackingState] = React.useState(stateRef.current);
|
||||
const [trackingState, setTrackingState] = React.useState(stateRef.current);
|
||||
|
||||
const getState = React.useCallback(() => stateRef.current, []);
|
||||
|
||||
@@ -21,8 +25,35 @@ export default function useSyncState<T>(initialState?: (() => T) | T) {
|
||||
}
|
||||
|
||||
stateRef.current = state;
|
||||
setTrackingState(state);
|
||||
|
||||
if (!isSchedulingRef.current) {
|
||||
setTrackingState(state);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return [state, getState, setState] as const;
|
||||
const scheduleUpdate = React.useCallback((callback: () => void) => {
|
||||
isSchedulingRef.current = true;
|
||||
|
||||
try {
|
||||
callback();
|
||||
} finally {
|
||||
isSchedulingRef.current = false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const flushUpdates = React.useCallback(() => {
|
||||
// Make sure that the tracking state is up-to-date.
|
||||
// We call it unconditionally, but React should skip the update if state is unchanged.
|
||||
setTrackingState(stateRef.current);
|
||||
}, []);
|
||||
|
||||
// If we're rendering and the tracking state is out of date, update it immediately
|
||||
// This will make sure that our updates are applied as early as possible.
|
||||
if (trackingState !== stateRef.current) {
|
||||
setTrackingState(stateRef.current);
|
||||
}
|
||||
|
||||
const state = stateRef.current;
|
||||
|
||||
return [state, getState, setState, scheduleUpdate, flushUpdates] as const;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,59 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.5.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.5.0...@react-navigation/drawer@5.5.1) (2020-04-27)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.5.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.4.1...@react-navigation/drawer@5.5.0) (2020-04-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix drawer not closing on web ([e2bcf51](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/e2bcf5168c389833eaaeadb4b8794aaea4a66d17)), closes [#6759](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/6759)
|
||||
* webkit style error in overlay ([821343f](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/821343fed38577cfdc87a78f13f991d5760bf8f5))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add openByDefault option to drawer ([36689e2](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/36689e24c21b474692bb7ecd0b901c8afbbe9a20))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.4.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.4.0...@react-navigation/drawer@5.4.1) (2020-04-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* don't hide content from accessibility with permanent drawer ([cb2f157](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/cb2f157a561a2ce3f073eb4ccb567532c77bd869)), closes [#7976](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/7976)
|
||||
* mark type exports for all packages ([b71de6c](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/b71de6cc799143f1d0e8a0cfcc34f0a2381f9840))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.4.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.3.4...@react-navigation/drawer@5.4.0) (2020-03-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* disable only swipe gesture on safari ([105da6a](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/105da6ab2fe69847b676c4d4117638212cda1f9a))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add swipeEnabled option to disable swipe gesture in drawer ([#7834](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/7834)) ([ac7f972](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/ac7f972e922a82cd32d943356941d100b68bd8b0))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.4](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.3.3...@react-navigation/drawer@5.3.4) (2020-03-23)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/drawer",
|
||||
"description": "Drawer navigator component with animated transitions and gesturess",
|
||||
"version": "5.3.4",
|
||||
"version": "5.5.1",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -40,7 +40,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@react-navigation/native": "^5.1.3",
|
||||
"@react-navigation/native": "^5.1.7",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-native": "^0.61.22",
|
||||
"del-cli": "^3.0.0",
|
||||
@@ -50,7 +50,7 @@
|
||||
"react-native-reanimated": "^1.7.0",
|
||||
"react-native-safe-area-context": "^0.7.3",
|
||||
"react-native-screens": "^2.3.0",
|
||||
"typescript": "^3.7.5"
|
||||
"typescript": "^3.8.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.5",
|
||||
|
||||
@@ -22,7 +22,7 @@ export { default as useIsDrawerOpen } from './utils/useIsDrawerOpen';
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
export {
|
||||
export type {
|
||||
DrawerNavigationOptions,
|
||||
DrawerNavigationProp,
|
||||
DrawerContentOptions,
|
||||
|
||||
@@ -21,6 +21,7 @@ type Props = DefaultNavigatorOptions<DrawerNavigationOptions> &
|
||||
|
||||
function DrawerNavigator({
|
||||
initialRouteName,
|
||||
openByDefault,
|
||||
backBehavior,
|
||||
children,
|
||||
screenOptions,
|
||||
@@ -33,6 +34,7 @@ function DrawerNavigator({
|
||||
DrawerNavigationEventMap
|
||||
>(DrawerRouter, {
|
||||
initialRouteName,
|
||||
openByDefault,
|
||||
backBehavior,
|
||||
children,
|
||||
screenOptions,
|
||||
|
||||
@@ -111,9 +111,18 @@ export type DrawerNavigationOptions = {
|
||||
|
||||
/**
|
||||
* Whether you can use gestures to open or close the drawer.
|
||||
* Setting this to `false` disables swipe gestures as well as tap on overlay to close.
|
||||
* See `swipeEnabled` to disable only the swipe gesture.
|
||||
* Defaults to `true`
|
||||
*/
|
||||
gestureEnabled?: boolean;
|
||||
|
||||
/**
|
||||
* Whether you can use swipe gestures to open or close the drawer.
|
||||
* Defaults to `true`
|
||||
*/
|
||||
swipeEnabled?: boolean;
|
||||
|
||||
/**
|
||||
* Whether this screen should be unmounted when navigating away from it.
|
||||
* Defaults to `false`.
|
||||
|
||||
@@ -14,7 +14,8 @@ import {
|
||||
import {
|
||||
PanGestureHandler,
|
||||
TapGestureHandler,
|
||||
State,
|
||||
State as GestureState,
|
||||
TapGestureHandlerStateChangeEvent,
|
||||
} from 'react-native-gesture-handler';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import Overlay from './Overlay';
|
||||
@@ -80,6 +81,7 @@ type Props = {
|
||||
onClose: () => void;
|
||||
onGestureRef?: (ref: PanGestureHandler | null) => void;
|
||||
gestureEnabled: boolean;
|
||||
swipeEnabled: boolean;
|
||||
drawerPosition: 'left' | 'right';
|
||||
drawerType: 'front' | 'back' | 'slide' | 'permanent';
|
||||
keyboardDismissMode: 'none' | 'on-drag';
|
||||
@@ -94,32 +96,15 @@ type Props = {
|
||||
renderDrawerContent: Renderer;
|
||||
renderSceneContent: Renderer;
|
||||
gestureHandlerProps?: React.ComponentProps<typeof PanGestureHandler>;
|
||||
dimensions: { width: number; height: number };
|
||||
};
|
||||
|
||||
/**
|
||||
* Disables the pan gesture by default on Apple devices in the browser.
|
||||
* https://stackoverflow.com/a/9039885
|
||||
*/
|
||||
function shouldEnableGesture(): boolean {
|
||||
if (
|
||||
Platform.OS === 'web' &&
|
||||
typeof navigator !== 'undefined' &&
|
||||
typeof window !== 'undefined'
|
||||
) {
|
||||
const isWebAppleDevice =
|
||||
/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
||||
|
||||
return !isWebAppleDevice;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export default class DrawerView extends React.PureComponent<Props> {
|
||||
export default class DrawerView extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
drawerPostion: I18nManager.isRTL ? 'left' : 'right',
|
||||
drawerType: 'front',
|
||||
gestureEnabled: shouldEnableGesture(),
|
||||
gestureEnabled: true,
|
||||
swipeEnabled: Platform.OS !== 'web',
|
||||
swipeEdgeWidth: 32,
|
||||
swipeVelocityThreshold: 500,
|
||||
keyboardDismissMode: 'on-drag',
|
||||
@@ -138,16 +123,11 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
open,
|
||||
drawerPosition,
|
||||
drawerType,
|
||||
gestureEnabled,
|
||||
swipeDistanceThreshold,
|
||||
swipeVelocityThreshold,
|
||||
hideStatusBar,
|
||||
} = this.props;
|
||||
|
||||
if (prevProps.gestureEnabled !== gestureEnabled) {
|
||||
this.isGestureEnabled.setValue(gestureEnabled ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
if (
|
||||
// If we're not in the middle of a transition, sync the drawer's open state
|
||||
typeof this.pendingOpenValue !== 'boolean' ||
|
||||
@@ -217,30 +197,54 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
}
|
||||
};
|
||||
|
||||
private getDrawerWidth = (): number => {
|
||||
const { drawerStyle, dimensions } = this.props;
|
||||
const { width } = StyleSheet.flatten(drawerStyle);
|
||||
|
||||
if (typeof width === 'string' && width.endsWith('%')) {
|
||||
// Try to calculate width if a percentage is given
|
||||
const percentage = Number(width.replace(/%$/, ''));
|
||||
|
||||
if (Number.isFinite(percentage)) {
|
||||
return dimensions.width * (percentage / 100);
|
||||
}
|
||||
}
|
||||
|
||||
return typeof width === 'number' ? width : 0;
|
||||
};
|
||||
|
||||
private clock = new Clock();
|
||||
private interactionHandle: number | undefined;
|
||||
|
||||
private isDrawerTypeFront = new Value<Binary>(
|
||||
this.props.drawerType === 'front' ? TRUE : FALSE
|
||||
);
|
||||
private isGestureEnabled = new Value(
|
||||
this.props.gestureEnabled ? TRUE : FALSE
|
||||
);
|
||||
|
||||
private isOpen = new Value<Binary>(this.props.open ? TRUE : FALSE);
|
||||
private nextIsOpen = new Value<Binary | -1>(UNSET);
|
||||
private isSwiping = new Value<Binary>(FALSE);
|
||||
|
||||
private gestureState = new Value<number>(State.UNDETERMINED);
|
||||
private initialDrawerWidth = this.getDrawerWidth();
|
||||
|
||||
private gestureState = new Value<number>(GestureState.UNDETERMINED);
|
||||
private touchX = new Value<number>(0);
|
||||
private velocityX = new Value<number>(0);
|
||||
private gestureX = new Value<number>(0);
|
||||
private offsetX = new Value<number>(0);
|
||||
private position = new Value<number>(0);
|
||||
private position = new Value<number>(
|
||||
this.props.open
|
||||
? this.initialDrawerWidth *
|
||||
(this.props.drawerPosition === 'right'
|
||||
? DIRECTION_RIGHT
|
||||
: DIRECTION_LEFT)
|
||||
: 0
|
||||
);
|
||||
|
||||
private containerWidth = new Value<number>(0);
|
||||
private drawerWidth = new Value<number>(0);
|
||||
private drawerOpacity = new Value<number>(0);
|
||||
private containerWidth = new Value<number>(this.props.dimensions.width);
|
||||
private drawerWidth = new Value<number>(this.initialDrawerWidth);
|
||||
private drawerOpacity = new Value<number>(
|
||||
this.initialDrawerWidth || this.props.drawerType === 'permanent' ? 1 : 0
|
||||
);
|
||||
private drawerPosition = new Value<number>(
|
||||
this.props.drawerPosition === 'right' ? DIRECTION_RIGHT : DIRECTION_LEFT
|
||||
);
|
||||
@@ -418,12 +422,12 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
onChange(
|
||||
this.gestureState,
|
||||
cond(
|
||||
eq(this.gestureState, State.ACTIVE),
|
||||
eq(this.gestureState, GestureState.ACTIVE),
|
||||
call([], this.handleStartInteraction)
|
||||
)
|
||||
),
|
||||
cond(
|
||||
eq(this.gestureState, State.ACTIVE),
|
||||
eq(this.gestureState, GestureState.ACTIVE),
|
||||
[
|
||||
cond(this.isSwiping, NOOP, [
|
||||
// We weren't dragging before, set it to true
|
||||
@@ -507,14 +511,28 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
},
|
||||
]);
|
||||
|
||||
private handleTapStateChange = event([
|
||||
{
|
||||
nativeEvent: {
|
||||
oldState: (s: Animated.Value<number>) =>
|
||||
cond(eq(s, State.ACTIVE), set(this.manuallyTriggerSpring, TRUE)),
|
||||
},
|
||||
},
|
||||
]);
|
||||
private handleTapStateChange =
|
||||
Platform.OS === 'web'
|
||||
? // FIXME: Drawer doesn't close on Web with the same code that we use for native
|
||||
({ nativeEvent }: TapGestureHandlerStateChangeEvent) => {
|
||||
if (
|
||||
nativeEvent.state === GestureState.END &&
|
||||
nativeEvent.oldState === GestureState.ACTIVE
|
||||
) {
|
||||
this.toggleDrawer(false);
|
||||
}
|
||||
}
|
||||
: event([
|
||||
{
|
||||
nativeEvent: {
|
||||
oldState: (s: Animated.Value<number>) =>
|
||||
cond(
|
||||
eq(s, GestureState.ACTIVE),
|
||||
set(this.manuallyTriggerSpring, TRUE)
|
||||
),
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
private handleContainerLayout = (e: LayoutChangeEvent) =>
|
||||
this.containerWidth.setValue(e.nativeEvent.layout.width);
|
||||
@@ -554,6 +572,7 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
const {
|
||||
open,
|
||||
gestureEnabled,
|
||||
swipeEnabled,
|
||||
drawerPosition,
|
||||
drawerType,
|
||||
swipeEdgeWidth,
|
||||
@@ -569,9 +588,15 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
const isOpen = drawerType === 'permanent' ? true : open;
|
||||
const isRight = drawerPosition === 'right';
|
||||
|
||||
const contentTranslateX = drawerType === 'front' ? 0 : this.translateX;
|
||||
const contentTranslateX =
|
||||
drawerType === 'front' || drawerType === 'permanent'
|
||||
? 0
|
||||
: this.translateX;
|
||||
|
||||
const drawerTranslateX =
|
||||
drawerType === 'back'
|
||||
drawerType === 'permanent'
|
||||
? 0
|
||||
: drawerType === 'back'
|
||||
? I18nManager.isRTL
|
||||
? multiply(
|
||||
sub(this.containerWidth, this.drawerWidth),
|
||||
@@ -605,7 +630,7 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
onGestureEvent={this.handleGestureEvent}
|
||||
onHandlerStateChange={this.handleGestureStateChange}
|
||||
hitSlop={hitSlop}
|
||||
enabled={drawerType !== 'permanent' && gestureEnabled}
|
||||
enabled={drawerType !== 'permanent' && gestureEnabled && swipeEnabled}
|
||||
{...gestureHandlerProps}
|
||||
>
|
||||
<Animated.View
|
||||
@@ -621,31 +646,38 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.content,
|
||||
drawerType !== 'permanent' && {
|
||||
transform: [{ translateX: contentTranslateX }],
|
||||
},
|
||||
{ transform: [{ translateX: contentTranslateX }] },
|
||||
sceneContainerStyle as any,
|
||||
]}
|
||||
>
|
||||
<View
|
||||
accessibilityElementsHidden={isOpen}
|
||||
accessibilityElementsHidden={isOpen && drawerType !== 'permanent'}
|
||||
importantForAccessibility={
|
||||
isOpen ? 'no-hide-descendants' : 'auto'
|
||||
isOpen && drawerType !== 'permanent'
|
||||
? 'no-hide-descendants'
|
||||
: 'auto'
|
||||
}
|
||||
style={styles.content}
|
||||
>
|
||||
{renderSceneContent({ progress })}
|
||||
</View>
|
||||
{// Disable overlay if sidebar is permanent
|
||||
drawerType === 'permanent' ? null : (
|
||||
<TapGestureHandler
|
||||
enabled={gestureEnabled}
|
||||
onHandlerStateChange={this.handleTapStateChange}
|
||||
>
|
||||
<Overlay progress={progress} style={overlayStyle} />
|
||||
</TapGestureHandler>
|
||||
)}
|
||||
{
|
||||
// Disable overlay if sidebar is permanent
|
||||
drawerType === 'permanent' ? null : (
|
||||
<TapGestureHandler
|
||||
enabled={gestureEnabled}
|
||||
onHandlerStateChange={this.handleTapStateChange}
|
||||
>
|
||||
<Overlay progress={progress} style={overlayStyle} />
|
||||
</TapGestureHandler>
|
||||
)
|
||||
}
|
||||
</Animated.View>
|
||||
<Animated.Code
|
||||
// This is needed to make sure that container width updates with `setValue`
|
||||
// Without this, it won't update when not used in styles
|
||||
exec={this.containerWidth}
|
||||
/>
|
||||
{drawerType === 'permanent' ? null : (
|
||||
<Animated.Code
|
||||
exec={block([
|
||||
@@ -659,11 +691,15 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
/>
|
||||
)}
|
||||
<Animated.View
|
||||
accessibilityViewIsModal={isOpen}
|
||||
accessibilityViewIsModal={isOpen && drawerType !== 'permanent'}
|
||||
removeClippedSubviews={Platform.OS !== 'ios'}
|
||||
onLayout={this.handleDrawerLayout}
|
||||
style={[
|
||||
styles.container,
|
||||
{
|
||||
transform: [{ translateX: drawerTranslateX }],
|
||||
opacity: this.drawerOpacity,
|
||||
},
|
||||
drawerType === 'permanent'
|
||||
? // Without this, the `left`/`right` values don't get reset
|
||||
isRight
|
||||
@@ -671,10 +707,6 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
: { left: 0 }
|
||||
: [
|
||||
styles.nonPermanent,
|
||||
{
|
||||
transform: [{ translateX: drawerTranslateX }],
|
||||
opacity: this.drawerOpacity,
|
||||
},
|
||||
isRight ? { right: offset } : { left: offset },
|
||||
{ zIndex: drawerType === 'back' ? -1 : 0 },
|
||||
],
|
||||
|
||||
@@ -89,11 +89,9 @@ export default function DrawerView({
|
||||
sceneContainerStyle,
|
||||
}: Props) {
|
||||
const [loaded, setLoaded] = React.useState([state.index]);
|
||||
const [drawerWidth, setDrawerWidth] = React.useState(() => {
|
||||
const { height = 0, width = 0 } = Dimensions.get('window');
|
||||
|
||||
return getDefaultDrawerWidth({ height, width });
|
||||
});
|
||||
const [dimensions, setDimensions] = React.useState(() =>
|
||||
Dimensions.get('window')
|
||||
);
|
||||
|
||||
const drawerGestureRef = React.useRef<PanGestureHandler>(null);
|
||||
|
||||
@@ -141,13 +139,13 @@ export default function DrawerView({
|
||||
}, [handleDrawerClose, isDrawerOpen, navigation, state.key]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const updateWidth = ({ window }: { window: ScaledSize }) => {
|
||||
setDrawerWidth(getDefaultDrawerWidth(window));
|
||||
const updateDimensions = ({ window }: { window: ScaledSize }) => {
|
||||
setDimensions(window);
|
||||
};
|
||||
|
||||
Dimensions.addEventListener('change', updateWidth);
|
||||
Dimensions.addEventListener('change', updateDimensions);
|
||||
|
||||
return () => Dimensions.removeEventListener('change', updateWidth);
|
||||
return () => Dimensions.removeEventListener('change', updateDimensions);
|
||||
}, []);
|
||||
|
||||
if (!loaded.includes(state.index)) {
|
||||
@@ -200,7 +198,7 @@ export default function DrawerView({
|
||||
};
|
||||
|
||||
const activeKey = state.routes[state.index].key;
|
||||
const { gestureEnabled } = descriptors[activeKey].options;
|
||||
const { gestureEnabled, swipeEnabled } = descriptors[activeKey].options;
|
||||
|
||||
return (
|
||||
<GestureHandlerWrapper style={styles.content}>
|
||||
@@ -210,6 +208,7 @@ export default function DrawerView({
|
||||
<Drawer
|
||||
open={isDrawerOpen}
|
||||
gestureEnabled={gestureEnabled}
|
||||
swipeEnabled={swipeEnabled}
|
||||
onOpen={handleDrawerOpen}
|
||||
onClose={handleDrawerClose}
|
||||
onGestureRef={(ref) => {
|
||||
@@ -224,7 +223,10 @@ export default function DrawerView({
|
||||
sceneContainerStyle,
|
||||
]}
|
||||
drawerStyle={[
|
||||
{ width: drawerWidth, backgroundColor: colors.card },
|
||||
{
|
||||
width: getDefaultDrawerWidth(dimensions),
|
||||
backgroundColor: colors.card,
|
||||
},
|
||||
drawerType === 'permanent' &&
|
||||
(drawerPosition === 'left'
|
||||
? {
|
||||
@@ -246,6 +248,7 @@ export default function DrawerView({
|
||||
renderSceneContent={renderContent}
|
||||
keyboardDismissMode={keyboardDismissMode}
|
||||
drawerPostion={drawerPosition}
|
||||
dimensions={dimensions}
|
||||
/>
|
||||
</DrawerOpenContext.Provider>
|
||||
</DrawerGestureContext.Provider>
|
||||
|
||||
@@ -29,22 +29,24 @@ const Overlay = React.forwardRef(function Overlay(
|
||||
<Animated.View
|
||||
{...props}
|
||||
ref={ref}
|
||||
style={[styles.overlay, animatedStyle, style]}
|
||||
style={[styles.overlay, overlayStyle, animatedStyle, style]}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const overlayStyle = Platform.select<Record<string, string>>({
|
||||
web: {
|
||||
// Disable touch highlight on mobile Safari.
|
||||
// WebkitTapHighlightColor must be used outside of StyleSheet.create because react-native-web will omit the property.
|
||||
WebkitTapHighlightColor: 'transparent',
|
||||
},
|
||||
default: {},
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
...Platform.select({
|
||||
web: {
|
||||
// Disable touch highlight on mobile Safari.
|
||||
WebkitTapHighlightColor: 'transparent',
|
||||
},
|
||||
default: {},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -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.1.10](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.9...@react-navigation/material-bottom-tabs@5.1.10) (2020-04-27)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.9](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.8...@react-navigation/material-bottom-tabs@5.1.9) (2020-04-17)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.8](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.7...@react-navigation/material-bottom-tabs@5.1.8) (2020-04-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* mark type exports for all packages ([b71de6c](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/commit/b71de6cc799143f1d0e8a0cfcc34f0a2381f9840))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.7](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.6...@react-navigation/material-bottom-tabs@5.1.7) (2020-03-30)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.6](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.5...@react-navigation/material-bottom-tabs@5.1.6) (2020-03-23)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/material-bottom-tabs",
|
||||
"description": "Integration for bottom navigation component from react-native-paper",
|
||||
"version": "5.1.6",
|
||||
"version": "5.1.10",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -36,16 +36,16 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@react-navigation/native": "^5.1.3",
|
||||
"@react-navigation/native": "^5.1.7",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-native": "^0.61.22",
|
||||
"@types/react-native-vector-icons": "^6.4.5",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-paper": "^3.6.0",
|
||||
"react-native-paper": "^3.7.0",
|
||||
"react-native-vector-icons": "^6.6.0",
|
||||
"typescript": "^3.7.5"
|
||||
"typescript": "^3.8.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.5",
|
||||
|
||||
@@ -11,7 +11,7 @@ export { default as MaterialBottomTabView } from './views/MaterialBottomTabView'
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
export {
|
||||
export type {
|
||||
MaterialBottomTabNavigationOptions,
|
||||
MaterialBottomTabNavigationProp,
|
||||
} from './types';
|
||||
|
||||
@@ -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.1.10](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.9...@react-navigation/material-top-tabs@5.1.10) (2020-04-27)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.9](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.8...@react-navigation/material-top-tabs@5.1.9) (2020-04-17)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.8](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.7...@react-navigation/material-top-tabs@5.1.8) (2020-04-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* mark type exports for all packages ([b71de6c](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/commit/b71de6cc799143f1d0e8a0cfcc34f0a2381f9840))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.7](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.6...@react-navigation/material-top-tabs@5.1.7) (2020-03-30)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.6](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.5...@react-navigation/material-top-tabs@5.1.6) (2020-03-23)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/material-top-tabs",
|
||||
"description": "Integration for the animated tab view component from react-native-tab-view",
|
||||
"version": "5.1.6",
|
||||
"version": "5.1.10",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -39,7 +39,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@react-navigation/native": "^5.1.3",
|
||||
"@react-navigation/native": "^5.1.7",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-native": "^0.61.22",
|
||||
"del-cli": "^3.0.0",
|
||||
@@ -47,8 +47,8 @@
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-gesture-handler": "^1.6.0",
|
||||
"react-native-reanimated": "^1.7.0",
|
||||
"react-native-tab-view": "^2.13.0",
|
||||
"typescript": "^3.7.5"
|
||||
"react-native-tab-view": "^2.14.0",
|
||||
"typescript": "^3.8.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.5",
|
||||
|
||||
@@ -12,7 +12,7 @@ export { default as MaterialTopTabBar } from './views/MaterialTopTabBar';
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
export {
|
||||
export type {
|
||||
MaterialTopTabNavigationOptions,
|
||||
MaterialTopTabNavigationProp,
|
||||
MaterialTopTabBarProps,
|
||||
|
||||
@@ -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.1.7](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.6...@react-navigation/native@5.1.7) (2020-04-27)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.6](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.5...@react-navigation/native@5.1.6) (2020-04-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* handle in-page go back when there's no history ([6bdf6ae](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/6bdf6ae4ed0f83ac1deb3172d9075a6a2adbbe11)), closes [#7852](https://github.com/react-navigation/react-navigation/tree/master/packages/native/issues/7852)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.5](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.4...@react-navigation/native@5.1.5) (2020-04-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.4](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.3...@react-navigation/native@5.1.4) (2020-03-30)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.3](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.2...@react-navigation/native@5.1.3) (2020-03-23)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/native",
|
||||
"description": "React Native integration for React Navigation",
|
||||
"version": "5.1.3",
|
||||
"version": "5.1.7",
|
||||
"keywords": [
|
||||
"react-native",
|
||||
"react-navigation",
|
||||
@@ -31,7 +31,7 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/core": "^5.3.1"
|
||||
"@react-navigation/core": "^5.3.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
@@ -41,7 +41,7 @@
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-testing-library": "^1.12.0",
|
||||
"typescript": "^3.7.5"
|
||||
"typescript": "^3.8.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
|
||||
@@ -26,7 +26,7 @@ type Props = NavigationContainerProps & {
|
||||
*/
|
||||
const NavigationContainer = React.forwardRef(function NavigationContainer(
|
||||
{ theme = DefaultTheme, ...rest }: Props,
|
||||
ref: React.Ref<NavigationContainerRef>
|
||||
ref?: React.Ref<NavigationContainerRef | null>
|
||||
) {
|
||||
const refContainer = React.useRef<NavigationContainerRef>(null);
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ export type LinkingOptions = {
|
||||
*/
|
||||
getStateFromPath?: typeof getStateFromPathDefault;
|
||||
/**
|
||||
* Custom function to conver the state object to a valid URL (advanced).
|
||||
* Custom function to convert the state object to a valid URL (advanced).
|
||||
*/
|
||||
getPathFromState?: typeof getPathFromStateDefault;
|
||||
};
|
||||
|
||||
@@ -224,14 +224,14 @@ export default function useLinking(
|
||||
let index = history.state?.index ?? 0;
|
||||
|
||||
if (previousStateLength === stateLength) {
|
||||
// If no new enrties were added to history in our navigation state, we want to replaceState
|
||||
// If no new entries were added to history in our navigation state, we want to replaceState
|
||||
if (location.pathname + location.search !== path) {
|
||||
history.replaceState({ index }, '', path);
|
||||
previousHistoryIndexRef.current = index;
|
||||
}
|
||||
} else if (stateLength > previousStateLength) {
|
||||
// If new enrties were added, pushState until we have same length
|
||||
// This won't be accurate if multiple enrties were added at once, but that's the best we can do
|
||||
// If new entries were added, pushState until we have same length
|
||||
// This won't be accurate if multiple entries were added at once, but that's the best we can do
|
||||
for (let i = 0, l = stateLength - previousStateLength; i < l; i++) {
|
||||
index++;
|
||||
history.pushState({ index }, '', path);
|
||||
@@ -239,13 +239,27 @@ export default function useLinking(
|
||||
|
||||
previousHistoryIndexRef.current = index;
|
||||
} else if (previousStateLength > stateLength) {
|
||||
const delta = previousStateLength - stateLength;
|
||||
const delta = Math.min(
|
||||
previousStateLength - stateLength,
|
||||
// We need to keep at least one item in the history
|
||||
// Otherwise we'll exit the page
|
||||
previousHistoryIndexRef.current - 1
|
||||
);
|
||||
|
||||
// We need to set this to ignore the `popstate` event
|
||||
pendingIndexChangeRef.current = index - delta;
|
||||
if (delta > 0) {
|
||||
// We need to set this to ignore the `popstate` event
|
||||
pendingIndexChangeRef.current = index - delta;
|
||||
|
||||
// If new enrties were removed, go back so that we have same length
|
||||
history.go(-delta);
|
||||
// If new entries were removed, go back so that we have same length
|
||||
history.go(-delta);
|
||||
} else {
|
||||
// We're not going back in history, but the navigation state changed
|
||||
// The URL probably also changed, so we need to re-sync the URL
|
||||
if (location.pathname + location.search !== path) {
|
||||
history.replaceState({ index }, '', path);
|
||||
previousHistoryIndexRef.current = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,52 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.4.1](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.4.0...@react-navigation/routers@5.4.1) (2020-04-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix behaviour of openByDefault in drawer when focus changes ([b172b51](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/commit/b172b51f175a9f8044cb2a8e9d74a86480d8f11e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.4.0](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.3.0...@react-navigation/routers@5.4.0) (2020-04-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add openByDefault option to drawer ([36689e2](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/commit/36689e24c21b474692bb7ecd0b901c8afbbe9a20))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.3.0](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.2.1...@react-navigation/routers@5.3.0) (2020-04-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* separate normal exports and type exports ([303f0b7](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/commit/303f0b78a5ab717b2d606cd9c8a22f3dae051f0f))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* make replace bubble up ([ba1f405](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/commit/ba1f4051299ad86001592b8d3601c16fece159df))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.1](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.2.0...@react-navigation/routers@5.2.1) (2020-03-30)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/routers
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.2.0](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.1.1...@react-navigation/routers@5.2.0) (2020-03-22)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/routers",
|
||||
"description": "Routers to help build custom navigators",
|
||||
"version": "5.2.0",
|
||||
"version": "5.4.1",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-native",
|
||||
@@ -30,12 +30,12 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"shortid": "^2.2.15"
|
||||
"nanoid": "^3.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"del-cli": "^3.0.0",
|
||||
"typescript": "^3.7.5"
|
||||
"typescript": "^3.8.3"
|
||||
},
|
||||
"@react-native-community/bob": {
|
||||
"source": "src",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import shortid from 'shortid';
|
||||
import { nanoid } from 'nanoid/non-secure';
|
||||
import { CommonNavigationAction, NavigationState, PartialState } from './types';
|
||||
|
||||
/**
|
||||
@@ -55,9 +55,7 @@ const BaseRouter = {
|
||||
return {
|
||||
...nextState,
|
||||
routes: nextState.routes.map((route) =>
|
||||
route.key
|
||||
? route
|
||||
: { ...route, key: `${route.name}-${shortid()}` }
|
||||
route.key ? route : { ...route, key: `${route.name}-${nanoid()}` }
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import shortid from 'shortid';
|
||||
import { nanoid } from 'nanoid/non-secure';
|
||||
import {
|
||||
PartialState,
|
||||
CommonNavigationAction,
|
||||
@@ -21,7 +21,9 @@ export type DrawerActionType =
|
||||
target?: string;
|
||||
};
|
||||
|
||||
export type DrawerRouterOptions = TabRouterOptions;
|
||||
export type DrawerRouterOptions = TabRouterOptions & {
|
||||
openByDefault?: boolean;
|
||||
};
|
||||
|
||||
export type DrawerNavigationState = Omit<
|
||||
TabNavigationState,
|
||||
@@ -95,10 +97,14 @@ const closeDrawer = (state: DrawerNavigationState): DrawerNavigationState => {
|
||||
};
|
||||
};
|
||||
|
||||
export default function DrawerRouter(
|
||||
options: DrawerRouterOptions
|
||||
): Router<DrawerNavigationState, DrawerActionType | CommonNavigationAction> {
|
||||
const router = (TabRouter(options) as unknown) as Router<
|
||||
export default function DrawerRouter({
|
||||
openByDefault,
|
||||
...rest
|
||||
}: DrawerRouterOptions): Router<
|
||||
DrawerNavigationState,
|
||||
DrawerActionType | CommonNavigationAction
|
||||
> {
|
||||
const router = (TabRouter(rest) as unknown) as Router<
|
||||
DrawerNavigationState,
|
||||
TabActionType | CommonNavigationAction
|
||||
>;
|
||||
@@ -109,13 +115,17 @@ export default function DrawerRouter(
|
||||
type: 'drawer',
|
||||
|
||||
getInitialState({ routeNames, routeParamList }) {
|
||||
const state = router.getInitialState({ routeNames, routeParamList });
|
||||
let state = router.getInitialState({ routeNames, routeParamList });
|
||||
|
||||
if (openByDefault) {
|
||||
state = openDrawer(state);
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
stale: false,
|
||||
type: 'drawer',
|
||||
key: `drawer-${shortid()}`,
|
||||
key: `drawer-${nanoid()}`,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -136,13 +146,17 @@ export default function DrawerRouter(
|
||||
return {
|
||||
...state,
|
||||
type: 'drawer',
|
||||
key: `drawer-${shortid()}`,
|
||||
key: `drawer-${nanoid()}`,
|
||||
};
|
||||
},
|
||||
|
||||
getStateForRouteFocus(state, key) {
|
||||
const result = router.getStateForRouteFocus(state, key);
|
||||
|
||||
if (openByDefault) {
|
||||
return openDrawer(result);
|
||||
}
|
||||
|
||||
return closeDrawer(result);
|
||||
},
|
||||
|
||||
@@ -162,8 +176,14 @@ export default function DrawerRouter(
|
||||
return openDrawer(state);
|
||||
|
||||
case 'GO_BACK':
|
||||
if (isDrawerOpen(state)) {
|
||||
return closeDrawer(state);
|
||||
if (openByDefault) {
|
||||
if (!isDrawerOpen(state)) {
|
||||
return openDrawer(state);
|
||||
}
|
||||
} else {
|
||||
if (isDrawerOpen(state)) {
|
||||
return closeDrawer(state);
|
||||
}
|
||||
}
|
||||
|
||||
return router.getStateForAction(state, action, options);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import shortid from 'shortid';
|
||||
import { nanoid } from 'nanoid/non-secure';
|
||||
import BaseRouter from './BaseRouter';
|
||||
import {
|
||||
NavigationState,
|
||||
@@ -113,12 +113,12 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
return {
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
key: `stack-${shortid()}`,
|
||||
key: `stack-${nanoid()}`,
|
||||
index: 0,
|
||||
routeNames,
|
||||
routes: [
|
||||
{
|
||||
key: `${initialRouteName}-${shortid()}`,
|
||||
key: `${initialRouteName}-${nanoid()}`,
|
||||
name: initialRouteName,
|
||||
params: routeParamList[initialRouteName],
|
||||
},
|
||||
@@ -139,7 +139,7 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
(route) =>
|
||||
({
|
||||
...route,
|
||||
key: route.key || `${route.name}-${shortid()}`,
|
||||
key: route.key || `${route.name}-${nanoid()}`,
|
||||
params:
|
||||
routeParamList[route.name] !== undefined
|
||||
? {
|
||||
@@ -157,7 +157,7 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
: routeNames[0];
|
||||
|
||||
routes.push({
|
||||
key: `${initialRouteName}-${shortid()}`,
|
||||
key: `${initialRouteName}-${nanoid()}`,
|
||||
name: initialRouteName,
|
||||
params: routeParamList[initialRouteName],
|
||||
});
|
||||
@@ -166,7 +166,7 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
return {
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
key: `stack-${shortid()}`,
|
||||
key: `stack-${nanoid()}`,
|
||||
index: routes.length - 1,
|
||||
routeNames,
|
||||
routes,
|
||||
@@ -186,7 +186,7 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
: routeNames[0];
|
||||
|
||||
routes.push({
|
||||
key: `${initialRouteName}-${shortid()}`,
|
||||
key: `${initialRouteName}-${nanoid()}`,
|
||||
name: initialRouteName,
|
||||
params: routeParamList[initialRouteName],
|
||||
});
|
||||
@@ -219,9 +219,10 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
|
||||
switch (action.type) {
|
||||
case 'REPLACE': {
|
||||
const index = action.source
|
||||
? state.routes.findIndex((r) => r.key === action.source)
|
||||
: state.index;
|
||||
const index =
|
||||
action.target === state.key && action.source
|
||||
? state.routes.findIndex((r) => r.key === action.source)
|
||||
: state.index;
|
||||
|
||||
if (index === -1) {
|
||||
return null;
|
||||
@@ -238,7 +239,7 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
routes: state.routes.map((route, i) =>
|
||||
i === index
|
||||
? {
|
||||
key: key !== undefined ? key : `${name}-${shortid()}`,
|
||||
key: key !== undefined ? key : `${name}-${nanoid()}`,
|
||||
name,
|
||||
params:
|
||||
routeParamList[name] !== undefined
|
||||
@@ -263,7 +264,7 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
{
|
||||
key:
|
||||
action.payload.key === undefined
|
||||
? `${action.payload.name}-${shortid()}`
|
||||
? `${action.payload.name}-${nanoid()}`
|
||||
: action.payload.key,
|
||||
name: action.payload.name,
|
||||
params:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import shortid from 'shortid';
|
||||
import { nanoid } from 'nanoid/non-secure';
|
||||
import BaseRouter from './BaseRouter';
|
||||
import {
|
||||
NavigationState,
|
||||
@@ -126,7 +126,7 @@ export default function TabRouter({
|
||||
|
||||
const routes = routeNames.map((name) => ({
|
||||
name,
|
||||
key: `${name}-${shortid()}`,
|
||||
key: `${name}-${nanoid()}`,
|
||||
params: routeParamList[name],
|
||||
}));
|
||||
|
||||
@@ -135,7 +135,7 @@ export default function TabRouter({
|
||||
return {
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
key: `tab-${shortid()}`,
|
||||
key: `tab-${nanoid()}`,
|
||||
index,
|
||||
routeNames,
|
||||
history,
|
||||
@@ -161,7 +161,7 @@ export default function TabRouter({
|
||||
key:
|
||||
route && route.name === name && route.key
|
||||
? route.key
|
||||
: `${name}-${shortid()}`,
|
||||
: `${name}-${nanoid()}`,
|
||||
params:
|
||||
routeParamList[name] !== undefined
|
||||
? {
|
||||
@@ -195,7 +195,7 @@ export default function TabRouter({
|
||||
return {
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
key: `tab-${shortid()}`,
|
||||
key: `tab-${nanoid()}`,
|
||||
index,
|
||||
routeNames,
|
||||
history,
|
||||
@@ -208,7 +208,7 @@ export default function TabRouter({
|
||||
(name) =>
|
||||
state.routes.find((r) => r.name === name) || {
|
||||
name,
|
||||
key: `${name}-${shortid()}`,
|
||||
key: `${name}-${nanoid()}`,
|
||||
params: routeParamList[name],
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import BaseRouter from '../BaseRouter';
|
||||
import * as CommonActions from '../CommonActions';
|
||||
|
||||
jest.mock('shortid', () => () => 'test');
|
||||
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
|
||||
|
||||
const STATE = {
|
||||
stale: false as const,
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
DrawerNavigationState,
|
||||
} from '..';
|
||||
|
||||
jest.mock('shortid', () => () => 'test');
|
||||
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
|
||||
|
||||
it('gets initial state from route names and params with initialRouteName', () => {
|
||||
const router = DrawerRouter({ initialRouteName: 'baz' });
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CommonActions, StackRouter, StackActions } from '..';
|
||||
|
||||
jest.mock('shortid', () => () => 'test');
|
||||
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
|
||||
|
||||
it('gets initial state from route names and params with initialRouteName', () => {
|
||||
const router = StackRouter({ initialRouteName: 'baz' });
|
||||
@@ -720,7 +720,7 @@ it('replaces focused screen with replace', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('replaces source screen with replace', () => {
|
||||
it('replaces active screen with replace', () => {
|
||||
const router = StackRouter({});
|
||||
const options = {
|
||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||
@@ -754,8 +754,8 @@ it('replaces source screen with replace', () => {
|
||||
index: 1,
|
||||
routes: [
|
||||
{ key: 'foo', name: 'foo' },
|
||||
{ key: 'bar', name: 'bar', params: { fruit: 'orange' } },
|
||||
{ key: 'qux-test', name: 'qux', params: { answer: 42 } },
|
||||
{ key: 'baz', name: 'baz' },
|
||||
],
|
||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||
});
|
||||
@@ -785,6 +785,7 @@ it("doesn't handle replace if source key isn't present", () => {
|
||||
{
|
||||
...StackActions.replace('qux', { answer: 42 }),
|
||||
source: 'magic',
|
||||
target: 'root',
|
||||
},
|
||||
options
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CommonActions, TabRouter, TabActions, TabNavigationState } from '..';
|
||||
|
||||
jest.mock('shortid', () => () => 'test');
|
||||
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
|
||||
|
||||
it('gets initial state from route names and params with initialRouteName', () => {
|
||||
const router = TabRouter({ initialRouteName: 'baz' });
|
||||
@@ -214,6 +214,161 @@ it("doesn't rehydrate state if it's not stale", () => {
|
||||
).toBe(state);
|
||||
});
|
||||
|
||||
it('restores correct history on rehydrating with backBehavior: order', () => {
|
||||
const router = TabRouter({ backBehavior: 'order' });
|
||||
|
||||
const options = {
|
||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||
routeParamList: {},
|
||||
};
|
||||
|
||||
expect(
|
||||
router.getRehydratedState(
|
||||
{
|
||||
index: 2,
|
||||
routes: [
|
||||
{ key: 'foo-0', name: 'foo' },
|
||||
{ key: 'bar-0', name: 'bar' },
|
||||
{ key: 'baz-0', name: 'baz' },
|
||||
{ key: 'qux-0', name: 'qux' },
|
||||
],
|
||||
},
|
||||
options
|
||||
)
|
||||
).toEqual({
|
||||
key: 'tab-test',
|
||||
index: 2,
|
||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||
routes: [
|
||||
{ key: 'foo-0', name: 'foo' },
|
||||
{ key: 'bar-0', name: 'bar' },
|
||||
{ key: 'baz-0', name: 'baz' },
|
||||
{ key: 'qux-0', name: 'qux' },
|
||||
],
|
||||
history: [
|
||||
{ key: 'foo-0', type: 'route' },
|
||||
{ key: 'bar-0', type: 'route' },
|
||||
{ key: 'baz-0', type: 'route' },
|
||||
],
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
});
|
||||
});
|
||||
|
||||
it('restores correct history on rehydrating with backBehavior: history', () => {
|
||||
const router = TabRouter({ backBehavior: 'history' });
|
||||
|
||||
const options = {
|
||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||
routeParamList: {},
|
||||
};
|
||||
|
||||
expect(
|
||||
router.getRehydratedState(
|
||||
{
|
||||
index: 2,
|
||||
routes: [
|
||||
{ key: 'foo-0', name: 'foo' },
|
||||
{ key: 'bar-0', name: 'bar' },
|
||||
{ key: 'baz-0', name: 'baz' },
|
||||
{ key: 'qux-0', name: 'qux' },
|
||||
],
|
||||
},
|
||||
options
|
||||
)
|
||||
).toEqual({
|
||||
key: 'tab-test',
|
||||
index: 2,
|
||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||
routes: [
|
||||
{ key: 'foo-0', name: 'foo' },
|
||||
{ key: 'bar-0', name: 'bar' },
|
||||
{ key: 'baz-0', name: 'baz' },
|
||||
{ key: 'qux-0', name: 'qux' },
|
||||
],
|
||||
history: [{ key: 'baz-0', type: 'route' }],
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
});
|
||||
});
|
||||
|
||||
it('restores correct history on rehydrating with backBehavior: initialRoute', () => {
|
||||
const router = TabRouter({ backBehavior: 'initialRoute' });
|
||||
|
||||
const options = {
|
||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||
routeParamList: {},
|
||||
};
|
||||
|
||||
expect(
|
||||
router.getRehydratedState(
|
||||
{
|
||||
index: 2,
|
||||
routes: [
|
||||
{ key: 'foo-0', name: 'foo' },
|
||||
{ key: 'bar-0', name: 'bar' },
|
||||
{ key: 'baz-0', name: 'baz' },
|
||||
{ key: 'qux-0', name: 'qux' },
|
||||
],
|
||||
},
|
||||
options
|
||||
)
|
||||
).toEqual({
|
||||
key: 'tab-test',
|
||||
index: 2,
|
||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||
routes: [
|
||||
{ key: 'foo-0', name: 'foo' },
|
||||
{ key: 'bar-0', name: 'bar' },
|
||||
{ key: 'baz-0', name: 'baz' },
|
||||
{ key: 'qux-0', name: 'qux' },
|
||||
],
|
||||
history: [
|
||||
{ key: 'foo-0', type: 'route' },
|
||||
{ key: 'baz-0', type: 'route' },
|
||||
],
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
});
|
||||
});
|
||||
|
||||
it('restores correct history on rehydrating with backBehavior: none', () => {
|
||||
const router = TabRouter({ backBehavior: 'none' });
|
||||
|
||||
const options = {
|
||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||
routeParamList: {},
|
||||
};
|
||||
|
||||
expect(
|
||||
router.getRehydratedState(
|
||||
{
|
||||
index: 2,
|
||||
routes: [
|
||||
{ key: 'foo-0', name: 'foo' },
|
||||
{ key: 'bar-0', name: 'bar' },
|
||||
{ key: 'baz-0', name: 'baz' },
|
||||
{ key: 'qux-0', name: 'qux' },
|
||||
],
|
||||
},
|
||||
options
|
||||
)
|
||||
).toEqual({
|
||||
key: 'tab-test',
|
||||
index: 2,
|
||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||
routes: [
|
||||
{ key: 'foo-0', name: 'foo' },
|
||||
{ key: 'bar-0', name: 'bar' },
|
||||
{ key: 'baz-0', name: 'baz' },
|
||||
{ key: 'qux-0', name: 'qux' },
|
||||
],
|
||||
history: [{ key: 'baz-0', type: 'route' }],
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets state on route names change', () => {
|
||||
const router = TabRouter({});
|
||||
|
||||
@@ -254,6 +409,38 @@ it('gets state on route names change', () => {
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
});
|
||||
|
||||
expect(
|
||||
router.getStateForRouteNamesChange(
|
||||
{
|
||||
index: 0,
|
||||
key: 'tab-test',
|
||||
routeNames: ['bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'bar-test', name: 'bar' },
|
||||
{ key: 'baz-test', name: 'baz', params: { answer: 42 } },
|
||||
],
|
||||
history: [{ type: 'route', key: 'bar-test' }],
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
},
|
||||
{
|
||||
routeNames: ['foo', 'fiz'],
|
||||
routeParamList: {},
|
||||
}
|
||||
)
|
||||
).toEqual({
|
||||
index: 0,
|
||||
key: 'tab-test',
|
||||
routeNames: ['foo', 'fiz'],
|
||||
routes: [
|
||||
{ key: 'foo-test', name: 'foo' },
|
||||
{ key: 'fiz-test', name: 'fiz' },
|
||||
],
|
||||
history: [{ type: 'route', key: 'foo-test' }],
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves focused route on route names change', () => {
|
||||
|
||||
@@ -4,27 +4,27 @@ export { CommonActions };
|
||||
|
||||
export { default as BaseRouter } from './BaseRouter';
|
||||
|
||||
export {
|
||||
default as StackRouter,
|
||||
StackActions,
|
||||
export { default as StackRouter, StackActions } from './StackRouter';
|
||||
|
||||
export type {
|
||||
StackActionHelpers,
|
||||
StackActionType,
|
||||
StackRouterOptions,
|
||||
StackNavigationState,
|
||||
} from './StackRouter';
|
||||
|
||||
export {
|
||||
default as TabRouter,
|
||||
TabActions,
|
||||
export { default as TabRouter, TabActions } from './TabRouter';
|
||||
|
||||
export type {
|
||||
TabActionHelpers,
|
||||
TabActionType,
|
||||
TabRouterOptions,
|
||||
TabNavigationState,
|
||||
} from './TabRouter';
|
||||
|
||||
export {
|
||||
default as DrawerRouter,
|
||||
DrawerActions,
|
||||
export { default as DrawerRouter, DrawerActions } from './DrawerRouter';
|
||||
|
||||
export type {
|
||||
DrawerActionHelpers,
|
||||
DrawerActionType,
|
||||
DrawerRouterOptions,
|
||||
|
||||
@@ -3,6 +3,66 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.2.14](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.13...@react-navigation/stack@5.2.14) (2020-04-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* don't add back the route being replaced ([a695cf9](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/a695cf9c058521ccb4a83eb206dc0da7ce100032))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.12](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.11...@react-navigation/stack@5.2.12) (2020-04-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* animate card to existing closing state on gesture end ([78485ce](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/78485cea6939b9ffec76e0c4b410bc426ed93402)), closes [#7938](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7938)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.11](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.10...@react-navigation/stack@5.2.11) (2020-04-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* disable animation by default on web for stack ([dfdba8d](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/dfdba8d741abb4aa82235688d9f49e26305d2bca))
|
||||
* hide inactive screens for stack on web ([#8010](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/8010)) ([82edb25](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/82edb2581bab960f206fd67368a45ad384955c97))
|
||||
* ios presentation modal cuts the topOffset on the bottom ([#7943](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7943)) ([6e51f59](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/6e51f596fa85796c2a3567222f51ff914c1f6c94)), closes [#7856](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7856)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.10](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.9...@react-navigation/stack@5.2.10) (2020-04-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* make color of shadow element same as card color in stack ([f1a8bce](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/f1a8bceba5b736e9f59862a8ae819342209a46f2))
|
||||
* mark type exports for all packages ([b71de6c](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/b71de6cc799143f1d0e8a0cfcc34f0a2381f9840))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.9](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.8...@react-navigation/stack@5.2.9) (2020-03-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* dismiss keyboard on screen change for android ([8432e5a](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/8432e5ab25f041af8538ea7fb35e97cfcf1f983e))
|
||||
* finish stack animation on CANCELLED event ([#7898](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7898)) ([d649fbc](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/d649fbc6691871f0348076bce185d11a183c02cf)), closes [#7897](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7897)
|
||||
* when comparing changed routes, only check keys ([9a8fea8](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/9a8fea8f2c1bdabfc5dd87e5c3ff4e7b97aef47d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.7](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.6...@react-navigation/stack@5.2.7) (2020-03-26)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/stack",
|
||||
"description": "Stack navigator component for iOS and Android with animated transitions and gestures",
|
||||
"version": "5.2.7",
|
||||
"version": "5.2.14",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -40,7 +40,7 @@
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@react-native-community/masked-view": "^0.1.7",
|
||||
"@react-navigation/native": "^5.1.3",
|
||||
"@react-navigation/native": "^5.1.7",
|
||||
"@types/color": "^3.0.1",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-native": "^0.61.22",
|
||||
@@ -50,7 +50,7 @@
|
||||
"react-native-gesture-handler": "^1.6.0",
|
||||
"react-native-safe-area-context": "^0.7.3",
|
||||
"react-native-screens": "^2.3.0",
|
||||
"typescript": "^3.7.5"
|
||||
"typescript": "^3.8.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-native-community/masked-view": ">= 0.1.0",
|
||||
|
||||
@@ -164,6 +164,7 @@ export function forModalPresentationIOS({
|
||||
borderTopLeftRadius: borderRadius,
|
||||
borderTopRightRadius: borderRadius,
|
||||
marginTop: index === 0 ? 0 : statusBarHeight,
|
||||
marginBottom: index === 0 ? 0 : topOffset,
|
||||
transform: [{ translateY }, { scale }],
|
||||
},
|
||||
overlayStyle: { opacity: overlayOpacity },
|
||||
|
||||
@@ -48,7 +48,7 @@ export { default as useGestureHandlerRef } from './utils/useGestureHandlerRef';
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
export {
|
||||
export type {
|
||||
StackNavigationOptions,
|
||||
StackNavigationProp,
|
||||
StackHeaderProps,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { Platform } from 'react-native';
|
||||
import {
|
||||
useNavigationBuilder,
|
||||
createNavigatorFactory,
|
||||
@@ -26,6 +27,11 @@ function StackNavigator({
|
||||
screenOptions,
|
||||
...rest
|
||||
}: Props) {
|
||||
const defaultOptions = {
|
||||
gestureEnabled: Platform.OS === 'ios',
|
||||
animationEnabled: Platform.OS !== 'web',
|
||||
};
|
||||
|
||||
const { state, descriptors, navigation } = useNavigationBuilder<
|
||||
StackNavigationState,
|
||||
StackRouterOptions,
|
||||
@@ -34,7 +40,16 @@ function StackNavigator({
|
||||
>(StackRouter, {
|
||||
initialRouteName,
|
||||
children,
|
||||
screenOptions,
|
||||
screenOptions:
|
||||
typeof screenOptions === 'function'
|
||||
? (...args) => ({
|
||||
...defaultOptions,
|
||||
...screenOptions(...args),
|
||||
})
|
||||
: {
|
||||
...defaultOptions,
|
||||
...screenOptions,
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { TextInput } from 'react-native';
|
||||
import { TextInput, Platform, Keyboard } from 'react-native';
|
||||
|
||||
type Props = {
|
||||
enabled: boolean;
|
||||
@@ -56,7 +56,9 @@ export default class KeyboardManager extends React.Component<Props> {
|
||||
|
||||
const input = this.previouslyFocusedTextInput;
|
||||
|
||||
if (input) {
|
||||
if (Platform.OS === 'android') {
|
||||
Keyboard.dismiss();
|
||||
} else if (input) {
|
||||
TextInput.State.blurTextInput(input);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
import * as React from 'react';
|
||||
import { UIManager } from 'react-native';
|
||||
import RNCMaskedView from '@react-native-community/masked-view';
|
||||
|
||||
type Props = React.ComponentProps<typeof RNCMaskedView> & {
|
||||
type MaskedViewType = typeof import('@react-native-community/masked-view').default;
|
||||
|
||||
type Props = React.ComponentProps<MaskedViewType> & {
|
||||
children: React.ReactElement;
|
||||
};
|
||||
|
||||
let RNCMaskedView: MaskedViewType | undefined;
|
||||
|
||||
try {
|
||||
RNCMaskedView = require('@react-native-community/masked-view').default;
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
const isMaskedViewAvailable =
|
||||
// @ts-ignore
|
||||
UIManager.getViewManagerConfig('RNCMaskedView') != null;
|
||||
|
||||
export default function MaskedView({ children, ...rest }: Props) {
|
||||
if (isMaskedViewAvailable) {
|
||||
if (isMaskedViewAvailable && RNCMaskedView) {
|
||||
return <RNCMaskedView {...rest}>{children}</RNCMaskedView>;
|
||||
}
|
||||
|
||||
|
||||
71
packages/stack/src/views/Screens.tsx
Normal file
71
packages/stack/src/views/Screens.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import * as React from 'react';
|
||||
import { Animated, View, Platform, ViewProps } from 'react-native';
|
||||
|
||||
let Screens: typeof import('react-native-screens') | undefined;
|
||||
|
||||
try {
|
||||
Screens = require('react-native-screens');
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
// The web implementation in react-native-screens seems buggy.
|
||||
// The view doesn't become visible after coming back in some cases.
|
||||
// So we use our custom implementation.
|
||||
class WebScreen extends React.Component<
|
||||
ViewProps & {
|
||||
active: number;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
> {
|
||||
render() {
|
||||
const { active, style, ...rest } = this.props;
|
||||
|
||||
return (
|
||||
<View
|
||||
// @ts-ignore
|
||||
hidden={!active}
|
||||
style={[style, { display: active ? 'flex' : 'none' }]}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const AnimatedWebScreen = Animated.createAnimatedComponent(WebScreen);
|
||||
|
||||
export const MaybeScreenContainer = ({
|
||||
enabled,
|
||||
...rest
|
||||
}: ViewProps & {
|
||||
enabled: boolean;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
if (enabled && Platform.OS !== 'web' && Screens && Screens.screensEnabled()) {
|
||||
return <Screens.ScreenContainer {...rest} />;
|
||||
}
|
||||
|
||||
return <View {...rest} />;
|
||||
};
|
||||
|
||||
export const MaybeScreen = ({
|
||||
enabled,
|
||||
active,
|
||||
...rest
|
||||
}: ViewProps & {
|
||||
enabled: boolean;
|
||||
active: number | Animated.AnimatedInterpolation;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
if (enabled && Platform.OS === 'web') {
|
||||
// @ts-ignore
|
||||
return <AnimatedWebScreen active={active} {...rest} />;
|
||||
}
|
||||
|
||||
if (enabled && Screens && Screens.screensEnabled()) {
|
||||
// @ts-ignore
|
||||
return <Screens.Screen active={active} {...rest} />;
|
||||
}
|
||||
|
||||
return <View {...rest} />;
|
||||
};
|
||||
@@ -246,11 +246,21 @@ export default class Card extends React.Component<Props> {
|
||||
this.handleStartInteraction();
|
||||
onGestureBegin?.();
|
||||
break;
|
||||
case GestureState.CANCELLED:
|
||||
case GestureState.CANCELLED: {
|
||||
this.isSwiping.setValue(FALSE);
|
||||
this.handleEndInteraction();
|
||||
|
||||
const velocity =
|
||||
gestureDirection === 'vertical' ||
|
||||
gestureDirection === 'vertical-inverted'
|
||||
? nativeEvent.velocityY
|
||||
: nativeEvent.velocityX;
|
||||
|
||||
this.animate({ closing: this.props.closing, velocity });
|
||||
|
||||
onGestureCanceled?.();
|
||||
break;
|
||||
}
|
||||
case GestureState.END: {
|
||||
this.isSwiping.setValue(FALSE);
|
||||
|
||||
@@ -276,7 +286,7 @@ export default class Card extends React.Component<Props> {
|
||||
getInvertedMultiplier(gestureDirection) >
|
||||
distance / 2
|
||||
? velocity !== 0 || translation !== 0
|
||||
: false;
|
||||
: this.props.closing;
|
||||
|
||||
this.animate({ closing, velocity });
|
||||
|
||||
@@ -507,6 +517,7 @@ export default class Card extends React.Component<Props> {
|
||||
: gestureDirection === 'vertical'
|
||||
? [styles.shadowVertical, styles.shadowTop]
|
||||
: [styles.shadowVertical, styles.shadowBottom],
|
||||
{ backgroundColor },
|
||||
shadowStyle,
|
||||
]}
|
||||
pointerEvents="none"
|
||||
@@ -543,7 +554,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
shadow: {
|
||||
position: 'absolute',
|
||||
backgroundColor: '#fff',
|
||||
shadowRadius: 5,
|
||||
shadowColor: '#000',
|
||||
shadowOpacity: 0.3,
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Animated,
|
||||
View,
|
||||
StyleSheet,
|
||||
LayoutChangeEvent,
|
||||
Dimensions,
|
||||
Platform,
|
||||
ViewProps,
|
||||
} from 'react-native';
|
||||
import { EdgeInsets } from 'react-native-safe-area-context';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { ScreenContainer, Screen, screensEnabled } from 'react-native-screens'; // Import with * as to prevent getters being called
|
||||
import { Route, StackNavigationState } from '@react-navigation/native';
|
||||
|
||||
import { MaybeScreenContainer, MaybeScreen } from '../Screens';
|
||||
import { getDefaultHeaderHeight } from '../Header/HeaderSegment';
|
||||
import { Props as HeaderContainerProps } from '../Header/HeaderContainer';
|
||||
import CardContainer from './CardContainer';
|
||||
@@ -75,37 +72,6 @@ type State = {
|
||||
|
||||
const EPSILON = 0.01;
|
||||
|
||||
const MaybeScreenContainer = ({
|
||||
enabled,
|
||||
...rest
|
||||
}: ViewProps & {
|
||||
enabled: boolean;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
if (enabled && screensEnabled()) {
|
||||
return <ScreenContainer {...rest} />;
|
||||
}
|
||||
|
||||
return <View {...rest} />;
|
||||
};
|
||||
|
||||
const MaybeScreen = ({
|
||||
enabled,
|
||||
active,
|
||||
...rest
|
||||
}: ViewProps & {
|
||||
enabled: boolean;
|
||||
active: number | Animated.AnimatedInterpolation;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
if (enabled && screensEnabled()) {
|
||||
// @ts-ignore
|
||||
return <Screen active={active} {...rest} />;
|
||||
}
|
||||
|
||||
return <View {...rest} />;
|
||||
};
|
||||
|
||||
const FALLBACK_DESCRIPTOR = Object.freeze({ options: {} });
|
||||
|
||||
const getHeaderHeights = (
|
||||
@@ -415,7 +381,7 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
|
||||
// Screens is buggy on iOS and web, so we only enable it on Android
|
||||
// For modals, usually we want the screen underneath to be visible, so also disable it there
|
||||
const isScreensEnabled = Platform.OS === 'android' && mode !== 'modal';
|
||||
const isScreensEnabled = Platform.OS !== 'ios' && mode !== 'modal';
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
||||
@@ -46,31 +46,63 @@ type State = {
|
||||
|
||||
const GestureHandlerWrapper = GestureHandlerRootView ?? View;
|
||||
|
||||
/**
|
||||
* Compare two arrays with primitive values as the content.
|
||||
* We need to make sure that both values and order match.
|
||||
*/
|
||||
const isArrayEqual = (a: any[], b: any[]) =>
|
||||
a.length === b.length && a.every((it, index) => it === b[index]);
|
||||
|
||||
export default class StackView extends React.Component<Props, State> {
|
||||
static getDerivedStateFromProps(
|
||||
props: Readonly<Props>,
|
||||
state: Readonly<State>
|
||||
) {
|
||||
// If there was no change in routes, we don't need to compute anything
|
||||
if (props.state.routes === state.previousRoutes && state.routes.length) {
|
||||
if (props.descriptors !== state.previousDescriptors) {
|
||||
const descriptors = state.routes.reduce<StackDescriptorMap>(
|
||||
(acc, route) => {
|
||||
acc[route.key] =
|
||||
props.descriptors[route.key] || state.descriptors[route.key];
|
||||
if (
|
||||
(props.state.routes === state.previousRoutes ||
|
||||
isArrayEqual(
|
||||
props.state.routes.map((r) => r.key),
|
||||
state.previousRoutes.map((r) => r.key)
|
||||
)) &&
|
||||
state.routes.length
|
||||
) {
|
||||
let routes = state.routes;
|
||||
let previousRoutes = state.previousRoutes;
|
||||
let descriptors = props.descriptors;
|
||||
let previousDescriptors = state.previousDescriptors;
|
||||
|
||||
if (props.descriptors !== state.previousDescriptors) {
|
||||
descriptors = state.routes.reduce<StackDescriptorMap>((acc, route) => {
|
||||
acc[route.key] =
|
||||
props.descriptors[route.key] || state.descriptors[route.key];
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
previousDescriptors = props.descriptors;
|
||||
}
|
||||
|
||||
if (props.state.routes !== state.previousRoutes) {
|
||||
// if any route objects have changed, we should update them
|
||||
const map = props.state.routes.reduce<Record<string, Route<string>>>(
|
||||
(acc, route) => {
|
||||
acc[route.key] = route;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
return {
|
||||
previousDescriptors: props.descriptors,
|
||||
descriptors,
|
||||
};
|
||||
routes = state.routes.map((route) => map[route.key] || route);
|
||||
previousRoutes = props.state.routes;
|
||||
}
|
||||
|
||||
return null;
|
||||
return {
|
||||
routes,
|
||||
previousRoutes,
|
||||
descriptors,
|
||||
previousDescriptors,
|
||||
};
|
||||
}
|
||||
|
||||
// Here we determine which routes were added or removed to animate them
|
||||
@@ -115,7 +147,7 @@ export default class StackView extends React.Component<Props, State> {
|
||||
// We only need to animate routes if the focused route changed
|
||||
// Animating previous routes won't be visible coz the focused route is on top of everything
|
||||
|
||||
if (!previousRoutes.find((r) => r.key === nextFocusedRoute.key)) {
|
||||
if (!previousRoutes.some((r) => r.key === nextFocusedRoute.key)) {
|
||||
// A new route has come to the focus, we treat this as a push
|
||||
// A replace can also trigger this, the animation should look like push
|
||||
|
||||
@@ -134,7 +166,7 @@ export default class StackView extends React.Component<Props, State> {
|
||||
(key) => key !== nextFocusedRoute.key
|
||||
);
|
||||
|
||||
if (!routes.find((r) => r.key === previousFocusedRoute.key)) {
|
||||
if (!routes.some((r) => r.key === previousFocusedRoute.key)) {
|
||||
// The previous focused route isn't present in state, we treat this as a replace
|
||||
|
||||
openingRouteKeys = openingRouteKeys.filter(
|
||||
@@ -174,7 +206,7 @@ export default class StackView extends React.Component<Props, State> {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!routes.find((r) => r.key === previousFocusedRoute.key)) {
|
||||
} else if (!routes.some((r) => r.key === previousFocusedRoute.key)) {
|
||||
// The previously focused route was removed, we treat this as a pop
|
||||
|
||||
if (
|
||||
@@ -260,9 +292,7 @@ export default class StackView extends React.Component<Props, State> {
|
||||
return false;
|
||||
}
|
||||
|
||||
return gestureEnabled !== undefined
|
||||
? gestureEnabled
|
||||
: Platform.OS !== 'android';
|
||||
return gestureEnabled !== false;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -298,24 +328,38 @@ export default class StackView extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
private handleOpenRoute = ({ route }: { route: Route<string> }) => {
|
||||
this.setState((state) => ({
|
||||
routes: state.replacingRouteKeys.length
|
||||
? state.routes.filter((r) => !state.replacingRouteKeys.includes(r.key))
|
||||
: state.routes,
|
||||
openingRouteKeys: state.openingRouteKeys.filter(
|
||||
(key) => key !== route.key
|
||||
),
|
||||
closingRouteKeys: state.closingRouteKeys.filter(
|
||||
(key) => key !== route.key
|
||||
),
|
||||
replacingRouteKeys: [],
|
||||
}));
|
||||
const { state, navigation } = this.props;
|
||||
|
||||
if (
|
||||
this.state.replacingRouteKeys.every((key) => key !== route.key) &&
|
||||
state.routeNames.includes(route.name) &&
|
||||
!state.routes.some((r) => r.key === route.key)
|
||||
) {
|
||||
// If route isn't present in current state, assume that a close animation was cancelled
|
||||
// So we need to add this route back to the state
|
||||
navigation.navigate(route);
|
||||
} else {
|
||||
this.setState((state) => ({
|
||||
routes: state.replacingRouteKeys.length
|
||||
? state.routes.filter(
|
||||
(r) => !state.replacingRouteKeys.includes(r.key)
|
||||
)
|
||||
: state.routes,
|
||||
openingRouteKeys: state.openingRouteKeys.filter(
|
||||
(key) => key !== route.key
|
||||
),
|
||||
closingRouteKeys: state.closingRouteKeys.filter(
|
||||
(key) => key !== route.key
|
||||
),
|
||||
replacingRouteKeys: [],
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
private handleCloseRoute = ({ route }: { route: Route<string> }) => {
|
||||
const { state, navigation } = this.props;
|
||||
|
||||
if (state.routes.find((r) => r.key === route.key)) {
|
||||
if (state.routes.some((r) => r.key === route.key)) {
|
||||
// If a route exists in state, trigger a pop
|
||||
// This will happen in when the route was closed from the card component
|
||||
// e.g. When the close animation triggered from a gesture ends
|
||||
|
||||
Reference in New Issue
Block a user