mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-13 09:30:30 +08:00
Compare commits
20 Commits
@react-nav
...
@react-nav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e05895b24 | ||
|
|
929c3e3a3b | ||
|
|
b5d539a11b | ||
|
|
7da45e1e89 | ||
|
|
47134d7052 | ||
|
|
a369ba3645 | ||
|
|
4294318210 | ||
|
|
8da4c58065 | ||
|
|
7809bc0650 | ||
|
|
8f2b95ca97 | ||
|
|
6866ad2cda | ||
|
|
459fd27050 | ||
|
|
14786594c0 | ||
|
|
b28bfddc17 | ||
|
|
1a6aebefcb | ||
|
|
33b2dbb85c | ||
|
|
4bb0b43f1a | ||
|
|
260da9b103 | ||
|
|
9ac709ea1e | ||
|
|
60fa3b9be9 |
@@ -3,7 +3,7 @@ version: 2.1
|
||||
executors:
|
||||
default:
|
||||
docker:
|
||||
- image: circleci/node:10
|
||||
- image: circleci/node:14
|
||||
working_directory: ~/project
|
||||
environment:
|
||||
YARN_CACHE_FOLDER: "~/.cache/yarn"
|
||||
@@ -36,10 +36,12 @@ jobs:
|
||||
command: yarn install --frozen-lockfile
|
||||
- save_cache:
|
||||
key: yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
paths: ~/.cache/yarn
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths: .
|
||||
paths:
|
||||
- .
|
||||
|
||||
lint-and-typecheck:
|
||||
executor: default
|
||||
|
||||
2
.github/workflows/expo-preview.yml
vendored
2
.github/workflows/expo-preview.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 10.x
|
||||
node-version: 14.x
|
||||
|
||||
- name: Setup Expo
|
||||
uses: expo/expo-github-action@v5
|
||||
|
||||
2
.github/workflows/expo.yml
vendored
2
.github/workflows/expo.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 10.x
|
||||
node-version: 14.x
|
||||
|
||||
- name: Setup Expo
|
||||
uses: expo/expo-github-action@v5
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -26,6 +26,7 @@ build
|
||||
|
||||
npm-debug.*
|
||||
|
||||
*.tsbuildinfo
|
||||
*.log
|
||||
*.jks
|
||||
*.p8
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
"dependencies": {
|
||||
"@expo/vector-icons": "^12.0.0",
|
||||
"@react-native-async-storage/async-storage": "^1.13.0",
|
||||
"@react-native-masked-view/masked-view": "0.2.3",
|
||||
"@react-native-masked-view/masked-view": "~0.2.4",
|
||||
"color": "^3.1.3",
|
||||
"expo": "^41.0.0-beta.2",
|
||||
"expo": "^41.0.1",
|
||||
"expo-asset": "~8.3.1",
|
||||
"expo-blur": "~9.0.3",
|
||||
"expo-linking": "~2.2.1",
|
||||
"expo-updates": "~0.5.3",
|
||||
"expo-linking": "~2.2.3",
|
||||
"expo-updates": "~0.5.4",
|
||||
"koa": "^2.13.0",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
@@ -51,7 +51,7 @@
|
||||
"babel-plugin-module-resolver": "^4.0.0",
|
||||
"babel-preset-expo": "8.3.0",
|
||||
"cheerio": "^1.0.0-rc.3",
|
||||
"expo-cli": "^4.3.4",
|
||||
"expo-cli": "^4.4.4",
|
||||
"jest": "^26.6.3",
|
||||
"jest-dev-server": "^4.4.0",
|
||||
"mock-require-assets": "^0.0.1",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import { Platform } from 'react-native';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import {
|
||||
getFocusedRouteNameFromRoute,
|
||||
@@ -9,7 +8,6 @@ import {
|
||||
import type { StackScreenProps } from '@react-navigation/stack';
|
||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||
import { HeaderBackButton } from '@react-navigation/elements';
|
||||
import TouchableBounce from '../Shared/TouchableBounce';
|
||||
import Albums from '../Shared/Albums';
|
||||
import Contacts from '../Shared/Contacts';
|
||||
import Chat from '../Shared/Chat';
|
||||
@@ -51,10 +49,6 @@ export default function BottomTabsScreen({
|
||||
headerLeft: (props) => (
|
||||
<HeaderBackButton {...props} onPress={navigation.goBack} />
|
||||
),
|
||||
tabBarButton:
|
||||
Platform.OS === 'web'
|
||||
? undefined
|
||||
: (props) => <TouchableBounce {...props} />,
|
||||
}}
|
||||
>
|
||||
<BottomTabs.Screen
|
||||
|
||||
@@ -24,7 +24,8 @@ const scrollEnabled = Platform.select({ web: true, default: false });
|
||||
const LinkButton = ({
|
||||
to,
|
||||
...rest
|
||||
}: React.ComponentProps<typeof Button> & { to: string }) => {
|
||||
}: React.ComponentProps<typeof Button> &
|
||||
Parameters<typeof useLinkProps>[0]) => {
|
||||
const props = useLinkProps({ to });
|
||||
|
||||
return <Button {...props} {...rest} />;
|
||||
@@ -57,6 +58,13 @@ const ArticleScreen = ({
|
||||
>
|
||||
Go to /link-component/music
|
||||
</LinkButton>
|
||||
<LinkButton
|
||||
to={{ screen: 'Home' }}
|
||||
mode="contained"
|
||||
style={styles.button}
|
||||
>
|
||||
Go to /
|
||||
</LinkButton>
|
||||
<Button
|
||||
mode="outlined"
|
||||
onPress={() => navigation.goBack()}
|
||||
|
||||
@@ -117,24 +117,29 @@ export default function SimpleStackScreen({
|
||||
return (
|
||||
<SimpleStack.Navigator
|
||||
screenOptions={{
|
||||
...TransitionPresets.SlideFromRightIOS,
|
||||
headerMode: 'float',
|
||||
headerStyleInterpolator: HeaderStyleInterpolators.forUIKit,
|
||||
}}
|
||||
>
|
||||
<SimpleStack.Screen
|
||||
name="Article"
|
||||
component={ArticleScreen}
|
||||
options={({ route }) => ({
|
||||
title: `Article by ${route.params?.author ?? 'Unknown'}`,
|
||||
})}
|
||||
initialParams={{ author: 'Gandalf' }}
|
||||
/>
|
||||
<SimpleStack.Screen
|
||||
name="NewsFeed"
|
||||
component={NewsFeedScreen}
|
||||
options={{ title: 'Feed' }}
|
||||
/>
|
||||
<SimpleStack.Group
|
||||
screenOptions={{
|
||||
...TransitionPresets.SlideFromRightIOS,
|
||||
headerMode: 'float',
|
||||
}}
|
||||
>
|
||||
<SimpleStack.Screen
|
||||
name="Article"
|
||||
component={ArticleScreen}
|
||||
options={({ route }) => ({
|
||||
title: `Article by ${route.params?.author ?? 'Unknown'}`,
|
||||
})}
|
||||
initialParams={{ author: 'Gandalf' }}
|
||||
/>
|
||||
<SimpleStack.Screen
|
||||
name="NewsFeed"
|
||||
component={NewsFeedScreen}
|
||||
options={{ title: 'Feed' }}
|
||||
/>
|
||||
</SimpleStack.Group>
|
||||
<SimpleStack.Screen
|
||||
name="Albums"
|
||||
component={AlbumsScreen}
|
||||
|
||||
137
example/src/Screens/MixedStack.tsx
Normal file
137
example/src/Screens/MixedStack.tsx
Normal file
@@ -0,0 +1,137 @@
|
||||
import * as React from 'react';
|
||||
import { View, StyleSheet, ScrollView, Platform } from 'react-native';
|
||||
import { Button } from 'react-native-paper';
|
||||
import type { ParamListBase } from '@react-navigation/native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
StackScreenProps,
|
||||
TransitionPresets,
|
||||
} from '@react-navigation/stack';
|
||||
import Article from '../Shared/Article';
|
||||
import Albums from '../Shared/Albums';
|
||||
|
||||
type MixedStackParams = {
|
||||
Article: { author: string };
|
||||
Albums: undefined;
|
||||
};
|
||||
|
||||
const scrollEnabled = Platform.select({ web: true, default: false });
|
||||
|
||||
const ArticleScreen = ({
|
||||
navigation,
|
||||
route,
|
||||
}: StackScreenProps<MixedStackParams, 'Article'>) => {
|
||||
return (
|
||||
<ScrollView>
|
||||
<View style={styles.buttons}>
|
||||
<View>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('Article', { author: 'Dalek' })}
|
||||
style={styles.button}
|
||||
>
|
||||
Push article
|
||||
</Button>
|
||||
<Button
|
||||
mode="outlined"
|
||||
onPress={() => navigation.goBack()}
|
||||
style={styles.button}
|
||||
>
|
||||
Go back
|
||||
</Button>
|
||||
</View>
|
||||
<View>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('Albums')}
|
||||
style={styles.button}
|
||||
>
|
||||
Push album
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
<Article
|
||||
author={{ name: route.params.author }}
|
||||
scrollEnabled={scrollEnabled}
|
||||
/>
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
const AlbumsScreen = ({ navigation }: StackScreenProps<MixedStackParams>) => {
|
||||
return (
|
||||
<ScrollView>
|
||||
<View style={styles.buttons}>
|
||||
<View>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('Albums')}
|
||||
style={styles.button}
|
||||
>
|
||||
Push album
|
||||
</Button>
|
||||
<Button
|
||||
mode="outlined"
|
||||
onPress={() => navigation.goBack()}
|
||||
style={styles.button}
|
||||
>
|
||||
Go back
|
||||
</Button>
|
||||
</View>
|
||||
<View>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('Article', { author: 'The Doctor' })}
|
||||
style={styles.button}
|
||||
>
|
||||
Push article
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
<Albums scrollEnabled={scrollEnabled} />
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
const MixedStack = createStackNavigator<MixedStackParams>();
|
||||
|
||||
type Props = StackScreenProps<ParamListBase>;
|
||||
|
||||
export default function MixedStackScreen({ navigation }: Props) {
|
||||
React.useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerShown: false,
|
||||
});
|
||||
}, [navigation]);
|
||||
|
||||
return (
|
||||
<MixedStack.Navigator>
|
||||
<MixedStack.Screen
|
||||
name="Article"
|
||||
component={ArticleScreen}
|
||||
options={({ route }) => ({
|
||||
title: `Article by ${route.params.author}`,
|
||||
})}
|
||||
initialParams={{ author: 'Gandalf' }}
|
||||
/>
|
||||
<MixedStack.Screen
|
||||
name="Albums"
|
||||
component={AlbumsScreen}
|
||||
options={{
|
||||
title: 'Albums',
|
||||
...TransitionPresets.ModalPresentationIOS,
|
||||
}}
|
||||
/>
|
||||
</MixedStack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
buttons: {
|
||||
flexDirection: 'row',
|
||||
padding: 8,
|
||||
},
|
||||
button: {
|
||||
margin: 8,
|
||||
},
|
||||
});
|
||||
@@ -82,7 +82,7 @@ export default function ModalStackScreen({ navigation }: Props) {
|
||||
}, [navigation]);
|
||||
|
||||
return (
|
||||
<ModalStack.Navigator mode="modal">
|
||||
<ModalStack.Navigator screenOptions={{ animationPresentation: 'modal' }}>
|
||||
<ModalStack.Screen
|
||||
name="Article"
|
||||
component={ArticleScreen}
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from '@react-navigation/stack';
|
||||
import Article from '../Shared/Article';
|
||||
|
||||
type SimpleStackParams = {
|
||||
type TransparentStackParams = {
|
||||
Article: { author: string };
|
||||
Dialog: undefined;
|
||||
};
|
||||
@@ -18,7 +18,7 @@ const scrollEnabled = Platform.select({ web: true, default: false });
|
||||
const ArticleScreen = ({
|
||||
navigation,
|
||||
route,
|
||||
}: StackScreenProps<SimpleStackParams, 'Article'>) => {
|
||||
}: StackScreenProps<TransparentStackParams, 'Article'>) => {
|
||||
return (
|
||||
<ScrollView>
|
||||
<View style={styles.buttons}>
|
||||
@@ -45,7 +45,9 @@ const ArticleScreen = ({
|
||||
);
|
||||
};
|
||||
|
||||
const DialogScreen = ({ navigation }: StackScreenProps<SimpleStackParams>) => {
|
||||
const DialogScreen = ({
|
||||
navigation,
|
||||
}: StackScreenProps<TransparentStackParams>) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
@@ -70,12 +72,11 @@ const DialogScreen = ({ navigation }: StackScreenProps<SimpleStackParams>) => {
|
||||
);
|
||||
};
|
||||
|
||||
const SimpleStack = createStackNavigator<SimpleStackParams>();
|
||||
const TransparentStack = createStackNavigator<TransparentStackParams>();
|
||||
|
||||
type Props = Partial<React.ComponentProps<typeof SimpleStack.Navigator>> &
|
||||
StackScreenProps<ParamListBase>;
|
||||
type Props = StackScreenProps<ParamListBase>;
|
||||
|
||||
export default function SimpleStackScreen({ navigation, ...rest }: Props) {
|
||||
export default function TransparentStackScreen({ navigation }: Props) {
|
||||
React.useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerShown: false,
|
||||
@@ -83,13 +84,15 @@ export default function SimpleStackScreen({ navigation, ...rest }: Props) {
|
||||
}, [navigation]);
|
||||
|
||||
return (
|
||||
<SimpleStack.Navigator mode="modal" {...rest}>
|
||||
<SimpleStack.Screen
|
||||
<TransparentStack.Navigator
|
||||
screenOptions={{ animationPresentation: 'modal' }}
|
||||
>
|
||||
<TransparentStack.Screen
|
||||
name="Article"
|
||||
component={ArticleScreen}
|
||||
initialParams={{ author: 'Gandalf' }}
|
||||
/>
|
||||
<SimpleStack.Screen
|
||||
<TransparentStack.Screen
|
||||
name="Dialog"
|
||||
component={DialogScreen}
|
||||
options={{
|
||||
@@ -122,7 +125,7 @@ export default function SimpleStackScreen({ navigation, ...rest }: Props) {
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</SimpleStack.Navigator>
|
||||
</TransparentStack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
// @ts-expect-error: there are no type definitions for deep imports
|
||||
import TouchableBounce from 'react-native/Libraries/Components/Touchable/TouchableBounce';
|
||||
|
||||
export default TouchableBounce;
|
||||
@@ -1,3 +0,0 @@
|
||||
import { TouchableOpacity } from 'react-native';
|
||||
|
||||
export default TouchableOpacity;
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
DarkTheme,
|
||||
PathConfigMap,
|
||||
useNavigationContainerRef,
|
||||
NavigatorScreenParams,
|
||||
} from '@react-navigation/native';
|
||||
import { createDrawerNavigator } from '@react-navigation/drawer';
|
||||
import {
|
||||
@@ -42,6 +43,7 @@ import { restartApp } from './Restart';
|
||||
import SettingsItem from './Shared/SettingsItem';
|
||||
import SimpleStack from './Screens/SimpleStack';
|
||||
import ModalStack from './Screens/ModalStack';
|
||||
import MixedStack from './Screens/MixedStack';
|
||||
import MixedHeaderMode from './Screens/MixedHeaderMode';
|
||||
import StackTransparent from './Screens/StackTransparent';
|
||||
import StackHeaderCustomization from './Screens/StackHeaderCustomization';
|
||||
@@ -61,6 +63,13 @@ if (Platform.OS !== 'web') {
|
||||
|
||||
enableScreens();
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace ReactNavigation {
|
||||
interface RootParamList extends RootStackParamList {}
|
||||
}
|
||||
}
|
||||
|
||||
type RootDrawerParamList = {
|
||||
Examples: undefined;
|
||||
};
|
||||
@@ -71,6 +80,10 @@ const SCREENS = {
|
||||
title: 'Modal Stack',
|
||||
component: ModalStack,
|
||||
},
|
||||
MixedStack: {
|
||||
title: 'Regular + Modal Stack',
|
||||
component: MixedStack,
|
||||
},
|
||||
MixedHeaderMode: {
|
||||
title: 'Float + Screen Header Stack',
|
||||
component: MixedHeaderMode,
|
||||
@@ -115,7 +128,7 @@ const SCREENS = {
|
||||
};
|
||||
|
||||
type RootStackParamList = {
|
||||
Home: undefined;
|
||||
Home: NavigatorScreenParams<RootDrawerParamList>;
|
||||
NotFound: undefined;
|
||||
} & {
|
||||
[P in keyof typeof SCREENS]: undefined;
|
||||
@@ -228,7 +241,9 @@ export default function App() {
|
||||
prefixes: [createURL('/')],
|
||||
config: {
|
||||
initialRouteName: 'Home',
|
||||
screens: Object.keys(SCREENS).reduce<PathConfigMap>(
|
||||
screens: Object.keys(SCREENS).reduce<
|
||||
PathConfigMap<RootStackParamList>
|
||||
>(
|
||||
(acc, name) => {
|
||||
// Convert screen names such as SimpleStack to kebab case (simple-stack)
|
||||
const path = name
|
||||
@@ -236,13 +251,15 @@ export default function App() {
|
||||
.replace(/^-/, '')
|
||||
.toLowerCase();
|
||||
|
||||
// @ts-expect-error: these types aren't accurate
|
||||
// But we aren't too concerned for now
|
||||
acc[name] = {
|
||||
path,
|
||||
screens: {
|
||||
Article: {
|
||||
path: 'article/:author?',
|
||||
parse: {
|
||||
author: (author) =>
|
||||
author: (author: string) =>
|
||||
author.charAt(0).toUpperCase() +
|
||||
author.slice(1).replace(/-/g, ' '),
|
||||
},
|
||||
|
||||
@@ -3,6 +3,34 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [6.0.0-next.9](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@6.0.0-next.8...@react-navigation/bottom-tabs@6.0.0-next.9) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.8](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@6.0.0-next.7...@react-navigation/bottom-tabs@6.0.0-next.8) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@6.0.0-next.6...@react-navigation/bottom-tabs@6.0.0-next.7) (2021-05-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* enable screens only on supported platforms ([#9494](https://github.com/react-navigation/react-navigation/issues/9494)) ([8da4c58](https://github.com/react-navigation/react-navigation/commit/8da4c58065607d44e9dc1ad8943e09537598dcd7))
|
||||
* make sure disabling react-native-screens works ([a369ba3](https://github.com/react-navigation/react-navigation/commit/a369ba36451ddc2bb5b247e61b725bce1e3fb5e5))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@6.0.0-next.5...@react-navigation/bottom-tabs@6.0.0-next.6) (2021-05-01)
|
||||
|
||||
**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": "6.0.0-next.6",
|
||||
"version": "6.0.0-next.9",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -36,16 +36,16 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/elements": "^1.0.0-next.5",
|
||||
"@react-navigation/elements": "^1.0.0-next.8",
|
||||
"color": "^3.1.3",
|
||||
"warn-once": "^0.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-navigation/native": "^6.0.0-next.3",
|
||||
"@react-navigation/native": "^6.0.0-next.6",
|
||||
"@testing-library/react-native": "^7.2.0",
|
||||
"@types/color": "^3.0.1",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-native": "~0.63.51",
|
||||
"@types/react-native": "~0.64.4",
|
||||
"del-cli": "^3.0.1",
|
||||
"react": "~16.13.1",
|
||||
"react-native": "~0.63.4",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { ScreenContainer } from 'react-native-screens';
|
||||
import { StyleSheet, Platform } from 'react-native';
|
||||
import { SafeAreaInsetsContext } from 'react-native-safe-area-context';
|
||||
import {
|
||||
NavigationHelpersContext,
|
||||
@@ -13,8 +12,7 @@ import {
|
||||
SafeAreaProviderCompat,
|
||||
getHeaderTitle,
|
||||
} from '@react-navigation/elements';
|
||||
|
||||
import ScreenFallback from './ScreenFallback';
|
||||
import { MaybeScreenContainer, MaybeScreen } from './ScreenFallback';
|
||||
import BottomTabBar, { getTabBarHeight } from './BottomTabBar';
|
||||
import BottomTabBarHeightCallbackContext from '../utils/BottomTabBarHeightCallbackContext';
|
||||
import BottomTabBarHeightContext from '../utils/BottomTabBarHeightContext';
|
||||
@@ -40,7 +38,9 @@ export default function BottomTabView(props: Props) {
|
||||
navigation,
|
||||
descriptors,
|
||||
safeAreaInsets,
|
||||
detachInactiveScreens = true,
|
||||
detachInactiveScreens = Platform.OS === 'web' ||
|
||||
Platform.OS === 'android' ||
|
||||
Platform.OS === 'ios',
|
||||
sceneContainerStyle,
|
||||
} = props;
|
||||
|
||||
@@ -91,8 +91,7 @@ export default function BottomTabView(props: Props) {
|
||||
return (
|
||||
<NavigationHelpersContext.Provider value={navigation}>
|
||||
<SafeAreaProviderCompat>
|
||||
<ScreenContainer
|
||||
// @ts-ignore
|
||||
<MaybeScreenContainer
|
||||
enabled={detachInactiveScreens}
|
||||
style={styles.container}
|
||||
>
|
||||
@@ -121,7 +120,7 @@ export default function BottomTabView(props: Props) {
|
||||
} = descriptor.options;
|
||||
|
||||
return (
|
||||
<ScreenFallback
|
||||
<MaybeScreen
|
||||
key={route.key}
|
||||
style={StyleSheet.absoluteFill}
|
||||
visible={isFocused}
|
||||
@@ -147,10 +146,10 @@ export default function BottomTabView(props: Props) {
|
||||
{descriptor.render()}
|
||||
</Screen>
|
||||
</BottomTabBarHeightContext.Provider>
|
||||
</ScreenFallback>
|
||||
</MaybeScreen>
|
||||
);
|
||||
})}
|
||||
</ScreenContainer>
|
||||
</MaybeScreenContainer>
|
||||
<BottomTabBarHeightCallbackContext.Provider value={setTabBarHeight}>
|
||||
{renderTabBar()}
|
||||
</BottomTabBarHeightCallbackContext.Provider>
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { Platform, StyleProp, ViewStyle } from 'react-native';
|
||||
import {
|
||||
Screen,
|
||||
screensEnabled,
|
||||
// @ts-ignore
|
||||
shouldUseActivityState,
|
||||
} from 'react-native-screens';
|
||||
import { StyleProp, View, ViewProps, ViewStyle } from 'react-native';
|
||||
import { ResourceSavingView } from '@react-navigation/elements';
|
||||
|
||||
type Props = {
|
||||
@@ -15,22 +9,35 @@ type Props = {
|
||||
style?: StyleProp<ViewStyle>;
|
||||
};
|
||||
|
||||
export default function ScreenFallback({ visible, children, ...rest }: Props) {
|
||||
// react-native-screens is buggy on web
|
||||
if (screensEnabled?.() && Platform.OS !== 'web') {
|
||||
if (shouldUseActivityState) {
|
||||
return (
|
||||
<Screen activityState={visible ? 2 : 0} {...rest}>
|
||||
{children}
|
||||
</Screen>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Screen active={visible ? 1 : 0} {...rest}>
|
||||
{children}
|
||||
</Screen>
|
||||
);
|
||||
}
|
||||
let Screens: typeof import('react-native-screens') | undefined;
|
||||
|
||||
try {
|
||||
Screens = require('react-native-screens');
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
export const MaybeScreenContainer = ({
|
||||
enabled,
|
||||
...rest
|
||||
}: ViewProps & {
|
||||
enabled: boolean;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
if (Screens?.screensEnabled?.()) {
|
||||
return <Screens.ScreenContainer enabled={enabled} {...rest} />;
|
||||
}
|
||||
|
||||
return <View {...rest} />;
|
||||
};
|
||||
|
||||
export function MaybeScreen({ visible, children, ...rest }: Props) {
|
||||
if (Screens?.screensEnabled?.()) {
|
||||
return (
|
||||
<Screens.Screen activityState={visible ? 2 : 0} {...rest}>
|
||||
{children}
|
||||
</Screens.Screen>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,6 +3,40 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [6.0.0-next.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@6.0.0-next.5...@react-navigation/core@6.0.0-next.6) (2021-05-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix type annotations for useNavigation (again) ([929c3e3](https://github.com/react-navigation/react-navigation/commit/929c3e3a3b3eb32d197ef1f887dc4cbdce48eaff))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@6.0.0-next.4...@react-navigation/core@6.0.0-next.5) (2021-05-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix type annotations for useNavigation ([7da45e1](https://github.com/react-navigation/react-navigation/commit/7da45e1e8951ff46e09db4ebc2c88085c67ab8e9))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@6.0.0-next.3...@react-navigation/core@6.0.0-next.4) (2021-05-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add a new component to group multiple screens with common options ([1a6aebe](https://github.com/react-navigation/react-navigation/commit/1a6aebefcb77ea708687475c55742407d69808ce))
|
||||
* add ability to specify root param list ([b28bfdd](https://github.com/react-navigation/react-navigation/commit/b28bfddc17cbf3996fac04a34b2a7085ecf88be5))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@6.0.0-next.2...@react-navigation/core@6.0.0-next.3) (2021-05-01)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/core",
|
||||
"description": "Core utilities for building navigators",
|
||||
"version": "6.0.0-next.3",
|
||||
"version": "6.0.0-next.6",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-native",
|
||||
|
||||
13
packages/core/src/Group.tsx
Normal file
13
packages/core/src/Group.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { ParamListBase } from '@react-navigation/routers';
|
||||
import type { RouteGroupConfig } from './types';
|
||||
|
||||
/**
|
||||
* Empty component used for grouping screen configs.
|
||||
*/
|
||||
export default function Group<
|
||||
ParamList extends ParamListBase,
|
||||
ScreenOptions extends {}
|
||||
>(_: RouteGroupConfig<ParamList, ScreenOptions>) {
|
||||
/* istanbul ignore next */
|
||||
return null;
|
||||
}
|
||||
@@ -9,14 +9,10 @@ import NavigationStateContext from './NavigationStateContext';
|
||||
import StaticContainer from './StaticContainer';
|
||||
import EnsureSingleNavigator from './EnsureSingleNavigator';
|
||||
import useOptionsGetters from './useOptionsGetters';
|
||||
import type { NavigationProp, RouteConfig, EventMapBase } from './types';
|
||||
import type { NavigationProp, RouteConfigComponent } from './types';
|
||||
|
||||
type Props<
|
||||
State extends NavigationState,
|
||||
ScreenOptions extends {},
|
||||
EventMap extends EventMapBase
|
||||
> = {
|
||||
screen: RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>;
|
||||
type Props<State extends NavigationState, ScreenOptions extends {}> = {
|
||||
screen: RouteConfigComponent<ParamListBase, string> & { name: string };
|
||||
navigation: NavigationProp<ParamListBase, string, State, ScreenOptions>;
|
||||
route: Route<string>;
|
||||
routeState: NavigationState | PartialState<NavigationState> | undefined;
|
||||
@@ -31,8 +27,7 @@ type Props<
|
||||
*/
|
||||
export default function SceneView<
|
||||
State extends NavigationState,
|
||||
ScreenOptions extends {},
|
||||
EventMap extends EventMapBase
|
||||
ScreenOptions extends {}
|
||||
>({
|
||||
screen,
|
||||
route,
|
||||
@@ -41,7 +36,7 @@ export default function SceneView<
|
||||
getState,
|
||||
setState,
|
||||
options,
|
||||
}: Props<State, ScreenOptions, EventMap>) {
|
||||
}: Props<State, ScreenOptions>) {
|
||||
const navigatorKeyRef = React.useRef<string | undefined>();
|
||||
const getKey = React.useCallback(() => navigatorKeyRef.current, []);
|
||||
|
||||
|
||||
@@ -33,8 +33,10 @@ it('converts state to path string', () => {
|
||||
|
||||
const path = '/foo/bar/baz%20qux?author=jane&valid=true';
|
||||
|
||||
expect(getPathFromState(state)).toBe(path);
|
||||
expect(getPathFromState(getStateFromPath(path) as State)).toBe(path);
|
||||
expect(getPathFromState<object>(state)).toBe(path);
|
||||
expect(
|
||||
getPathFromState<object>(getStateFromPath<object>(path) as State)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
it('converts state to path string with config', () => {
|
||||
@@ -97,9 +99,12 @@ it('converts state to path string with config', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -116,8 +121,10 @@ it('handles route without param', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state)).toBe(path);
|
||||
expect(getPathFromState(getStateFromPath(path) as State)).toBe(path);
|
||||
expect(getPathFromState<object>(state)).toBe(path);
|
||||
expect(
|
||||
getPathFromState<object>(getStateFromPath<object>(path) as State)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
it("doesn't add query param for empty params", () => {
|
||||
@@ -131,8 +138,10 @@ it("doesn't add query param for empty params", () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state)).toBe(path);
|
||||
expect(getPathFromState(getStateFromPath(path) as State)).toBe(path);
|
||||
expect(getPathFromState<object>(state)).toBe(path);
|
||||
expect(
|
||||
getPathFromState<object>(getStateFromPath<object>(path) as State)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
it('handles state with config with nested screens', () => {
|
||||
@@ -208,9 +217,12 @@ it('handles state with config with nested screens', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -287,9 +299,12 @@ it('handles state with config with nested screens and exact', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -352,9 +367,12 @@ it('handles state with config with nested screens and unused configs', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -418,9 +436,12 @@ it('handles state with config with nested screens and unused configs with exact'
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -498,9 +519,12 @@ it('handles nested object with stringify in it', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -580,9 +604,12 @@ it('handles nested object with stringify in it with exact', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -623,9 +650,12 @@ it('handles nested object for second route depth', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -669,9 +699,12 @@ it('handles nested object for second route depth with exact', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -719,9 +752,12 @@ it('handles nested object for second route depth and path and stringify in roots
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -774,9 +810,12 @@ it('handles nested object for second route depth and path and stringify in roots
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -805,9 +844,12 @@ it('ignores empty string paths', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -850,9 +892,12 @@ it('keeps query params if path is empty', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toEqual(path);
|
||||
});
|
||||
|
||||
@@ -894,9 +939,12 @@ it('cuts nested configs too', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -939,9 +987,12 @@ it('cuts nested configs too with exact', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -977,9 +1028,12 @@ it('handles empty path at the end', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1012,9 +1066,12 @@ it('returns "/" for empty path', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1042,9 +1099,12 @@ it('parses no path specified', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1121,9 +1181,12 @@ it('strips undefined query params', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1201,9 +1264,12 @@ it('strips undefined query params with exact', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1278,9 +1344,12 @@ it('handles stripping all query params', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1357,9 +1426,12 @@ it('handles stripping all query params with exact', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1380,9 +1452,12 @@ it('replaces undefined query params', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1405,9 +1480,12 @@ it('matches wildcard patterns at root', () => {
|
||||
routes: [{ name: '404' }],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe('/404');
|
||||
expect(getPathFromState<object>(state, config)).toBe('/404');
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe('/404');
|
||||
});
|
||||
|
||||
@@ -1447,9 +1525,12 @@ it('matches wildcard patterns at nested level', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe('/bar/42/404');
|
||||
expect(getPathFromState<object>(state, config)).toBe('/bar/42/404');
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe('/bar/42/404');
|
||||
});
|
||||
|
||||
@@ -1492,9 +1573,12 @@ it('matches wildcard patterns at nested level with exact', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe('/404');
|
||||
expect(getPathFromState<object>(state, config)).toBe('/404');
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe('/404');
|
||||
});
|
||||
|
||||
@@ -1535,9 +1619,12 @@ it('tries to match wildcard patterns at the end', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState<object>(state, config)).toBe(path);
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe(path);
|
||||
});
|
||||
|
||||
@@ -1570,8 +1657,11 @@ it('uses nearest parent wildcard match for unmatched paths', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe('/404');
|
||||
expect(getPathFromState<object>(state, config)).toBe('/404');
|
||||
expect(
|
||||
getPathFromState(getStateFromPath(path, config) as State, config)
|
||||
getPathFromState<object>(
|
||||
getStateFromPath<object>(path, config) as State,
|
||||
config
|
||||
)
|
||||
).toBe('/404');
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@ const changePath = <T extends InitialState>(state: T, path: string): T =>
|
||||
});
|
||||
|
||||
it('returns undefined for invalid path', () => {
|
||||
expect(getStateFromPath('//')).toBe(undefined);
|
||||
expect(getStateFromPath<object>('//')).toBe(undefined);
|
||||
});
|
||||
|
||||
it('converts path string to initial state', () => {
|
||||
@@ -41,8 +41,8 @@ it('converts path string to initial state', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state))).toEqual(
|
||||
expect(getStateFromPath<object>(path)).toEqual(state);
|
||||
expect(getStateFromPath<object>(getPathFromState<object>(state))).toEqual(
|
||||
changePath(state, '/foo/bar/baz%20qux?author=jane%20%26%20co&valid=true')
|
||||
);
|
||||
});
|
||||
@@ -106,16 +106,16 @@ it('converts path string to initial state with config', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles leading slash when converting', () => {
|
||||
const path = '/foo/bar/?count=42';
|
||||
|
||||
expect(getStateFromPath(path)).toEqual({
|
||||
expect(getStateFromPath<object>(path)).toEqual({
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
@@ -136,7 +136,7 @@ it('handles leading slash when converting', () => {
|
||||
it('handles ending slash when converting', () => {
|
||||
const path = 'foo/bar/?count=42';
|
||||
|
||||
expect(getStateFromPath(path)).toEqual({
|
||||
expect(getStateFromPath<object>(path)).toEqual({
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
@@ -167,8 +167,8 @@ it('handles route without param', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state))).toEqual(
|
||||
expect(getStateFromPath<object>(path)).toEqual(state);
|
||||
expect(getStateFromPath<object>(getPathFromState<object>(state))).toEqual(
|
||||
changePath(state, '/foo/bar')
|
||||
);
|
||||
});
|
||||
@@ -245,10 +245,10 @@ it('converts path string to initial state with config with nested screens', () =
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('converts path string to initial state with config with nested screens and unused parse functions', () => {
|
||||
@@ -308,10 +308,10 @@ it('converts path string to initial state with config with nested screens and un
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/foe/baz/Jane?count=10&answer=42&valid=true')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/foe/baz/Jane?count=10&answer=42&valid=true'));
|
||||
});
|
||||
|
||||
it('handles nested object with unused configs and with parse in it', () => {
|
||||
@@ -400,10 +400,10 @@ it('handles nested object with unused configs and with parse in it', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles parse in nested object for second route depth', () => {
|
||||
@@ -450,10 +450,10 @@ it('handles parse in nested object for second route depth', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles parse in nested object for second route depth and and path and parse in roots', () => {
|
||||
@@ -501,10 +501,10 @@ it('handles parse in nested object for second route depth and and path and parse
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles initialRouteName at top level', () => {
|
||||
@@ -545,10 +545,10 @@ it('handles initialRouteName at top level', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles initialRouteName inside a screen', () => {
|
||||
@@ -591,10 +591,10 @@ it('handles initialRouteName inside a screen', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles initialRouteName included in path', () => {
|
||||
@@ -633,10 +633,10 @@ it('handles initialRouteName included in path', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles two initialRouteNames', () => {
|
||||
@@ -728,10 +728,10 @@ it('handles two initialRouteNames', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('accepts initialRouteName without config for it', () => {
|
||||
@@ -823,10 +823,10 @@ it('accepts initialRouteName without config for it', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('returns undefined if path is empty and no matching screen is present', () => {
|
||||
@@ -847,7 +847,7 @@ it('returns undefined if path is empty and no matching screen is present', () =>
|
||||
|
||||
const path = '';
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(undefined);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('returns matching screen if path is empty', () => {
|
||||
@@ -886,10 +886,10 @@ it('returns matching screen if path is empty', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/'));
|
||||
});
|
||||
|
||||
it('returns matching screen with params if path is empty', () => {
|
||||
@@ -931,10 +931,10 @@ it('returns matching screen with params if path is empty', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/?foo=42')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/?foo=42'));
|
||||
});
|
||||
|
||||
it("doesn't match nested screen if path is empty", () => {
|
||||
@@ -959,7 +959,7 @@ it("doesn't match nested screen if path is empty", () => {
|
||||
|
||||
const path = '';
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(undefined);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('chooses more exhaustive pattern', () => {
|
||||
@@ -1004,10 +1004,10 @@ it('chooses more exhaustive pattern', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles same paths beginnings', () => {
|
||||
@@ -1048,10 +1048,10 @@ it('handles same paths beginnings', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles same paths beginnings with params', () => {
|
||||
@@ -1096,10 +1096,10 @@ it('handles same paths beginnings with params', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles not taking path with too many segments', () => {
|
||||
@@ -1151,10 +1151,10 @@ it('handles not taking path with too many segments', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles differently ordered params v1', () => {
|
||||
@@ -1206,10 +1206,10 @@ it('handles differently ordered params v1', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles differently ordered params v2', () => {
|
||||
@@ -1261,10 +1261,10 @@ it('handles differently ordered params v2', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles differently ordered params v3', () => {
|
||||
@@ -1316,10 +1316,10 @@ it('handles differently ordered params v3', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handles differently ordered params v4', () => {
|
||||
@@ -1371,10 +1371,10 @@ it('handles differently ordered params v4', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/5/foos/res/20')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/5/foos/res/20'));
|
||||
});
|
||||
|
||||
it('handles simple optional params', () => {
|
||||
@@ -1426,10 +1426,10 @@ it('handles simple optional params', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle 2 optional params at the end v1', () => {
|
||||
@@ -1481,10 +1481,10 @@ it('handle 2 optional params at the end v1', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle 2 optional params at the end v2', () => {
|
||||
@@ -1536,10 +1536,10 @@ it('handle 2 optional params at the end v2', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle 2 optional params at the end v3', () => {
|
||||
@@ -1592,10 +1592,10 @@ it('handle 2 optional params at the end v3', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle optional params in the middle v1', () => {
|
||||
@@ -1648,10 +1648,10 @@ it('handle optional params in the middle v1', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle optional params in the middle v2', () => {
|
||||
@@ -1704,10 +1704,10 @@ it('handle optional params in the middle v2', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle optional params in the middle v3', () => {
|
||||
@@ -1761,10 +1761,10 @@ it('handle optional params in the middle v3', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle optional params in the middle v4', () => {
|
||||
@@ -1818,10 +1818,10 @@ it('handle optional params in the middle v4', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle optional params in the middle v5', () => {
|
||||
@@ -1875,10 +1875,10 @@ it('handle optional params in the middle v5', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('handle optional params in the beginning v1', () => {
|
||||
@@ -1932,10 +1932,10 @@ it('handle optional params in the beginning v1', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/5/10/foos/15')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/5/10/foos/15'));
|
||||
});
|
||||
|
||||
it('handle optional params in the beginning v2', () => {
|
||||
@@ -1989,10 +1989,10 @@ it('handle optional params in the beginning v2', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/5/10/foos/15')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/5/10/foos/15'));
|
||||
});
|
||||
|
||||
it('merges parent patterns if needed', () => {
|
||||
@@ -2030,10 +2030,10 @@ it('merges parent patterns if needed', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/foo/42/baz/babel')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/foo/42/baz/babel'));
|
||||
});
|
||||
|
||||
it('ignores extra slashes in the pattern', () => {
|
||||
@@ -2067,10 +2067,10 @@ it('ignores extra slashes in the pattern', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('matches wildcard patterns at root', () => {
|
||||
@@ -2092,10 +2092,10 @@ it('matches wildcard patterns at root', () => {
|
||||
routes: [{ name: '404', path }],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/404')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/404'));
|
||||
});
|
||||
|
||||
it('matches wildcard patterns at nested level', () => {
|
||||
@@ -2134,10 +2134,10 @@ it('matches wildcard patterns at nested level', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/bar/42/404')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/bar/42/404'));
|
||||
});
|
||||
|
||||
it('matches wildcard patterns at nested level with exact', () => {
|
||||
@@ -2179,10 +2179,10 @@ it('matches wildcard patterns at nested level with exact', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/404')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/404'));
|
||||
});
|
||||
|
||||
it('tries to match wildcard patterns at the end', () => {
|
||||
@@ -2222,10 +2222,10 @@ it('tries to match wildcard patterns at the end', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('uses nearest parent wildcard match for unmatched paths', () => {
|
||||
@@ -2257,17 +2257,17 @@ it('uses nearest parent wildcard match for unmatched paths', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
changePath(state, '/404')
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(changePath(state, '/404'));
|
||||
});
|
||||
|
||||
it('throws if two screens map to the same pattern', () => {
|
||||
const path = '/bar/42/baz/test';
|
||||
|
||||
expect(() =>
|
||||
getStateFromPath(path, {
|
||||
getStateFromPath<object>(path, {
|
||||
screens: {
|
||||
Foo: {
|
||||
screens: {
|
||||
@@ -2287,7 +2287,7 @@ it('throws if two screens map to the same pattern', () => {
|
||||
);
|
||||
|
||||
expect(() =>
|
||||
getStateFromPath(path, {
|
||||
getStateFromPath<object>(path, {
|
||||
screens: {
|
||||
Foo: {
|
||||
screens: {
|
||||
@@ -2354,10 +2354,10 @@ it('correctly applies initialRouteName for config with similar route names', ()
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
it('correctly applies initialRouteName for config with similar route names v2', () => {
|
||||
@@ -2414,8 +2414,8 @@ it('correctly applies initialRouteName for config with similar route names v2',
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
expect(getStateFromPath<object>(path, config)).toEqual(state);
|
||||
expect(
|
||||
getStateFromPath<object>(getPathFromState<object>(state, config), config)
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { render, act } from '@testing-library/react-native';
|
||||
import type { NavigationState, ParamListBase } from '@react-navigation/routers';
|
||||
import Group from '../Group';
|
||||
import Screen from '../Screen';
|
||||
import BaseNavigationContainer from '../BaseNavigationContainer';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
@@ -248,6 +249,53 @@ it('initializes state for nested screens in React.Fragment', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('initializes state for nested screens in Group', () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const TestScreen = (props: any) => {
|
||||
React.useEffect(() => {
|
||||
props.navigation.dispatch({ type: 'UPDATE' });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<BaseNavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={TestScreen} />
|
||||
<Group>
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
<Screen name="baz" component={jest.fn()} />
|
||||
</Group>
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onStateChange).toBeCalledWith({
|
||||
stale: false,
|
||||
type: 'test',
|
||||
index: 0,
|
||||
key: '0',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'foo', name: 'foo' },
|
||||
{ key: 'bar', name: 'bar' },
|
||||
{ key: 'baz', name: 'baz' },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('initializes state for nested navigator on navigation', () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
@@ -1450,7 +1498,7 @@ it('throws when Screen is not the direct children', () => {
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
"A navigator can only contain 'Screen' components as its direct children (found 'Bar')"
|
||||
"A navigator can only contain 'Screen', 'Group' or 'React.Fragment' as its direct children (found 'Bar')"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1475,7 +1523,7 @@ it('throws when undefined component is a direct children', () => {
|
||||
spy.mockRestore();
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
"A navigator can only contain 'Screen' components as its direct children (found 'undefined' for the screen 'foo')"
|
||||
"A navigator can only contain 'Screen', 'Group' or 'React.Fragment' as its direct children (found 'undefined' for the screen 'foo')"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1495,7 +1543,7 @@ it('throws when a tag is a direct children', () => {
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
"A navigator can only contain 'Screen' components as its direct children (found 'screen' for the screen 'foo')"
|
||||
"A navigator can only contain 'Screen', 'Group' or 'React.Fragment' as its direct children (found 'screen' for the screen 'foo')"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1515,7 +1563,7 @@ it('throws when a React Element is not the direct children', () => {
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
"A navigator can only contain 'Screen' components as its direct children (found 'Hello world')"
|
||||
"A navigator can only contain 'Screen', 'Group' or 'React.Fragment' as its direct children (found 'Hello world')"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1838,19 +1886,16 @@ it("returns focused screen's options with getCurrentOptions when focused screen
|
||||
<TestNavigator>
|
||||
<Screen name="bar" options={{ a: 'b' }}>
|
||||
{() => (
|
||||
<TestNavigator
|
||||
initialRouteName="bar-a"
|
||||
screenOptions={() => ({ sample2: 'data' })}
|
||||
>
|
||||
<TestNavigator initialRouteName="bar-a">
|
||||
<Screen
|
||||
name="bar-a"
|
||||
component={TestScreen}
|
||||
options={{ sample: 'data' }}
|
||||
options={{ sample: '1' }}
|
||||
/>
|
||||
<Screen
|
||||
name="bar-b"
|
||||
component={TestScreen}
|
||||
options={{ sample3: 'data' }}
|
||||
options={{ sample2: '2' }}
|
||||
/>
|
||||
</TestNavigator>
|
||||
)}
|
||||
@@ -1863,15 +1908,122 @@ it("returns focused screen's options with getCurrentOptions when focused screen
|
||||
render(container).update(container);
|
||||
|
||||
expect(navigation.getCurrentOptions()).toEqual({
|
||||
sample: 'data',
|
||||
sample2: 'data',
|
||||
sample: '1',
|
||||
});
|
||||
|
||||
act(() => navigation.navigate('bar-b'));
|
||||
|
||||
expect(navigation.getCurrentOptions()).toEqual({
|
||||
sample2: 'data',
|
||||
sample3: 'data',
|
||||
sample2: '2',
|
||||
});
|
||||
});
|
||||
|
||||
it("returns focused screen's options with getCurrentOptions when focused screen is rendered when using screenOptions", () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const TestScreen = () => null;
|
||||
|
||||
const navigation = createNavigationContainerRef<ParamListBase>();
|
||||
|
||||
const container = (
|
||||
<BaseNavigationContainer ref={navigation}>
|
||||
<TestNavigator>
|
||||
<Screen name="bar" options={{ a: 'b' }}>
|
||||
{() => (
|
||||
<TestNavigator
|
||||
initialRouteName="bar-a"
|
||||
screenOptions={() => ({ sample2: '2' })}
|
||||
>
|
||||
<Screen
|
||||
name="bar-a"
|
||||
component={TestScreen}
|
||||
options={{ sample: '1' }}
|
||||
/>
|
||||
<Screen
|
||||
name="bar-b"
|
||||
component={TestScreen}
|
||||
options={{ sample3: '3' }}
|
||||
/>
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
<Screen name="xux" component={TestScreen} />
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(container).update(container);
|
||||
|
||||
expect(navigation.getCurrentOptions()).toEqual({
|
||||
sample: '1',
|
||||
sample2: '2',
|
||||
});
|
||||
|
||||
act(() => navigation.navigate('bar-b'));
|
||||
|
||||
expect(navigation.getCurrentOptions()).toEqual({
|
||||
sample2: '2',
|
||||
sample3: '3',
|
||||
});
|
||||
});
|
||||
|
||||
it("returns focused screen's options with getCurrentOptions when focused screen is rendered when using Group", () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const TestScreen = () => null;
|
||||
|
||||
const navigation = createNavigationContainerRef<ParamListBase>();
|
||||
|
||||
const container = (
|
||||
<BaseNavigationContainer ref={navigation}>
|
||||
<TestNavigator>
|
||||
<Screen name="bar" options={{ a: 'b' }}>
|
||||
{() => (
|
||||
<TestNavigator
|
||||
initialRouteName="bar-a"
|
||||
screenOptions={() => ({ sample2: '2' })}
|
||||
>
|
||||
<Screen
|
||||
name="bar-a"
|
||||
component={TestScreen}
|
||||
options={{ sample: '1' }}
|
||||
/>
|
||||
<Group screenOptions={{ sample4: '4' }}>
|
||||
<Screen
|
||||
name="bar-b"
|
||||
component={TestScreen}
|
||||
options={{ sample3: '3' }}
|
||||
/>
|
||||
</Group>
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
<Screen name="xux" component={TestScreen} />
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(container).update(container);
|
||||
|
||||
expect(navigation.getCurrentOptions()).toEqual({
|
||||
sample: '1',
|
||||
sample2: '2',
|
||||
});
|
||||
|
||||
act(() => navigation.navigate('bar-b'));
|
||||
|
||||
expect(navigation.getCurrentOptions()).toEqual({
|
||||
sample2: '2',
|
||||
sample3: '3',
|
||||
sample4: '4',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1891,19 +2043,16 @@ it("returns focused screen's options with getCurrentOptions when all screens are
|
||||
<TestNavigator>
|
||||
<Screen name="bar" options={{ a: 'b' }}>
|
||||
{() => (
|
||||
<TestNavigator
|
||||
initialRouteName="bar-a"
|
||||
screenOptions={() => ({ sample2: 'data' })}
|
||||
>
|
||||
<TestNavigator initialRouteName="bar-a">
|
||||
<Screen
|
||||
name="bar-a"
|
||||
component={TestScreen}
|
||||
options={{ sample: 'data' }}
|
||||
options={{ sample: '1' }}
|
||||
/>
|
||||
<Screen
|
||||
name="bar-b"
|
||||
component={TestScreen}
|
||||
options={{ sample3: 'data' }}
|
||||
options={{ sample2: '2' }}
|
||||
/>
|
||||
</TestNavigator>
|
||||
)}
|
||||
@@ -1916,15 +2065,122 @@ it("returns focused screen's options with getCurrentOptions when all screens are
|
||||
render(container).update(container);
|
||||
|
||||
expect(navigation.getCurrentOptions()).toEqual({
|
||||
sample: 'data',
|
||||
sample2: 'data',
|
||||
sample: '1',
|
||||
});
|
||||
|
||||
act(() => navigation.navigate('bar-b'));
|
||||
|
||||
expect(navigation.getCurrentOptions()).toEqual({
|
||||
sample2: 'data',
|
||||
sample3: 'data',
|
||||
sample2: '2',
|
||||
});
|
||||
});
|
||||
|
||||
it("returns focused screen's options with getCurrentOptions when all screens are rendered with screenOptions", () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return <>{state.routes.map((route) => descriptors[route.key].render())}</>;
|
||||
};
|
||||
|
||||
const TestScreen = () => null;
|
||||
|
||||
const navigation = createNavigationContainerRef<ParamListBase>();
|
||||
|
||||
const container = (
|
||||
<BaseNavigationContainer ref={navigation}>
|
||||
<TestNavigator>
|
||||
<Screen name="bar" options={{ a: 'b' }}>
|
||||
{() => (
|
||||
<TestNavigator
|
||||
initialRouteName="bar-a"
|
||||
screenOptions={() => ({ sample2: '2' })}
|
||||
>
|
||||
<Screen
|
||||
name="bar-a"
|
||||
component={TestScreen}
|
||||
options={{ sample: '1' }}
|
||||
/>
|
||||
<Screen
|
||||
name="bar-b"
|
||||
component={TestScreen}
|
||||
options={{ sample3: '3' }}
|
||||
/>
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
<Screen name="xux" component={TestScreen} />
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(container).update(container);
|
||||
|
||||
expect(navigation.getCurrentOptions()).toEqual({
|
||||
sample: '1',
|
||||
sample2: '2',
|
||||
});
|
||||
|
||||
act(() => navigation.navigate('bar-b'));
|
||||
|
||||
expect(navigation.getCurrentOptions()).toEqual({
|
||||
sample2: '2',
|
||||
sample3: '3',
|
||||
});
|
||||
});
|
||||
|
||||
it("returns focused screen's options with getCurrentOptions when all screens are rendered with Group", () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return <>{state.routes.map((route) => descriptors[route.key].render())}</>;
|
||||
};
|
||||
|
||||
const TestScreen = () => null;
|
||||
|
||||
const navigation = createNavigationContainerRef<ParamListBase>();
|
||||
|
||||
const container = (
|
||||
<BaseNavigationContainer ref={navigation}>
|
||||
<TestNavigator>
|
||||
<Screen name="bar" options={{ a: 'b' }}>
|
||||
{() => (
|
||||
<TestNavigator
|
||||
initialRouteName="bar-a"
|
||||
screenOptions={() => ({ sample2: '2' })}
|
||||
>
|
||||
<Screen
|
||||
name="bar-a"
|
||||
component={TestScreen}
|
||||
options={{ sample: '1' }}
|
||||
/>
|
||||
<Group screenOptions={{ sample4: '4' }}>
|
||||
<Screen
|
||||
name="bar-b"
|
||||
component={TestScreen}
|
||||
options={{ sample3: '3' }}
|
||||
/>
|
||||
</Group>
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
<Screen name="xux" component={TestScreen} />
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(container).update(container);
|
||||
|
||||
expect(navigation.getCurrentOptions()).toEqual({
|
||||
sample: '1',
|
||||
sample2: '2',
|
||||
});
|
||||
|
||||
act(() => navigation.navigate('bar-b'));
|
||||
|
||||
expect(navigation.getCurrentOptions()).toEqual({
|
||||
sample2: '2',
|
||||
sample3: '3',
|
||||
sample4: '4',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { CommonActions, ParamListBase } from '@react-navigation/routers';
|
||||
import { CommonActions } from '@react-navigation/routers';
|
||||
import type { NavigationContainerRefWithCurrent } from './types';
|
||||
|
||||
export const NOT_INITIALIZED_ERROR =
|
||||
"The 'navigation' object hasn't been initialized yet. This might happen if you don't have a navigator mounted, or if the navigator hasn't finished mounting. See https://reactnavigation.org/docs/navigating-without-navigation-prop#handling-initialization for more details.";
|
||||
|
||||
export default function createNavigationContainerRef<
|
||||
ParamList extends ParamListBase
|
||||
ParamList extends {} = ReactNavigation.RootParamList
|
||||
>(): NavigationContainerRefWithCurrent<ParamList> {
|
||||
const methods = [
|
||||
...Object.keys(CommonActions),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type * as React from 'react';
|
||||
import type { ParamListBase, NavigationState } from '@react-navigation/routers';
|
||||
import Group from './Group';
|
||||
import Screen from './Screen';
|
||||
import type { TypedNavigator, EventMapBase } from './types';
|
||||
|
||||
@@ -31,6 +32,7 @@ export default function createNavigatorFactory<
|
||||
|
||||
return {
|
||||
Navigator,
|
||||
Group,
|
||||
Screen,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -13,7 +13,10 @@ type ConfigItem = {
|
||||
screens?: Record<string, ConfigItem>;
|
||||
};
|
||||
|
||||
type Options = { initialRouteName?: string; screens: PathConfigMap };
|
||||
type Options = {
|
||||
initialRouteName?: string;
|
||||
screens: PathConfigMap<object>;
|
||||
};
|
||||
|
||||
type NavigateAction<State extends NavigationState> = {
|
||||
type: 'NAVIGATE';
|
||||
@@ -29,7 +32,9 @@ export default function getActionFromState(
|
||||
options?: Options
|
||||
): NavigateAction<NavigationState> | CommonActions.Action | undefined {
|
||||
// Create a normalized configs object which will be easier to use
|
||||
const normalizedConfig = options ? createNormalizedConfigItem(options) : {};
|
||||
const normalizedConfig = options
|
||||
? createNormalizedConfigItem(options as PathConfig<object> | string)
|
||||
: {};
|
||||
|
||||
const routes =
|
||||
state.index != null ? state.routes.slice(0, state.index + 1) : state.routes;
|
||||
@@ -130,7 +135,7 @@ export default function getActionFromState(
|
||||
};
|
||||
}
|
||||
|
||||
const createNormalizedConfigItem = (config: PathConfig | string) =>
|
||||
const createNormalizedConfigItem = (config: PathConfig<object> | string) =>
|
||||
typeof config === 'object' && config != null
|
||||
? {
|
||||
initialRouteName: config.initialRouteName,
|
||||
@@ -141,7 +146,7 @@ const createNormalizedConfigItem = (config: PathConfig | string) =>
|
||||
}
|
||||
: {};
|
||||
|
||||
const createNormalizedConfigs = (options: PathConfigMap) =>
|
||||
const createNormalizedConfigs = (options: PathConfigMap<object>) =>
|
||||
Object.entries(options).reduce<Record<string, ConfigItem>>((acc, [k, v]) => {
|
||||
acc[k] = createNormalizedConfigItem(v);
|
||||
return acc;
|
||||
|
||||
@@ -7,7 +7,10 @@ import type {
|
||||
import fromEntries from './fromEntries';
|
||||
import type { PathConfig, PathConfigMap } from './types';
|
||||
|
||||
type Options = { initialRouteName?: string; screens: PathConfigMap };
|
||||
type Options<ParamList> = {
|
||||
initialRouteName?: string;
|
||||
screens: PathConfigMap<ParamList>;
|
||||
};
|
||||
|
||||
type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>;
|
||||
|
||||
@@ -61,9 +64,9 @@ const getActiveRoute = (state: State): { name: string; params?: object } => {
|
||||
* @param options Extra options to fine-tune how to serialize the path.
|
||||
* @returns Path representing the state, e.g. /foo/bar?count=42.
|
||||
*/
|
||||
export default function getPathFromState(
|
||||
export default function getPathFromState<ParamList extends {}>(
|
||||
state: State,
|
||||
options?: Options
|
||||
options?: Options<ParamList>
|
||||
): string {
|
||||
if (state == null) {
|
||||
throw Error(
|
||||
@@ -238,7 +241,7 @@ const joinPaths = (...paths: string[]): string =>
|
||||
.join('/');
|
||||
|
||||
const createConfigItem = (
|
||||
config: PathConfig | string,
|
||||
config: PathConfig<object> | string,
|
||||
parentPattern?: string
|
||||
): ConfigItem => {
|
||||
if (typeof config === 'string') {
|
||||
@@ -276,7 +279,7 @@ const createConfigItem = (
|
||||
};
|
||||
|
||||
const createNormalizedConfigs = (
|
||||
options: PathConfigMap,
|
||||
options: PathConfigMap<object>,
|
||||
pattern?: string
|
||||
): Record<string, ConfigItem> =>
|
||||
fromEntries(
|
||||
|
||||
@@ -8,9 +8,9 @@ import type {
|
||||
import findFocusedRoute from './findFocusedRoute';
|
||||
import type { PathConfigMap } from './types';
|
||||
|
||||
type Options = {
|
||||
type Options<ParamList extends {}> = {
|
||||
initialRouteName?: string;
|
||||
screens: PathConfigMap;
|
||||
screens: PathConfigMap<ParamList>;
|
||||
};
|
||||
|
||||
type ParseConfig = Record<string, (value: string) => any>;
|
||||
@@ -60,9 +60,9 @@ type ParsedRoute = {
|
||||
* @param path Path string to parse and convert, e.g. /foo/bar?count=42.
|
||||
* @param options Extra options to fine-tune how to parse the path.
|
||||
*/
|
||||
export default function getStateFromPath(
|
||||
export default function getStateFromPath<ParamList extends {}>(
|
||||
path: string,
|
||||
options?: Options
|
||||
options?: Options<ParamList>
|
||||
): ResultState | undefined {
|
||||
let initialRoutes: InitialRouteConfig[] = [];
|
||||
|
||||
@@ -106,7 +106,7 @@ export default function getStateFromPath(
|
||||
...Object.keys(screens).map((key) =>
|
||||
createNormalizedConfigs(
|
||||
key,
|
||||
screens as PathConfigMap,
|
||||
screens as PathConfigMap<object>,
|
||||
[],
|
||||
initialRoutes,
|
||||
[]
|
||||
@@ -307,7 +307,7 @@ const matchAgainstConfigs = (remaining: string, configs: RouteConfig[]) => {
|
||||
|
||||
const createNormalizedConfigs = (
|
||||
screen: string,
|
||||
routeConfig: PathConfigMap,
|
||||
routeConfig: PathConfigMap<object>,
|
||||
routeNames: string[] = [],
|
||||
initials: InitialRouteConfig[],
|
||||
parentScreens: string[],
|
||||
@@ -319,6 +319,7 @@ const createNormalizedConfigs = (
|
||||
|
||||
parentScreens.push(screen);
|
||||
|
||||
// @ts-expect-error: we can't strongly typecheck this for now
|
||||
const config = routeConfig[screen];
|
||||
|
||||
if (typeof config === 'string') {
|
||||
@@ -345,7 +346,13 @@ const createNormalizedConfigs = (
|
||||
: config.path || '';
|
||||
|
||||
configs.push(
|
||||
createConfigItem(screen, routeNames, pattern, config.path, config.parse)
|
||||
createConfigItem(
|
||||
screen,
|
||||
routeNames,
|
||||
pattern!,
|
||||
config.path,
|
||||
config.parse
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -361,7 +368,7 @@ const createNormalizedConfigs = (
|
||||
Object.keys(config.screens).forEach((nestedConfig) => {
|
||||
const result = createNormalizedConfigs(
|
||||
nestedConfig,
|
||||
config.screens as PathConfigMap,
|
||||
config.screens as PathConfigMap<object>,
|
||||
routeNames,
|
||||
initials,
|
||||
[...parentScreens],
|
||||
|
||||
@@ -9,6 +9,14 @@ import type {
|
||||
ParamListBase,
|
||||
} from '@react-navigation/routers';
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace ReactNavigation {
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface RootParamList {}
|
||||
}
|
||||
}
|
||||
|
||||
type Keyof<T extends {}> = Extract<keyof T, string>;
|
||||
|
||||
export type DefaultNavigatorOptions<
|
||||
@@ -17,7 +25,7 @@ export type DefaultNavigatorOptions<
|
||||
> = DefaultRouterOptions<Keyof<ParamList>> & {
|
||||
/**
|
||||
* Children React Elements to extract the route configuration from.
|
||||
* Only `Screen` components are supported as children.
|
||||
* Only `Screen`, `Group` and `React.Fragment` are supported as children.
|
||||
*/
|
||||
children: React.ReactNode;
|
||||
/**
|
||||
@@ -265,7 +273,7 @@ export type NavigationContainerProps = {
|
||||
};
|
||||
|
||||
export type NavigationProp<
|
||||
ParamList extends ParamListBase,
|
||||
ParamList extends {},
|
||||
RouteName extends keyof ParamList = Keyof<ParamList>,
|
||||
State extends NavigationState = NavigationState<ParamList>,
|
||||
ScreenOptions extends {} = {},
|
||||
@@ -376,6 +384,38 @@ export type ScreenListeners<
|
||||
}
|
||||
>;
|
||||
|
||||
export type RouteConfigComponent<
|
||||
ParamList extends ParamListBase,
|
||||
RouteName extends keyof ParamList
|
||||
> =
|
||||
| {
|
||||
/**
|
||||
* React component to render for this screen.
|
||||
*/
|
||||
component: React.ComponentType<any>;
|
||||
getComponent?: never;
|
||||
children?: never;
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* Lazily get a React component to render for this screen.
|
||||
*/
|
||||
getComponent: () => React.ComponentType<any>;
|
||||
component?: never;
|
||||
children?: never;
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* Render callback to render content of this screen.
|
||||
*/
|
||||
children: (props: {
|
||||
route: RouteProp<ParamList, RouteName>;
|
||||
navigation: any;
|
||||
}) => React.ReactNode;
|
||||
component?: never;
|
||||
getComponent?: never;
|
||||
};
|
||||
|
||||
export type RouteConfig<
|
||||
ParamList extends ParamListBase,
|
||||
RouteName extends keyof ParamList,
|
||||
@@ -420,35 +460,27 @@ export type RouteConfig<
|
||||
* Initial params object for the route.
|
||||
*/
|
||||
initialParams?: Partial<ParamList[RouteName]>;
|
||||
} & (
|
||||
| {
|
||||
/**
|
||||
* React component to render for this screen.
|
||||
*/
|
||||
component: React.ComponentType<any>;
|
||||
getComponent?: never;
|
||||
children?: never;
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* Lazily get a React component to render for this screen.
|
||||
*/
|
||||
getComponent: () => React.ComponentType<any>;
|
||||
component?: never;
|
||||
children?: never;
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* Render callback to render content of this screen.
|
||||
*/
|
||||
children: (props: {
|
||||
route: RouteProp<ParamList, RouteName>;
|
||||
} & RouteConfigComponent<ParamList, RouteName>;
|
||||
|
||||
export type RouteGroupConfig<
|
||||
ParamList extends ParamListBase,
|
||||
ScreenOptions extends {}
|
||||
> = {
|
||||
/**
|
||||
* Navigator options for this screen.
|
||||
*/
|
||||
screenOptions?:
|
||||
| ScreenOptions
|
||||
| ((props: {
|
||||
route: RouteProp<ParamList, keyof ParamList>;
|
||||
navigation: any;
|
||||
}) => React.ReactNode;
|
||||
component?: never;
|
||||
getComponent?: never;
|
||||
}
|
||||
);
|
||||
}) => ScreenOptions);
|
||||
/**
|
||||
* Children React Elements to extract the route configuration from.
|
||||
* Only `Screen`, `Group` and `React.Fragment` are supported as children.
|
||||
*/
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export type NavigationContainerEventMap = {
|
||||
/**
|
||||
@@ -486,7 +518,7 @@ export type NavigationContainerEventMap = {
|
||||
};
|
||||
|
||||
export type NavigationContainerRef<
|
||||
ParamList extends ParamListBase
|
||||
ParamList extends {}
|
||||
> = NavigationHelpers<ParamList> &
|
||||
EventConsumer<NavigationContainerEventMap> & {
|
||||
/**
|
||||
@@ -514,7 +546,7 @@ export type NavigationContainerRef<
|
||||
};
|
||||
|
||||
export type NavigationContainerRefWithCurrent<
|
||||
ParamList extends ParamListBase
|
||||
ParamList extends {}
|
||||
> = NavigationContainerRef<ParamList> & {
|
||||
current: NavigationContainerRef<ParamList> | null;
|
||||
};
|
||||
@@ -536,6 +568,10 @@ export type TypedNavigator<
|
||||
> &
|
||||
DefaultNavigatorOptions<ScreenOptions, ParamList>
|
||||
>;
|
||||
/**
|
||||
* Component used for grouping multiple route configuration.
|
||||
*/
|
||||
Group: React.ComponentType<RouteGroupConfig<ParamList, ScreenOptions>>;
|
||||
/**
|
||||
* Component used for specifying route configuration.
|
||||
*/
|
||||
@@ -573,15 +609,20 @@ export type NavigatorScreenParams<
|
||||
};
|
||||
}[keyof ParamList];
|
||||
|
||||
export type PathConfig = {
|
||||
export type PathConfig<ParamList extends {}> = {
|
||||
path?: string;
|
||||
exact?: boolean;
|
||||
parse?: Record<string, (value: string) => any>;
|
||||
stringify?: Record<string, (value: any) => string>;
|
||||
screens?: PathConfigMap;
|
||||
initialRouteName?: string;
|
||||
screens?: PathConfigMap<ParamList>;
|
||||
initialRouteName?: keyof ParamList;
|
||||
};
|
||||
|
||||
export type PathConfigMap = {
|
||||
[routeName: string]: string | PathConfig;
|
||||
export type PathConfigMap<ParamList extends {}> = {
|
||||
[RouteName in keyof ParamList]?: ParamList[RouteName] extends NavigatorScreenParams<
|
||||
infer T,
|
||||
any
|
||||
>
|
||||
? string | PathConfig<T>
|
||||
: string | Omit<PathConfig<{}>, 'screens' | 'initialRouteName'>;
|
||||
};
|
||||
|
||||
@@ -24,6 +24,22 @@ import type {
|
||||
NavigationProp,
|
||||
} from './types';
|
||||
|
||||
export type ScreenConfigWithParent<
|
||||
State extends NavigationState,
|
||||
ScreenOptions extends {},
|
||||
EventMap extends EventMapBase
|
||||
> = [
|
||||
(ScreenOptionsOrCallback<ScreenOptions> | undefined)[] | undefined,
|
||||
RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>
|
||||
];
|
||||
|
||||
type ScreenOptionsOrCallback<ScreenOptions extends {}> =
|
||||
| ScreenOptions
|
||||
| ((props: {
|
||||
route: RouteProp<ParamListBase, string>;
|
||||
navigation: any;
|
||||
}) => ScreenOptions);
|
||||
|
||||
type Options<
|
||||
State extends NavigationState,
|
||||
ScreenOptions extends {},
|
||||
@@ -32,15 +48,10 @@ type Options<
|
||||
state: State;
|
||||
screens: Record<
|
||||
string,
|
||||
RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>
|
||||
ScreenConfigWithParent<State, ScreenOptions, EventMap>
|
||||
>;
|
||||
navigation: NavigationHelpers<ParamListBase>;
|
||||
screenOptions?:
|
||||
| ScreenOptions
|
||||
| ((props: {
|
||||
route: RouteProp<ParamListBase>;
|
||||
navigation: any;
|
||||
}) => ScreenOptions);
|
||||
screenOptions?: ScreenOptionsOrCallback<ScreenOptions>;
|
||||
defaultScreenOptions?:
|
||||
| ScreenOptions
|
||||
| ((props: {
|
||||
@@ -137,29 +148,31 @@ export default function useDescriptors<
|
||||
>
|
||||
>
|
||||
>((acc, route, i) => {
|
||||
const screen = screens[route.name];
|
||||
const config = screens[route.name];
|
||||
const screen = config[1];
|
||||
const navigation = navigations[route.key];
|
||||
|
||||
const customOptions = {
|
||||
const optionsList = [
|
||||
// The default `screenOptions` passed to the navigator
|
||||
...(typeof screenOptions === 'object' || screenOptions == null
|
||||
? screenOptions
|
||||
: // @ts-expect-error: this is a function, but typescript doesn't think so
|
||||
screenOptions({
|
||||
route,
|
||||
navigation,
|
||||
})),
|
||||
// The `options` prop passed to `Screen` elements
|
||||
...(typeof screen.options === 'object' || screen.options == null
|
||||
? screen.options
|
||||
: // @ts-expect-error: this is a function, but typescript doesn't think so
|
||||
screen.options({
|
||||
route,
|
||||
navigation,
|
||||
})),
|
||||
screenOptions,
|
||||
// The `screenOptions` props passed to `Group` elements
|
||||
...((config[0]
|
||||
? config[0].filter(Boolean)
|
||||
: []) as ScreenOptionsOrCallback<ScreenOptions>[]),
|
||||
// The `options` prop passed to `Screen` elements,
|
||||
screen.options,
|
||||
// The options set via `navigation.setOptions`
|
||||
...options[route.key],
|
||||
};
|
||||
options[route.key],
|
||||
];
|
||||
|
||||
const customOptions = optionsList.reduce<ScreenOptions>(
|
||||
(acc, curr) =>
|
||||
Object.assign(
|
||||
acc,
|
||||
typeof curr !== 'function' ? curr : curr({ route, navigation })
|
||||
),
|
||||
{} as ScreenOptions
|
||||
);
|
||||
|
||||
const mergedOptions = {
|
||||
...(typeof defaultScreenOptions === 'function'
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import type { ParamListBase } from '@react-navigation/routers';
|
||||
import NavigationContext from './NavigationContext';
|
||||
import type { NavigationProp } from './types';
|
||||
|
||||
@@ -9,7 +8,7 @@ import type { NavigationProp } from './types';
|
||||
* @returns Navigation prop of the parent screen.
|
||||
*/
|
||||
export default function useNavigation<
|
||||
T extends NavigationProp<ParamListBase>
|
||||
T = NavigationProp<ReactNavigation.RootParamList>
|
||||
>(): T {
|
||||
const navigation = React.useContext(NavigationContext);
|
||||
|
||||
@@ -19,5 +18,6 @@ export default function useNavigation<
|
||||
);
|
||||
}
|
||||
|
||||
return navigation as T;
|
||||
// FIXME: Figure out a better way to do this
|
||||
return (navigation as unknown) as T;
|
||||
}
|
||||
|
||||
@@ -14,10 +14,11 @@ import {
|
||||
} from '@react-navigation/routers';
|
||||
import NavigationStateContext from './NavigationStateContext';
|
||||
import NavigationRouteContext from './NavigationRouteContext';
|
||||
import Group from './Group';
|
||||
import Screen from './Screen';
|
||||
import useEventEmitter from './useEventEmitter';
|
||||
import useRegisterNavigator from './useRegisterNavigator';
|
||||
import useDescriptors from './useDescriptors';
|
||||
import useDescriptors, { ScreenConfigWithParent } from './useDescriptors';
|
||||
import useNavigationHelpers from './useNavigationHelpers';
|
||||
import useOnAction from './useOnAction';
|
||||
import useFocusEvents from './useFocusEvents';
|
||||
@@ -57,33 +58,40 @@ const getRouteConfigsFromChildren = <
|
||||
ScreenOptions extends {},
|
||||
EventMap extends EventMapBase
|
||||
>(
|
||||
children: React.ReactNode
|
||||
children: React.ReactNode,
|
||||
options?: ScreenConfigWithParent<State, ScreenOptions, EventMap>[0]
|
||||
) => {
|
||||
const configs = React.Children.toArray(children).reduce<
|
||||
RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>[]
|
||||
ScreenConfigWithParent<State, ScreenOptions, EventMap>[]
|
||||
>((acc, child) => {
|
||||
if (React.isValidElement(child)) {
|
||||
if (child.type === Screen) {
|
||||
// We can only extract the config from `Screen` elements
|
||||
// If something else was rendered, it's probably a bug
|
||||
acc.push(
|
||||
acc.push([
|
||||
options,
|
||||
child.props as RouteConfig<
|
||||
ParamListBase,
|
||||
string,
|
||||
State,
|
||||
ScreenOptions,
|
||||
EventMap
|
||||
>
|
||||
);
|
||||
>,
|
||||
]);
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (child.type === React.Fragment) {
|
||||
// When we encounter a fragment, we need to dive into its children to extract the configs
|
||||
if (child.type === React.Fragment || child.type === Group) {
|
||||
// When we encounter a fragment or group, we need to dive into its children to extract the configs
|
||||
// This is handy to conditionally define a group of screens
|
||||
acc.push(
|
||||
...getRouteConfigsFromChildren<State, ScreenOptions, EventMap>(
|
||||
child.props.children
|
||||
child.props.children,
|
||||
child.type !== Group
|
||||
? options
|
||||
: options != null
|
||||
? [...options, child.props.screenOptions]
|
||||
: [child.props.screenOptions]
|
||||
)
|
||||
);
|
||||
return acc;
|
||||
@@ -91,7 +99,7 @@ const getRouteConfigsFromChildren = <
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`A navigator can only contain 'Screen' components as its direct children (found ${
|
||||
`A navigator can only contain 'Screen', 'Group' or 'React.Fragment' as its direct children (found ${
|
||||
React.isValidElement(child)
|
||||
? `'${
|
||||
typeof child.type === 'string' ? child.type : child.type?.name
|
||||
@@ -107,7 +115,7 @@ const getRouteConfigsFromChildren = <
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
configs.forEach((config) => {
|
||||
const { name, children, component, getComponent } = config;
|
||||
const { name, children, component, getComponent } = config[1];
|
||||
|
||||
if (typeof name !== 'string' || !name) {
|
||||
throw new Error(
|
||||
@@ -220,25 +228,22 @@ export default function useNavigationBuilder<
|
||||
>(children);
|
||||
|
||||
const screens = routeConfigs.reduce<
|
||||
Record<
|
||||
string,
|
||||
RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>
|
||||
>
|
||||
Record<string, ScreenConfigWithParent<State, ScreenOptions, EventMap>>
|
||||
>((acc, config) => {
|
||||
if (config.name in acc) {
|
||||
if (config[1].name in acc) {
|
||||
throw new Error(
|
||||
`A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${config.name}')`
|
||||
`A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${config[1].name}')`
|
||||
);
|
||||
}
|
||||
|
||||
acc[config.name] = config;
|
||||
acc[config[1].name] = config;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const routeNames = routeConfigs.map((config) => config.name);
|
||||
const routeNames = routeConfigs.map((config) => config[1].name);
|
||||
const routeParamList = routeNames.reduce<Record<string, object | undefined>>(
|
||||
(acc, curr) => {
|
||||
const { initialParams } = screens[curr];
|
||||
const { initialParams } = screens[curr][1];
|
||||
const initialParamsFromParams =
|
||||
route?.params?.state == null &&
|
||||
route?.params?.initial !== false &&
|
||||
@@ -263,7 +268,7 @@ export default function useNavigationBuilder<
|
||||
>(
|
||||
(acc, curr) =>
|
||||
Object.assign(acc, {
|
||||
[curr]: screens[curr].getId,
|
||||
[curr]: screens[curr][1].getId,
|
||||
}),
|
||||
{}
|
||||
);
|
||||
@@ -481,7 +486,7 @@ export default function useNavigationBuilder<
|
||||
const listeners = ([] as (((e: any) => void) | undefined)[])
|
||||
.concat(
|
||||
...routeNames.map((name) => {
|
||||
const { listeners } = screens[name];
|
||||
const { listeners } = screens[name][1];
|
||||
const map =
|
||||
typeof listeners === 'function'
|
||||
? listeners({ route: route as any, navigation })
|
||||
|
||||
@@ -3,6 +3,30 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [6.0.0-next.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@6.0.0-next.5...@react-navigation/devtools@6.0.0-next.6) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@6.0.0-next.4...@react-navigation/devtools@6.0.0-next.5) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@6.0.0-next.3...@react-navigation/devtools@6.0.0-next.4) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@6.0.0-next.2...@react-navigation/devtools@6.0.0-next.3) (2021-05-01)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/devtools",
|
||||
"description": "Developer tools for React Navigation",
|
||||
"version": "6.0.0-next.3",
|
||||
"version": "6.0.0-next.6",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-native",
|
||||
@@ -36,7 +36,7 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/core": "^6.0.0-next.3",
|
||||
"@react-navigation/core": "^6.0.0-next.6",
|
||||
"deep-equal": "^2.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -3,6 +3,34 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [6.0.0-next.8](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@6.0.0-next.7...@react-navigation/drawer@6.0.0-next.8) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@6.0.0-next.6...@react-navigation/drawer@6.0.0-next.7) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@6.0.0-next.5...@react-navigation/drawer@6.0.0-next.6) (2021-05-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* enable screens only on supported platforms ([#9494](https://github.com/react-navigation/react-navigation/issues/9494)) ([8da4c58](https://github.com/react-navigation/react-navigation/commit/8da4c58065607d44e9dc1ad8943e09537598dcd7))
|
||||
* make sure disabling react-native-screens works ([a369ba3](https://github.com/react-navigation/react-navigation/commit/a369ba36451ddc2bb5b247e61b725bce1e3fb5e5))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@6.0.0-next.4...@react-navigation/drawer@6.0.0-next.5) (2021-05-01)
|
||||
|
||||
**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": "6.0.0-next.5",
|
||||
"version": "6.0.0-next.8",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -41,15 +41,15 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/elements": "^1.0.0-next.5",
|
||||
"@react-navigation/elements": "^1.0.0-next.8",
|
||||
"color": "^3.1.3",
|
||||
"warn-once": "^0.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-navigation/native": "^6.0.0-next.3",
|
||||
"@react-navigation/native": "^6.0.0-next.6",
|
||||
"@testing-library/react-native": "^7.2.0",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-native": "~0.63.51",
|
||||
"@types/react-native": "~0.64.4",
|
||||
"del-cli": "^3.0.1",
|
||||
"react": "~16.13.1",
|
||||
"react-native": "~0.63.4",
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
Platform,
|
||||
BackHandler,
|
||||
} from 'react-native';
|
||||
import { ScreenContainer } from 'react-native-screens';
|
||||
import { useSafeAreaFrame } from 'react-native-safe-area-context';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import {
|
||||
@@ -22,9 +21,8 @@ import {
|
||||
SafeAreaProviderCompat,
|
||||
getHeaderTitle,
|
||||
} from '@react-navigation/elements';
|
||||
|
||||
import { MaybeScreenContainer, MaybeScreen } from './ScreenFallback';
|
||||
import { GestureHandlerRootView } from './GestureHandler';
|
||||
import ScreenFallback from './ScreenFallback';
|
||||
import DrawerToggleButton from './DrawerToggleButton';
|
||||
import DrawerContent from './DrawerContent';
|
||||
import DrawerStatusContext from '../utils/DrawerStatusContext';
|
||||
@@ -76,7 +74,9 @@ function DrawerViewBase({
|
||||
drawerContent = (props: DrawerContentComponentProps) => (
|
||||
<DrawerContent {...props} />
|
||||
),
|
||||
detachInactiveScreens = true,
|
||||
detachInactiveScreens = Platform.OS === 'web' ||
|
||||
Platform.OS === 'android' ||
|
||||
Platform.OS === 'ios',
|
||||
// Running in chrome debugger
|
||||
// @ts-expect-error
|
||||
useLegacyImplementation = !global.nativeCallSyncHook ||
|
||||
@@ -189,8 +189,10 @@ function DrawerViewBase({
|
||||
|
||||
const renderSceneContent = () => {
|
||||
return (
|
||||
// @ts-ignore
|
||||
<ScreenContainer enabled={detachInactiveScreens} style={styles.content}>
|
||||
<MaybeScreenContainer
|
||||
enabled={detachInactiveScreens}
|
||||
style={styles.content}
|
||||
>
|
||||
{state.routes.map((route, index) => {
|
||||
const descriptor = descriptors[route.key];
|
||||
const { lazy = true, unmountOnBlur } = descriptor.options;
|
||||
@@ -221,7 +223,7 @@ function DrawerViewBase({
|
||||
} = descriptor.options;
|
||||
|
||||
return (
|
||||
<ScreenFallback
|
||||
<MaybeScreen
|
||||
key={route.key}
|
||||
style={[StyleSheet.absoluteFill, { opacity: isFocused ? 1 : 0 }]}
|
||||
visible={isFocused}
|
||||
@@ -243,10 +245,10 @@ function DrawerViewBase({
|
||||
>
|
||||
{descriptor.render()}
|
||||
</Screen>
|
||||
</ScreenFallback>
|
||||
</MaybeScreen>
|
||||
);
|
||||
})}
|
||||
</ScreenContainer>
|
||||
</MaybeScreenContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { Platform, StyleProp, ViewStyle } from 'react-native';
|
||||
import {
|
||||
Screen,
|
||||
screensEnabled,
|
||||
// @ts-ignore
|
||||
shouldUseActivityState,
|
||||
} from 'react-native-screens';
|
||||
import { StyleProp, View, ViewProps, ViewStyle } from 'react-native';
|
||||
import { ResourceSavingView } from '@react-navigation/elements';
|
||||
|
||||
type Props = {
|
||||
@@ -15,22 +9,35 @@ type Props = {
|
||||
style?: StyleProp<ViewStyle>;
|
||||
};
|
||||
|
||||
export default function ScreenFallback({ visible, children, ...rest }: Props) {
|
||||
// react-native-screens is buggy on web
|
||||
if (screensEnabled?.() && Platform.OS !== 'web') {
|
||||
if (shouldUseActivityState) {
|
||||
return (
|
||||
<Screen activityState={visible ? 2 : 0} {...rest}>
|
||||
{children}
|
||||
</Screen>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Screen active={visible ? 1 : 0} {...rest}>
|
||||
{children}
|
||||
</Screen>
|
||||
);
|
||||
}
|
||||
let Screens: typeof import('react-native-screens') | undefined;
|
||||
|
||||
try {
|
||||
Screens = require('react-native-screens');
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
export const MaybeScreenContainer = ({
|
||||
enabled,
|
||||
...rest
|
||||
}: ViewProps & {
|
||||
enabled: boolean;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
if (Screens?.screensEnabled?.()) {
|
||||
return <Screens.ScreenContainer enabled={enabled} {...rest} />;
|
||||
}
|
||||
|
||||
return <View {...rest} />;
|
||||
};
|
||||
|
||||
export function MaybeScreen({ visible, children, ...rest }: Props) {
|
||||
if (Screens?.screensEnabled?.()) {
|
||||
return (
|
||||
<Screens.Screen activityState={visible ? 2 : 0} {...rest}>
|
||||
{children}
|
||||
</Screens.Screen>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,6 +3,33 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.0.0-next.8](https://github.com/react-navigation/react-navigation/compare/@react-navigation/elements@1.0.0-next.7...@react-navigation/elements@1.0.0-next.8) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/elements
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [1.0.0-next.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/elements@1.0.0-next.6...@react-navigation/elements@1.0.0-next.7) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/elements
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [1.0.0-next.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/elements@1.0.0-next.5...@react-navigation/elements@1.0.0-next.6) (2021-05-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* animate pressable opacity ([459fd27](https://github.com/react-navigation/react-navigation/commit/459fd270503075343b71ad446efdc2517eedcf21))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [1.0.0-next.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/elements@1.0.0-next.4...@react-navigation/elements@1.0.0-next.5) (2021-05-01)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/elements
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/elements",
|
||||
"description": "UI Components for React Navigation",
|
||||
"version": "1.0.0-next.5",
|
||||
"version": "1.0.0-next.8",
|
||||
"keywords": [
|
||||
"react-native",
|
||||
"react-navigation",
|
||||
@@ -38,10 +38,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-masked-view/masked-view": "^0.2.3",
|
||||
"@react-navigation/native": "^6.0.0-next.3",
|
||||
"@react-navigation/native": "^6.0.0-next.6",
|
||||
"@testing-library/react-native": "^7.2.0",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-native": "~0.63.51",
|
||||
"@types/react-native": "~0.64.4",
|
||||
"del-cli": "^3.0.1",
|
||||
"react": "~16.13.1",
|
||||
"react-native": "~0.63.4",
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
import * as React from 'react';
|
||||
import { Platform, Pressable, PressableProps } from 'react-native';
|
||||
import {
|
||||
Animated,
|
||||
Easing,
|
||||
GestureResponderEvent,
|
||||
Platform,
|
||||
Pressable,
|
||||
PressableProps,
|
||||
StyleProp,
|
||||
ViewStyle,
|
||||
} from 'react-native';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
|
||||
export type Props = PressableProps & {
|
||||
export type Props = Omit<PressableProps, 'style'> & {
|
||||
pressColor?: string;
|
||||
pressOpacity?: number;
|
||||
style?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
|
||||
|
||||
const ANDROID_VERSION_LOLLIPOP = 21;
|
||||
const ANDROID_SUPPORTS_RIPPLE =
|
||||
Platform.OS === 'android' && Platform.Version >= ANDROID_VERSION_LOLLIPOP;
|
||||
@@ -16,16 +28,44 @@ const ANDROID_SUPPORTS_RIPPLE =
|
||||
* PlatformPressable provides an abstraction on top of Pressable to handle platform differences.
|
||||
*/
|
||||
export default function PlatformPressable({
|
||||
onPressIn,
|
||||
onPressOut,
|
||||
android_ripple,
|
||||
pressColor,
|
||||
pressOpacity,
|
||||
pressOpacity = 0.3,
|
||||
style,
|
||||
...rest
|
||||
}: Props) {
|
||||
const { dark } = useTheme();
|
||||
const [opacity] = React.useState(() => new Animated.Value(1));
|
||||
|
||||
const animateTo = (toValue: number, duration: number) => {
|
||||
if (ANDROID_SUPPORTS_RIPPLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
Animated.timing(opacity, {
|
||||
toValue,
|
||||
duration,
|
||||
easing: Easing.inOut(Easing.quad),
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
};
|
||||
|
||||
const handlePressIn = (e: GestureResponderEvent) => {
|
||||
animateTo(pressOpacity, 150);
|
||||
onPressIn?.(e);
|
||||
};
|
||||
|
||||
const handlePressOut = (e: GestureResponderEvent) => {
|
||||
animateTo(1, 200);
|
||||
onPressOut?.(e);
|
||||
};
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
<AnimatedPressable
|
||||
onPressIn={handlePressIn}
|
||||
onPressOut={handlePressOut}
|
||||
android_ripple={
|
||||
ANDROID_SUPPORTS_RIPPLE
|
||||
? {
|
||||
@@ -39,10 +79,7 @@ export default function PlatformPressable({
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
style={({ pressed }) => [
|
||||
{ opacity: pressed && !ANDROID_SUPPORTS_RIPPLE ? pressOpacity : 1 },
|
||||
typeof style === 'function' ? style({ pressed }) : style,
|
||||
]}
|
||||
style={[{ opacity: !ANDROID_SUPPORTS_RIPPLE ? opacity : 1 }, style]}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,30 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [6.0.0-next.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@6.0.0-next.5...@react-navigation/material-bottom-tabs@6.0.0-next.6) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@6.0.0-next.4...@react-navigation/material-bottom-tabs@6.0.0-next.5) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@6.0.0-next.3...@react-navigation/material-bottom-tabs@6.0.0-next.4) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@6.0.0-next.2...@react-navigation/material-bottom-tabs@6.0.0-next.3) (2021-05-01)
|
||||
|
||||
**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": "6.0.0-next.3",
|
||||
"version": "6.0.0-next.6",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -41,10 +41,10 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-navigation/native": "^6.0.0-next.3",
|
||||
"@react-navigation/native": "^6.0.0-next.6",
|
||||
"@testing-library/react-native": "^7.2.0",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-native": "~0.63.51",
|
||||
"@types/react-native": "~0.64.4",
|
||||
"@types/react-native-vector-icons": "^6.4.6",
|
||||
"del-cli": "^3.0.1",
|
||||
"react": "~16.13.1",
|
||||
|
||||
@@ -3,6 +3,30 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [6.0.0-next.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@6.0.0-next.6...@react-navigation/material-top-tabs@6.0.0-next.7) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@6.0.0-next.5...@react-navigation/material-top-tabs@6.0.0-next.6) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@6.0.0-next.4...@react-navigation/material-top-tabs@6.0.0-next.5) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@6.0.0-next.3...@react-navigation/material-top-tabs@6.0.0-next.4) (2021-05-01)
|
||||
|
||||
**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": "6.0.0-next.4",
|
||||
"version": "6.0.0-next.7",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -45,10 +45,10 @@
|
||||
"warn-once": "^0.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-navigation/native": "^6.0.0-next.3",
|
||||
"@react-navigation/native": "^6.0.0-next.6",
|
||||
"@testing-library/react-native": "^7.2.0",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-native": "~0.63.51",
|
||||
"@types/react-native": "~0.64.4",
|
||||
"del-cli": "^3.0.1",
|
||||
"react": "~16.13.1",
|
||||
"react-native": "~0.63.4",
|
||||
|
||||
@@ -3,6 +3,34 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [6.0.0-next.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@6.0.0-next.5...@react-navigation/native@6.0.0-next.6) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@6.0.0-next.4...@react-navigation/native@6.0.0-next.5) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@6.0.0-next.3...@react-navigation/native@6.0.0-next.4) (2021-05-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add ability to specify root param list ([b28bfdd](https://github.com/react-navigation/react-navigation/commit/b28bfddc17cbf3996fac04a34b2a7085ecf88be5))
|
||||
* support navigate-like object in Link ([1478659](https://github.com/react-navigation/react-navigation/commit/14786594c004d8176570f1a4ab013b57b3180665))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@6.0.0-next.2...@react-navigation/native@6.0.0-next.3) (2021-05-01)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/native",
|
||||
"description": "React Native integration for React Navigation",
|
||||
"version": "6.0.0-next.3",
|
||||
"version": "6.0.0-next.6",
|
||||
"keywords": [
|
||||
"react-native",
|
||||
"react-navigation",
|
||||
@@ -37,7 +37,7 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/core": "^6.0.0-next.3",
|
||||
"@react-navigation/core": "^6.0.0-next.6",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"nanoid": "^3.1.22"
|
||||
},
|
||||
@@ -45,7 +45,7 @@
|
||||
"@testing-library/react-native": "^7.2.0",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@types/react-native": "~0.63.51",
|
||||
"@types/react-native": "~0.64.4",
|
||||
"del-cli": "^3.0.1",
|
||||
"react": "~16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
|
||||
@@ -2,9 +2,10 @@ import * as React from 'react';
|
||||
import { Text, TextProps, GestureResponderEvent, Platform } from 'react-native';
|
||||
import type { NavigationAction } from '@react-navigation/core';
|
||||
import useLinkProps from './useLinkProps';
|
||||
import type { To } from './useLinkTo';
|
||||
|
||||
type Props = {
|
||||
to: string;
|
||||
to: To;
|
||||
action?: NavigationAction;
|
||||
target?: string;
|
||||
onPress?: (
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import type { ParamListBase } from '@react-navigation/core';
|
||||
import type { LinkingOptions } from './types';
|
||||
|
||||
const LinkingContext = React.createContext<{
|
||||
options: LinkingOptions | undefined;
|
||||
options: LinkingOptions<ParamListBase> | undefined;
|
||||
}>({ options: undefined });
|
||||
|
||||
LinkingContext.displayName = 'LinkingContext';
|
||||
|
||||
@@ -14,9 +14,9 @@ import useDocumentTitle from './useDocumentTitle';
|
||||
import useBackButton from './useBackButton';
|
||||
import type { Theme, LinkingOptions, DocumentTitleOptions } from './types';
|
||||
|
||||
type Props = NavigationContainerProps & {
|
||||
type Props<ParamList extends {}> = NavigationContainerProps & {
|
||||
theme?: Theme;
|
||||
linking?: LinkingOptions;
|
||||
linking?: LinkingOptions<ParamList>;
|
||||
fallback?: React.ReactNode;
|
||||
documentTitle?: DocumentTitleOptions;
|
||||
onReady?: () => void;
|
||||
@@ -36,7 +36,7 @@ type Props = NavigationContainerProps & {
|
||||
* @param props.children Child elements to render the content.
|
||||
* @param props.ref Ref object which refers to the navigation object containing helper methods.
|
||||
*/
|
||||
const NavigationContainer = React.forwardRef(function NavigationContainer(
|
||||
function NavigationContainerInner(
|
||||
{
|
||||
theme = DefaultTheme,
|
||||
linking,
|
||||
@@ -44,7 +44,7 @@ const NavigationContainer = React.forwardRef(function NavigationContainer(
|
||||
documentTitle,
|
||||
onReady,
|
||||
...rest
|
||||
}: Props,
|
||||
}: Props<ParamListBase>,
|
||||
ref?: React.Ref<NavigationContainerRef<ParamListBase> | null>
|
||||
) {
|
||||
const isLinkingEnabled = linking ? linking.enabled !== false : false;
|
||||
@@ -101,6 +101,14 @@ const NavigationContainer = React.forwardRef(function NavigationContainer(
|
||||
</ThemeProvider>
|
||||
</LinkingContext.Provider>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const NavigationContainer = React.forwardRef(NavigationContainerInner) as <
|
||||
RootParamList extends {} = ReactNavigation.RootParamList
|
||||
>(
|
||||
props: Props<RootParamList> & {
|
||||
ref?: React.Ref<NavigationContainerRef<RootParamList>>;
|
||||
}
|
||||
) => React.ReactElement;
|
||||
|
||||
export default NavigationContainer;
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
StackRouter,
|
||||
TabRouter,
|
||||
NavigationHelpersContext,
|
||||
NavigatorScreenParams,
|
||||
} from '@react-navigation/core';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import NavigationContainer from '../NavigationContainer';
|
||||
@@ -36,24 +37,37 @@ it('renders correct state with location', () => {
|
||||
);
|
||||
});
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
type StackAParamList = {
|
||||
Home: NavigatorScreenParams<StackBParamList>;
|
||||
Chat: undefined;
|
||||
};
|
||||
|
||||
type StackBParamList = {
|
||||
Profile: undefined;
|
||||
Settings: undefined;
|
||||
Feed: undefined;
|
||||
Updates: undefined;
|
||||
};
|
||||
|
||||
const StackA = createStackNavigator<StackAParamList>();
|
||||
const StackB = createStackNavigator<StackBParamList>();
|
||||
|
||||
const TestScreen = ({ route }: any): any =>
|
||||
`${route.name} ${JSON.stringify(route.params)}`;
|
||||
|
||||
const NestedStack = () => {
|
||||
return (
|
||||
<Stack.Navigator initialRouteName="Feed">
|
||||
<Stack.Screen name="Profile" component={TestScreen} />
|
||||
<Stack.Screen name="Settings" component={TestScreen} />
|
||||
<Stack.Screen name="Feed" component={TestScreen} />
|
||||
<Stack.Screen name="Updates" component={TestScreen} />
|
||||
</Stack.Navigator>
|
||||
<StackB.Navigator initialRouteName="Feed">
|
||||
<StackB.Screen name="Profile" component={TestScreen} />
|
||||
<StackB.Screen name="Settings" component={TestScreen} />
|
||||
<StackB.Screen name="Feed" component={TestScreen} />
|
||||
<StackB.Screen name="Updates" component={TestScreen} />
|
||||
</StackB.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer
|
||||
<NavigationContainer<StackAParamList>
|
||||
linking={{
|
||||
prefixes: [],
|
||||
config: {
|
||||
@@ -73,10 +87,10 @@ it('renders correct state with location', () => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack.Navigator>
|
||||
<Stack.Screen name="Home" component={NestedStack} />
|
||||
<Stack.Screen name="Chat" component={TestScreen} />
|
||||
</Stack.Navigator>
|
||||
<StackA.Navigator>
|
||||
<StackA.Screen name="Home" component={NestedStack} />
|
||||
<StackA.Screen name="Chat" component={TestScreen} />
|
||||
</StackA.Navigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export type Theme = {
|
||||
};
|
||||
};
|
||||
|
||||
export type LinkingOptions = {
|
||||
export type LinkingOptions<ParamList extends {}> = {
|
||||
/**
|
||||
* Whether deep link handling should be enabled.
|
||||
* Defaults to true.
|
||||
@@ -53,7 +53,10 @@ export type LinkingOptions = {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
config?: { initialRouteName?: string; screens: PathConfigMap };
|
||||
config?: {
|
||||
initialRouteName?: keyof ParamList;
|
||||
screens: PathConfigMap<ParamList>;
|
||||
};
|
||||
/**
|
||||
* Custom function to get the initial URL used for linking.
|
||||
* Uses `Linking.getInitialURL()` by default.
|
||||
|
||||
@@ -4,10 +4,10 @@ import {
|
||||
NavigationAction,
|
||||
NavigationHelpersContext,
|
||||
} from '@react-navigation/core';
|
||||
import useLinkTo from './useLinkTo';
|
||||
import useLinkTo, { To } from './useLinkTo';
|
||||
|
||||
type Props = {
|
||||
to: string;
|
||||
to: To;
|
||||
action?: NavigationAction;
|
||||
};
|
||||
|
||||
@@ -49,14 +49,6 @@ export default function useLinkProps({ to, action }: Props) {
|
||||
throw new Error("Couldn't find a navigation object.");
|
||||
}
|
||||
} else {
|
||||
if (typeof to !== 'string') {
|
||||
throw new Error(
|
||||
`To 'to' option is invalid (found '${String(
|
||||
to
|
||||
)}'. It must be a valid string for navigation.`
|
||||
);
|
||||
}
|
||||
|
||||
linkTo(to);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,37 +6,57 @@ import {
|
||||
} from '@react-navigation/core';
|
||||
import LinkingContext from './LinkingContext';
|
||||
|
||||
export type To<
|
||||
ParamList extends ReactNavigation.RootParamList = ReactNavigation.RootParamList,
|
||||
RouteName extends keyof ParamList = keyof ParamList
|
||||
> =
|
||||
| string
|
||||
| (undefined extends ParamList[RouteName]
|
||||
? {
|
||||
screen: RouteName;
|
||||
params?: ParamList[RouteName];
|
||||
}
|
||||
: {
|
||||
screen: RouteName;
|
||||
params: ParamList[RouteName];
|
||||
});
|
||||
|
||||
export default function useLinkTo() {
|
||||
const navigation = React.useContext(NavigationContext);
|
||||
const linking = React.useContext(LinkingContext);
|
||||
|
||||
const linkTo = React.useCallback(
|
||||
(path: string) => {
|
||||
if (!path.startsWith('/')) {
|
||||
throw new Error(`The path must start with '/' (${path}).`);
|
||||
}
|
||||
|
||||
(to: To) => {
|
||||
if (navigation === undefined) {
|
||||
throw new Error(
|
||||
"Couldn't find a navigation object. Is your component inside a screen in a navigator?"
|
||||
);
|
||||
}
|
||||
|
||||
let root = navigation;
|
||||
let current;
|
||||
|
||||
// Traverse up to get the root navigation
|
||||
while ((current = root.getParent())) {
|
||||
root = current;
|
||||
}
|
||||
|
||||
if (typeof to !== 'string') {
|
||||
root.navigate(to.screen, to.params);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!to.startsWith('/')) {
|
||||
throw new Error(`The path must start with '/' (${to}).`);
|
||||
}
|
||||
|
||||
const { options } = linking;
|
||||
|
||||
const state = options?.getStateFromPath
|
||||
? options.getStateFromPath(path, options.config)
|
||||
: getStateFromPath(path, options?.config);
|
||||
? options.getStateFromPath(to, options.config)
|
||||
: getStateFromPath(to, options?.config);
|
||||
|
||||
if (state) {
|
||||
let root = navigation;
|
||||
let current;
|
||||
|
||||
// Traverse up to get the root navigation
|
||||
while ((current = root.getParent())) {
|
||||
root = current;
|
||||
}
|
||||
|
||||
const action = getActionFromState(state, options?.config);
|
||||
|
||||
if (action !== undefined) {
|
||||
|
||||
@@ -45,7 +45,7 @@ export default function useLinking(
|
||||
};
|
||||
},
|
||||
getStateFromPath = getStateFromPathDefault,
|
||||
}: LinkingOptions
|
||||
}: LinkingOptions<ParamListBase>
|
||||
) {
|
||||
React.useEffect(() => {
|
||||
if (enabled !== false && isUsingLinking) {
|
||||
|
||||
@@ -295,7 +295,7 @@ export default function useLinking(
|
||||
config,
|
||||
getStateFromPath = getStateFromPathDefault,
|
||||
getPathFromState = getPathFromStateDefault,
|
||||
}: LinkingOptions
|
||||
}: LinkingOptions<ParamListBase>
|
||||
) {
|
||||
React.useEffect(() => {
|
||||
if (enabled !== false && isUsingLinking) {
|
||||
|
||||
@@ -3,6 +3,50 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [6.0.0-next.13](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@6.0.0-next.12...@react-navigation/stack@6.0.0-next.13) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.12](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@6.0.0-next.11...@react-navigation/stack@6.0.0-next.12) (2021-05-09)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.11](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@6.0.0-next.10...@react-navigation/stack@6.0.0-next.11) (2021-05-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* enable screens only on supported platforms ([#9494](https://github.com/react-navigation/react-navigation/issues/9494)) ([8da4c58](https://github.com/react-navigation/react-navigation/commit/8da4c58065607d44e9dc1ad8943e09537598dcd7))
|
||||
* make sure disabling react-native-screens works ([a369ba3](https://github.com/react-navigation/react-navigation/commit/a369ba36451ddc2bb5b247e61b725bce1e3fb5e5))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* drop mode prop in favor of animationPresentation option ([9ac709e](https://github.com/react-navigation/react-navigation/commit/9ac709ea1e5a63c3a48abfa334ff6a6925095a72))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* automatically set headerMode if it's modal presentation style ([4bb0b43](https://github.com/react-navigation/react-navigation/commit/4bb0b43f1a0f27c96843415de6eaa37edebfb561))
|
||||
* support mixing regular and modal presentation in same stack ([60fa3b9](https://github.com/react-navigation/react-navigation/commit/60fa3b9be976a73a5b04b632b4b37672674c956b))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* This drops the mode prop on the navigator in favor of a per-screen option animationPresentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [6.0.0-next.10](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@6.0.0-next.9...@react-navigation/stack@6.0.0-next.10) (2021-05-01)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/stack
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/stack",
|
||||
"description": "Stack navigator component for iOS and Android with animated transitions and gestures",
|
||||
"version": "6.0.0-next.10",
|
||||
"version": "6.0.0-next.13",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -40,17 +40,17 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/elements": "^1.0.0-next.5",
|
||||
"@react-navigation/elements": "^1.0.0-next.8",
|
||||
"color": "^3.1.3",
|
||||
"react-native-iphone-x-helper": "^1.3.0",
|
||||
"warn-once": "^0.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-navigation/native": "^6.0.0-next.3",
|
||||
"@react-navigation/native": "^6.0.0-next.6",
|
||||
"@testing-library/react-native": "^7.2.0",
|
||||
"@types/color": "^3.0.1",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-native": "~0.63.51",
|
||||
"@types/react-native": "~0.64.4",
|
||||
"del-cli": "^3.0.1",
|
||||
"react": "~16.13.1",
|
||||
"react-native": "~0.63.4",
|
||||
|
||||
@@ -118,13 +118,15 @@ export function forModalPresentationIOS({
|
||||
: 0
|
||||
);
|
||||
|
||||
const isFirst = index === 0;
|
||||
|
||||
const translateY = multiply(
|
||||
progress.interpolate({
|
||||
inputRange: [0, 1, 2],
|
||||
outputRange: [
|
||||
screen.height,
|
||||
index === 0 ? 0 : topOffset,
|
||||
(index === 0 ? statusBarHeight : 0) - topOffset * aspectRatio,
|
||||
isFirst ? 0 : topOffset,
|
||||
(isFirst ? statusBarHeight : 0) - topOffset * aspectRatio,
|
||||
],
|
||||
}),
|
||||
inverted
|
||||
@@ -148,7 +150,7 @@ export function forModalPresentationIOS({
|
||||
|
||||
const borderRadius = isLandscape
|
||||
? 0
|
||||
: index === 0
|
||||
: isFirst
|
||||
? progress.interpolate({
|
||||
inputRange: [0, 1, 1.0001, 2],
|
||||
outputRange: [0, 0, isIphoneX() ? 38 : 0, 10],
|
||||
@@ -164,8 +166,8 @@ export function forModalPresentationIOS({
|
||||
// But different border radius for corners improves animation perf
|
||||
borderBottomLeftRadius: isIphoneX() ? borderRadius : 0,
|
||||
borderBottomRightRadius: isIphoneX() ? borderRadius : 0,
|
||||
marginTop: index === 0 ? 0 : statusBarHeight,
|
||||
marginBottom: index === 0 ? 0 : topOffset,
|
||||
marginTop: isFirst ? 0 : statusBarHeight,
|
||||
marginBottom: isFirst ? 0 : topOffset,
|
||||
transform: [{ translateY }, { scale }],
|
||||
},
|
||||
overlayStyle: { opacity: overlayOpacity },
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import { Platform } from 'react-native';
|
||||
import {
|
||||
useNavigationBuilder,
|
||||
createNavigatorFactory,
|
||||
@@ -54,21 +53,9 @@ function StackNavigator({
|
||||
initialRouteName,
|
||||
children,
|
||||
screenOptions,
|
||||
defaultScreenOptions: ({ options }) => ({
|
||||
defaultScreenOptions: () => ({
|
||||
headerShown: headerMode ? headerMode !== 'none' : true,
|
||||
headerMode:
|
||||
headerMode && headerMode !== 'none'
|
||||
? headerMode
|
||||
: rest.mode !== 'modal' &&
|
||||
Platform.OS === 'ios' &&
|
||||
options.header === undefined
|
||||
? 'float'
|
||||
: 'screen',
|
||||
gestureEnabled: Platform.OS === 'ios',
|
||||
animationEnabled:
|
||||
Platform.OS !== 'web' &&
|
||||
Platform.OS !== 'windows' &&
|
||||
Platform.OS !== 'macos',
|
||||
headerMode: headerMode && headerMode !== 'none' ? headerMode : undefined,
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -72,11 +72,21 @@ export type GestureDirection =
|
||||
| 'vertical'
|
||||
| 'vertical-inverted';
|
||||
|
||||
type SceneOptionsDefaults = TransitionPreset & {
|
||||
animationEnabled: boolean;
|
||||
gestureEnabled: boolean;
|
||||
cardOverlayEnabled: boolean;
|
||||
headerMode: StackHeaderMode;
|
||||
};
|
||||
|
||||
export type Scene = {
|
||||
/**
|
||||
* Descriptor object for the screen.
|
||||
*/
|
||||
descriptor: StackDescriptor;
|
||||
descriptor: Omit<StackDescriptor, 'options'> & {
|
||||
options: Omit<StackDescriptor['options'], keyof SceneOptionsDefaults> &
|
||||
SceneOptionsDefaults;
|
||||
};
|
||||
/**
|
||||
* Animated nodes representing the progress of the animation.
|
||||
*/
|
||||
@@ -102,7 +112,7 @@ export type SceneProgress = {
|
||||
|
||||
export type StackHeaderMode = 'float' | 'screen';
|
||||
|
||||
export type StackCardMode = 'card' | 'modal';
|
||||
export type StackPresentationMode = 'card' | 'modal';
|
||||
|
||||
export type StackHeaderOptions = HeaderOptions & {
|
||||
/**
|
||||
@@ -228,8 +238,12 @@ export type StackNavigationOptions = StackHeaderOptions &
|
||||
*
|
||||
* You can also specify `{ backgroundColor: 'transparent' }` to make the previous screen visible underneath.
|
||||
* This is useful to implement things like modal dialogs.
|
||||
* If you use [`react-native-screens`](https://github.com/kmagiera/react-native-screens), you should also specify `mode: 'modal'`
|
||||
* in the stack view config when using a transparent background so previous screens aren't detached.
|
||||
*
|
||||
* You should also specify `detachPreviousScreen: false` in options when using a transparent background
|
||||
* so that the previous screen isn't detached and stays below the current screen.
|
||||
*
|
||||
* You might also need to change the animation of the screen using `cardStyleInterpolator`
|
||||
* so that the previous screen isn't transformed or invisible.
|
||||
*/
|
||||
cardStyle?: StyleProp<ViewStyle>;
|
||||
/**
|
||||
@@ -238,6 +252,16 @@ export type StackNavigationOptions = StackHeaderOptions &
|
||||
* Defaults to `true` on Android and iOS, `false` on Web.
|
||||
*/
|
||||
animationEnabled?: boolean;
|
||||
/**
|
||||
* Whether this screen should be presented as a modal or a regular card.
|
||||
*
|
||||
* If you haven't customized the animations separately, the animation will change based on the value:
|
||||
* - 'modal' - modal animation on iOS and Android. It'll also default `headerMode` to `screen`.
|
||||
* - 'card' - horizontal slide animation on iOS, OS-default animation on Android.
|
||||
*
|
||||
* Defaults to 'card'.
|
||||
*/
|
||||
animationPresentation?: 'card' | 'modal';
|
||||
/**
|
||||
* The type of animation to use when this screen replaces another screen. Defaults to `push`.
|
||||
* When `pop` is used, the `pop` animation is applied to the screen being replaced.
|
||||
@@ -262,13 +286,12 @@ export type StackNavigationOptions = StackHeaderOptions &
|
||||
* Whether to detach the previous screen from the view hierarchy to save memory.
|
||||
* Set it to `false` if you need the previous screen to be seen through the active screen.
|
||||
* Only applicable if `detachInactiveScreens` isn't set to `false`.
|
||||
* Defaults to `false` for the last screen when mode='modal', otherwise `true`.
|
||||
* Defaults to `false` for the last screen for modals, otherwise `true`.
|
||||
*/
|
||||
detachPreviousScreen?: boolean;
|
||||
};
|
||||
|
||||
export type StackNavigationConfig = {
|
||||
mode?: StackCardMode;
|
||||
/**
|
||||
* If `false`, the keyboard will NOT automatically dismiss when navigating to a new screen.
|
||||
* Defaults to `true`.
|
||||
@@ -319,7 +342,7 @@ export type StackCardInterpolationProps = {
|
||||
progress: Animated.AnimatedInterpolation;
|
||||
};
|
||||
/**
|
||||
* The index of the card in the stack.
|
||||
* The index of the card with this interpolation in the stack.
|
||||
*/
|
||||
index: number;
|
||||
/**
|
||||
|
||||
@@ -18,13 +18,13 @@ import {
|
||||
import type {
|
||||
Layout,
|
||||
Scene,
|
||||
StackHeaderStyleInterpolator,
|
||||
StackNavigationProp,
|
||||
StackHeaderProps,
|
||||
StackHeaderMode,
|
||||
} from '../../types';
|
||||
|
||||
export type Props = {
|
||||
mode: 'float' | 'screen';
|
||||
mode: StackHeaderMode;
|
||||
layout: Layout;
|
||||
scenes: (Scene | undefined)[];
|
||||
getPreviousScene: (props: { route: Route<string> }) => Scene | undefined;
|
||||
@@ -33,7 +33,6 @@ export type Props = {
|
||||
route: Route<string>;
|
||||
height: number;
|
||||
}) => void;
|
||||
styleInterpolator: StackHeaderStyleInterpolator;
|
||||
style?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
|
||||
};
|
||||
|
||||
@@ -44,7 +43,6 @@ export default function HeaderContainer({
|
||||
getPreviousScene,
|
||||
getFocusedRoute,
|
||||
onContentHeightChange,
|
||||
styleInterpolator,
|
||||
style,
|
||||
}: Props) {
|
||||
const focusedRoute = getFocusedRoute();
|
||||
@@ -57,8 +55,13 @@ export default function HeaderContainer({
|
||||
return null;
|
||||
}
|
||||
|
||||
const { header, headerMode, headerShown = true, headerTransparent } =
|
||||
scene.descriptor.options || {};
|
||||
const {
|
||||
header,
|
||||
headerMode,
|
||||
headerShown = true,
|
||||
headerTransparent,
|
||||
headerStyleInterpolator,
|
||||
} = scene.descriptor.options;
|
||||
|
||||
if (headerMode !== mode || !headerShown) {
|
||||
return null;
|
||||
@@ -120,7 +123,7 @@ export default function HeaderContainer({
|
||||
: nextGestureDirection === 'horizontal-inverted'
|
||||
? forSlideRight
|
||||
: forSlideLeft
|
||||
: styleInterpolator
|
||||
: headerStyleInterpolator
|
||||
: forNoAnimation,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { Animated, View, Platform, ViewProps } from 'react-native';
|
||||
import { Animated, View, ViewProps } from 'react-native';
|
||||
|
||||
let Screens: typeof import('react-native-screens') | undefined;
|
||||
|
||||
@@ -9,31 +9,6 @@ try {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
export const shouldUseActivityState = Screens?.shouldUseActivityState;
|
||||
|
||||
// So we use our custom implementation to handle a11y better
|
||||
class WebScreen extends React.Component<
|
||||
ViewProps & {
|
||||
active: number;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
> {
|
||||
render() {
|
||||
const { active, style, ...rest } = this.props;
|
||||
|
||||
return (
|
||||
<View
|
||||
// @ts-expect-error: hidden exists on web, but not in React Native
|
||||
hidden={!active}
|
||||
style={[style, { display: active ? 'flex' : 'none' }]}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const AnimatedWebScreen = Animated.createAnimatedComponent(WebScreen);
|
||||
|
||||
export const MaybeScreenContainer = ({
|
||||
enabled,
|
||||
...rest
|
||||
@@ -41,11 +16,8 @@ export const MaybeScreenContainer = ({
|
||||
enabled: boolean;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
if (enabled && Platform.OS !== 'web' && Screens?.screensEnabled()) {
|
||||
return (
|
||||
// @ts-ignore
|
||||
<Screens.ScreenContainer enabled={enabled} {...rest} />
|
||||
);
|
||||
if (Screens != null) {
|
||||
return <Screens.ScreenContainer enabled={enabled} {...rest} />;
|
||||
}
|
||||
|
||||
return <View {...rest} />;
|
||||
@@ -60,18 +32,10 @@ export const MaybeScreen = ({
|
||||
active: 0 | 1 | Animated.AnimatedInterpolation;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
if (enabled && Platform.OS === 'web') {
|
||||
return <AnimatedWebScreen active={active} {...rest} />;
|
||||
}
|
||||
|
||||
if (enabled && Screens?.screensEnabled()) {
|
||||
if (shouldUseActivityState) {
|
||||
return (
|
||||
<Screens.Screen enabled={enabled} activityState={active} {...rest} />
|
||||
);
|
||||
} else {
|
||||
return <Screens.Screen enabled={enabled} active={active} {...rest} />;
|
||||
}
|
||||
if (Screens != null) {
|
||||
return (
|
||||
<Screens.Screen enabled={enabled} activityState={active} {...rest} />
|
||||
);
|
||||
}
|
||||
|
||||
return <View {...rest} />;
|
||||
|
||||
@@ -29,10 +29,12 @@ import type {
|
||||
StackCardStyleInterpolator,
|
||||
GestureDirection,
|
||||
Layout,
|
||||
StackCardInterpolationProps,
|
||||
} from '../../types';
|
||||
|
||||
type Props = ViewProps & {
|
||||
index: number;
|
||||
// index: number;
|
||||
interpolationIndex: number;
|
||||
closing: boolean;
|
||||
next?: Animated.AnimatedInterpolation;
|
||||
current: Animated.AnimatedInterpolation;
|
||||
@@ -344,38 +346,14 @@ export default class Card extends React.Component<Props> {
|
||||
private getInterpolatedStyle = memoize(
|
||||
(
|
||||
styleInterpolator: StackCardStyleInterpolator,
|
||||
index: number,
|
||||
current: Animated.AnimatedInterpolation,
|
||||
next: Animated.AnimatedInterpolation | undefined,
|
||||
layout: Layout,
|
||||
insetTop: number,
|
||||
insetRight: number,
|
||||
insetBottom: number,
|
||||
insetLeft: number
|
||||
) =>
|
||||
styleInterpolator({
|
||||
index,
|
||||
current: { progress: current },
|
||||
next: next && { progress: next },
|
||||
closing: this.isClosing,
|
||||
swiping: this.isSwiping,
|
||||
inverted: this.inverted,
|
||||
layouts: {
|
||||
screen: layout,
|
||||
},
|
||||
insets: {
|
||||
top: insetTop,
|
||||
right: insetRight,
|
||||
bottom: insetBottom,
|
||||
left: insetLeft,
|
||||
},
|
||||
})
|
||||
animation: StackCardInterpolationProps
|
||||
) => styleInterpolator(animation)
|
||||
);
|
||||
|
||||
// Keep track of the animation context when deps changes.
|
||||
private getCardAnimationContext = memoize(
|
||||
private getCardAnimation = memoize(
|
||||
(
|
||||
index: number,
|
||||
interpolationIndex: number,
|
||||
current: Animated.AnimatedInterpolation,
|
||||
next: Animated.AnimatedInterpolation | undefined,
|
||||
layout: Layout,
|
||||
@@ -384,7 +362,7 @@ export default class Card extends React.Component<Props> {
|
||||
insetBottom: number,
|
||||
insetLeft: number
|
||||
) => ({
|
||||
index,
|
||||
index: interpolationIndex,
|
||||
current: { progress: current },
|
||||
next: next && { progress: next },
|
||||
closing: this.isClosing,
|
||||
@@ -450,7 +428,8 @@ export default class Card extends React.Component<Props> {
|
||||
render() {
|
||||
const {
|
||||
styleInterpolator,
|
||||
index,
|
||||
// index,
|
||||
interpolationIndex,
|
||||
current,
|
||||
gesture,
|
||||
next,
|
||||
@@ -469,9 +448,8 @@ export default class Card extends React.Component<Props> {
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
const interpolatedStyle = this.getInterpolatedStyle(
|
||||
styleInterpolator,
|
||||
index,
|
||||
const interpolationProps = this.getCardAnimation(
|
||||
interpolationIndex,
|
||||
current,
|
||||
next,
|
||||
layout,
|
||||
@@ -481,15 +459,9 @@ export default class Card extends React.Component<Props> {
|
||||
insets.left
|
||||
);
|
||||
|
||||
const animationContext = this.getCardAnimationContext(
|
||||
index,
|
||||
current,
|
||||
next,
|
||||
layout,
|
||||
insets.top,
|
||||
insets.right,
|
||||
insets.bottom,
|
||||
insets.left
|
||||
const interpolatedStyle = this.getInterpolatedStyle(
|
||||
styleInterpolator,
|
||||
interpolationProps
|
||||
);
|
||||
|
||||
const {
|
||||
@@ -521,13 +493,13 @@ export default class Card extends React.Component<Props> {
|
||||
: false;
|
||||
|
||||
return (
|
||||
<CardAnimationContext.Provider value={animationContext}>
|
||||
<CardAnimationContext.Provider value={interpolationProps}>
|
||||
{
|
||||
// StatusBar messes with translucent status bar on Android
|
||||
// So we should only enable it on iOS
|
||||
Platform.OS === 'ios' &&
|
||||
overlayEnabled &&
|
||||
index === 0 &&
|
||||
interpolationIndex === 0 &&
|
||||
next &&
|
||||
styleInterpolator === forModalPresentationIOS ? (
|
||||
<ModalStatusBarManager
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { Animated, View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
|
||||
import { Animated, View, StyleSheet } from 'react-native';
|
||||
import { Route, useTheme } from '@react-navigation/native';
|
||||
import {
|
||||
HeaderShownContext,
|
||||
@@ -11,16 +11,10 @@ import type { Props as HeaderContainerProps } from '../Header/HeaderContainer';
|
||||
import Card from './Card';
|
||||
import { forModalPresentationIOS } from '../../TransitionConfigs/CardStyleInterpolators';
|
||||
import ModalPresentationContext from '../../utils/ModalPresentationContext';
|
||||
import type {
|
||||
Layout,
|
||||
StackHeaderMode,
|
||||
StackCardMode,
|
||||
TransitionPreset,
|
||||
Scene,
|
||||
} from '../../types';
|
||||
import type { Layout, Scene } from '../../types';
|
||||
|
||||
type Props = TransitionPreset & {
|
||||
index: number;
|
||||
type Props = {
|
||||
interpolationIndex: number;
|
||||
active: boolean;
|
||||
focused: boolean;
|
||||
closing: boolean;
|
||||
@@ -32,12 +26,6 @@ type Props = TransitionPreset & {
|
||||
safeAreaInsetRight: number;
|
||||
safeAreaInsetBottom: number;
|
||||
safeAreaInsetLeft: number;
|
||||
cardOverlay?: (props: {
|
||||
style: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
|
||||
}) => React.ReactNode;
|
||||
cardOverlayEnabled: boolean;
|
||||
cardShadowEnabled?: boolean;
|
||||
cardStyle?: StyleProp<ViewStyle>;
|
||||
getPreviousScene: (props: { route: Route<string> }) => Scene | undefined;
|
||||
getFocusedRoute: () => Route<string>;
|
||||
renderHeader: (props: HeaderContainerProps) => React.ReactNode;
|
||||
@@ -55,12 +43,6 @@ type Props = TransitionPreset & {
|
||||
onGestureStart?: (props: { route: Route<string> }) => void;
|
||||
onGestureEnd?: (props: { route: Route<string> }) => void;
|
||||
onGestureCancel?: (props: { route: Route<string> }) => void;
|
||||
gestureEnabled?: boolean;
|
||||
gestureResponseDistance?: number;
|
||||
gestureVelocityImpact?: number;
|
||||
mode: StackCardMode;
|
||||
headerMode: StackHeaderMode;
|
||||
headerShown: boolean;
|
||||
hasAbsoluteFloatHeader: boolean;
|
||||
headerHeight: number;
|
||||
onHeaderHeightChange: (props: {
|
||||
@@ -74,30 +56,17 @@ const EPSILON = 0.1;
|
||||
|
||||
function CardContainer({
|
||||
active,
|
||||
cardOverlay,
|
||||
cardOverlayEnabled,
|
||||
cardShadowEnabled,
|
||||
cardStyle,
|
||||
cardStyleInterpolator,
|
||||
closing,
|
||||
gesture,
|
||||
focused,
|
||||
gestureDirection,
|
||||
gestureEnabled,
|
||||
gestureResponseDistance,
|
||||
gestureVelocityImpact,
|
||||
getPreviousScene,
|
||||
getFocusedRoute,
|
||||
mode,
|
||||
headerDarkContent,
|
||||
headerMode,
|
||||
headerShown,
|
||||
headerStyleInterpolator,
|
||||
hasAbsoluteFloatHeader,
|
||||
headerHeight,
|
||||
onHeaderHeightChange,
|
||||
isParentHeaderShown,
|
||||
index,
|
||||
interpolationIndex,
|
||||
layout,
|
||||
onCloseRoute,
|
||||
onOpenRoute,
|
||||
@@ -116,7 +85,6 @@ function CardContainer({
|
||||
safeAreaInsetRight,
|
||||
safeAreaInsetTop,
|
||||
scene,
|
||||
transitionSpec,
|
||||
}: Props) {
|
||||
const parentHeaderHeight = React.useContext(HeaderHeightContext);
|
||||
|
||||
@@ -188,7 +156,6 @@ function CardContainer({
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
// @ts-expect-error: AnimatedInterpolation optionally has addListener, but the type defs don't think so
|
||||
const listener = scene.progress.next?.addListener?.(
|
||||
({ value }: { value: number }) => {
|
||||
setPointerEvents(value <= EPSILON ? 'box-none' : 'none');
|
||||
@@ -197,12 +164,27 @@ function CardContainer({
|
||||
|
||||
return () => {
|
||||
if (listener) {
|
||||
// @ts-expect-error: AnimatedInterpolation optionally has removedListener, but the type defs don't think so
|
||||
scene.progress.next?.removeListener?.(listener);
|
||||
}
|
||||
};
|
||||
}, [pointerEvents, scene.progress.next]);
|
||||
|
||||
const {
|
||||
animationPresentation,
|
||||
cardOverlay,
|
||||
cardOverlayEnabled,
|
||||
cardShadowEnabled,
|
||||
cardStyle,
|
||||
cardStyleInterpolator,
|
||||
gestureDirection,
|
||||
gestureEnabled,
|
||||
gestureResponseDistance,
|
||||
gestureVelocityImpact,
|
||||
headerMode,
|
||||
headerShown,
|
||||
transitionSpec,
|
||||
} = scene.descriptor.options;
|
||||
|
||||
const isModalPresentation = cardStyleInterpolator === forModalPresentationIOS;
|
||||
const previousScene = getPreviousScene({ route: scene.descriptor.route });
|
||||
|
||||
@@ -221,7 +203,7 @@ function CardContainer({
|
||||
|
||||
return (
|
||||
<Card
|
||||
index={index}
|
||||
interpolationIndex={interpolationIndex}
|
||||
gestureDirection={gestureDirection}
|
||||
layout={layout}
|
||||
insets={insets}
|
||||
@@ -246,7 +228,9 @@ function CardContainer({
|
||||
accessibilityElementsHidden={!focused}
|
||||
importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'}
|
||||
pointerEvents={active ? 'box-none' : pointerEvents}
|
||||
pageOverflowEnabled={headerMode !== 'float' && mode === 'card'}
|
||||
pageOverflowEnabled={
|
||||
headerMode !== 'float' && animationPresentation !== 'modal'
|
||||
}
|
||||
headerDarkContent={headerDarkContent}
|
||||
containerStyle={
|
||||
hasAbsoluteFloatHeader && headerMode !== 'screen'
|
||||
@@ -278,14 +262,15 @@ function CardContainer({
|
||||
</HeaderBackContext.Provider>
|
||||
</View>
|
||||
{headerMode !== 'float' ? (
|
||||
<ModalPresentationContext.Provider value={isModalPresentation}>
|
||||
<ModalPresentationContext.Provider
|
||||
value={isModalPresentation && interpolationIndex !== 0}
|
||||
>
|
||||
{renderHeader({
|
||||
mode: 'screen',
|
||||
layout,
|
||||
scenes: [previousScene, scene],
|
||||
getPreviousScene,
|
||||
getFocusedRoute,
|
||||
styleInterpolator: headerStyleInterpolator,
|
||||
onContentHeightChange: onHeaderHeightChange,
|
||||
})}
|
||||
</ModalPresentationContext.Provider>
|
||||
|
||||
@@ -18,26 +18,25 @@ import {
|
||||
Background,
|
||||
} from '@react-navigation/elements';
|
||||
|
||||
import {
|
||||
MaybeScreenContainer,
|
||||
MaybeScreen,
|
||||
shouldUseActivityState,
|
||||
} from '../Screens';
|
||||
import { MaybeScreenContainer, MaybeScreen } from '../Screens';
|
||||
import type { Props as HeaderContainerProps } from '../Header/HeaderContainer';
|
||||
import CardContainer from './CardContainer';
|
||||
import {
|
||||
DefaultTransition,
|
||||
ModalTransition,
|
||||
} from '../../TransitionConfigs/TransitionPresets';
|
||||
import { forNoAnimation as forNoAnimationCard } from '../../TransitionConfigs/CardStyleInterpolators';
|
||||
import {
|
||||
forModalPresentationIOS,
|
||||
forNoAnimation as forNoAnimationCard,
|
||||
} from '../../TransitionConfigs/CardStyleInterpolators';
|
||||
import getDistanceForDirection from '../../utils/getDistanceForDirection';
|
||||
import type {
|
||||
Layout,
|
||||
StackCardMode,
|
||||
StackDescriptorMap,
|
||||
StackNavigationOptions,
|
||||
StackDescriptor,
|
||||
Scene,
|
||||
StackDescriptor,
|
||||
StackDescriptorMap,
|
||||
StackHeaderMode,
|
||||
StackNavigationOptions,
|
||||
} from '../../types';
|
||||
|
||||
type GestureValues = {
|
||||
@@ -45,7 +44,6 @@ type GestureValues = {
|
||||
};
|
||||
|
||||
type Props = {
|
||||
mode: StackCardMode;
|
||||
insets: EdgeInsets;
|
||||
state: StackNavigationState<ParamListBase>;
|
||||
descriptors: StackDescriptorMap;
|
||||
@@ -57,7 +55,6 @@ type Props = {
|
||||
getPreviousRoute: (props: {
|
||||
route: Route<string>;
|
||||
}) => Route<string> | undefined;
|
||||
getGesturesEnabled: (props: { route: Route<string> }) => boolean;
|
||||
renderHeader: (props: HeaderContainerProps) => React.ReactNode;
|
||||
renderScene: (props: { route: Route<string> }) => React.ReactNode;
|
||||
isParentHeaderShown: boolean;
|
||||
@@ -121,27 +118,25 @@ const getHeaderHeights = (
|
||||
};
|
||||
|
||||
const getDistanceFromOptions = (
|
||||
mode: StackCardMode,
|
||||
layout: Layout,
|
||||
descriptor?: StackDescriptor
|
||||
) => {
|
||||
const {
|
||||
gestureDirection = mode === 'modal'
|
||||
animationPresentation,
|
||||
gestureDirection = animationPresentation === 'modal'
|
||||
? ModalTransition.gestureDirection
|
||||
: DefaultTransition.gestureDirection,
|
||||
} = descriptor?.options || {};
|
||||
} = (descriptor?.options || {}) as StackNavigationOptions;
|
||||
|
||||
return getDistanceForDirection(layout, gestureDirection);
|
||||
};
|
||||
|
||||
const getProgressFromGesture = (
|
||||
mode: StackCardMode,
|
||||
gesture: Animated.Value,
|
||||
layout: Layout,
|
||||
descriptor?: StackDescriptor
|
||||
) => {
|
||||
const distance = getDistanceFromOptions(
|
||||
mode,
|
||||
{
|
||||
// Make sure that we have a non-zero distance, otherwise there will be incorrect progress
|
||||
// This causes blank screen on web if it was previously inside container with display: none
|
||||
@@ -165,7 +160,10 @@ const getProgressFromGesture = (
|
||||
};
|
||||
|
||||
export default class CardStack extends React.Component<Props, State> {
|
||||
static getDerivedStateFromProps(props: Props, state: State) {
|
||||
static getDerivedStateFromProps(
|
||||
props: Props,
|
||||
state: State
|
||||
): Partial<State> | null {
|
||||
if (
|
||||
props.routes === state.routes &&
|
||||
props.descriptors === state.descriptors
|
||||
@@ -182,7 +180,7 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
new Animated.Value(
|
||||
props.openingRouteKeys.includes(curr.key) &&
|
||||
animationEnabled !== false
|
||||
? getDistanceFromOptions(props.mode, state.layout, descriptor)
|
||||
? getDistanceFromOptions(state.layout, descriptor)
|
||||
: 0
|
||||
);
|
||||
|
||||
@@ -216,19 +214,97 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
props.descriptors[previousRoute?.key] ||
|
||||
state.descriptors[previousRoute?.key];
|
||||
|
||||
const { options } = descriptor;
|
||||
|
||||
let defaultTransitionPreset =
|
||||
options.animationPresentation === 'modal'
|
||||
? ModalTransition
|
||||
: DefaultTransition;
|
||||
|
||||
const {
|
||||
animationEnabled = Platform.OS !== 'web' &&
|
||||
Platform.OS !== 'windows' &&
|
||||
Platform.OS !== 'macos',
|
||||
gestureEnabled = Platform.OS === 'ios' &&
|
||||
animationEnabled &&
|
||||
index !== 0,
|
||||
gestureDirection = defaultTransitionPreset.gestureDirection,
|
||||
transitionSpec = defaultTransitionPreset.transitionSpec,
|
||||
cardStyleInterpolator = animationEnabled === false
|
||||
? forNoAnimationCard
|
||||
: defaultTransitionPreset.cardStyleInterpolator,
|
||||
headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator,
|
||||
cardOverlayEnabled = Platform.OS !== 'ios' ||
|
||||
cardStyleInterpolator === forModalPresentationIOS,
|
||||
} = options;
|
||||
|
||||
let transitionConfig = {
|
||||
gestureDirection,
|
||||
transitionSpec,
|
||||
cardStyleInterpolator,
|
||||
headerStyleInterpolator,
|
||||
cardOverlayEnabled,
|
||||
};
|
||||
|
||||
// When a screen is not the last, it should use next screen's transition config
|
||||
// Many transitions also animate the previous screen, so using 2 different transitions doesn't look right
|
||||
// For example combining a slide and a modal transition would look wrong otherwise
|
||||
// With this approach, combining different transition styles in the same navigator mostly looks right
|
||||
// This will still be broken when 2 transitions have different idle state (e.g. modal presentation),
|
||||
// but majority of the transitions look alright
|
||||
if (index !== self.length - 1) {
|
||||
if (nextDescriptor) {
|
||||
const {
|
||||
animationEnabled,
|
||||
gestureDirection = defaultTransitionPreset.gestureDirection,
|
||||
transitionSpec = defaultTransitionPreset.transitionSpec,
|
||||
cardStyleInterpolator = animationEnabled === false
|
||||
? forNoAnimationCard
|
||||
: defaultTransitionPreset.cardStyleInterpolator,
|
||||
headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator,
|
||||
cardOverlayEnabled = descriptor.options.cardOverlayEnabled ??
|
||||
cardStyleInterpolator === forModalPresentationIOS,
|
||||
} = nextDescriptor.options;
|
||||
|
||||
transitionConfig = {
|
||||
gestureDirection,
|
||||
transitionSpec,
|
||||
cardStyleInterpolator,
|
||||
headerStyleInterpolator,
|
||||
cardOverlayEnabled,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const headerMode: StackHeaderMode =
|
||||
options.headerMode ??
|
||||
(options.animationPresentation !== 'modal' &&
|
||||
transitionConfig.cardStyleInterpolator !== forModalPresentationIOS &&
|
||||
Platform.OS === 'ios' &&
|
||||
options.header === undefined
|
||||
? 'float'
|
||||
: 'screen');
|
||||
|
||||
const scene = {
|
||||
route,
|
||||
descriptor,
|
||||
descriptor: {
|
||||
...descriptor,
|
||||
options: {
|
||||
...options,
|
||||
...transitionConfig,
|
||||
animationEnabled,
|
||||
gestureEnabled,
|
||||
headerMode,
|
||||
},
|
||||
},
|
||||
progress: {
|
||||
current: getProgressFromGesture(
|
||||
props.mode,
|
||||
currentGesture,
|
||||
state.layout,
|
||||
descriptor
|
||||
),
|
||||
next: nextGesture
|
||||
? getProgressFromGesture(
|
||||
props.mode,
|
||||
nextGesture,
|
||||
state.layout,
|
||||
nextDescriptor
|
||||
@@ -236,7 +312,6 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
: undefined,
|
||||
previous: previousGesture
|
||||
? getProgressFromGesture(
|
||||
props.mode,
|
||||
previousGesture,
|
||||
state.layout,
|
||||
previousDescriptor
|
||||
@@ -369,15 +444,12 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
|
||||
render() {
|
||||
const {
|
||||
mode,
|
||||
insets,
|
||||
descriptors,
|
||||
state,
|
||||
routes,
|
||||
closingRouteKeys,
|
||||
onOpenRoute,
|
||||
onCloseRoute,
|
||||
getGesturesEnabled,
|
||||
renderHeader,
|
||||
renderScene,
|
||||
isParentHeaderShown,
|
||||
@@ -389,47 +461,19 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
onGestureStart,
|
||||
onGestureEnd,
|
||||
onGestureCancel,
|
||||
// Enable on new versions of `react-native-screens`
|
||||
// On older versions of `react-native-screens`, there's an issue with screens not being responsive to user interaction.
|
||||
detachInactiveScreens = Platform.OS === 'web'
|
||||
? true
|
||||
: shouldUseActivityState ?? false,
|
||||
detachInactiveScreens = Platform.OS === 'web' ||
|
||||
Platform.OS === 'android' ||
|
||||
Platform.OS === 'ios',
|
||||
} = this.props;
|
||||
|
||||
const { scenes, layout, gestures, headerHeights } = this.state;
|
||||
|
||||
const focusedRoute = state.routes[state.index];
|
||||
const focusedDescriptor = descriptors[focusedRoute.key];
|
||||
const focusedOptions = focusedDescriptor ? focusedDescriptor.options : {};
|
||||
const focusedHeaderHeight = headerHeights[focusedRoute.key];
|
||||
|
||||
let defaultTransitionPreset =
|
||||
mode === 'modal' ? ModalTransition : DefaultTransition;
|
||||
|
||||
let activeScreensLimit = 1;
|
||||
|
||||
for (let i = scenes.length - 1; i >= 0; i--) {
|
||||
const {
|
||||
// By default, we don't want to detach the previous screen of the active one for modals
|
||||
detachPreviousScreen = mode === 'modal'
|
||||
? i !== scenes.length - 1
|
||||
: true,
|
||||
} = scenes[i].descriptor.options;
|
||||
|
||||
if (detachPreviousScreen === false) {
|
||||
activeScreensLimit++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const isFloatHeaderAbsolute = this.state.scenes.slice(-2).some((scene) => {
|
||||
const options = scene.descriptor.options ?? {};
|
||||
const {
|
||||
headerMode = 'screen',
|
||||
headerTransparent,
|
||||
headerShown = true,
|
||||
} = options;
|
||||
const { headerMode, headerTransparent, headerShown = true } = options;
|
||||
|
||||
if (
|
||||
headerTransparent ||
|
||||
@@ -442,6 +486,25 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
return false;
|
||||
});
|
||||
|
||||
let activeScreensLimit = 1;
|
||||
|
||||
for (let i = scenes.length - 1; i >= 0; i--) {
|
||||
const { options } = scenes[i].descriptor;
|
||||
const {
|
||||
// By default, we don't want to detach the previous screen of the active one for modals
|
||||
detachPreviousScreen = options.animationPresentation === 'modal' ||
|
||||
options.cardStyleInterpolator === forModalPresentationIOS
|
||||
? i !== scenes.length - 1
|
||||
: true,
|
||||
} = options;
|
||||
|
||||
if (detachPreviousScreen === false) {
|
||||
activeScreensLimit++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const floatingHeader = (
|
||||
<React.Fragment key="header">
|
||||
{renderHeader({
|
||||
@@ -451,10 +514,6 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
getPreviousScene: this.getPreviousScene,
|
||||
getFocusedRoute: this.getFocusedRoute,
|
||||
onContentHeightChange: this.handleHeaderLayout,
|
||||
styleInterpolator:
|
||||
focusedOptions.headerStyleInterpolator !== undefined
|
||||
? focusedOptions.headerStyleInterpolator
|
||||
: defaultTransitionPreset.headerStyleInterpolator,
|
||||
style: [
|
||||
styles.floating,
|
||||
isFloatHeaderAbsolute && [
|
||||
@@ -486,96 +545,33 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
// For the old implementation, it stays the same it was
|
||||
let isScreenActive: Animated.AnimatedInterpolation | 2 | 1 | 0 = 1;
|
||||
|
||||
if (shouldUseActivityState || Platform.OS === 'web') {
|
||||
if (index < self.length - activeScreensLimit - 1) {
|
||||
// screen should be inactive because it is too deep in the stack
|
||||
isScreenActive = STATE_INACTIVE;
|
||||
} else {
|
||||
const sceneForActivity = scenes[self.length - 1];
|
||||
const outputValue =
|
||||
index === self.length - 1
|
||||
? STATE_ON_TOP // the screen is on top after the transition
|
||||
: index >= self.length - activeScreensLimit
|
||||
? STATE_TRANSITIONING_OR_BELOW_TOP // the screen should stay active after the transition, it is not on top but is in activeLimit
|
||||
: STATE_INACTIVE; // the screen should be active only during the transition, it is at the edge of activeLimit
|
||||
isScreenActive = sceneForActivity
|
||||
? sceneForActivity.progress.current.interpolate({
|
||||
inputRange: [0, 1 - EPSILON, 1],
|
||||
outputRange: [1, 1, outputValue],
|
||||
extrapolate: 'clamp',
|
||||
})
|
||||
: STATE_TRANSITIONING_OR_BELOW_TOP;
|
||||
}
|
||||
if (index < self.length - activeScreensLimit - 1) {
|
||||
// screen should be inactive because it is too deep in the stack
|
||||
isScreenActive = STATE_INACTIVE;
|
||||
} else {
|
||||
isScreenActive = scene.progress.next
|
||||
? scene.progress.next.interpolate({
|
||||
const sceneForActivity = scenes[self.length - 1];
|
||||
const outputValue =
|
||||
index === self.length - 1
|
||||
? STATE_ON_TOP // the screen is on top after the transition
|
||||
: index >= self.length - activeScreensLimit
|
||||
? STATE_TRANSITIONING_OR_BELOW_TOP // the screen should stay active after the transition, it is not on top but is in activeLimit
|
||||
: STATE_INACTIVE; // the screen should be active only during the transition, it is at the edge of activeLimit
|
||||
isScreenActive = sceneForActivity
|
||||
? sceneForActivity.progress.current.interpolate({
|
||||
inputRange: [0, 1 - EPSILON, 1],
|
||||
outputRange: [1, 1, 0],
|
||||
outputRange: [1, 1, outputValue],
|
||||
extrapolate: 'clamp',
|
||||
})
|
||||
: 1;
|
||||
: STATE_TRANSITIONING_OR_BELOW_TOP;
|
||||
}
|
||||
|
||||
const {
|
||||
cardStyleInterpolator,
|
||||
headerShown = true,
|
||||
headerMode = 'screen',
|
||||
headerTransparent,
|
||||
headerStyle,
|
||||
headerTintColor,
|
||||
cardShadowEnabled,
|
||||
cardOverlayEnabled = Platform.OS !== 'ios' || mode === 'modal',
|
||||
cardOverlay,
|
||||
cardStyle,
|
||||
animationEnabled,
|
||||
gestureResponseDistance,
|
||||
gestureVelocityImpact,
|
||||
gestureDirection = defaultTransitionPreset.gestureDirection,
|
||||
transitionSpec = defaultTransitionPreset.transitionSpec,
|
||||
cardStyleInterpolator = animationEnabled === false
|
||||
? forNoAnimationCard
|
||||
: defaultTransitionPreset.cardStyleInterpolator,
|
||||
headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator,
|
||||
} = scene.descriptor
|
||||
? scene.descriptor.options
|
||||
: ({} as StackNavigationOptions);
|
||||
|
||||
let transitionConfig = {
|
||||
gestureDirection,
|
||||
transitionSpec,
|
||||
cardStyleInterpolator,
|
||||
headerStyleInterpolator,
|
||||
};
|
||||
|
||||
// When a screen is not the last, it should use next screen's transition config
|
||||
// Many transitions also animate the previous screen, so using 2 different transitions doesn't look right
|
||||
// For example combining a slide and a modal transition would look wrong otherwise
|
||||
// With this approach, combining different transition styles in the same navigator mostly looks right
|
||||
// This will still be broken when 2 transitions have different idle state (e.g. modal presentation),
|
||||
// but majority of the transitions look alright
|
||||
if (index !== self.length - 1) {
|
||||
const nextScene = scenes[index + 1];
|
||||
|
||||
if (nextScene) {
|
||||
const {
|
||||
animationEnabled,
|
||||
gestureDirection = defaultTransitionPreset.gestureDirection,
|
||||
transitionSpec = defaultTransitionPreset.transitionSpec,
|
||||
cardStyleInterpolator = animationEnabled === false
|
||||
? forNoAnimationCard
|
||||
: defaultTransitionPreset.cardStyleInterpolator,
|
||||
headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator,
|
||||
} = nextScene.descriptor
|
||||
? nextScene.descriptor.options
|
||||
: ({} as StackNavigationOptions);
|
||||
|
||||
transitionConfig = {
|
||||
gestureDirection,
|
||||
transitionSpec,
|
||||
cardStyleInterpolator,
|
||||
headerStyleInterpolator,
|
||||
};
|
||||
}
|
||||
}
|
||||
} = scene.descriptor.options;
|
||||
|
||||
const safeAreaInsetTop = insets.top;
|
||||
const safeAreaInsetRight = insets.right;
|
||||
@@ -598,6 +594,20 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
// Start from current card and count backwards the number of cards with same interpolation
|
||||
let interpolationIndex = 0;
|
||||
|
||||
for (let i = index - 1; i >= 0; i--) {
|
||||
const cardStyleInterpolatorCurrent =
|
||||
scenes[i]?.descriptor.options.cardStyleInterpolator;
|
||||
|
||||
if (cardStyleInterpolatorCurrent !== cardStyleInterpolator) {
|
||||
break;
|
||||
}
|
||||
|
||||
interpolationIndex++;
|
||||
}
|
||||
|
||||
return (
|
||||
<MaybeScreen
|
||||
key={route.key}
|
||||
@@ -607,7 +617,7 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
pointerEvents="box-none"
|
||||
>
|
||||
<CardContainer
|
||||
index={index}
|
||||
interpolationIndex={interpolationIndex}
|
||||
active={index === self.length - 1}
|
||||
focused={focused}
|
||||
closing={closingRouteKeys.includes(route.key)}
|
||||
@@ -618,25 +628,17 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
safeAreaInsetRight={safeAreaInsetRight}
|
||||
safeAreaInsetBottom={safeAreaInsetBottom}
|
||||
safeAreaInsetLeft={safeAreaInsetLeft}
|
||||
cardOverlay={cardOverlay}
|
||||
cardOverlayEnabled={cardOverlayEnabled}
|
||||
cardShadowEnabled={cardShadowEnabled}
|
||||
cardStyle={cardStyle}
|
||||
onPageChangeStart={onPageChangeStart}
|
||||
onPageChangeConfirm={onPageChangeConfirm}
|
||||
onPageChangeCancel={onPageChangeCancel}
|
||||
onGestureStart={onGestureStart}
|
||||
onGestureCancel={onGestureCancel}
|
||||
onGestureEnd={onGestureEnd}
|
||||
gestureResponseDistance={gestureResponseDistance}
|
||||
headerHeight={headerHeight}
|
||||
isParentHeaderShown={isParentHeaderShown}
|
||||
onHeaderHeightChange={this.handleHeaderLayout}
|
||||
getPreviousScene={this.getPreviousScene}
|
||||
getFocusedRoute={this.getFocusedRoute}
|
||||
mode={mode}
|
||||
headerMode={headerMode}
|
||||
headerShown={headerShown}
|
||||
headerDarkContent={headerDarkContent}
|
||||
hasAbsoluteFloatHeader={
|
||||
isFloatHeaderAbsolute && !headerTransparent
|
||||
@@ -647,9 +649,6 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
onCloseRoute={onCloseRoute}
|
||||
onTransitionStart={onTransitionStart}
|
||||
onTransitionEnd={onTransitionEnd}
|
||||
gestureEnabled={index !== 0 && getGesturesEnabled({ route })}
|
||||
gestureVelocityImpact={gestureVelocityImpact}
|
||||
{...transitionConfig}
|
||||
/>
|
||||
</MaybeScreen>
|
||||
);
|
||||
|
||||
@@ -288,24 +288,6 @@ export default class StackView extends React.Component<Props, State> {
|
||||
descriptors: {},
|
||||
};
|
||||
|
||||
private getGesturesEnabled = ({ route }: { route: Route<string> }) => {
|
||||
const descriptor = this.state.descriptors[route.key];
|
||||
|
||||
if (descriptor) {
|
||||
const { gestureEnabled, animationEnabled } = descriptor.options;
|
||||
|
||||
if (animationEnabled === false) {
|
||||
// When animation is disabled, also disable gestures
|
||||
// The gesture to dismiss a route will look weird when not animated
|
||||
return false;
|
||||
}
|
||||
|
||||
return gestureEnabled !== false;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
private getPreviousRoute = ({ route }: { route: Route<string> }) => {
|
||||
const { closingRouteKeys, replacingRouteKeys } = this.state;
|
||||
const routes = this.state.routes.filter(
|
||||
@@ -438,7 +420,6 @@ export default class StackView extends React.Component<Props, State> {
|
||||
state,
|
||||
navigation,
|
||||
keyboardHandlingEnabled,
|
||||
mode = 'card',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
descriptors: _,
|
||||
...rest
|
||||
@@ -462,11 +443,9 @@ export default class StackView extends React.Component<Props, State> {
|
||||
<HeaderShownContext.Consumer>
|
||||
{(isParentHeaderShown) => (
|
||||
<CardStack
|
||||
mode={mode}
|
||||
insets={insets as EdgeInsets}
|
||||
isParentHeaderShown={isParentHeaderShown}
|
||||
getPreviousRoute={this.getPreviousRoute}
|
||||
getGesturesEnabled={this.getGesturesEnabled}
|
||||
routes={routes}
|
||||
openingRouteKeys={openingRouteKeys}
|
||||
closingRouteKeys={closingRouteKeys}
|
||||
|
||||
418
yarn.lock
418
yarn.lock
@@ -1397,12 +1397,32 @@
|
||||
xcode "^3.0.1"
|
||||
xml2js "^0.4.23"
|
||||
|
||||
"@expo/config-plugins@1.0.29":
|
||||
version "1.0.29"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-1.0.29.tgz#fc36a7a90a08e1a67ed2c19241f4309b08e88a25"
|
||||
integrity sha512-dKtb4dTRiyWmCr/HyMFqt1tHIFpuuvIPnmvakOrg012sY7be81W9WOXjsr6WP7uLT2BfSLFhj1A9EAUzpl6sJw==
|
||||
dependencies:
|
||||
"@expo/config-types" "^40.0.0-beta.2"
|
||||
"@expo/configure-splash-screen" "0.3.4"
|
||||
"@expo/image-utils" "0.3.13"
|
||||
"@expo/json-file" "8.2.29"
|
||||
"@expo/plist" "0.0.12"
|
||||
find-up "~5.0.0"
|
||||
fs-extra "9.0.0"
|
||||
getenv "^1.0.0"
|
||||
glob "7.1.6"
|
||||
resolve-from "^5.0.0"
|
||||
slash "^3.0.0"
|
||||
slugify "^1.3.4"
|
||||
xcode "^3.0.1"
|
||||
xml2js "^0.4.23"
|
||||
|
||||
"@expo/config-types@^40.0.0-beta.2":
|
||||
version "40.0.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-40.0.0-beta.2.tgz#4fea4ef5654d02218b02b0b3772529a9ce5b0471"
|
||||
integrity sha512-t9pHCQMXOP4nwd7LGXuHkLlFy0JdfknRSCAeVF4Kw2/y+5OBbR9hW9ZVnetpBf0kORrekgiI7K/qDaa3hh5+Qg==
|
||||
|
||||
"@expo/config@3.3.34", "@expo/config@^3.3.18":
|
||||
"@expo/config@3.3.34":
|
||||
version "3.3.34"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config/-/config-3.3.34.tgz#ab8cab4c04a406cc92ed483f7a7f071e8e0f3e98"
|
||||
integrity sha512-Yekmn9sIm70vGUwugXlL/jpTQufTJXV7IrYWvFKd4B8ZwdMBFK08NY2XBwvl+jJOVdhmLe+yHc44bCmrEPb6vA==
|
||||
@@ -1422,6 +1442,26 @@
|
||||
semver "7.3.2"
|
||||
slugify "^1.3.4"
|
||||
|
||||
"@expo/config@3.3.39", "@expo/config@^3.3.35":
|
||||
version "3.3.39"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config/-/config-3.3.39.tgz#c5b02544aa6f1c4efb208f34fdee062d42e092e8"
|
||||
integrity sha512-xT+OeMSeKrX+OtOs15Plvr8YIsfUfyT+ofpSxzMuAHQ4ivbhR5bIW7XcJTwg4ie3GUTatraoDUYz84TLoirKaA==
|
||||
dependencies:
|
||||
"@babel/core" "7.9.0"
|
||||
"@babel/plugin-proposal-class-properties" "~7.12.13"
|
||||
"@babel/preset-env" "~7.12.13"
|
||||
"@babel/preset-typescript" "~7.12.13"
|
||||
"@expo/config-plugins" "1.0.29"
|
||||
"@expo/config-types" "^40.0.0-beta.2"
|
||||
"@expo/json-file" "8.2.29"
|
||||
fs-extra "9.0.0"
|
||||
getenv "^1.0.0"
|
||||
glob "7.1.6"
|
||||
require-from-string "^2.0.2"
|
||||
resolve-from "^5.0.0"
|
||||
semver "7.3.2"
|
||||
slugify "^1.3.4"
|
||||
|
||||
"@expo/configure-splash-screen@0.3.4":
|
||||
version "0.3.4"
|
||||
resolved "https://registry.yarnpkg.com/@expo/configure-splash-screen/-/configure-splash-screen-0.3.4.tgz#b91d8f08fd96272bd3d7aaa9b51d6189b932c7cc"
|
||||
@@ -1438,24 +1478,24 @@
|
||||
xcode "^3.0.0"
|
||||
xml-js "^1.6.11"
|
||||
|
||||
"@expo/dev-server@0.1.60":
|
||||
version "0.1.60"
|
||||
resolved "https://registry.yarnpkg.com/@expo/dev-server/-/dev-server-0.1.60.tgz#eb88f5c52e2617611a41325089a3fb516e3d813d"
|
||||
integrity sha512-tm+l8enWZ//2nst6s91V+29KwXlA69bFhG6b50lTXfY++7Q0OaOsDoGl3cQaWHKo2K3MQ/7C8riIGf7q6T5wjg==
|
||||
"@expo/dev-server@0.1.65":
|
||||
version "0.1.65"
|
||||
resolved "https://registry.yarnpkg.com/@expo/dev-server/-/dev-server-0.1.65.tgz#e20ff69786c9f0a39156a920a2ffdc328686a193"
|
||||
integrity sha512-3N2omGlKQrvcU2T/LmtWf+YoQc6yGwFHlikdy1Zh+Z1DBI0rnXPb4YSie3a7k9iBhZlXbCWp2AmzxRRUiQKoMg==
|
||||
dependencies:
|
||||
"@expo/bunyan" "4.0.0"
|
||||
"@expo/metro-config" "0.1.60"
|
||||
"@expo/metro-config" "0.1.65"
|
||||
"@react-native-community/cli-server-api" "4.9.0"
|
||||
body-parser "1.19.0"
|
||||
resolve-from "^5.0.0"
|
||||
serialize-error "6.0.0"
|
||||
|
||||
"@expo/dev-tools@0.13.89":
|
||||
version "0.13.89"
|
||||
resolved "https://registry.yarnpkg.com/@expo/dev-tools/-/dev-tools-0.13.89.tgz#743d8d496a94abb6f55c4c01e4dc1fbf3f91aa25"
|
||||
integrity sha512-8qlcd46x5CJU2VR+IL3MoHY0eScLe9Io2ibzvuFK+7kNP3gQle/LU+biAKtWIfUVlp4+xzEaAiDc202RTtHhqA==
|
||||
"@expo/dev-tools@0.13.95":
|
||||
version "0.13.95"
|
||||
resolved "https://registry.yarnpkg.com/@expo/dev-tools/-/dev-tools-0.13.95.tgz#62f3655cf4fc12c59ed969399a79bc6417521290"
|
||||
integrity sha512-g75GOKTfhaZsPSQw7uHHhObwmkZHb9DPtg5/ptDfwzt7FEkvU10gLvepin6dfwOnzTZ1M/HMKMExxa68FCav7Q==
|
||||
dependencies:
|
||||
"@expo/config" "3.3.34"
|
||||
"@expo/config" "3.3.39"
|
||||
base64url "3.0.1"
|
||||
express "4.16.4"
|
||||
freeport-async "2.0.0"
|
||||
@@ -1501,6 +1541,23 @@
|
||||
semver "7.3.2"
|
||||
tempy "0.3.0"
|
||||
|
||||
"@expo/image-utils@0.3.13":
|
||||
version "0.3.13"
|
||||
resolved "https://registry.yarnpkg.com/@expo/image-utils/-/image-utils-0.3.13.tgz#cba070a61ce89e6c113b8e6afd5655cb83da6377"
|
||||
integrity sha512-BpKoFVJBjG9H5AU040Skrm3R2uDGpWXBU/4TivB5H10cyJphoJKp3GNJVPHYLOVc70OtAxjWDIhLMW6xsWfrgw==
|
||||
dependencies:
|
||||
"@expo/spawn-async" "1.5.0"
|
||||
chalk "^4.0.0"
|
||||
fs-extra "9.0.0"
|
||||
getenv "^1.0.0"
|
||||
jimp "0.12.1"
|
||||
mime "^2.4.4"
|
||||
node-fetch "^2.6.0"
|
||||
parse-png "^2.1.0"
|
||||
resolve-from "^5.0.0"
|
||||
semver "7.3.2"
|
||||
tempy "0.3.0"
|
||||
|
||||
"@expo/json-file@8.2.28":
|
||||
version "8.2.28"
|
||||
resolved "https://registry.yarnpkg.com/@expo/json-file/-/json-file-8.2.28.tgz#9218bb70ad12d8be03c6376990b01fbeb4a15b72"
|
||||
@@ -1511,7 +1568,27 @@
|
||||
json5 "^1.0.1"
|
||||
write-file-atomic "^2.3.0"
|
||||
|
||||
"@expo/metro-config@0.1.60", "@expo/metro-config@^0.1.59":
|
||||
"@expo/json-file@8.2.29":
|
||||
version "8.2.29"
|
||||
resolved "https://registry.yarnpkg.com/@expo/json-file/-/json-file-8.2.29.tgz#5e66c4c1dc531a583fe654d99fe417246d91741d"
|
||||
integrity sha512-9C8XwpJiJN9fyClnnNDSTh034zJU9hon6MCUqbBa4dZYQN7OZ00KFZEpbtjy+FndE7YD+KagDxRD6O1HS5vU0g==
|
||||
dependencies:
|
||||
"@babel/code-frame" "~7.10.4"
|
||||
fs-extra "9.0.0"
|
||||
json5 "^1.0.1"
|
||||
write-file-atomic "^2.3.0"
|
||||
|
||||
"@expo/metro-config@0.1.65", "@expo/metro-config@^0.1.63":
|
||||
version "0.1.65"
|
||||
resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-0.1.65.tgz#95bd4f265def2edca6d229e3860d933425045d83"
|
||||
integrity sha512-iREEn0RXueAUAuiu0Z4ECxed2UhEkI0KZD2avRggEmCBdDS6uo4TgfFo/2pmMLfXv56nS0wRmtB6o3SqUcgh3g==
|
||||
dependencies:
|
||||
"@expo/config" "3.3.39"
|
||||
chalk "^4.1.0"
|
||||
getenv "^1.0.0"
|
||||
metro-react-native-babel-transformer "^0.59.0"
|
||||
|
||||
"@expo/metro-config@^0.1.59":
|
||||
version "0.1.60"
|
||||
resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-0.1.60.tgz#43b2bf4d5e00a02a700f756f7ae6bbd9e755e124"
|
||||
integrity sha512-aA/UMZ5ga1QfF9pN1rYvwTg7ez/Ptz6vSFWyOdYM00X+egKlQFDwe8UuMKiUZUHaLFJ3XbixMLTIvDVdnVxk7w==
|
||||
@@ -1521,20 +1598,20 @@
|
||||
getenv "^1.0.0"
|
||||
metro-react-native-babel-transformer "^0.59.0"
|
||||
|
||||
"@expo/osascript@2.0.25":
|
||||
version "2.0.25"
|
||||
resolved "https://registry.yarnpkg.com/@expo/osascript/-/osascript-2.0.25.tgz#bd88b08ba4c32c09a349c66745ec60d34f0f55f9"
|
||||
integrity sha512-rB+RLHCp72q0OBWmisoBswfTpyzc91OJMs3UQVWJP9mXVNJhemONt7PKjE+FinBm33uH1HCC6U7JPGigpVsJBg==
|
||||
"@expo/osascript@2.0.27":
|
||||
version "2.0.27"
|
||||
resolved "https://registry.yarnpkg.com/@expo/osascript/-/osascript-2.0.27.tgz#738fa409e4bff1288c0cf29bb6606fbf130ff0b1"
|
||||
integrity sha512-q2fh4EuegZkr48v8lvZSQdCRhHbtoBJ3CUpmipVjAXJ+jMJGwwCcZvGpE57N5ODxCTKi5RP0NiEfFAfEnpGnQw==
|
||||
dependencies:
|
||||
"@expo/spawn-async" "^1.5.0"
|
||||
exec-async "^2.2.0"
|
||||
|
||||
"@expo/package-manager@0.0.39":
|
||||
version "0.0.39"
|
||||
resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-0.0.39.tgz#6cf7458af198184e5c4dbcf73546f581bc328ae4"
|
||||
integrity sha512-2KSx1jjdDmWsnkZfWZL4TwveeIyAcORQna5VOZdd+sBalK8taMtPQEdchwAGD4mOFsFFPmpbQHwboRfXoSlbOQ==
|
||||
"@expo/package-manager@0.0.42":
|
||||
version "0.0.42"
|
||||
resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-0.0.42.tgz#6b33854ee72c3936010c6527b6aafc731d8f12c0"
|
||||
integrity sha512-8237/Gpz1NvcWv+Ja2ytMaVxgVPiiiai8wjvyGSvD0GWm3vUm66qi9lAN9m3EtfL0DvcwLIz0GYzleM8mOSxXw==
|
||||
dependencies:
|
||||
"@expo/json-file" "8.2.28"
|
||||
"@expo/json-file" "8.2.29"
|
||||
"@expo/spawn-async" "^1.5.0"
|
||||
ansi-regex "^5.0.0"
|
||||
chalk "^4.0.0"
|
||||
@@ -1558,10 +1635,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@expo/results/-/results-1.0.0.tgz#fd4b22f936ceafce23b04799f54b87fe2a9e18d1"
|
||||
integrity sha512-qECzzXX5oJot3m2Gu9pfRDz50USdBieQVwYAzeAtQRUTD3PVeTK1tlRUoDcrK8PSruDLuVYdKkLebX4w/o55VA==
|
||||
|
||||
"@expo/schemer@1.3.27":
|
||||
version "1.3.27"
|
||||
resolved "https://registry.yarnpkg.com/@expo/schemer/-/schemer-1.3.27.tgz#55d7e7a28bfbd4eca26f59306e305cc5fc8b33e0"
|
||||
integrity sha512-cuCvSo6qErgK7OOM8CoCtCsVifq+WX1wUCeu+fmSyhnZcqnz45ZTK20Ghk5bT3OrTNQipbTiCjn9RCDrkEMMkg==
|
||||
"@expo/schemer@1.3.28":
|
||||
version "1.3.28"
|
||||
resolved "https://registry.yarnpkg.com/@expo/schemer/-/schemer-1.3.28.tgz#3ad544f3c1b61bea9dda0ec5d195002b70d8a3ca"
|
||||
integrity sha512-9wmnhlD1X1ro8FTFzM/J3nSxaFpI9X+bcaimP3hKkc3flIR8cGjQcLmE+MOEgE2LET0ScxRBtM3hteernFI6Ww==
|
||||
dependencies:
|
||||
ajv "^5.2.2"
|
||||
json-schema-traverse "0.3.1"
|
||||
@@ -1593,7 +1670,47 @@
|
||||
lodash.pick "^4.4.0"
|
||||
lodash.template "^4.5.0"
|
||||
|
||||
"@expo/webpack-config@0.12.64", "@expo/webpack-config@~0.12.63":
|
||||
"@expo/webpack-config@0.12.69":
|
||||
version "0.12.69"
|
||||
resolved "https://registry.yarnpkg.com/@expo/webpack-config/-/webpack-config-0.12.69.tgz#e71da0c2a2db5fce0d940311a558d01cd01cd034"
|
||||
integrity sha512-S/gJvzcsd0M6nHsjsVftQgazFwpmcOfAh2ryOTd6QTn5OL9tFsNXfWfP7gad+a+hJLKz43hEDAlJKljNgy9KDw==
|
||||
dependencies:
|
||||
"@babel/core" "7.9.0"
|
||||
"@babel/runtime" "7.9.0"
|
||||
"@expo/config" "3.3.39"
|
||||
"@pmmmwh/react-refresh-webpack-plugin" "^0.3.3"
|
||||
babel-loader "8.1.0"
|
||||
chalk "^4.0.0"
|
||||
clean-webpack-plugin "^3.0.0"
|
||||
copy-webpack-plugin "~6.0.3"
|
||||
css-loader "~3.6.0"
|
||||
expo-pwa "0.0.75"
|
||||
file-loader "~6.0.0"
|
||||
find-yarn-workspace-root "~2.0.0"
|
||||
getenv "^1.0.0"
|
||||
html-loader "~1.1.0"
|
||||
html-webpack-plugin "~4.3.0"
|
||||
is-wsl "^2.0.0"
|
||||
mini-css-extract-plugin "^0.5.0"
|
||||
node-html-parser "^1.2.12"
|
||||
optimize-css-assets-webpack-plugin "^5.0.3"
|
||||
pnp-webpack-plugin "^1.5.0"
|
||||
postcss-safe-parser "^4.0.2"
|
||||
progress "^2.0.3"
|
||||
react-dev-utils "~11.0.1"
|
||||
react-refresh "^0.8.2"
|
||||
semver "~7.3.2"
|
||||
style-loader "~1.2.1"
|
||||
terser-webpack-plugin "^3.0.6"
|
||||
url-loader "~4.1.0"
|
||||
webpack "4.43.0"
|
||||
webpack-deep-scope-plugin "1.6.0"
|
||||
webpack-manifest-plugin "~2.2.0"
|
||||
webpackbar "^4.0.0"
|
||||
workbox-webpack-plugin "^3.6.3"
|
||||
worker-loader "^2.0.0"
|
||||
|
||||
"@expo/webpack-config@~0.12.63":
|
||||
version "0.12.64"
|
||||
resolved "https://registry.yarnpkg.com/@expo/webpack-config/-/webpack-config-0.12.64.tgz#0b44fa37416e765bb9ffe65399d0a40c0d3387f0"
|
||||
integrity sha512-STR9ctDYG36JUIjgcGClvsi3ZU55lc2jtIxf04+0TN8IVYOCoYTF3P971zA1dd3En/q//CpgtKX/UAoimdlL9A==
|
||||
@@ -1633,10 +1750,10 @@
|
||||
workbox-webpack-plugin "^3.6.3"
|
||||
worker-loader "^2.0.0"
|
||||
|
||||
"@expo/xcpretty@^1.0.4":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@expo/xcpretty/-/xcpretty-1.1.1.tgz#2b5b1679f256449e13f25cebfc0dcf008b687b57"
|
||||
integrity sha512-YospUT3y7tPheNCW0SgzbDM8KzipuWukW/qxJRX1MnlgVb1wnnsvtBrY0z/ndRCBMlZFVUu+FkNfI0qDKo8YiA==
|
||||
"@expo/xcpretty@~2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@expo/xcpretty/-/xcpretty-2.0.1.tgz#2c912166ca50a88f710cfe3b6b441144f5df14a2"
|
||||
integrity sha512-fyQbzvZpLiKpx68QDzeLlL1AtFhhEW35dqxIqb4QQ6e3iofu57NdWBQTmIAQzDOPcNNXUR9SFncu3M4iyWwQ7Q==
|
||||
dependencies:
|
||||
"@babel/code-frame" "7.10.4"
|
||||
chalk "^4.1.0"
|
||||
@@ -3256,11 +3373,16 @@
|
||||
sudo-prompt "^9.0.0"
|
||||
wcwidth "^1.0.1"
|
||||
|
||||
"@react-native-masked-view/masked-view@0.2.3", "@react-native-masked-view/masked-view@^0.2.3":
|
||||
"@react-native-masked-view/masked-view@^0.2.3":
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@react-native-masked-view/masked-view/-/masked-view-0.2.3.tgz#5f7a39be4787c89d5b35ac80de2ea5d0a0bb64d7"
|
||||
integrity sha512-t8VcdaFbyXscNs26h/NCsPqVjawWHhIMzGBR9oCIx6kbDa7JW3Q67lNLGkzDudSVnw3Qv09JZrkLXXOGjOABpg==
|
||||
|
||||
"@react-native-masked-view/masked-view@~0.2.4":
|
||||
version "0.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@react-native-masked-view/masked-view/-/masked-view-0.2.4.tgz#305a548abd47dee494a38f90016432d3a6e4164c"
|
||||
integrity sha512-2Y9OXWHRutYmdyW+bNMaFTW8uTBpaaK20xdIFoVtqahEOO9++B+ut3CAWKPZcdRtb9ikG0LUKChGqMeocg0PGA==
|
||||
|
||||
"@segment/loosely-validate-event@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz#87dfc979e5b4e7b82c5f1d8b722dfd5d77644681"
|
||||
@@ -3677,13 +3799,20 @@
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-native@~0.63.2", "@types/react-native@~0.63.51":
|
||||
"@types/react-native@~0.63.2":
|
||||
version "0.63.52"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.63.52.tgz#449beb4a413ec0f2c172cbf676a95f5b0952adf4"
|
||||
integrity sha512-sBXvvtJaIUSXQLDh9NZitx1KHkKUdBLZy34lFKJaIXtpHIh5OEbBXeyUTFBtFwjk/RD0tneAtUqsdhheZRzAzw==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-native@~0.64.4":
|
||||
version "0.64.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.4.tgz#9f11bef7dd5520801884829c73b19d75aa42e73c"
|
||||
integrity sha512-VqnlmadGkD5usREvnuyVpWDS1W8f6cCz6MP5fZdgONsaZ9/Ijfb9Iq9MZ5O3bnW1OyJixDX9HtSp3COsFSLD8Q==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*":
|
||||
version "17.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.3.tgz#ba6e215368501ac3826951eef2904574c262cc79"
|
||||
@@ -3755,11 +3884,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.7.tgz#545158342f949e8fd3bfd813224971ecddc3fac4"
|
||||
integrity sha512-0VBprVqfgFD7Ehb2vd8Lh9TG3jP98gvr8rgehQqzztZNI7o8zS8Ad4jyZneKELphpuE212D8J70LnSNQSyO6bQ==
|
||||
|
||||
"@types/text-table@^0.2.1":
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/text-table/-/text-table-0.2.1.tgz#39c4d4a058a82f677392dfd09976e83d9b4c9264"
|
||||
integrity sha512-dchbFCWfVgUSWEvhOkXGS7zjm+K7jCUvGrQkAHPk2Fmslfofp4HQTH2pqnQ3Pw5GPYv0zWa2AQjKtsfZThuemQ==
|
||||
|
||||
"@types/uglify-js@*":
|
||||
version "3.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.13.0.tgz#1cad8df1fb0b143c5aba08de5712ea9d1ff71124"
|
||||
@@ -3896,13 +4020,12 @@
|
||||
dependencies:
|
||||
compare-versions "^3.4.0"
|
||||
|
||||
"@unimodules/react-native-adapter@~6.2.1":
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@unimodules/react-native-adapter/-/react-native-adapter-6.2.1.tgz#e944bca491aee79bdef68b5d70f9534b79e7a785"
|
||||
integrity sha512-dVg0FnaH04VvLr6hRPQJuFoiTQhR1RgShjtm1w/vndAGK4CJ1oerLSBL+p887UFmNTk/NSMLkcbdS1ZIRH06Rg==
|
||||
"@unimodules/react-native-adapter@~6.2.2":
|
||||
version "6.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@unimodules/react-native-adapter/-/react-native-adapter-6.2.2.tgz#0b76af76589ec5ff2c52f274e40ed609e91c5e4c"
|
||||
integrity sha512-hBXL+IX3u+4TcAHu9lIItdycA7pYWZn3Tt7s5TTna9QKHjyrwo0zVss27LkpJ40tXRHyh/GJ8VzN2CD+0M5I2A==
|
||||
dependencies:
|
||||
invariant "^2.2.4"
|
||||
lodash "^4.5.0"
|
||||
|
||||
"@webassemblyjs/ast@1.9.0":
|
||||
version "1.9.0"
|
||||
@@ -5508,14 +5631,6 @@ callsites@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||
|
||||
camel-case@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
|
||||
integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
upper-case "^1.1.1"
|
||||
|
||||
camel-case@^4.1.1:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a"
|
||||
@@ -7996,10 +8111,10 @@ expect@^26.6.2:
|
||||
jest-message-util "^26.6.2"
|
||||
jest-regex-util "^26.0.0"
|
||||
|
||||
expo-application@~3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/expo-application/-/expo-application-3.1.0.tgz#7c04b31a202ac24d60a00982276cbe8b2fb87e34"
|
||||
integrity sha512-s/lLjkuBf9TnfSANPgOJe+T2xB+Nztl+vyjyBhoeLgxo4E7YQRHqKFofwHnQJ308suOAB5vt0MI87EgFgnSR/Q==
|
||||
expo-application@~3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/expo-application/-/expo-application-3.1.2.tgz#7c93e8108a7f2a0be0afe37151f36c98e0111e7c"
|
||||
integrity sha512-JZcKUpGmqzQ1zLxRxUditGzPqnPCXY6JT/Pbq4nyV4VPzjDd8wihVPuud+cuv8gHgdj8QLvKs/lcJJqN94EX5Q==
|
||||
|
||||
expo-asset@~8.3.1:
|
||||
version "8.3.1"
|
||||
@@ -8017,33 +8132,33 @@ expo-blur@~9.0.3:
|
||||
resolved "https://registry.yarnpkg.com/expo-blur/-/expo-blur-9.0.3.tgz#5095b5bd04d000f73b351d0e4736767b86b49bbd"
|
||||
integrity sha512-7jMqzo53eg5t9Uc1Tg+yGu9lKL9the4CWAQO0kC6YMhrsDiB13mwOUesC+IScHDTSUKNMGcHI8kN+OmvEWfPig==
|
||||
|
||||
expo-cli@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/expo-cli/-/expo-cli-4.3.4.tgz#54af9c9f6cb91096a73583f189a2f01fb3c74127"
|
||||
integrity sha512-feD7OAnzf7PKZgi4VpEpkuI8TYdplPaQVILoS+6ejMLvjjgDOTPh9Tn47xdox35Q3S1VPWS6qivNvOXjhUNDDQ==
|
||||
expo-cli@^4.4.4:
|
||||
version "4.4.4"
|
||||
resolved "https://registry.yarnpkg.com/expo-cli/-/expo-cli-4.4.4.tgz#6ddc3ebba6c7cf82dd620df328dc8f3fba8f73d6"
|
||||
integrity sha512-bQN+XxZYoJnSY3fCNSBAZ1ChZEhHOmv5KhwNmPa/Tc2/IPRFCzdopNGf+Uf5FrpjDex/UqewjVrAkb4Ak4/1Hg==
|
||||
dependencies:
|
||||
"@expo/apple-utils" "0.0.0-alpha.17"
|
||||
"@expo/bunyan" "4.0.0"
|
||||
"@expo/config" "3.3.34"
|
||||
"@expo/config-plugins" "1.0.24"
|
||||
"@expo/dev-tools" "0.13.89"
|
||||
"@expo/json-file" "8.2.28"
|
||||
"@expo/osascript" "2.0.25"
|
||||
"@expo/package-manager" "0.0.39"
|
||||
"@expo/config" "3.3.39"
|
||||
"@expo/config-plugins" "1.0.29"
|
||||
"@expo/dev-tools" "0.13.95"
|
||||
"@expo/json-file" "8.2.29"
|
||||
"@expo/osascript" "2.0.27"
|
||||
"@expo/package-manager" "0.0.42"
|
||||
"@expo/plist" "0.0.12"
|
||||
"@expo/results" "^1.0.0"
|
||||
"@expo/simple-spinner" "1.0.2"
|
||||
"@expo/spawn-async" "1.5.0"
|
||||
"@expo/xcpretty" "^1.0.4"
|
||||
"@expo/xcpretty" "~2.0.1"
|
||||
"@hapi/joi" "^17.1.1"
|
||||
babel-runtime "6.26.0"
|
||||
base32.js "0.1.0"
|
||||
boxen "4.1.0"
|
||||
bplist-parser "0.2.0"
|
||||
chalk "^4.0.0"
|
||||
cli-table3 "^0.6.0"
|
||||
command-exists "^1.2.8"
|
||||
commander "2.17.1"
|
||||
concat-stream "1.6.2"
|
||||
dateformat "3.0.3"
|
||||
env-editor "^0.4.1"
|
||||
envinfo "7.5.0"
|
||||
@@ -8072,6 +8187,7 @@ expo-cli@^4.3.4:
|
||||
react-dev-utils "~11.0.1"
|
||||
read-last-lines "1.6.0"
|
||||
semver "7.3.2"
|
||||
slugify "^1.3.4"
|
||||
strip-ansi "^6.0.0"
|
||||
tar "^6.0.5"
|
||||
tempy "^0.7.1"
|
||||
@@ -8081,15 +8197,14 @@ expo-cli@^4.3.4:
|
||||
url-join "4.0.0"
|
||||
uuid "^8.0.0"
|
||||
wrap-ansi "^7.0.0"
|
||||
xcode "^3.0.1"
|
||||
xdl "59.0.29"
|
||||
xdl "59.0.35"
|
||||
|
||||
expo-constants@~10.1.1:
|
||||
version "10.1.1"
|
||||
resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-10.1.1.tgz#1cdf8c23c63bc3923da199d608037ae845361dd9"
|
||||
integrity sha512-sj8NgtJ97hh8HempI1z9Eo2nYFPV2PdjmmflvZLUBKbxqU4k+9+FiE6aTKNb23LsdbadZu2D/hzXT0pDub/GdQ==
|
||||
expo-constants@~10.1.3:
|
||||
version "10.1.3"
|
||||
resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-10.1.3.tgz#dfbe30362d27d6f500318eb528621424034b72d5"
|
||||
integrity sha512-Eq/xeshnhSoe4ok89d5lrHvI9jq3bMe1FhJUbiHVGcGmW8mGCotwbQBIfDkkMrAKnSOwQq/Qfyg0XBxnG2XFjw==
|
||||
dependencies:
|
||||
"@expo/config" "^3.3.18"
|
||||
"@expo/config" "^3.3.35"
|
||||
uuid "^3.3.2"
|
||||
|
||||
expo-error-recovery@~2.1.0:
|
||||
@@ -8097,10 +8212,10 @@ expo-error-recovery@~2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/expo-error-recovery/-/expo-error-recovery-2.1.0.tgz#7baf6cabc53162cdd2b36edb20b8aa6d1cdc1107"
|
||||
integrity sha512-N5g2QKtdNntUNGQVnB/tG1jHdtJP1+kLMWDS+7ZKRcKfulm3JX/M3l460fsEtqg84n/latxPkBT0yfKw2DSq+Q==
|
||||
|
||||
expo-file-system@~11.0.0:
|
||||
version "11.0.0"
|
||||
resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-11.0.0.tgz#3a6d2f8a9ad4033fae2cabe1a2133372a90a4f20"
|
||||
integrity sha512-SkJ3HwPLYoLFUR4w7KMvyhxRdCZcDsW/jeoUffGSh52wHs3+1OTKDMta4nQImeiWGZvXsOJ1KyJ+WubRxAgtMg==
|
||||
expo-file-system@~11.0.2:
|
||||
version "11.0.2"
|
||||
resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-11.0.2.tgz#c3f9b9c6ba25456a0d32c7a9bb38e55310d471bd"
|
||||
integrity sha512-nodNvUVa+US4N4xnj5BFw8W9ZF/qCHJVC2t45cHWrBiwkVVxz45wjE7uSHUmkMWyWT7a/7AJuL3XJfYp7h90IQ==
|
||||
dependencies:
|
||||
"@expo/config-plugins" "^1.0.18"
|
||||
uuid "^3.4.0"
|
||||
@@ -8112,17 +8227,17 @@ expo-font@~9.1.0:
|
||||
dependencies:
|
||||
fontfaceobserver "^2.1.0"
|
||||
|
||||
expo-keep-awake@~9.1.1:
|
||||
version "9.1.1"
|
||||
resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-9.1.1.tgz#d56d520ae62bc22b9178afacaafea73a1adc89cf"
|
||||
integrity sha512-2NLUN/be9gjsvk0SF0ArA5omzUUIiVc/cMLfHdb/YnGGzBmNNGYOg9x/4iMzzASuKblttzT3iaf0uqgBqCxM6Q==
|
||||
expo-keep-awake@~9.1.2:
|
||||
version "9.1.2"
|
||||
resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-9.1.2.tgz#b3e52c7bef0ade975ae88637a2bf980f6b573368"
|
||||
integrity sha512-CCuEOQUNLYtMA0rt0sQ9u5LlIMH7lDJG7dImoorfKMsP95yHXy8dl3oCdtaz2zbsPgggVYeom9gE+gQu+Ki4rQ==
|
||||
|
||||
expo-linking@~2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/expo-linking/-/expo-linking-2.2.1.tgz#9365ea80be10016ac8b88ec49784c413b921beac"
|
||||
integrity sha512-/6kjAJId/v4b4qgkH+VT1e2GuBK5KonUO++FdxaK6UC4X0mdZIsWZfFCTqEARWKISedaLMT5v1+DQQRkiVj6cw==
|
||||
expo-linking@~2.2.3:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/expo-linking/-/expo-linking-2.2.3.tgz#925584a0ed7a617065fe92a39ba4509f70972664"
|
||||
integrity sha512-1NqL8sY4EzwRPVnBxbvNKBCfsG/tNZm72tVwzjoLxhO0XA6VTtZcb72Jaku4IlqEyH0lejxV2YAU0rbLv1pAVA==
|
||||
dependencies:
|
||||
expo-constants "~10.1.1"
|
||||
expo-constants "~10.1.3"
|
||||
invariant "^2.2.4"
|
||||
qs "^6.5.0"
|
||||
url-parse "^1.4.4"
|
||||
@@ -8138,15 +8253,26 @@ expo-pwa@0.0.70:
|
||||
commander "2.20.0"
|
||||
update-check "1.5.3"
|
||||
|
||||
expo-pwa@0.0.75:
|
||||
version "0.0.75"
|
||||
resolved "https://registry.yarnpkg.com/expo-pwa/-/expo-pwa-0.0.75.tgz#9511d9226aea2715007ab898ab28b74555b3ee28"
|
||||
integrity sha512-EULfm5yg+xxluhj/qYZokUuFZKoarX8DTHFXNzW8+TlsL38n89PBR7okT5na7YpFEdAaOPTBNQEZZfN5O5tPHQ==
|
||||
dependencies:
|
||||
"@expo/config" "3.3.39"
|
||||
"@expo/image-utils" "0.3.13"
|
||||
chalk "^4.0.0"
|
||||
commander "2.20.0"
|
||||
update-check "1.5.3"
|
||||
|
||||
expo-structured-headers@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/expo-structured-headers/-/expo-structured-headers-1.0.1.tgz#2f3117fc9480e01aa6705493d3213060c2dcf683"
|
||||
integrity sha512-U4frE8rT5x1LqZv5Ru/9CtzY8gh0wp1MfwTu5hetPydnwxXfWchuXvKgWRjEHglFU2X/29kYFY/J1UkPvCN8xg==
|
||||
|
||||
expo-updates@~0.5.3:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/expo-updates/-/expo-updates-0.5.3.tgz#b8806260f84fb0bd4e2b276f797dc5fab536824d"
|
||||
integrity sha512-BmTvD6qtSEArg4GUpFbGOfRddn2pL4waR1JhDkBl6Af0RdzkzzLDpxdPyr9cuRMg2iWhxpbHag0TaRNlBeuhiA==
|
||||
expo-updates@~0.5.4:
|
||||
version "0.5.4"
|
||||
resolved "https://registry.yarnpkg.com/expo-updates/-/expo-updates-0.5.4.tgz#ee1b395fee701fb230e7ae28729f4d7055dd3514"
|
||||
integrity sha512-2lwvcBqZutUT4TwqPgTpcBuomjTHt781k1DjGKpf1lS9X/E4w1dgklESXfSASaTS84YuXfK/i6/7LjTWucCv/A==
|
||||
dependencies:
|
||||
"@expo/config-plugins" "^1.0.18"
|
||||
"@expo/metro-config" "^0.1.59"
|
||||
@@ -8155,25 +8281,25 @@ expo-updates@~0.5.3:
|
||||
resolve-from "^5.0.0"
|
||||
uuid "^3.4.0"
|
||||
|
||||
expo@^41.0.0-beta.2:
|
||||
version "41.0.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/expo/-/expo-41.0.0-beta.2.tgz#444702ade489b69bf6f396b9aa7eb255e76e81b1"
|
||||
integrity sha512-TIr1VmV79SnpcUsWoiDPYtj6scFEuQwmZTkxkbf8fXO5nKsEXz8KMJfp2Lbo+n2Lv27LZ4wP0ZETQqHGWG9jRA==
|
||||
expo@^41.0.1:
|
||||
version "41.0.1"
|
||||
resolved "https://registry.yarnpkg.com/expo/-/expo-41.0.1.tgz#2689003212dcc948d010f86dadf055721a6314b4"
|
||||
integrity sha512-Lk4Xdst+OfsLgBNeu89hxk0qFrZnHwwXFmBbJkYLlZkdC3tvNJ8jgGHsKg6jYpsnal/z0uVc+uk2ev91qXQykg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.1.2"
|
||||
"@expo/metro-config" "^0.1.59"
|
||||
"@expo/metro-config" "^0.1.63"
|
||||
"@expo/vector-icons" "^12.0.4"
|
||||
"@unimodules/core" "~7.1.0"
|
||||
"@unimodules/react-native-adapter" "~6.2.1"
|
||||
"@unimodules/react-native-adapter" "~6.2.2"
|
||||
babel-preset-expo "~8.3.0"
|
||||
cross-spawn "^6.0.5"
|
||||
expo-application "~3.1.0"
|
||||
expo-application "~3.1.2"
|
||||
expo-asset "~8.3.1"
|
||||
expo-constants "~10.1.1"
|
||||
expo-constants "~10.1.3"
|
||||
expo-error-recovery "~2.1.0"
|
||||
expo-file-system "~11.0.0"
|
||||
expo-file-system "~11.0.2"
|
||||
expo-font "~9.1.0"
|
||||
expo-keep-awake "~9.1.1"
|
||||
expo-keep-awake "~9.1.2"
|
||||
fbemitter "^2.1.1"
|
||||
invariant "^2.2.2"
|
||||
md5-file "^3.2.3"
|
||||
@@ -9921,11 +10047,6 @@ imurmurhash@^0.1.4:
|
||||
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
|
||||
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
|
||||
|
||||
indent-string@3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289"
|
||||
integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=
|
||||
|
||||
indent-string@4.0.0, indent-string@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
|
||||
@@ -11862,7 +11983,7 @@ lodash.uniq@^4.5.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
||||
|
||||
"lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.7.0:
|
||||
"lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.7.0:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
@@ -11903,11 +12024,6 @@ loud-rejection@^1.0.0:
|
||||
currently-unhandled "^0.4.1"
|
||||
signal-exit "^3.0.0"
|
||||
|
||||
lower-case@^1.1.1:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
|
||||
integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
|
||||
|
||||
lower-case@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
|
||||
@@ -12864,7 +12980,7 @@ natural-compare@^1.4.0:
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
|
||||
|
||||
ncp@2.0.0, ncp@~2.0.0:
|
||||
ncp@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3"
|
||||
integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=
|
||||
@@ -12898,13 +13014,6 @@ nice-try@^1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||
|
||||
no-case@^2.2.0:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
|
||||
integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
|
||||
dependencies:
|
||||
lower-case "^1.1.1"
|
||||
|
||||
no-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
|
||||
@@ -13909,14 +14018,6 @@ parseurl@^1.3.2, parseurl@~1.3.2, parseurl@~1.3.3:
|
||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
||||
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
|
||||
|
||||
pascal-case@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-2.0.1.tgz#2d578d3455f660da65eca18ef95b4e0de912761e"
|
||||
integrity sha1-LVeNNFX2YNpl7KGO+VtODekSdh4=
|
||||
dependencies:
|
||||
camel-case "^3.0.0"
|
||||
upper-case-first "^1.1.0"
|
||||
|
||||
pascal-case@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb"
|
||||
@@ -15165,9 +15266,9 @@ react-native-vector-icons@^8.1.0:
|
||||
yargs "^16.1.1"
|
||||
|
||||
react-native-web@~0.15.0:
|
||||
version "0.15.5"
|
||||
resolved "https://registry.yarnpkg.com/react-native-web/-/react-native-web-0.15.5.tgz#6ad8d4553df080c9a9c30b63cffc7b41d0e076ec"
|
||||
integrity sha512-SSXQfVJ8Zm5p633JM5QgkZ1BhZHqJEkRaSUa1BUzgzpMcqWX+XUFM0ZFwsRO5O9yd0wgi4yH89Da4qdoaCjoDw==
|
||||
version "0.15.7"
|
||||
resolved "https://registry.yarnpkg.com/react-native-web/-/react-native-web-0.15.7.tgz#5b07d98032a36af3cb558357c7a1470e879fc422"
|
||||
integrity sha512-mrbQayl1luIO4Gfyw6KLdlWc30JcJOQgRn84iOj6dLJYVRwcE6W5U2Af68hQYFJsGgVb9sdlYC0ppfFM7ywqXQ==
|
||||
dependencies:
|
||||
array-find-index "^1.0.2"
|
||||
create-react-class "^15.7.0"
|
||||
@@ -15612,11 +15713,6 @@ repeating@^2.0.0:
|
||||
dependencies:
|
||||
is-finite "^1.0.0"
|
||||
|
||||
replace-string@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/replace-string/-/replace-string-1.1.0.tgz#87062117f823fe5800c306bacb2cfa359b935fea"
|
||||
integrity sha1-hwYhF/gj/lgAwwa6yyz6NZuTX+o=
|
||||
|
||||
request-promise-core@1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f"
|
||||
@@ -16367,7 +16463,7 @@ slugid@1.1.0:
|
||||
dependencies:
|
||||
uuid "^2.0.1"
|
||||
|
||||
slugify@^1.3.4, slugify@^1.3.6:
|
||||
slugify@^1.3.4:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.5.0.tgz#5f3c8e2a84105b54eb51486db1b468a599b3c9b8"
|
||||
integrity sha512-Q2UPZ2udzquy1ElHfOLILMBMqBEXkiD3wE75qtBvV+FsDdZZjUqPZ44vqLTejAVq+wLLHacOMcENnP8+ZbzmIA==
|
||||
@@ -17632,11 +17728,16 @@ typedarray@^0.0.6:
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||
|
||||
typescript@^4.2.3, typescript@~4.2.3:
|
||||
typescript@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
|
||||
integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
|
||||
|
||||
typescript@~4.2.3:
|
||||
version "4.2.4"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961"
|
||||
integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==
|
||||
|
||||
ua-parser-js@^0.7.18:
|
||||
version "0.7.27"
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.27.tgz#b54f8ce9eb6c7abf3584edeaf9a3d8b3bd92edba"
|
||||
@@ -17856,18 +17957,6 @@ update-notifier@^4.1.0:
|
||||
semver-diff "^3.1.1"
|
||||
xdg-basedir "^4.0.0"
|
||||
|
||||
upper-case-first@^1.1.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115"
|
||||
integrity sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU=
|
||||
dependencies:
|
||||
upper-case "^1.1.1"
|
||||
|
||||
upper-case@^1.1.1:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
|
||||
integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
|
||||
@@ -18737,24 +18826,24 @@ xdg-basedir@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
|
||||
integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
|
||||
|
||||
xdl@59.0.29:
|
||||
version "59.0.29"
|
||||
resolved "https://registry.yarnpkg.com/xdl/-/xdl-59.0.29.tgz#ae2fa01dbf195d3e2d4f91cf6ad61277c4323fa7"
|
||||
integrity sha512-f593BX+PUoAy/Sz+OG6GFGhobKKWbi5nzW9Pph9RKZK3ovpiKc+dVX8zNY+bJxWCkgPCv7c11smb/8mOPsWAQA==
|
||||
xdl@59.0.35:
|
||||
version "59.0.35"
|
||||
resolved "https://registry.yarnpkg.com/xdl/-/xdl-59.0.35.tgz#70f807e1c2930260563ca0169bca9f80abb6ef84"
|
||||
integrity sha512-nXVjmVKLA/Z+jxhXQm9ZFwaLPehbg0xxT0tW9IbeDVK/QqXbrX2UV/qHdTw9Qa95rxHqaccjX2qbPPgRQqa/Rg==
|
||||
dependencies:
|
||||
"@expo/bunyan" "4.0.0"
|
||||
"@expo/config" "3.3.34"
|
||||
"@expo/dev-server" "0.1.60"
|
||||
"@expo/config" "3.3.39"
|
||||
"@expo/config-plugins" "1.0.29"
|
||||
"@expo/dev-server" "0.1.65"
|
||||
"@expo/devcert" "^1.0.0"
|
||||
"@expo/json-file" "8.2.28"
|
||||
"@expo/osascript" "2.0.25"
|
||||
"@expo/package-manager" "0.0.39"
|
||||
"@expo/json-file" "8.2.29"
|
||||
"@expo/osascript" "2.0.27"
|
||||
"@expo/package-manager" "0.0.42"
|
||||
"@expo/plist" "0.0.12"
|
||||
"@expo/schemer" "1.3.27"
|
||||
"@expo/schemer" "1.3.28"
|
||||
"@expo/spawn-async" "1.5.0"
|
||||
"@expo/webpack-config" "0.12.64"
|
||||
"@expo/webpack-config" "0.12.69"
|
||||
"@hapi/joi" "^17.1.1"
|
||||
"@types/text-table" "^0.2.1"
|
||||
analytics-node "3.5.0"
|
||||
axios "0.21.1"
|
||||
boxen "4.1.0"
|
||||
@@ -18768,7 +18857,6 @@ xdl@59.0.29:
|
||||
getenv "^1.0.0"
|
||||
glob "7.1.6"
|
||||
hasbin "1.2.3"
|
||||
indent-string "3.2.0"
|
||||
internal-ip "4.3.0"
|
||||
is-reachable "^4.0.0"
|
||||
is-root "^2.1.0"
|
||||
@@ -18778,25 +18866,22 @@ xdl@59.0.29:
|
||||
md5hex "1.0.0"
|
||||
minimatch "3.0.4"
|
||||
mv "2.1.1"
|
||||
ncp "2.0.0"
|
||||
node-forge "0.10.0"
|
||||
p-map "3.0.0"
|
||||
p-retry "4.1.0"
|
||||
p-timeout "3.1.0"
|
||||
package-json "6.4.0"
|
||||
pascal-case "2.0.1"
|
||||
pretty-bytes "^5.3.0"
|
||||
probe-image-size "~6.0.0"
|
||||
progress "2.0.3"
|
||||
prompts "^2.3.2"
|
||||
raven "2.6.3"
|
||||
react-dev-utils "~11.0.1"
|
||||
replace-string "1.1.0"
|
||||
requireg "^0.2.2"
|
||||
resolve-from "^5.0.0"
|
||||
semver "7.3.2"
|
||||
serialize-error "6.0.0"
|
||||
slugid "1.1.0"
|
||||
slugify "^1.3.6"
|
||||
source-map-support "0.4.18"
|
||||
split "1.0.1"
|
||||
strip-ansi "^6.0.0"
|
||||
@@ -18808,6 +18893,7 @@ xdl@59.0.29:
|
||||
uuid "3.3.2"
|
||||
webpack "4.43.0"
|
||||
webpack-dev-server "3.11.0"
|
||||
wrap-ansi "^7.0.0"
|
||||
|
||||
xhr@^2.0.1:
|
||||
version "2.6.0"
|
||||
|
||||
Reference in New Issue
Block a user