mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-13 09:30:30 +08:00
Compare commits
34 Commits
@react-nav
...
@react-nav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
853740bfaf | ||
|
|
179b6312fe | ||
|
|
043924ca48 | ||
|
|
813a5903b5 | ||
|
|
3709e652f4 | ||
|
|
5b15c7164f | ||
|
|
e030932497 | ||
|
|
adbfedcd58 | ||
|
|
bc9b044fb3 | ||
|
|
f24d3a3461 | ||
|
|
3df65e2819 | ||
|
|
5c4afc5cb4 | ||
|
|
d5bb357053 | ||
|
|
b1fe73097f | ||
|
|
49f6fed6d3 | ||
|
|
b1a65fc73e | ||
|
|
3ea8eec432 | ||
|
|
00e0f05190 | ||
|
|
193c344ba5 | ||
|
|
358d9e9feb | ||
|
|
6a5d0a035a | ||
|
|
b75744abd5 | ||
|
|
6dbda1a0c2 | ||
|
|
70029d6c13 | ||
|
|
469d0542c7 | ||
|
|
0dcaea3242 | ||
|
|
646cbfb28e | ||
|
|
660cac3557 | ||
|
|
e637250a7e | ||
|
|
82af7bed71 | ||
|
|
cb46d0bca4 | ||
|
|
b3665a325d | ||
|
|
0cc7a12b9c | ||
|
|
90e417248d |
2
.github/workflows/expo-preview.yml
vendored
2
.github/workflows/expo-preview.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
|
||||
- name: Get expo link
|
||||
id: expo
|
||||
run: echo "::set-output name=path::@react-navigation/react-react-navigationample?release-channel=pr-${{ github.event.number }}"
|
||||
run: echo "::set-output name=path::@react-navigation/react-navigation-example?release-channel=pr-${{ github.event.number }}"
|
||||
|
||||
- name: Comment on PR
|
||||
uses: unsplash/comment-on-pr@master
|
||||
|
||||
20
.github/workflows/stale.yml
vendored
20
.github/workflows/stale.yml
vendored
@@ -1,20 +0,0 @@
|
||||
name: "Close stale issues and pull requests"
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'Hello 👋, this issue has been open for more than 3 months with no activity on it. If the issue is still present in the latest version, please leave a comment within 7 days to keep it open, otherwise it will be closed automatically. If you found a solution on workaround for the issue, please comment here for others to find. If this issue is critical for you, please consider sending a pull request to fix the issue.'
|
||||
stale-pr-message: 'Hello 👋, this pull request has been open for more than 3 months with no activity on it. If you think this is still necessary with the latest version, please comment and ping a maintainer to get this reviewed, otherwise it will be closed automatically in 7 days.'
|
||||
days-before-stale: 90
|
||||
days-before-close: 7
|
||||
stale-issue-label: 'stale'
|
||||
stale-pr-label: 'stale'
|
||||
exempt-issue-label: 'keep open'
|
||||
exempt-pr-label: 'keep open'
|
||||
11
.github/workflows/triage.yml
vendored
11
.github/workflows/triage.yml
vendored
@@ -36,3 +36,14 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
args: comment "Hey! Thanks for opening the issue. The issue tracker is intended for only tracking bug reports. This helps us prioritize fixing bugs in the library. Seems you have a usage question. Please ask the question on [StackOverflow](https://stackoverflow.com/questions/tagged/react-navigation) instead using the `react-navigation` label. You can also chat with other community members on [Reactiflux Discord server](https://www.reactiflux.com/) in the `#react-navigation` channel."
|
||||
|
||||
feature-request:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.label.name == 'feature-request'
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/github@v1.0.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
args: comment "Hey! Thanks for opening the issue. The issue tracker is intended for only tracking bug reports. Seems you have a feature request. Please post the feature request on [Canny](https://react-navigation.canny.io/feature-requests). This lets other users upvote your feature request and helps us prioritize the most requested features."
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@
|
||||
.gradle
|
||||
.project
|
||||
.settings
|
||||
.history
|
||||
|
||||
local.properties
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = function(api) {
|
||||
module.exports = function (api) {
|
||||
api.cache(true);
|
||||
return {
|
||||
presets: ['babel-preset-expo'],
|
||||
|
||||
@@ -15,8 +15,8 @@ const modules = ['@expo/vector-icons']
|
||||
// List all packages under `packages/`
|
||||
.readdirSync(packages)
|
||||
// Ignore hidden files such as .DS_Store
|
||||
.filter(p => !p.startsWith('.'))
|
||||
.map(p => {
|
||||
.filter((p) => !p.startsWith('.'))
|
||||
.map((p) => {
|
||||
const pak = JSON.parse(
|
||||
fs.readFileSync(path.join(packages, p, 'package.json'), 'utf8')
|
||||
);
|
||||
@@ -50,9 +50,9 @@ module.exports = {
|
||||
blacklistRE: blacklist(
|
||||
fs
|
||||
.readdirSync(packages)
|
||||
.map(p => path.join(packages, p))
|
||||
.map((p) => path.join(packages, p))
|
||||
.map(
|
||||
it => new RegExp(`^${escape(path.join(it, 'node_modules'))}\\/.*$`)
|
||||
(it) => new RegExp(`^${escape(path.join(it, 'node_modules'))}\\/.*$`)
|
||||
)
|
||||
),
|
||||
|
||||
@@ -65,7 +65,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
server: {
|
||||
enhanceMiddleware: middleware => {
|
||||
enhanceMiddleware: (middleware) => {
|
||||
return (req, res, next) => {
|
||||
// When an asset is imported outside the project root, it has wrong path on Android
|
||||
// This happens for the back button in stack, so we fix the path to correct one
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/vector-icons": "^10.0.0",
|
||||
"@react-native-community/masked-view": "0.1.6",
|
||||
"@react-native-community/masked-view": "0.1.7",
|
||||
"@types/react-native-restart": "^0.0.0",
|
||||
"color": "^3.1.2",
|
||||
"expo": "^36.0.2",
|
||||
@@ -21,22 +21,22 @@
|
||||
"react": "~16.9.0",
|
||||
"react-dom": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-gesture-handler": "^1.5.6",
|
||||
"react-native-gesture-handler": "^1.6.0",
|
||||
"react-native-paper": "^3.6.0",
|
||||
"react-native-reanimated": "^1.7.0",
|
||||
"react-native-restart": "^0.0.13",
|
||||
"react-native-safe-area-context": "^0.7.2",
|
||||
"react-native-screens": "^2.0.0-beta.2",
|
||||
"react-native-restart": "^0.0.14",
|
||||
"react-native-safe-area-context": "^0.7.3",
|
||||
"react-native-screens": "^2.3.0",
|
||||
"react-native-tab-view": "2.13.0",
|
||||
"react-native-unimodules": "^0.7.0",
|
||||
"react-native-web": "^0.11.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@expo/webpack-config": "^0.10.12",
|
||||
"@types/react": "^16.9.19",
|
||||
"@types/react-native": "^0.60.30",
|
||||
"@expo/webpack-config": "^0.11.7",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-native": "^0.61.22",
|
||||
"babel-preset-expo": "^8.0.0",
|
||||
"expo-cli": "^3.11.9",
|
||||
"expo-cli": "^3.13.8",
|
||||
"typescript": "^3.7.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function BottomTabsScreen() {
|
||||
return (
|
||||
<BottomTabs.Navigator
|
||||
screenOptions={{
|
||||
tabBarButton: props => <TouchableBounce {...props} />,
|
||||
tabBarButton: (props) => <TouchableBounce {...props} />,
|
||||
}}
|
||||
>
|
||||
<BottomTabs.Screen
|
||||
@@ -38,7 +38,7 @@ export default function BottomTabsScreen() {
|
||||
tabBarIcon: getTabBarIcon('file-document-box'),
|
||||
}}
|
||||
>
|
||||
{props => <SimpleStackScreen {...props} headerMode="none" />}
|
||||
{(props) => <SimpleStackScreen {...props} headerMode="none" />}
|
||||
</BottomTabs.Screen>
|
||||
<BottomTabs.Screen
|
||||
name="Chat"
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function BottomTabsScreen() {
|
||||
|
||||
return (
|
||||
<BottomTabs.Navigator>
|
||||
{tabs.map(i => (
|
||||
{tabs.map((i) => (
|
||||
<BottomTabs.Screen
|
||||
key={i}
|
||||
name={`tab-${i}`}
|
||||
@@ -29,12 +29,14 @@ export default function BottomTabsScreen() {
|
||||
{() => (
|
||||
<View style={styles.container}>
|
||||
<Title>Tab {i}</Title>
|
||||
<Button onPress={() => setTabs(tabs => [...tabs, tabs.length])}>
|
||||
<Button onPress={() => setTabs((tabs) => [...tabs, tabs.length])}>
|
||||
Add a tab
|
||||
</Button>
|
||||
<Button
|
||||
onPress={() =>
|
||||
setTabs(tabs => (tabs.length > 1 ? tabs.slice(0, -1) : tabs))
|
||||
setTabs((tabs) =>
|
||||
tabs.length > 1 ? tabs.slice(0, -1) : tabs
|
||||
)
|
||||
}
|
||||
>
|
||||
Remove a tab
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function MaterialBottomTabsScreen() {
|
||||
tabBarColor: '#C9E7F8',
|
||||
}}
|
||||
>
|
||||
{props => <SimpleStackScreen {...props} headerMode="none" />}
|
||||
{(props) => <SimpleStackScreen {...props} headerMode="none" />}
|
||||
</MaterialBottomTabs.Screen>
|
||||
<MaterialBottomTabs.Screen
|
||||
name="Chat"
|
||||
|
||||
@@ -68,10 +68,7 @@ export default function Chat(props: Partial<ScrollViewProps>) {
|
||||
styles.input,
|
||||
{ backgroundColor: colors.card, color: colors.text },
|
||||
]}
|
||||
placeholderTextColor={Color(colors.text)
|
||||
.alpha(0.5)
|
||||
.rgb()
|
||||
.string()}
|
||||
placeholderTextColor={Color(colors.text).alpha(0.5).rgb().string()}
|
||||
placeholder="Write a message"
|
||||
underlineColorAndroid="transparent"
|
||||
/>
|
||||
|
||||
@@ -51,10 +51,7 @@ export default function NewsFeed(props: Props) {
|
||||
<Card style={styles.card}>
|
||||
<TextInput
|
||||
placeholder="What's on your mind?"
|
||||
placeholderTextColor={Color(colors.text)
|
||||
.alpha(0.5)
|
||||
.rgb()
|
||||
.string()}
|
||||
placeholderTextColor={Color(colors.text).alpha(0.5).rgb().string()}
|
||||
style={styles.input}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
Platform,
|
||||
StatusBar,
|
||||
I18nManager,
|
||||
Dimensions,
|
||||
ScaledSize,
|
||||
} from 'react-native';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { enableScreens } from 'react-native-screens';
|
||||
@@ -196,10 +198,24 @@ export default function App() {
|
||||
};
|
||||
}, [theme.colors, theme.dark]);
|
||||
|
||||
const [dimensions, setDimensions] = React.useState(Dimensions.get('window'));
|
||||
|
||||
React.useEffect(() => {
|
||||
const onDimensionsChange = ({ window }: { window: ScaledSize }) => {
|
||||
setDimensions(window);
|
||||
};
|
||||
|
||||
Dimensions.addEventListener('change', onDimensionsChange);
|
||||
|
||||
return () => Dimensions.removeEventListener('change', onDimensionsChange);
|
||||
}, []);
|
||||
|
||||
if (!isReady) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isLargeScreen = dimensions.width > 900;
|
||||
|
||||
return (
|
||||
<PaperProvider theme={paperTheme}>
|
||||
{Platform.OS === 'ios' && (
|
||||
@@ -208,7 +224,7 @@ export default function App() {
|
||||
<NavigationContainer
|
||||
ref={containerRef}
|
||||
initialState={initialState}
|
||||
onStateChange={state =>
|
||||
onStateChange={(state) =>
|
||||
AsyncStorage.setItem(
|
||||
NAVIGATION_PERSISTENCE_KEY,
|
||||
JSON.stringify(state)
|
||||
@@ -216,7 +232,7 @@ export default function App() {
|
||||
}
|
||||
theme={theme}
|
||||
>
|
||||
<Drawer.Navigator>
|
||||
<Drawer.Navigator drawerType={isLargeScreen ? 'permanent' : undefined}>
|
||||
<Drawer.Screen
|
||||
name="Root"
|
||||
options={{
|
||||
@@ -240,13 +256,15 @@ export default function App() {
|
||||
name="Home"
|
||||
options={{
|
||||
title: 'Examples',
|
||||
headerLeft: () => (
|
||||
<Appbar.Action
|
||||
color={theme.colors.text}
|
||||
icon="menu"
|
||||
onPress={() => navigation.toggleDrawer()}
|
||||
/>
|
||||
),
|
||||
headerLeft: isLargeScreen
|
||||
? undefined
|
||||
: () => (
|
||||
<Appbar.Action
|
||||
color={theme.colors.text}
|
||||
icon="menu"
|
||||
onPress={() => navigation.toggleDrawer()}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{({
|
||||
@@ -280,12 +298,12 @@ export default function App() {
|
||||
theme.dark ? 'light' : 'dark'
|
||||
);
|
||||
|
||||
setTheme(t => (t.dark ? DefaultTheme : DarkTheme));
|
||||
setTheme((t) => (t.dark ? DefaultTheme : DarkTheme));
|
||||
}}
|
||||
/>
|
||||
<Divider />
|
||||
{(Object.keys(SCREENS) as (keyof typeof SCREENS)[]).map(
|
||||
name => (
|
||||
(name) => (
|
||||
<List.Item
|
||||
key={name}
|
||||
title={SCREENS[name].title}
|
||||
@@ -297,7 +315,7 @@ export default function App() {
|
||||
)}
|
||||
</Stack.Screen>
|
||||
{(Object.keys(SCREENS) as (keyof typeof SCREENS)[]).map(
|
||||
name => (
|
||||
(name) => (
|
||||
<Stack.Screen
|
||||
key={name}
|
||||
name={name}
|
||||
|
||||
@@ -7,7 +7,7 @@ const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||
const node_modules = path.resolve(__dirname, '..', 'node_modules');
|
||||
const packages = path.resolve(__dirname, '..', 'packages');
|
||||
|
||||
module.exports = async function(env, argv) {
|
||||
module.exports = async function (env, argv) {
|
||||
const config = await createExpoWebpackConfigAsync(env, argv);
|
||||
|
||||
config.context = path.resolve(__dirname, '..');
|
||||
@@ -20,7 +20,7 @@ module.exports = async function(env, argv) {
|
||||
});
|
||||
|
||||
config.resolve.plugins = config.resolve.plugins.filter(
|
||||
p => !(p instanceof ModuleScopePlugin)
|
||||
(p) => !(p instanceof ModuleScopePlugin)
|
||||
);
|
||||
|
||||
Object.assign(config.resolve.alias, {
|
||||
@@ -30,7 +30,7 @@ module.exports = async function(env, argv) {
|
||||
'@expo/vector-icons': path.resolve(node_modules, '@expo/vector-icons'),
|
||||
});
|
||||
|
||||
fs.readdirSync(packages).forEach(name => {
|
||||
fs.readdirSync(packages).forEach((name) => {
|
||||
config.resolve.alias[`@react-navigation/${name}`] = path.resolve(
|
||||
packages,
|
||||
name,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const error = console.error;
|
||||
|
||||
console.error = (...args) =>
|
||||
// Supress error messages regarding error boundary in tests
|
||||
// Suppress error messages regarding error boundary in tests
|
||||
/(Consider adding an error boundary to your tree to customize error handling behavior|React will try to recreate this component tree from scratch using the error boundary you provided|Error boundaries should implement getDerivedStateFromError)/m.test(
|
||||
args[0]
|
||||
)
|
||||
|
||||
12
package.json
12
package.json
@@ -27,23 +27,23 @@
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.8.3",
|
||||
"@babel/preset-env": "^7.8.4",
|
||||
"@babel/preset-env": "^7.8.7",
|
||||
"@babel/preset-flow": "^7.8.3",
|
||||
"@babel/preset-react": "^7.8.3",
|
||||
"@babel/preset-typescript": "^7.8.3",
|
||||
"@babel/runtime": "^7.8.4",
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"@commitlint/config-conventional": "^8.3.4",
|
||||
"@types/jest": "^25.1.2",
|
||||
"@types/jest": "^25.1.4",
|
||||
"codecov": "^3.6.5",
|
||||
"commitlint": "^8.3.5",
|
||||
"core-js": "^3.6.4",
|
||||
"detox": "^15.1.4",
|
||||
"detox": "^16.0.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-satya164": "^3.1.5",
|
||||
"husky": "^4.2.1",
|
||||
"husky": "^4.2.3",
|
||||
"jest": "^25.1.0",
|
||||
"lerna": "^3.20.2",
|
||||
"prettier": "^1.19.1",
|
||||
"prettier": "^2.0.1",
|
||||
"typescript": "^3.7.5"
|
||||
},
|
||||
"resolutions": {
|
||||
|
||||
@@ -3,6 +3,45 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.2.3](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.2...@react-navigation/bottom-tabs@5.2.3) (2020-03-22)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.2](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.1...@react-navigation/bottom-tabs@5.2.2) (2020-03-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* don't use react-native-screens on web ([b1a65fc](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/b1a65fc73e8603ae2c06ef101a74df31e80bb9b2)), closes [#7485](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/issues/7485)
|
||||
* initialize height and width to zero if undefined ([3df65e2](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/3df65e28197db3bb8371059146546d57661c5ba3)), closes [#6789](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/issues/6789)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.1](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.0...@react-navigation/bottom-tabs@5.2.1) (2020-03-17)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.2.0](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.1.1...@react-navigation/bottom-tabs@5.2.0) (2020-03-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add safeAreaInsets to bottom tabs ([82af7be](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/82af7bed7135e42e24693b48cf7f1c6f9f5a6981))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.1](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.1.0...@react-navigation/bottom-tabs@5.1.1) (2020-03-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/bottom-tabs",
|
||||
"description": "Bottom tab navigator following iOS design guidelines",
|
||||
"version": "5.1.1",
|
||||
"version": "5.2.3",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -34,22 +34,24 @@
|
||||
"react-native-iphone-x-helper": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.9.3",
|
||||
"@react-navigation/native": "^5.0.9",
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@react-navigation/native": "^5.1.2",
|
||||
"@types/color": "^3.0.1",
|
||||
"@types/react": "^16.9.19",
|
||||
"@types/react-native": "^0.60.30",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-native": "^0.61.22",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-safe-area-context": "^0.7.2",
|
||||
"react-native-safe-area-context": "^0.7.3",
|
||||
"react-native-screens": "^2.3.0",
|
||||
"typescript": "^3.7.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.5",
|
||||
"react": "*",
|
||||
"react-native": "*",
|
||||
"react-native-safe-area-context": ">= 0.6.0"
|
||||
"react-native-safe-area-context": ">= 0.6.0",
|
||||
"react-native-screens": ">= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0"
|
||||
},
|
||||
"@react-native-community/bob": {
|
||||
"source": "src",
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
ParamListBase,
|
||||
Descriptor,
|
||||
TabNavigationState,
|
||||
TabActionHelpers,
|
||||
} from '@react-navigation/native';
|
||||
|
||||
export type BottomTabNavigationEventMap = {
|
||||
@@ -40,19 +41,8 @@ export type BottomTabNavigationProp<
|
||||
TabNavigationState,
|
||||
BottomTabNavigationOptions,
|
||||
BottomTabNavigationEventMap
|
||||
> & {
|
||||
/**
|
||||
* Jump to an existing tab.
|
||||
*
|
||||
* @param name Name of the route for the tab.
|
||||
* @param [params] Params object for the route.
|
||||
*/
|
||||
jumpTo<RouteName extends Extract<keyof ParamList, string>>(
|
||||
...args: ParamList[RouteName] extends undefined | any
|
||||
? [RouteName] | [RouteName, ParamList[RouteName]]
|
||||
: [RouteName, ParamList[RouteName]]
|
||||
): void;
|
||||
};
|
||||
> &
|
||||
TabActionHelpers<ParamList>;
|
||||
|
||||
export type BottomTabNavigationOptions = {
|
||||
/**
|
||||
@@ -184,6 +174,16 @@ export type BottomTabBarOptions = {
|
||||
* Whether the label position should adapt to the orientation.
|
||||
*/
|
||||
adaptive?: boolean;
|
||||
/**
|
||||
* Safe area insets for the tab bar. This is used to avoid elements like the navigation bar on Android and bottom safe area on iOS.
|
||||
* By default, the device's safe area insets are automatically detected. You can override the behavior with this option.
|
||||
*/
|
||||
safeAreaInsets?: {
|
||||
top?: number;
|
||||
right?: number;
|
||||
bottom?: number;
|
||||
left?: number;
|
||||
};
|
||||
/**
|
||||
* Style object for the tab bar container.
|
||||
*/
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
CommonActions,
|
||||
useTheme,
|
||||
} from '@react-navigation/native';
|
||||
import { SafeAreaConsumer } from 'react-native-safe-area-context';
|
||||
import { useSafeArea } from 'react-native-safe-area-context';
|
||||
|
||||
import BottomTabItem from './BottomTabItem';
|
||||
import { BottomTabBarProps } from '../types';
|
||||
@@ -43,6 +43,7 @@ export default function BottomTabBar({
|
||||
keyboardHidesTabBar = false,
|
||||
labelPosition,
|
||||
labelStyle,
|
||||
safeAreaInsets,
|
||||
showIcon,
|
||||
showLabel,
|
||||
style,
|
||||
@@ -50,7 +51,12 @@ export default function BottomTabBar({
|
||||
}: Props) {
|
||||
const { colors } = useTheme();
|
||||
|
||||
const [dimensions, setDimensions] = React.useState(Dimensions.get('window'));
|
||||
const [dimensions, setDimensions] = React.useState(() => {
|
||||
const { height = 0, width = 0 } = Dimensions.get('window');
|
||||
|
||||
return { height, width };
|
||||
});
|
||||
|
||||
const [layout, setLayout] = React.useState({
|
||||
height: 0,
|
||||
width: dimensions.width,
|
||||
@@ -115,7 +121,7 @@ export default function BottomTabBar({
|
||||
const handleLayout = (e: LayoutChangeEvent) => {
|
||||
const { height, width } = e.nativeEvent.layout;
|
||||
|
||||
setLayout(layout => {
|
||||
setLayout((layout) => {
|
||||
if (height === layout.height && width === layout.width) {
|
||||
return layout;
|
||||
} else {
|
||||
@@ -158,116 +164,122 @@ export default function BottomTabBar({
|
||||
}
|
||||
};
|
||||
|
||||
const defaultInsets = useSafeArea();
|
||||
|
||||
const insets = {
|
||||
top: safeAreaInsets?.top ?? defaultInsets.top,
|
||||
right: safeAreaInsets?.right ?? defaultInsets.right,
|
||||
bottom: safeAreaInsets?.bottom ?? defaultInsets.bottom,
|
||||
left: safeAreaInsets?.left ?? defaultInsets.left,
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaConsumer>
|
||||
{insets => (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.tabBar,
|
||||
{
|
||||
backgroundColor: colors.card,
|
||||
borderTopColor: colors.border,
|
||||
},
|
||||
keyboardHidesTabBar
|
||||
? {
|
||||
// When the keyboard is shown, slide down the tab bar
|
||||
transform: [
|
||||
{
|
||||
translateY: visible.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [layout.height, 0],
|
||||
}),
|
||||
},
|
||||
],
|
||||
// Absolutely position the tab bar so that the content is below it
|
||||
// This is needed to avoid gap at bottom when the tab bar is hidden
|
||||
position: keyboardShown ? 'absolute' : null,
|
||||
}
|
||||
: null,
|
||||
{
|
||||
height: DEFAULT_TABBAR_HEIGHT + (insets ? insets.bottom : 0),
|
||||
paddingBottom: insets ? insets.bottom : 0,
|
||||
},
|
||||
style,
|
||||
]}
|
||||
pointerEvents={keyboardHidesTabBar && keyboardShown ? 'none' : 'auto'}
|
||||
>
|
||||
<View style={styles.content} onLayout={handleLayout}>
|
||||
{routes.map((route, index) => {
|
||||
const focused = index === state.index;
|
||||
const { options } = descriptors[route.key];
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.tabBar,
|
||||
{
|
||||
backgroundColor: colors.card,
|
||||
borderTopColor: colors.border,
|
||||
},
|
||||
keyboardHidesTabBar
|
||||
? {
|
||||
// When the keyboard is shown, slide down the tab bar
|
||||
transform: [
|
||||
{
|
||||
translateY: visible.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [layout.height, 0],
|
||||
}),
|
||||
},
|
||||
],
|
||||
// Absolutely position the tab bar so that the content is below it
|
||||
// This is needed to avoid gap at bottom when the tab bar is hidden
|
||||
position: keyboardShown ? 'absolute' : null,
|
||||
}
|
||||
: null,
|
||||
{
|
||||
height: DEFAULT_TABBAR_HEIGHT + insets.bottom,
|
||||
paddingBottom: insets.bottom,
|
||||
paddingHorizontal: Math.max(insets.left, insets.right),
|
||||
},
|
||||
style,
|
||||
]}
|
||||
pointerEvents={keyboardHidesTabBar && keyboardShown ? 'none' : 'auto'}
|
||||
>
|
||||
<View style={styles.content} onLayout={handleLayout}>
|
||||
{routes.map((route, index) => {
|
||||
const focused = index === state.index;
|
||||
const { options } = descriptors[route.key];
|
||||
|
||||
const onPress = () => {
|
||||
const event = navigation.emit({
|
||||
type: 'tabPress',
|
||||
target: route.key,
|
||||
canPreventDefault: true,
|
||||
});
|
||||
const onPress = () => {
|
||||
const event = navigation.emit({
|
||||
type: 'tabPress',
|
||||
target: route.key,
|
||||
canPreventDefault: true,
|
||||
});
|
||||
|
||||
if (!focused && !event.defaultPrevented) {
|
||||
navigation.dispatch({
|
||||
...CommonActions.navigate(route.name),
|
||||
target: state.key,
|
||||
});
|
||||
}
|
||||
};
|
||||
if (!focused && !event.defaultPrevented) {
|
||||
navigation.dispatch({
|
||||
...CommonActions.navigate(route.name),
|
||||
target: state.key,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onLongPress = () => {
|
||||
navigation.emit({
|
||||
type: 'tabLongPress',
|
||||
target: route.key,
|
||||
});
|
||||
};
|
||||
const onLongPress = () => {
|
||||
navigation.emit({
|
||||
type: 'tabLongPress',
|
||||
target: route.key,
|
||||
});
|
||||
};
|
||||
|
||||
const label =
|
||||
options.tabBarLabel !== undefined
|
||||
? options.tabBarLabel
|
||||
: options.title !== undefined
|
||||
? options.title
|
||||
: route.name;
|
||||
const label =
|
||||
options.tabBarLabel !== undefined
|
||||
? options.tabBarLabel
|
||||
: options.title !== undefined
|
||||
? options.title
|
||||
: route.name;
|
||||
|
||||
const accessibilityLabel =
|
||||
options.tabBarAccessibilityLabel !== undefined
|
||||
? options.tabBarAccessibilityLabel
|
||||
: typeof label === 'string'
|
||||
? `${label}, tab, ${index + 1} of ${routes.length}`
|
||||
: undefined;
|
||||
const accessibilityLabel =
|
||||
options.tabBarAccessibilityLabel !== undefined
|
||||
? options.tabBarAccessibilityLabel
|
||||
: typeof label === 'string'
|
||||
? `${label}, tab, ${index + 1} of ${routes.length}`
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<NavigationContext.Provider
|
||||
key={route.key}
|
||||
value={descriptors[route.key].navigation}
|
||||
>
|
||||
<NavigationRouteContext.Provider value={route}>
|
||||
<BottomTabItem
|
||||
route={route}
|
||||
focused={focused}
|
||||
horizontal={shouldUseHorizontalLabels()}
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
testID={options.tabBarTestID}
|
||||
allowFontScaling={allowFontScaling}
|
||||
activeTintColor={activeTintColor}
|
||||
inactiveTintColor={inactiveTintColor}
|
||||
activeBackgroundColor={activeBackgroundColor}
|
||||
inactiveBackgroundColor={inactiveBackgroundColor}
|
||||
button={options.tabBarButton}
|
||||
icon={options.tabBarIcon}
|
||||
label={label}
|
||||
showIcon={showIcon}
|
||||
showLabel={showLabel}
|
||||
labelStyle={labelStyle}
|
||||
style={tabStyle}
|
||||
/>
|
||||
</NavigationRouteContext.Provider>
|
||||
</NavigationContext.Provider>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</Animated.View>
|
||||
)}
|
||||
</SafeAreaConsumer>
|
||||
return (
|
||||
<NavigationContext.Provider
|
||||
key={route.key}
|
||||
value={descriptors[route.key].navigation}
|
||||
>
|
||||
<NavigationRouteContext.Provider value={route}>
|
||||
<BottomTabItem
|
||||
route={route}
|
||||
focused={focused}
|
||||
horizontal={shouldUseHorizontalLabels()}
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
testID={options.tabBarTestID}
|
||||
allowFontScaling={allowFontScaling}
|
||||
activeTintColor={activeTintColor}
|
||||
inactiveTintColor={inactiveTintColor}
|
||||
activeBackgroundColor={activeBackgroundColor}
|
||||
inactiveBackgroundColor={inactiveBackgroundColor}
|
||||
button={options.tabBarButton}
|
||||
icon={options.tabBarIcon}
|
||||
label={label}
|
||||
showIcon={showIcon}
|
||||
showLabel={showLabel}
|
||||
labelStyle={labelStyle}
|
||||
style={tabStyle}
|
||||
/>
|
||||
</NavigationRouteContext.Provider>
|
||||
</NavigationContext.Provider>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -133,9 +133,7 @@ export default function BottomTabBarItem({
|
||||
|
||||
const inactiveTintColor =
|
||||
customInactiveTintColor === undefined
|
||||
? Color(colors.text)
|
||||
.mix(Color(colors.card), 0.5)
|
||||
.hex()
|
||||
? Color(colors.text).mix(Color(colors.card), 0.5).hex()
|
||||
: customInactiveTintColor;
|
||||
|
||||
const renderLabel = ({ focused }: { focused: boolean }) => {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { Platform, StyleSheet, View } from 'react-native';
|
||||
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { Screen, screensEnabled } from 'react-native-screens';
|
||||
|
||||
@@ -10,12 +9,14 @@ type Props = {
|
||||
style?: any;
|
||||
};
|
||||
|
||||
const FAR_FAR_AWAY = 3000; // this should be big enough to move the whole view out of its container
|
||||
const FAR_FAR_AWAY = 30000; // this should be big enough to move the whole view out of its container
|
||||
|
||||
export default class ResourceSavingScene extends React.Component<Props> {
|
||||
render() {
|
||||
if (screensEnabled?.()) {
|
||||
// react-native-screens is buggy on web
|
||||
if (screensEnabled?.() && Platform.OS !== 'web') {
|
||||
const { isVisible, ...rest } = this.props;
|
||||
|
||||
// @ts-ignore
|
||||
return <Screen active={isVisible ? 1 : 0} {...rest} />;
|
||||
}
|
||||
@@ -24,7 +25,13 @@ export default class ResourceSavingScene extends React.Component<Props> {
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[styles.container, style, { opacity: isVisible ? 1 : 0 }]}
|
||||
style={[
|
||||
styles.container,
|
||||
Platform.OS === 'web'
|
||||
? { display: isVisible ? 'flex' : 'none' }
|
||||
: null,
|
||||
style,
|
||||
]}
|
||||
collapsable={false}
|
||||
removeClippedSubviews={
|
||||
// On iOS, set removeClippedSubviews to true only when not focused
|
||||
|
||||
@@ -30,7 +30,7 @@ type Props = {
|
||||
export default function SafeAreaProviderCompat({ children }: Props) {
|
||||
return (
|
||||
<SafeAreaConsumer>
|
||||
{insets => {
|
||||
{(insets) => {
|
||||
if (insets) {
|
||||
// If we already have insets, don't wrap the stack in another safe area provider
|
||||
// This avoids an issue with updates at the cost of potentially incorrect values
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.1.5](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.4...@react-navigation/compat@5.1.5) (2020-03-22)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.4](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.3...@react-navigation/compat@5.1.4) (2020-03-19)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.3](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.2...@react-navigation/compat@5.1.3) (2020-03-17)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.2](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.1...@react-navigation/compat@5.1.2) (2020-03-16)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.1](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.0...@react-navigation/compat@5.1.1) (2020-03-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/compat",
|
||||
"description": "Compatibility layer to write navigator definitions in static configuration format",
|
||||
"version": "5.1.1",
|
||||
"version": "5.1.5",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/compat",
|
||||
"bugs": {
|
||||
@@ -25,9 +25,9 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.9.3",
|
||||
"@react-navigation/native": "^5.0.9",
|
||||
"@types/react": "^16.9.19",
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@react-navigation/native": "^5.1.2",
|
||||
"@types/react": "^16.9.23",
|
||||
"react": "~16.9.0",
|
||||
"typescript": "^3.7.5"
|
||||
},
|
||||
|
||||
@@ -69,7 +69,7 @@ export default function createCompatNavigatorFactory<
|
||||
function Navigator({ screenProps }: { screenProps?: unknown }) {
|
||||
const screens = React.useMemo(
|
||||
() =>
|
||||
routeNames.map(name => {
|
||||
routeNames.map((name) => {
|
||||
let getScreenComponent: () => CompatScreenType<NavigationPropType>;
|
||||
|
||||
let initialParams;
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function useCompatNavigation<
|
||||
const route = useRoute();
|
||||
|
||||
const isFirstRouteInParent = useNavigationState(
|
||||
state => state.routes[0].key === route.key
|
||||
(state) => state.routes[0].key === route.key
|
||||
);
|
||||
|
||||
const context = React.useRef<Record<string, any>>({});
|
||||
|
||||
@@ -26,8 +26,9 @@ export default function withNavigation<
|
||||
return <Comp ref={onRef} navigation={navigation} {...rest} />;
|
||||
};
|
||||
|
||||
WrappedComponent.displayName = `withNavigation(${Comp.displayName ||
|
||||
Comp.name})`;
|
||||
WrappedComponent.displayName = `withNavigation(${
|
||||
Comp.displayName || Comp.name
|
||||
})`;
|
||||
|
||||
return WrappedComponent;
|
||||
}
|
||||
|
||||
@@ -23,8 +23,9 @@ export default function withNavigationFocus<
|
||||
return <Comp ref={onRef} isFocused={isFocused} {...rest} />;
|
||||
};
|
||||
|
||||
WrappedComponent.displayName = `withNavigationFocus(${Comp.displayName ||
|
||||
Comp.name})`;
|
||||
WrappedComponent.displayName = `withNavigationFocus(${
|
||||
Comp.displayName || Comp.name
|
||||
})`;
|
||||
|
||||
return WrappedComponent;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.3.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.2.3...@react-navigation/core@5.3.0) (2020-03-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* return correct value for isFocused after changing screens ([5b15c71](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/5b15c7164f5503f2f0d51006a3f23bd0c58fd9b7)), closes [#7843](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/7843)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* support function in listeners prop ([3709e65](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/3709e652f41a16c2c2b05d5dbbe1da2017ba2c3f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.3](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.2.2...@react-navigation/core@5.2.3) (2020-03-19)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.2](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.2.1...@react-navigation/core@5.2.2) (2020-03-16)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.1](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.2.0...@react-navigation/core@5.2.1) (2020-03-03)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/core",
|
||||
"description": "Core utilities for building navigators",
|
||||
"version": "5.2.1",
|
||||
"version": "5.3.0",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-native",
|
||||
@@ -29,23 +29,23 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.1.0",
|
||||
"@react-navigation/routers": "^5.2.0",
|
||||
"escape-string-regexp": "^2.0.0",
|
||||
"query-string": "^6.10.1",
|
||||
"react-is": "^16.12.0",
|
||||
"query-string": "^6.11.1",
|
||||
"react-is": "^16.13.0",
|
||||
"shortid": "^2.2.15",
|
||||
"use-subscription": "^1.3.0"
|
||||
"use-subscription": "^1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.9.3",
|
||||
"@types/react": "^16.9.19",
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-is": "^16.7.1",
|
||||
"@types/shortid": "^0.0.29",
|
||||
"@types/use-subscription": "^1.0.0",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
"react-native-testing-library": "^1.12.0",
|
||||
"react-test-renderer": "~16.12.0",
|
||||
"react-test-renderer": "~16.9.0",
|
||||
"typescript": "^3.7.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -73,7 +73,7 @@ const getPartialState = (
|
||||
return {
|
||||
...partialState,
|
||||
stale: true,
|
||||
routes: state.routes.map(route => {
|
||||
routes: state.routes.map((route) => {
|
||||
if (route.state === undefined) {
|
||||
return route as Route<string> & {
|
||||
state?: PartialState<NavigationState>;
|
||||
@@ -136,6 +136,7 @@ const BaseNavigationContainer = React.forwardRef(
|
||||
);
|
||||
|
||||
const { trackState, trackAction } = useDevTools({
|
||||
enabled: false,
|
||||
name: '@react-navigation',
|
||||
reset,
|
||||
state,
|
||||
@@ -155,7 +156,7 @@ const BaseNavigationContainer = React.forwardRef(
|
||||
throw new Error(NOT_INITIALIZED_ERROR);
|
||||
}
|
||||
|
||||
listeners[0](navigation => navigation.dispatch(action));
|
||||
listeners[0]((navigation) => navigation.dispatch(action));
|
||||
};
|
||||
|
||||
const canGoBack = () => {
|
||||
@@ -163,7 +164,7 @@ const BaseNavigationContainer = React.forwardRef(
|
||||
return false;
|
||||
}
|
||||
|
||||
const { result, handled } = listeners[0](navigation =>
|
||||
const { result, handled } = listeners[0]((navigation) =>
|
||||
navigation.canGoBack()
|
||||
);
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ export default function SceneView<
|
||||
|
||||
const getCurrentState = React.useCallback(() => {
|
||||
const state = getState();
|
||||
const currentRoute = state.routes.find(r => r.key === route.key);
|
||||
const currentRoute = state.routes.find((r) => r.key === route.key);
|
||||
|
||||
return currentRoute ? currentRoute.state : undefined;
|
||||
}, [getState, route.key]);
|
||||
@@ -62,7 +62,7 @@ export default function SceneView<
|
||||
|
||||
setState({
|
||||
...state,
|
||||
routes: state.routes.map(r =>
|
||||
routes: state.routes.map((r) =>
|
||||
r.key === route.key ? { ...r, state: child } : r
|
||||
),
|
||||
});
|
||||
|
||||
@@ -122,7 +122,7 @@ it('handle dispatching with ref', () => {
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{state.routes.map(route => descriptors[route.key].render())}
|
||||
{state.routes.map((route) => descriptors[route.key].render())}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
@@ -220,7 +220,7 @@ it('handle resetting state with ref', () => {
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{state.routes.map(route => descriptors[route.key].render())}
|
||||
{state.routes.map((route) => descriptors[route.key].render())}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
@@ -371,7 +371,7 @@ it('emits state events when the state changes', () => {
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{state.routes.map(route => descriptors[route.key].render())}
|
||||
{state.routes.map((route) => descriptors[route.key].render())}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -27,7 +27,7 @@ export default function MockRouter(options: DefaultRouterOptions) {
|
||||
key: String(MockRouterKey.current++),
|
||||
index,
|
||||
routeNames,
|
||||
routes: routeNames.map(name => ({
|
||||
routes: routeNames.map((name) => ({
|
||||
name,
|
||||
key: name,
|
||||
params: routeParamList[name],
|
||||
@@ -43,9 +43,9 @@ export default function MockRouter(options: DefaultRouterOptions) {
|
||||
}
|
||||
|
||||
const routes = state.routes
|
||||
.filter(route => routeNames.includes(route.name))
|
||||
.filter((route) => routeNames.includes(route.name))
|
||||
.map(
|
||||
route =>
|
||||
(route) =>
|
||||
({
|
||||
...route,
|
||||
key: route.key || `${route.name}-${MockRouterKey.current++}`,
|
||||
@@ -73,7 +73,7 @@ export default function MockRouter(options: DefaultRouterOptions) {
|
||||
},
|
||||
|
||||
getStateForRouteNamesChange(state, { routeNames }) {
|
||||
const routes = state.routes.filter(route =>
|
||||
const routes = state.routes.filter((route) =>
|
||||
routeNames.includes(route.name)
|
||||
);
|
||||
|
||||
@@ -86,7 +86,7 @@ export default function MockRouter(options: DefaultRouterOptions) {
|
||||
},
|
||||
|
||||
getStateForRouteFocus(state, key) {
|
||||
const index = state.routes.findIndex(r => r.key === key);
|
||||
const index = state.routes.findIndex((r) => r.key === key);
|
||||
|
||||
if (index === -1 || index === state.index) {
|
||||
return state;
|
||||
@@ -105,7 +105,7 @@ export default function MockRouter(options: DefaultRouterOptions) {
|
||||
|
||||
case 'NAVIGATE': {
|
||||
const index = state.routes.findIndex(
|
||||
route => route.name === action.payload.name
|
||||
(route) => route.name === action.payload.name
|
||||
);
|
||||
|
||||
if (index === -1) {
|
||||
|
||||
@@ -42,7 +42,8 @@ it('converts state to path string with config', () => {
|
||||
Baz: {
|
||||
path: 'baz/:author',
|
||||
parse: {
|
||||
author: (author: string) => author.replace(/^\w/, c => c.toUpperCase()),
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, (c) => c.toUpperCase()),
|
||||
id: (id: string) => Number(id.replace(/^x/, '')),
|
||||
valid: Boolean,
|
||||
},
|
||||
@@ -128,7 +129,8 @@ it('handles state with config with nested screens', () => {
|
||||
Baz: {
|
||||
path: 'baz/:author',
|
||||
parse: {
|
||||
author: (author: string) => author.replace(/^\w/, c => c.toUpperCase()),
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, (c) => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
@@ -192,12 +194,14 @@ it('handles state with config with nested screens and unused configs', () => {
|
||||
Baz: {
|
||||
path: 'baz/:author',
|
||||
parse: {
|
||||
author: (author: string) => author.replace(/^\w/, c => c.toUpperCase()),
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, (c) => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
stringify: {
|
||||
author: (author: string) => author.replace(/^\w/, c => c.toLowerCase()),
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, (c) => c.toLowerCase()),
|
||||
unknown: (_: unknown) => 'x',
|
||||
},
|
||||
},
|
||||
@@ -255,11 +259,11 @@ it('handles nested object with stringify in it', () => {
|
||||
path: 'bis/:author',
|
||||
stringify: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toLowerCase()),
|
||||
author.replace(/^\w/, (c) => c.toLowerCase()),
|
||||
},
|
||||
parse: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toUpperCase()),
|
||||
author.replace(/^\w/, (c) => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
|
||||
@@ -42,7 +42,8 @@ it('converts path string to initial state with config', () => {
|
||||
Baz: {
|
||||
path: 'baz/:author',
|
||||
parse: {
|
||||
author: (author: string) => author.replace(/^\w/, c => c.toUpperCase()),
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, (c) => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
@@ -153,7 +154,8 @@ it('converts path string to initial state with config with nested screens', () =
|
||||
Baz: {
|
||||
path: 'baz/:author',
|
||||
parse: {
|
||||
author: (author: string) => author.replace(/^\w/, c => c.toUpperCase()),
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, (c) => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
@@ -217,7 +219,8 @@ it('converts path string to initial state with config with nested screens and un
|
||||
Baz: {
|
||||
path: 'baz/:author',
|
||||
parse: {
|
||||
author: (author: string) => author.replace(/^\w/, c => c.toUpperCase()),
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, (c) => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
id: Boolean,
|
||||
@@ -277,11 +280,11 @@ it('handles nested object with unused configs and with parse in it', () => {
|
||||
path: 'bis/:author',
|
||||
stringify: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toLowerCase()),
|
||||
author.replace(/^\w/, (c) => c.toLowerCase()),
|
||||
},
|
||||
parse: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toUpperCase()),
|
||||
author.replace(/^\w/, (c) => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
@@ -528,11 +531,11 @@ it('handles two initialRouteNames', () => {
|
||||
path: 'bis/:author',
|
||||
stringify: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toLowerCase()),
|
||||
author.replace(/^\w/, (c) => c.toLowerCase()),
|
||||
},
|
||||
parse: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toUpperCase()),
|
||||
author.replace(/^\w/, (c) => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
@@ -610,11 +613,11 @@ it('accepts initialRouteName without config for it', () => {
|
||||
path: 'bis/:author',
|
||||
stringify: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toLowerCase()),
|
||||
author.replace(/^\w/, (c) => c.toLowerCase()),
|
||||
},
|
||||
parse: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toUpperCase()),
|
||||
author.replace(/^\w/, (c) => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
|
||||
@@ -119,7 +119,7 @@ it('sets options with screenOptions prop as an object', () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{state.routes.map(route => {
|
||||
{state.routes.map((route) => {
|
||||
const { render, options } = descriptors[route.key];
|
||||
|
||||
return (
|
||||
@@ -179,7 +179,7 @@ it('sets options with screenOptions prop as a fuction', () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{state.routes.map(route => {
|
||||
{state.routes.map((route) => {
|
||||
const { render, options } = descriptors[route.key];
|
||||
|
||||
return (
|
||||
@@ -273,7 +273,7 @@ it('sets initial options with setOptions', () => {
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" options={{ color: 'blue' }}>
|
||||
{props => <TestScreen {...props} />}
|
||||
{(props) => <TestScreen {...props} />}
|
||||
</Screen>
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
@@ -338,7 +338,7 @@ it('updates options with setOptions', () => {
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" options={{ color: 'blue' }}>
|
||||
{props => <TestScreen {...props} />}
|
||||
{(props) => <TestScreen {...props} />}
|
||||
</Screen>
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
|
||||
@@ -15,7 +15,7 @@ it('fires focus and blur events in root navigator', () => {
|
||||
|
||||
React.useImperativeHandle(ref, () => navigation, [navigation]);
|
||||
|
||||
return state.routes.map(route => descriptors[route.key].render());
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
});
|
||||
|
||||
const firstFocusCallback = jest.fn();
|
||||
@@ -106,7 +106,7 @@ it('fires focus and blur events in nested navigator', () => {
|
||||
|
||||
React.useImperativeHandle(ref, () => navigation, [navigation]);
|
||||
|
||||
return state.routes.map(route => descriptors[route.key].render());
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
});
|
||||
|
||||
const firstFocusCallback = jest.fn();
|
||||
@@ -376,7 +376,7 @@ it('fires custom events added with addListener', () => {
|
||||
state,
|
||||
]);
|
||||
|
||||
return state.routes.map(route => descriptors[route.key].render());
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
});
|
||||
|
||||
const firstCallback = jest.fn();
|
||||
@@ -456,7 +456,7 @@ it("doesn't call same listener multiple times with addListener", () => {
|
||||
state,
|
||||
]);
|
||||
|
||||
return state.routes.map(route => descriptors[route.key].render());
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
});
|
||||
|
||||
const callback = jest.fn();
|
||||
@@ -640,7 +640,7 @@ it('has option to prevent default', () => {
|
||||
state,
|
||||
]);
|
||||
|
||||
return state.routes.map(route => descriptors[route.key].render());
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
});
|
||||
|
||||
const callback = (e: any) => {
|
||||
|
||||
@@ -10,7 +10,7 @@ it('runs focus effect on focus change', () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return state.routes.map(route => descriptors[route.key].render());
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
};
|
||||
|
||||
const focusEffect = jest.fn();
|
||||
@@ -107,7 +107,7 @@ it('runs focus effect when initial state is given', () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return state.routes.map(route => descriptors[route.key].render());
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
};
|
||||
|
||||
const focusEffect = jest.fn();
|
||||
|
||||
@@ -10,7 +10,7 @@ it('renders correct focus state', () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return state.routes.map(route => descriptors[route.key].render());
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
};
|
||||
|
||||
const Test = () => {
|
||||
|
||||
@@ -12,7 +12,7 @@ it('gets navigation prop from context', () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return state.routes.map(route => descriptors[route.key].render());
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
};
|
||||
|
||||
const Test = () => {
|
||||
@@ -38,7 +38,7 @@ it("gets navigation's parent from context", () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return state.routes.map(route => descriptors[route.key].render());
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
};
|
||||
|
||||
const Test = () => {
|
||||
@@ -70,7 +70,7 @@ it("gets navigation's parent's parent from context", () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return state.routes.map(route => descriptors[route.key].render());
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
};
|
||||
|
||||
const Test = () => {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import { render } from 'react-native-testing-library';
|
||||
import { render, act } from 'react-native-testing-library';
|
||||
import useEventEmitter from '../useEventEmitter';
|
||||
import useNavigationCache from '../useNavigationCache';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
import BaseNavigationContainer from '../BaseNavigationContainer';
|
||||
import Screen from '../Screen';
|
||||
import MockRouter, { MockRouterKey } from './__fixtures__/MockRouter';
|
||||
|
||||
beforeEach(() => (MockRouterKey.current = 0));
|
||||
@@ -40,7 +43,7 @@ it('preserves reference for navigation objects', () => {
|
||||
});
|
||||
|
||||
if (previous.current) {
|
||||
Object.keys(navigations).forEach(key => {
|
||||
Object.keys(navigations).forEach((key) => {
|
||||
expect(navigations[key]).toBe(previous.current[key]);
|
||||
});
|
||||
}
|
||||
@@ -56,3 +59,136 @@ it('preserves reference for navigation objects', () => {
|
||||
|
||||
root.update(<Test />);
|
||||
});
|
||||
|
||||
it('returns correct value for isFocused', () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
};
|
||||
|
||||
let navigation: any;
|
||||
|
||||
const Test = (props: any) => {
|
||||
navigation = props.navigation;
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
render(
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="first">{() => null}</Screen>
|
||||
<Screen name="second" component={Test} />
|
||||
<Screen name="third">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(navigation.isFocused()).toBe(false);
|
||||
|
||||
act(() => navigation.navigate('second'));
|
||||
|
||||
expect(navigation.isFocused()).toBe(true);
|
||||
|
||||
act(() => navigation.navigate('third'));
|
||||
|
||||
expect(navigation.isFocused()).toBe(false);
|
||||
|
||||
act(() => navigation.navigate('second'));
|
||||
|
||||
expect(navigation.isFocused()).toBe(true);
|
||||
});
|
||||
|
||||
it('returns correct value for isFocused after changing screens', () => {
|
||||
const TestRouter = (
|
||||
options: Parameters<typeof MockRouter>[0]
|
||||
): ReturnType<typeof MockRouter> => {
|
||||
const router = MockRouter(options);
|
||||
|
||||
return {
|
||||
...router,
|
||||
|
||||
getStateForRouteNamesChange(state, { routeNames }) {
|
||||
const routes = routeNames.map(
|
||||
(name) =>
|
||||
state.routes.find((r) => r.name === name) || {
|
||||
name,
|
||||
key: name,
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
routeNames,
|
||||
routes,
|
||||
index: routes.length - 1,
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(TestRouter, props);
|
||||
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
};
|
||||
|
||||
let navigation: any;
|
||||
|
||||
const Test = (props: any) => {
|
||||
navigation = props.navigation;
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const root = render(
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="first">{() => null}</Screen>
|
||||
<Screen name="second" component={Test} />
|
||||
<Screen name="third">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(navigation.isFocused()).toBe(false);
|
||||
|
||||
root.update(
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="first">{() => null}</Screen>
|
||||
<Screen name="third">{() => null}</Screen>
|
||||
<Screen name="second" component={Test} />
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(navigation.isFocused()).toBe(true);
|
||||
|
||||
root.update(
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="first">{() => null}</Screen>
|
||||
<Screen name="third">{() => null}</Screen>
|
||||
<Screen name="fourth">{() => null}</Screen>
|
||||
<Screen name="second" component={Test} />
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(navigation.isFocused()).toBe(true);
|
||||
|
||||
root.update(
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="first">{() => null}</Screen>
|
||||
<Screen name="third">{() => null}</Screen>
|
||||
<Screen name="second" component={Test} />
|
||||
<Screen name="fourth">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(navigation.isFocused()).toBe(false);
|
||||
});
|
||||
|
||||
@@ -11,13 +11,13 @@ it('gets the current navigation state', () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return state.routes.map(route => descriptors[route.key].render());
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
};
|
||||
|
||||
const callback = jest.fn();
|
||||
|
||||
const Test = () => {
|
||||
const state = useNavigationState(state => state);
|
||||
const state = useNavigationState((state) => state);
|
||||
|
||||
callback(state);
|
||||
|
||||
@@ -62,13 +62,13 @@ it('gets the current navigation state with selector', () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return state.routes.map(route => descriptors[route.key].render());
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
};
|
||||
|
||||
const callback = jest.fn();
|
||||
|
||||
const Test = () => {
|
||||
const index = useNavigationState(state => state.index);
|
||||
const index = useNavigationState((state) => state.index);
|
||||
|
||||
callback(index);
|
||||
|
||||
@@ -112,7 +112,7 @@ it('gets the correct value if selector changes', () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return state.routes.map(route => descriptors[route.key].render());
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
};
|
||||
|
||||
const callback = jest.fn();
|
||||
@@ -144,12 +144,12 @@ it('gets the correct value if selector changes', () => {
|
||||
);
|
||||
};
|
||||
|
||||
const root = render(<App selector={state => state.index} />);
|
||||
const root = render(<App selector={(state) => state.index} />);
|
||||
|
||||
expect(callback).toBeCalledTimes(1);
|
||||
expect(callback.mock.calls[0][0]).toBe(0);
|
||||
|
||||
root.update(<App selector={state => state.routes[state.index].name} />);
|
||||
root.update(<App selector={(state) => state.routes[state.index].name} />);
|
||||
|
||||
expect(callback).toBeCalledTimes(2);
|
||||
expect(callback.mock.calls[1][0]).toBe('first');
|
||||
|
||||
@@ -137,7 +137,7 @@ it("lets children handle the action if parent didn't", () => {
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{state.routes.map(route => descriptors[route.key].render())}
|
||||
{state.routes.map((route) => descriptors[route.key].render())}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
@@ -270,7 +270,7 @@ it("action doesn't bubble if target is specified", () => {
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{state.routes.map(route => descriptors[route.key].render())}
|
||||
{state.routes.map((route) => descriptors[route.key].render())}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
@@ -317,7 +317,7 @@ it('logs error if no navigator handled the action', () => {
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{state.routes.map(route => descriptors[route.key].render())}
|
||||
{state.routes.map((route) => descriptors[route.key].render())}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ it('gets route prop from context', () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return state.routes.map(route => descriptors[route.key].render());
|
||||
return state.routes.map((route) => descriptors[route.key].render());
|
||||
};
|
||||
|
||||
const Test = () => {
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function createNavigatorFactory<
|
||||
EventMap extends EventMapBase,
|
||||
NavigatorComponent extends React.ComponentType<any>
|
||||
>(Navigator: NavigatorComponent) {
|
||||
return function<ParamList extends ParamListBase>(): TypedNavigator<
|
||||
return function <ParamList extends ParamListBase>(): TypedNavigator<
|
||||
ParamList,
|
||||
State,
|
||||
ScreenOptions,
|
||||
|
||||
@@ -125,7 +125,7 @@ export default function getPathFromState(
|
||||
if (currentOptions[route.name] !== undefined) {
|
||||
path += pattern
|
||||
.split('/')
|
||||
.map(p => {
|
||||
.map((p) => {
|
||||
const name = p.replace(/^:/, '');
|
||||
|
||||
// If the path has a pattern for a param, put the param in the path
|
||||
|
||||
@@ -64,7 +64,7 @@ export default function getStateFromPath(
|
||||
let initialRoutes: InitialRouteConfig[] = [];
|
||||
// Create a normalized configs array which will be easier to use
|
||||
const configs = ([] as RouteConfig[]).concat(
|
||||
...Object.keys(options).map(key =>
|
||||
...Object.keys(options).map((key) =>
|
||||
createNormalizedConfigs(key, options, [], initialRoutes)
|
||||
)
|
||||
);
|
||||
@@ -91,7 +91,7 @@ export default function getStateFromPath(
|
||||
|
||||
const paramPatterns = config.pattern
|
||||
.split('/')
|
||||
.filter(p => p.startsWith(':'));
|
||||
.filter((p) => p.startsWith(':'));
|
||||
|
||||
if (paramPatterns.length) {
|
||||
params = paramPatterns.reduce<Record<string, any>>((acc, p, i) => {
|
||||
@@ -188,7 +188,7 @@ export default function getStateFromPath(
|
||||
const parseFunction = findParseConfigForRoute(route.name, configs);
|
||||
|
||||
if (parseFunction) {
|
||||
Object.keys(params).forEach(name => {
|
||||
Object.keys(params).forEach((name) => {
|
||||
if (parseFunction[name] && typeof params[name] === 'string') {
|
||||
params[name] = parseFunction[name](params[name] as string);
|
||||
}
|
||||
@@ -233,7 +233,7 @@ function createNormalizedConfigs(
|
||||
connectedRoutes: Object.keys(value.screens),
|
||||
});
|
||||
}
|
||||
Object.keys(value.screens).forEach(nestedConfig => {
|
||||
Object.keys(value.screens).forEach((nestedConfig) => {
|
||||
const result = createNormalizedConfigs(
|
||||
nestedConfig,
|
||||
value.screens as Options,
|
||||
|
||||
@@ -168,18 +168,6 @@ type NavigationHelpersCommon<
|
||||
| { name: RouteName; key?: string; params: ParamList[RouteName] }
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Replace the current route with a new one.
|
||||
*
|
||||
* @param name Route name of the new route.
|
||||
* @param [params] Params object for the new route.
|
||||
*/
|
||||
replace<RouteName extends keyof ParamList>(
|
||||
...args: ParamList[RouteName] extends undefined
|
||||
? [RouteName] | [RouteName, ParamList[RouteName]]
|
||||
: [RouteName, ParamList[RouteName]]
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Reset the navigation state to the provided state.
|
||||
*
|
||||
@@ -358,6 +346,16 @@ export type Descriptor<
|
||||
>;
|
||||
};
|
||||
|
||||
export type ScreenListeners<
|
||||
State extends NavigationState,
|
||||
EventMap extends EventMapBase
|
||||
> = Partial<
|
||||
{
|
||||
[EventName in keyof (EventMap &
|
||||
EventMapCore<State>)]: EventListenerCallback<EventMap, EventName>;
|
||||
}
|
||||
>;
|
||||
|
||||
export type RouteConfig<
|
||||
ParamList extends ParamListBase,
|
||||
RouteName extends keyof ParamList,
|
||||
@@ -383,12 +381,12 @@ export type RouteConfig<
|
||||
/**
|
||||
* Event listeners for this screen.
|
||||
*/
|
||||
listeners?: Partial<
|
||||
{
|
||||
[EventName in keyof (EventMap &
|
||||
EventMapCore<State>)]: EventListenerCallback<EventMap, EventName>;
|
||||
}
|
||||
>;
|
||||
listeners?:
|
||||
| ScreenListeners<State, EventMap>
|
||||
| ((props: {
|
||||
route: RouteProp<ParamList, RouteName>;
|
||||
navigation: any;
|
||||
}) => ScreenListeners<State, EventMap>);
|
||||
|
||||
/**
|
||||
* Initial params object for the route.
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
type State = NavigationState | PartialState<NavigationState> | undefined;
|
||||
|
||||
type Options = {
|
||||
enabled: boolean;
|
||||
name: string;
|
||||
reset: (state: NavigationState) => void;
|
||||
state: State;
|
||||
@@ -35,10 +36,11 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
export default function useDevTools({ name, reset, state }: Options) {
|
||||
export default function useDevTools({ name, reset, state, enabled }: Options) {
|
||||
const devToolsRef = React.useRef<DevTools>();
|
||||
|
||||
if (
|
||||
enabled &&
|
||||
process.env.NODE_ENV !== 'production' &&
|
||||
global.__REDUX_DEVTOOLS_EXTENSION__ &&
|
||||
devToolsRef.current === undefined
|
||||
@@ -56,7 +58,7 @@ export default function useDevTools({ name, reset, state }: Options) {
|
||||
|
||||
React.useEffect(
|
||||
() =>
|
||||
devTools?.subscribe(message => {
|
||||
devTools?.subscribe((message) => {
|
||||
if (message.type === 'DISPATCH' && message.state) {
|
||||
reset(JSON.parse(message.state));
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ export default function useEventEmitter(
|
||||
target !== undefined
|
||||
? items[target] && items[target].slice()
|
||||
: ([] as Listeners)
|
||||
.concat(...Object.keys(items).map(t => items[t]))
|
||||
.concat(...Object.keys(items).map((t) => items[t]))
|
||||
.filter((cb, i, self) => self.lastIndexOf(cb) === i);
|
||||
|
||||
const event: EventArg<any, any, any> = {
|
||||
@@ -117,7 +117,7 @@ export default function useEventEmitter(
|
||||
|
||||
listenRef.current?.(event);
|
||||
|
||||
callbacks?.forEach(cb => cb(event));
|
||||
callbacks?.forEach((cb) => cb(event));
|
||||
|
||||
return event as any;
|
||||
},
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
RouterFactory,
|
||||
PartialState,
|
||||
NavigationAction,
|
||||
Route,
|
||||
} from '@react-navigation/routers';
|
||||
import { NavigationStateContext } from './BaseNavigationContainer';
|
||||
import NavigationRouteContext from './NavigationRouteContext';
|
||||
@@ -103,7 +104,7 @@ const getRouteConfigsFromChildren = <
|
||||
}, []);
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
configs.forEach(config => {
|
||||
configs.forEach((config) => {
|
||||
const { name, children, component } = config as any;
|
||||
|
||||
if (typeof name !== 'string' || !name) {
|
||||
@@ -212,7 +213,7 @@ export default function useNavigationBuilder<
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const routeNames = routeConfigs.map(config => config.name);
|
||||
const routeNames = routeConfigs.map((config) => config.name);
|
||||
const routeParamList = routeNames.reduce<Record<string, object | undefined>>(
|
||||
(acc, curr) => {
|
||||
const { initialParams } = screens[curr];
|
||||
@@ -241,12 +242,12 @@ export default function useNavigationBuilder<
|
||||
}
|
||||
|
||||
const isStateValid = React.useCallback(
|
||||
state => state.type === undefined || state.type === router.type,
|
||||
(state) => state.type === undefined || state.type === router.type,
|
||||
[router.type]
|
||||
);
|
||||
|
||||
const isStateInitialized = React.useCallback(
|
||||
state =>
|
||||
(state) =>
|
||||
state !== undefined && state.stale === false && isStateValid(state),
|
||||
[isStateValid]
|
||||
);
|
||||
@@ -367,14 +368,16 @@ export default function useNavigationBuilder<
|
||||
: (initializedStateRef.current as State);
|
||||
}, [getCurrentState, isStateInitialized]);
|
||||
|
||||
const emitter = useEventEmitter(e => {
|
||||
const emitter = useEventEmitter((e) => {
|
||||
let routeNames = [];
|
||||
|
||||
if (e.target) {
|
||||
const name = state.routes.find(route => route.key === e.target)?.name;
|
||||
let target: Route<string> | undefined;
|
||||
|
||||
if (name) {
|
||||
routeNames.push(name);
|
||||
if (e.target) {
|
||||
target = state.routes.find((route) => route.key === e.target);
|
||||
|
||||
if (target?.name) {
|
||||
routeNames.push(target.name);
|
||||
}
|
||||
} else {
|
||||
routeNames.push(...Object.keys(screens));
|
||||
@@ -382,19 +385,28 @@ export default function useNavigationBuilder<
|
||||
|
||||
const listeners = ([] as (((e: any) => void) | undefined)[])
|
||||
.concat(
|
||||
...routeNames.map(name => {
|
||||
...routeNames.map((name) => {
|
||||
const { listeners } = screens[name];
|
||||
|
||||
return listeners
|
||||
? Object.keys(listeners)
|
||||
.filter(type => type === e.type)
|
||||
.map(type => listeners[type])
|
||||
.filter((type) => type === e.type)
|
||||
.map((type) => {
|
||||
if (typeof listeners === 'function') {
|
||||
const route = target ?? state.routes[state.index];
|
||||
const navigation = descriptors[route.key].navigation;
|
||||
|
||||
return listeners({ route: route as any, navigation })[type];
|
||||
}
|
||||
|
||||
return listeners[type];
|
||||
})
|
||||
: undefined;
|
||||
})
|
||||
)
|
||||
.filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
|
||||
|
||||
listeners.forEach(listener => listener?.(e));
|
||||
listeners.forEach((listener) => listener?.(e));
|
||||
});
|
||||
|
||||
useFocusEvents({ state, emitter });
|
||||
|
||||
@@ -63,7 +63,7 @@ export default function useNavigationCache<
|
||||
};
|
||||
|
||||
cache.current = state.routes.reduce<NavigationCache<State, ScreenOptions>>(
|
||||
(acc, route, index) => {
|
||||
(acc, route) => {
|
||||
const previous = cache.current[route.key];
|
||||
|
||||
if (previous) {
|
||||
@@ -103,14 +103,14 @@ export default function useNavigationCache<
|
||||
dangerouslyGetState: getState,
|
||||
dispatch,
|
||||
setOptions: (options: object) =>
|
||||
setOptions(o => ({
|
||||
setOptions((o) => ({
|
||||
...o,
|
||||
[route.key]: { ...o[route.key], ...options },
|
||||
})),
|
||||
isFocused: () => {
|
||||
const state = getState();
|
||||
|
||||
if (index !== state.index) {
|
||||
if (state.routes[state.index].key !== route.key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function useNavigationState<T>(selector: Selector<T>): T {
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
const unsubscribe = navigation.addListener('state', e => {
|
||||
const unsubscribe = navigation.addListener('state', (e) => {
|
||||
setResult(selectorRef.current(e.data.state));
|
||||
});
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function useOnGetState({
|
||||
const state = getState();
|
||||
return {
|
||||
...state,
|
||||
routes: state.routes.map(route => ({
|
||||
routes: state.routes.map((route) => ({
|
||||
...route,
|
||||
state: getStateForRoute(route.key),
|
||||
})),
|
||||
|
||||
@@ -3,6 +3,58 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.3.3](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.3.2...@react-navigation/drawer@5.3.3) (2020-03-22)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.2](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.3.1...@react-navigation/drawer@5.3.2) (2020-03-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* close drawer on pressing Esc on web ([5c4afc5](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/5c4afc5cb40c1206a9d8c40efe3cf947030da48e)), closes [#6745](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/6745)
|
||||
* don't use react-native-screens on web ([b1a65fc](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/b1a65fc73e8603ae2c06ef101a74df31e80bb9b2)), closes [#7485](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/7485)
|
||||
* fix permanent sidebar position ([#7830](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/7830)) ([3ea8eec](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/3ea8eec4324ea82f0ed427f4662e68e1115e60ab))
|
||||
* initialize height and width to zero if undefined ([3df65e2](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/3df65e28197db3bb8371059146546d57661c5ba3)), closes [#6789](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/6789)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.3.0...@react-navigation/drawer@5.3.1) (2020-03-17)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.3.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.2.0...@react-navigation/drawer@5.3.0) (2020-03-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add permanent drawer type ([#7818](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/7818)) ([6a5d0a0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/6a5d0a035afae60d91aef78401ec8826295746fe))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.2.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.1.1...@react-navigation/drawer@5.2.0) (2020-03-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* make useIsDrawerOpen workable inside drawer content ([#7746](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/7746)) ([cb46d0b](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/cb46d0bca4e17e847fff46ac94276213ac9697bf))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.1.0...@react-navigation/drawer@5.1.1) (2020-03-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/drawer",
|
||||
"description": "Drawer navigator component with animated transitions and gesturess",
|
||||
"version": "5.1.1",
|
||||
"version": "5.3.3",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -39,17 +39,17 @@
|
||||
"react-native-iphone-x-helper": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.9.3",
|
||||
"@react-navigation/native": "^5.0.9",
|
||||
"@types/react": "^16.9.19",
|
||||
"@types/react-native": "^0.60.30",
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@react-navigation/native": "^5.1.2",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-native": "^0.61.22",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-gesture-handler": "^1.5.6",
|
||||
"react-native-gesture-handler": "^1.6.0",
|
||||
"react-native-reanimated": "^1.7.0",
|
||||
"react-native-safe-area-context": "^0.7.2",
|
||||
"react-native-screens": "^2.0.0-beta.2",
|
||||
"react-native-safe-area-context": "^0.7.3",
|
||||
"react-native-screens": "^2.3.0",
|
||||
"typescript": "^3.7.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -59,7 +59,7 @@
|
||||
"react-native-gesture-handler": ">= 1.0.0",
|
||||
"react-native-reanimated": ">= 1.0.0",
|
||||
"react-native-safe-area-context": ">= 0.6.0",
|
||||
"react-native-screens": ">= 2.0.0-alpha.0 || >= 2.0.0-beta.0"
|
||||
"react-native-screens": ">= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0"
|
||||
},
|
||||
"@react-native-community/bob": {
|
||||
"source": "src",
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
Descriptor,
|
||||
NavigationHelpers,
|
||||
DrawerNavigationState,
|
||||
DrawerActionHelpers,
|
||||
} from '@react-navigation/native';
|
||||
import { PanGestureHandler } from 'react-native-gesture-handler';
|
||||
|
||||
@@ -26,8 +27,9 @@ export type DrawerNavigationConfig<T = DrawerContentOptions> = {
|
||||
* - `front`: Traditional drawer which covers the screen with a overlay behind it.
|
||||
* - `back`: The drawer is revealed behind the screen on swipe.
|
||||
* - `slide`: Both the screen and the drawer slide on swipe to reveal the drawer.
|
||||
* - `permanent`: A permanent drawer is shown as a sidebar.
|
||||
*/
|
||||
drawerType?: 'front' | 'back' | 'slide';
|
||||
drawerType?: 'front' | 'back' | 'slide' | 'permanent';
|
||||
/**
|
||||
* How far from the edge of the screen the swipe gesture should activate.
|
||||
*/
|
||||
@@ -190,22 +192,8 @@ export type DrawerNavigationProp<
|
||||
DrawerNavigationState,
|
||||
DrawerNavigationOptions,
|
||||
DrawerNavigationEventMap
|
||||
> & {
|
||||
/**
|
||||
* Open the drawer sidebar.
|
||||
*/
|
||||
openDrawer(): void;
|
||||
|
||||
/**
|
||||
* Close the drawer sidebar.
|
||||
*/
|
||||
closeDrawer(): void;
|
||||
|
||||
/**
|
||||
* Open the drawer sidebar if closed, or close if opened.
|
||||
*/
|
||||
toggleDrawer(): void;
|
||||
};
|
||||
> &
|
||||
DrawerActionHelpers<ParamList>;
|
||||
|
||||
export type DrawerDescriptor = Descriptor<
|
||||
ParamListBase,
|
||||
|
||||
5
packages/drawer/src/utils/DrawerOpenContext.tsx
Normal file
5
packages/drawer/src/utils/DrawerOpenContext.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import * as React from 'react';
|
||||
|
||||
const DrawerOpenContext = React.createContext<boolean | null>(null);
|
||||
|
||||
export default DrawerOpenContext;
|
||||
@@ -1,38 +1,17 @@
|
||||
import * as React from 'react';
|
||||
import { useNavigation, ParamListBase } from '@react-navigation/native';
|
||||
import { DrawerNavigationProp } from '../types';
|
||||
import DrawerOpenContext from './DrawerOpenContext';
|
||||
|
||||
/**
|
||||
* Hook to detect if the drawer is open in a parent navigator.
|
||||
*/
|
||||
export default function useIsDrawerOpen() {
|
||||
const navigation = useNavigation();
|
||||
const isDrawerOpen = React.useContext(DrawerOpenContext);
|
||||
|
||||
let drawer = navigation as DrawerNavigationProp<ParamListBase>;
|
||||
|
||||
// The screen might be inside another navigator such as stack nested in drawer
|
||||
// We need to find the closest drawer navigator and add the listener there
|
||||
while (drawer && drawer.dangerouslyGetState().type !== 'drawer') {
|
||||
drawer = drawer.dangerouslyGetParent();
|
||||
if (typeof isDrawerOpen !== 'boolean') {
|
||||
throw new Error(
|
||||
"Couldn't find a drawer. Is your component inside a drawer navigator?"
|
||||
);
|
||||
}
|
||||
|
||||
const [isDrawerOpen, setIsDrawerOpen] = React.useState(() =>
|
||||
drawer
|
||||
? Boolean(
|
||||
drawer.dangerouslyGetState().history.find(it => it.type === 'drawer')
|
||||
)
|
||||
: false
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const unsubscribe = drawer.addListener('state', e => {
|
||||
setIsDrawerOpen(
|
||||
Boolean(e.data.state.history.find(it => it.type === 'drawer'))
|
||||
);
|
||||
});
|
||||
|
||||
return unsubscribe;
|
||||
}, [drawer, isDrawerOpen]);
|
||||
|
||||
return isDrawerOpen;
|
||||
}
|
||||
|
||||
@@ -68,6 +68,8 @@ const SPRING_CONFIG = {
|
||||
restSpeedThreshold: 0.01,
|
||||
};
|
||||
|
||||
const ANIMATED_ONE = new Animated.Value(1);
|
||||
|
||||
type Binary = 0 | 1;
|
||||
|
||||
type Renderer = (props: { progress: Animated.Node<number> }) => React.ReactNode;
|
||||
@@ -79,7 +81,7 @@ type Props = {
|
||||
onGestureRef?: (ref: PanGestureHandler | null) => void;
|
||||
gestureEnabled: boolean;
|
||||
drawerPosition: 'left' | 'right';
|
||||
drawerType: 'front' | 'back' | 'slide';
|
||||
drawerType: 'front' | 'back' | 'slide' | 'permanent';
|
||||
keyboardDismissMode: 'none' | 'on-drag';
|
||||
swipeEdgeWidth: number;
|
||||
swipeDistanceThreshold?: number;
|
||||
@@ -125,6 +127,12 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
statusBarAnimation: 'slide',
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
if (Platform.OS === 'web') {
|
||||
document?.body?.addEventListener?.('keyup', this.handleEscape);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
const {
|
||||
open,
|
||||
@@ -180,8 +188,22 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
componentWillUnmount() {
|
||||
this.toggleStatusBar(false);
|
||||
this.handleEndInteraction();
|
||||
|
||||
if (Platform.OS === 'web') {
|
||||
document?.body?.removeEventListener?.('keyup', this.handleEscape);
|
||||
}
|
||||
}
|
||||
|
||||
private handleEscape = (e: KeyboardEvent) => {
|
||||
const { open, onClose } = this.props;
|
||||
|
||||
if (e.key === 'Escape') {
|
||||
if (open) {
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private handleEndInteraction = () => {
|
||||
if (this.interactionHandle !== undefined) {
|
||||
InteractionManager.clearInteractionHandle(this.interactionHandle);
|
||||
@@ -544,6 +566,7 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
gestureHandlerProps,
|
||||
} = this.props;
|
||||
|
||||
const isOpen = drawerType === 'permanent' ? true : open;
|
||||
const isRight = drawerPosition === 'right';
|
||||
|
||||
const contentTranslateX = drawerType === 'front' ? 0 : this.translateX;
|
||||
@@ -569,8 +592,10 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
const hitSlop = isRight
|
||||
? // Extend hitSlop to the side of the screen when drawer is closed
|
||||
// This lets the user drag the drawer from the side of the screen
|
||||
{ right: 0, width: open ? undefined : swipeEdgeWidth }
|
||||
: { left: 0, width: open ? undefined : swipeEdgeWidth };
|
||||
{ right: 0, width: isOpen ? undefined : swipeEdgeWidth }
|
||||
: { left: 0, width: isOpen ? undefined : swipeEdgeWidth };
|
||||
|
||||
const progress = drawerType === 'permanent' ? ANIMATED_ONE : this.progress;
|
||||
|
||||
return (
|
||||
<PanGestureHandler
|
||||
@@ -580,62 +605,83 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
onGestureEvent={this.handleGestureEvent}
|
||||
onHandlerStateChange={this.handleGestureStateChange}
|
||||
hitSlop={hitSlop}
|
||||
enabled={gestureEnabled}
|
||||
enabled={drawerType !== 'permanent' && gestureEnabled}
|
||||
{...gestureHandlerProps}
|
||||
>
|
||||
<Animated.View
|
||||
onLayout={this.handleContainerLayout}
|
||||
style={styles.main}
|
||||
style={[
|
||||
styles.main,
|
||||
{
|
||||
flexDirection:
|
||||
drawerType === 'permanent' && !isRight ? 'row-reverse' : 'row',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.content,
|
||||
{
|
||||
drawerType !== 'permanent' && {
|
||||
transform: [{ translateX: contentTranslateX }],
|
||||
},
|
||||
sceneContainerStyle as any,
|
||||
]}
|
||||
>
|
||||
<View
|
||||
accessibilityElementsHidden={open}
|
||||
importantForAccessibility={open ? 'no-hide-descendants' : 'auto'}
|
||||
accessibilityElementsHidden={isOpen}
|
||||
importantForAccessibility={
|
||||
isOpen ? 'no-hide-descendants' : 'auto'
|
||||
}
|
||||
style={styles.content}
|
||||
>
|
||||
{renderSceneContent({ progress: this.progress })}
|
||||
{renderSceneContent({ progress })}
|
||||
</View>
|
||||
<TapGestureHandler
|
||||
enabled={gestureEnabled}
|
||||
onHandlerStateChange={this.handleTapStateChange}
|
||||
>
|
||||
<Overlay progress={this.progress} style={overlayStyle} />
|
||||
</TapGestureHandler>
|
||||
{// Disable overlay if sidebar is permanent
|
||||
drawerType === 'permanent' ? null : (
|
||||
<TapGestureHandler
|
||||
enabled={gestureEnabled}
|
||||
onHandlerStateChange={this.handleTapStateChange}
|
||||
>
|
||||
<Overlay progress={progress} style={overlayStyle} />
|
||||
</TapGestureHandler>
|
||||
)}
|
||||
</Animated.View>
|
||||
<Animated.Code
|
||||
exec={block([
|
||||
onChange(this.manuallyTriggerSpring, [
|
||||
cond(eq(this.manuallyTriggerSpring, TRUE), [
|
||||
set(this.nextIsOpen, FALSE),
|
||||
call([], () => (this.currentOpenValue = false)),
|
||||
{drawerType === 'permanent' ? null : (
|
||||
<Animated.Code
|
||||
exec={block([
|
||||
onChange(this.manuallyTriggerSpring, [
|
||||
cond(eq(this.manuallyTriggerSpring, TRUE), [
|
||||
set(this.nextIsOpen, FALSE),
|
||||
call([], () => (this.currentOpenValue = false)),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
])}
|
||||
/>
|
||||
])}
|
||||
/>
|
||||
)}
|
||||
<Animated.View
|
||||
accessibilityViewIsModal={open}
|
||||
accessibilityViewIsModal={isOpen}
|
||||
removeClippedSubviews={Platform.OS !== 'ios'}
|
||||
onLayout={this.handleDrawerLayout}
|
||||
style={[
|
||||
styles.container,
|
||||
isRight ? { right: offset } : { left: offset },
|
||||
{
|
||||
transform: [{ translateX: drawerTranslateX }],
|
||||
opacity: this.drawerOpacity,
|
||||
zIndex: drawerType === 'back' ? -1 : 0,
|
||||
},
|
||||
drawerType === 'permanent'
|
||||
? // Without this, the `left`/`right` values don't get reset
|
||||
isRight
|
||||
? { right: 0 }
|
||||
: { left: 0 }
|
||||
: [
|
||||
styles.nonPermanent,
|
||||
{
|
||||
transform: [{ translateX: drawerTranslateX }],
|
||||
opacity: this.drawerOpacity,
|
||||
},
|
||||
isRight ? { right: offset } : { left: offset },
|
||||
{ zIndex: drawerType === 'back' ? -1 : 0 },
|
||||
],
|
||||
drawerStyle as any,
|
||||
]}
|
||||
>
|
||||
{renderDrawerContent({ progress: this.progress })}
|
||||
{renderDrawerContent({ progress })}
|
||||
</Animated.View>
|
||||
</Animated.View>
|
||||
</PanGestureHandler>
|
||||
@@ -646,11 +692,13 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: 'white',
|
||||
maxWidth: '100%',
|
||||
},
|
||||
nonPermanent: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: '80%',
|
||||
maxWidth: '100%',
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
|
||||
@@ -72,14 +72,8 @@ export default function DrawerItem(props: Props) {
|
||||
labelStyle,
|
||||
focused = false,
|
||||
activeTintColor = colors.primary,
|
||||
inactiveTintColor = Color(colors.text)
|
||||
.alpha(0.68)
|
||||
.rgb()
|
||||
.string(),
|
||||
activeBackgroundColor = Color(activeTintColor)
|
||||
.alpha(0.12)
|
||||
.rgb()
|
||||
.string(),
|
||||
inactiveTintColor = Color(colors.text).alpha(0.68).rgb().string(),
|
||||
activeBackgroundColor = Color(activeTintColor).alpha(0.12).rgb().string(),
|
||||
inactiveBackgroundColor = 'transparent',
|
||||
style,
|
||||
onPress,
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
DrawerNavigationHelpers,
|
||||
DrawerContentComponentProps,
|
||||
} from '../types';
|
||||
import DrawerOpenContext from '../utils/DrawerOpenContext';
|
||||
import DrawerPositionContext from '../utils/DrawerPositionContext';
|
||||
|
||||
type Props = DrawerNavigationConfig & {
|
||||
@@ -88,15 +89,17 @@ export default function DrawerView({
|
||||
sceneContainerStyle,
|
||||
}: Props) {
|
||||
const [loaded, setLoaded] = React.useState([state.index]);
|
||||
const [drawerWidth, setDrawerWidth] = React.useState(() =>
|
||||
getDefaultDrawerWidth(Dimensions.get('window'))
|
||||
);
|
||||
const [drawerWidth, setDrawerWidth] = React.useState(() => {
|
||||
const { height = 0, width = 0 } = Dimensions.get('window');
|
||||
|
||||
return getDefaultDrawerWidth({ height, width });
|
||||
});
|
||||
|
||||
const drawerGestureRef = React.useRef<PanGestureHandler>(null);
|
||||
|
||||
const { colors } = useTheme();
|
||||
|
||||
const isDrawerOpen = Boolean(state.history.find(it => it.type === 'drawer'));
|
||||
const isDrawerOpen = state.history.some((it) => it.type === 'drawer');
|
||||
|
||||
const handleDrawerOpen = React.useCallback(() => {
|
||||
navigation.dispatch({
|
||||
@@ -203,36 +206,48 @@ export default function DrawerView({
|
||||
<GestureHandlerWrapper style={styles.content}>
|
||||
<SafeAreaProviderCompat>
|
||||
<DrawerGestureContext.Provider value={drawerGestureRef}>
|
||||
<Drawer
|
||||
open={isDrawerOpen}
|
||||
gestureEnabled={gestureEnabled}
|
||||
onOpen={handleDrawerOpen}
|
||||
onClose={handleDrawerClose}
|
||||
onGestureRef={ref => {
|
||||
// @ts-ignore
|
||||
drawerGestureRef.current = ref;
|
||||
}}
|
||||
gestureHandlerProps={gestureHandlerProps}
|
||||
drawerType={drawerType}
|
||||
drawerPosition={drawerPosition}
|
||||
sceneContainerStyle={[
|
||||
{ backgroundColor: colors.background },
|
||||
sceneContainerStyle,
|
||||
]}
|
||||
drawerStyle={[
|
||||
{ width: drawerWidth, backgroundColor: colors.card },
|
||||
drawerStyle,
|
||||
]}
|
||||
overlayStyle={{ backgroundColor: overlayColor }}
|
||||
swipeEdgeWidth={edgeWidth}
|
||||
swipeDistanceThreshold={minSwipeDistance}
|
||||
hideStatusBar={hideStatusBar}
|
||||
statusBarAnimation={statusBarAnimation}
|
||||
renderDrawerContent={renderNavigationView}
|
||||
renderSceneContent={renderContent}
|
||||
keyboardDismissMode={keyboardDismissMode}
|
||||
drawerPostion={drawerPosition}
|
||||
/>
|
||||
<DrawerOpenContext.Provider value={isDrawerOpen}>
|
||||
<Drawer
|
||||
open={isDrawerOpen}
|
||||
gestureEnabled={gestureEnabled}
|
||||
onOpen={handleDrawerOpen}
|
||||
onClose={handleDrawerClose}
|
||||
onGestureRef={(ref) => {
|
||||
// @ts-ignore
|
||||
drawerGestureRef.current = ref;
|
||||
}}
|
||||
gestureHandlerProps={gestureHandlerProps}
|
||||
drawerType={drawerType}
|
||||
drawerPosition={drawerPosition}
|
||||
sceneContainerStyle={[
|
||||
{ backgroundColor: colors.background },
|
||||
sceneContainerStyle,
|
||||
]}
|
||||
drawerStyle={[
|
||||
{ width: drawerWidth, backgroundColor: colors.card },
|
||||
drawerType === 'permanent' &&
|
||||
(drawerPosition === 'left'
|
||||
? {
|
||||
borderRightColor: colors.border,
|
||||
borderRightWidth: StyleSheet.hairlineWidth,
|
||||
}
|
||||
: {
|
||||
borderLeftColor: colors.border,
|
||||
borderLeftWidth: StyleSheet.hairlineWidth,
|
||||
}),
|
||||
drawerStyle,
|
||||
]}
|
||||
overlayStyle={{ backgroundColor: overlayColor }}
|
||||
swipeEdgeWidth={edgeWidth}
|
||||
swipeDistanceThreshold={minSwipeDistance}
|
||||
hideStatusBar={hideStatusBar}
|
||||
statusBarAnimation={statusBarAnimation}
|
||||
renderDrawerContent={renderNavigationView}
|
||||
renderSceneContent={renderContent}
|
||||
keyboardDismissMode={keyboardDismissMode}
|
||||
drawerPostion={drawerPosition}
|
||||
/>
|
||||
</DrawerOpenContext.Provider>
|
||||
</DrawerGestureContext.Provider>
|
||||
</SafeAreaProviderCompat>
|
||||
</GestureHandlerWrapper>
|
||||
|
||||
@@ -9,21 +9,29 @@ type Props = {
|
||||
style?: any;
|
||||
};
|
||||
|
||||
const FAR_FAR_AWAY = 3000; // this should be big enough to move the whole view out of its container
|
||||
const FAR_FAR_AWAY = 30000; // this should be big enough to move the whole view out of its container
|
||||
|
||||
export default class ResourceSavingScene extends React.Component<Props> {
|
||||
render() {
|
||||
if (screensEnabled?.()) {
|
||||
// react-native-screens is buggy on web
|
||||
if (screensEnabled?.() && Platform.OS !== 'web') {
|
||||
const { isVisible, ...rest } = this.props;
|
||||
|
||||
// @ts-ignore
|
||||
return <Screen active={isVisible ? 1 : 0} {...rest} />;
|
||||
}
|
||||
|
||||
const { isVisible, children, style, ...rest } = this.props;
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[styles.container, style]}
|
||||
style={[
|
||||
styles.container,
|
||||
Platform.OS === 'web'
|
||||
? { display: isVisible ? 'flex' : 'none' }
|
||||
: null,
|
||||
style,
|
||||
]}
|
||||
collapsable={false}
|
||||
removeClippedSubviews={
|
||||
// On iOS, set removeClippedSubviews to true only when not focused
|
||||
|
||||
@@ -30,7 +30,7 @@ type Props = {
|
||||
export default function SafeAreaProviderCompat({ children }: Props) {
|
||||
return (
|
||||
<SafeAreaConsumer>
|
||||
{insets => {
|
||||
{(insets) => {
|
||||
if (insets) {
|
||||
// If we already have insets, don't wrap the stack in another safe area provider
|
||||
// This avoids an issue with updates at the cost of potentially incorrect values
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.1.5](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.4...@react-navigation/material-bottom-tabs@5.1.5) (2020-03-22)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.4](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.3...@react-navigation/material-bottom-tabs@5.1.4) (2020-03-19)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.3](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.2...@react-navigation/material-bottom-tabs@5.1.3) (2020-03-17)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.2](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.1...@react-navigation/material-bottom-tabs@5.1.2) (2020-03-16)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.1](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.0...@react-navigation/material-bottom-tabs@5.1.1) (2020-03-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/material-bottom-tabs",
|
||||
"description": "Integration for bottom navigation component from react-native-paper",
|
||||
"version": "5.1.1",
|
||||
"version": "5.1.5",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -35,10 +35,10 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.9.3",
|
||||
"@react-navigation/native": "^5.0.9",
|
||||
"@types/react": "^16.9.19",
|
||||
"@types/react-native": "^0.60.30",
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@react-navigation/native": "^5.1.2",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-native": "^0.61.22",
|
||||
"@types/react-native-vector-icons": "^6.4.5",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
NavigationProp,
|
||||
NavigationHelpers,
|
||||
TabNavigationState,
|
||||
TabActionHelpers,
|
||||
} from '@react-navigation/native';
|
||||
|
||||
export type MaterialBottomTabNavigationEventMap = {
|
||||
@@ -28,19 +29,8 @@ export type MaterialBottomTabNavigationProp<
|
||||
TabNavigationState,
|
||||
MaterialBottomTabNavigationOptions,
|
||||
MaterialBottomTabNavigationEventMap
|
||||
> & {
|
||||
/**
|
||||
* Jump to an existing tab.
|
||||
*
|
||||
* @param name Name of the route for the tab.
|
||||
* @param [params] Params object for the route.
|
||||
*/
|
||||
jumpTo<RouteName extends Extract<keyof ParamList, string>>(
|
||||
...args: ParamList[RouteName] extends undefined | any
|
||||
? [RouteName] | [RouteName, ParamList[RouteName]]
|
||||
: [RouteName, ParamList[RouteName]]
|
||||
): void;
|
||||
};
|
||||
> &
|
||||
TabActionHelpers<ParamList>;
|
||||
|
||||
export type MaterialBottomTabNavigationOptions = {
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.1.5](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.4...@react-navigation/material-top-tabs@5.1.5) (2020-03-22)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.4](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.3...@react-navigation/material-top-tabs@5.1.4) (2020-03-19)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.3](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.2...@react-navigation/material-top-tabs@5.1.3) (2020-03-17)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.2](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.1...@react-navigation/material-top-tabs@5.1.2) (2020-03-16)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.1](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.0...@react-navigation/material-top-tabs@5.1.1) (2020-03-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/material-top-tabs",
|
||||
"description": "Integration for the animated tab view component from react-native-tab-view",
|
||||
"version": "5.1.1",
|
||||
"version": "5.1.5",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -38,14 +38,14 @@
|
||||
"color": "^3.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.9.3",
|
||||
"@react-navigation/native": "^5.0.9",
|
||||
"@types/react": "^16.9.19",
|
||||
"@types/react-native": "^0.60.30",
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@react-navigation/native": "^5.1.2",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-native": "^0.61.22",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-gesture-handler": "^1.5.6",
|
||||
"react-native-gesture-handler": "^1.6.0",
|
||||
"react-native-reanimated": "^1.7.0",
|
||||
"react-native-tab-view": "^2.13.0",
|
||||
"typescript": "^3.7.5"
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
Route,
|
||||
NavigationProp,
|
||||
TabNavigationState,
|
||||
TabActionHelpers,
|
||||
} from '@react-navigation/native';
|
||||
|
||||
export type MaterialTopTabNavigationEventMap = {
|
||||
@@ -42,19 +43,8 @@ export type MaterialTopTabNavigationProp<
|
||||
TabNavigationState,
|
||||
MaterialTopTabNavigationOptions,
|
||||
MaterialTopTabNavigationEventMap
|
||||
> & {
|
||||
/**
|
||||
* Jump to an existing tab.
|
||||
*
|
||||
* @param name Name of the route for the tab.
|
||||
* @param [params] Params object for the route.
|
||||
*/
|
||||
jumpTo<RouteName extends Extract<keyof ParamList, string>>(
|
||||
...args: ParamList[RouteName] extends undefined | any
|
||||
? [RouteName] | [RouteName, ParamList[RouteName]]
|
||||
: [RouteName, ParamList[RouteName]]
|
||||
): void;
|
||||
};
|
||||
> &
|
||||
TabActionHelpers<ParamList>;
|
||||
|
||||
export type MaterialTopTabNavigationOptions = {
|
||||
/**
|
||||
|
||||
@@ -14,17 +14,11 @@ export default function TabBarTop(props: MaterialTopTabBarProps) {
|
||||
navigation,
|
||||
descriptors,
|
||||
activeTintColor = colors.text,
|
||||
inactiveTintColor = Color(activeTintColor)
|
||||
.alpha(0.5)
|
||||
.rgb()
|
||||
.string(),
|
||||
inactiveTintColor = Color(activeTintColor).alpha(0.5).rgb().string(),
|
||||
allowFontScaling = true,
|
||||
showIcon = false,
|
||||
showLabel = true,
|
||||
pressColor = Color(activeTintColor)
|
||||
.alpha(0.08)
|
||||
.rgb()
|
||||
.string(),
|
||||
pressColor = Color(activeTintColor).alpha(0.08).rgb().string(),
|
||||
iconStyle,
|
||||
labelStyle,
|
||||
indicatorStyle,
|
||||
|
||||
@@ -47,7 +47,7 @@ export default function MaterialTopTabView({
|
||||
return (
|
||||
<TabView
|
||||
{...rest}
|
||||
onIndexChange={index =>
|
||||
onIndexChange={(index) =>
|
||||
navigation.dispatch({
|
||||
...TabActions.jumpTo(state.routes[index].name),
|
||||
target: state.key,
|
||||
|
||||
@@ -3,6 +3,41 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.1.2](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.1...@react-navigation/native@5.1.2) (2020-03-22)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.1](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.0...@react-navigation/native@5.1.1) (2020-03-19)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.0.10...@react-navigation/native@5.1.0) (2020-03-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add permanent drawer type ([#7818](https://github.com/react-navigation/react-navigation/tree/master/packages/native/issues/7818)) ([6a5d0a0](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/6a5d0a035afae60d91aef78401ec8826295746fe))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.0.10](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.0.9...@react-navigation/native@5.0.10) (2020-03-16)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.0.9](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.0.8...@react-navigation/native@5.0.9) (2020-03-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/native",
|
||||
"description": "React Native integration for React Navigation",
|
||||
"version": "5.0.9",
|
||||
"version": "5.1.2",
|
||||
"keywords": [
|
||||
"react-native",
|
||||
"react-navigation",
|
||||
@@ -31,12 +31,12 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/core": "^5.2.1"
|
||||
"@react-navigation/core": "^5.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.9.3",
|
||||
"@types/react": "^16.9.19",
|
||||
"@types/react-native": "^0.60.30",
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-native": "^0.61.22",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export default function() {
|
||||
export default function () {
|
||||
throw new Error(
|
||||
"'NavigationNativeContainer' has been renamed to 'NavigationContainer"
|
||||
);
|
||||
|
||||
@@ -7,7 +7,7 @@ const DefaultTheme: Theme = {
|
||||
background: 'rgb(242, 242, 242)',
|
||||
card: 'rgb(255, 255, 255)',
|
||||
text: 'rgb(28, 28, 30)',
|
||||
border: 'rgb(199, 199, 204)',
|
||||
border: 'rgb(224, 224, 224)',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@ type ScrollOptions = { y?: number; animated?: boolean };
|
||||
type ScrollableView =
|
||||
| { scrollToTop(): void }
|
||||
| { scrollTo(options: ScrollOptions): void }
|
||||
| { scrollToOffset(options: ScrollOptions): void }
|
||||
| { scrollToOffset(options: { offset?: number; animated?: boolean }): void }
|
||||
| { scrollResponderScrollTo(options: ScrollOptions): void };
|
||||
|
||||
type ScrollableWrapper =
|
||||
| { getScrollResponder(): ScrollableView }
|
||||
| { getScrollResponder(): React.ReactNode }
|
||||
| { getNode(): ScrollableView }
|
||||
| ScrollableView;
|
||||
|
||||
@@ -79,7 +79,7 @@ export default function useScrollToTop(
|
||||
// Run the operation in the next frame so we're sure all listeners have been run
|
||||
// This is necessary to know if preventDefault() has been called
|
||||
requestAnimationFrame(() => {
|
||||
const scrollable = getScrollableNode(ref);
|
||||
const scrollable = getScrollableNode(ref) as ScrollableWrapper;
|
||||
|
||||
if (isFocused && isFirst && scrollable && !e.defaultPrevented) {
|
||||
if ('scrollToTop' in scrollable) {
|
||||
@@ -87,7 +87,7 @@ export default function useScrollToTop(
|
||||
} else if ('scrollTo' in scrollable) {
|
||||
scrollable.scrollTo({ y: 0, animated: true });
|
||||
} else if ('scrollToOffset' in scrollable) {
|
||||
scrollable.scrollToOffset({ y: 0, animated: true });
|
||||
scrollable.scrollToOffset({ offset: 0, animated: true });
|
||||
} else if ('scrollResponderScrollTo' in scrollable) {
|
||||
scrollable.scrollResponderScrollTo({ y: 0, animated: true });
|
||||
}
|
||||
|
||||
@@ -3,6 +3,28 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.2.0](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.1.1...@react-navigation/routers@5.2.0) (2020-03-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add keys to routes missing keys during reset ([813a590](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/commit/813a5903b5f44506b9097538ed85229e565b855e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.1](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.1.0...@react-navigation/routers@5.1.1) (2020-03-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* don't handle action if no routes are present ([660cac3](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/commit/660cac3557bce8978812ce2750e961e7ada92d13))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.0.3...@react-navigation/routers@5.1.0) (2020-03-03)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/routers",
|
||||
"description": "Routers to help build custom navigators",
|
||||
"version": "5.1.0",
|
||||
"version": "5.2.0",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-native",
|
||||
@@ -33,7 +33,7 @@
|
||||
"shortid": "^2.2.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.9.3",
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"del-cli": "^3.0.0",
|
||||
"typescript": "^3.7.5"
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import shortid from 'shortid';
|
||||
import { CommonNavigationAction, NavigationState, PartialState } from './types';
|
||||
|
||||
/**
|
||||
@@ -12,7 +13,7 @@ const BaseRouter = {
|
||||
switch (action.type) {
|
||||
case 'SET_PARAMS': {
|
||||
const index = action.source
|
||||
? state.routes.findIndex(r => r.key === action.source)
|
||||
? state.routes.findIndex((r) => r.key === action.source)
|
||||
: state.index;
|
||||
|
||||
if (index === -1) {
|
||||
@@ -32,16 +33,8 @@ const BaseRouter = {
|
||||
case 'RESET': {
|
||||
const nextState = action.payload as State | PartialState<State>;
|
||||
|
||||
if (nextState.stale === false) {
|
||||
if (
|
||||
state.routeNames.length !== nextState.routeNames.length ||
|
||||
nextState.routeNames.some(name => !state.routeNames.includes(name))
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
nextState.routes.length === 0 ||
|
||||
nextState.routes.some(
|
||||
(route: { name: string }) => !state.routeNames.includes(route.name)
|
||||
)
|
||||
@@ -49,6 +42,26 @@ const BaseRouter = {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (nextState.stale === false) {
|
||||
if (
|
||||
state.routeNames.length !== nextState.routeNames.length ||
|
||||
nextState.routeNames.some(
|
||||
(name) => !state.routeNames.includes(name)
|
||||
)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
...nextState,
|
||||
routes: nextState.routes.map((route) =>
|
||||
route.key
|
||||
? route
|
||||
: { ...route, key: `${route.name}-${shortid()}` }
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return nextState;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import shortid from 'shortid';
|
||||
import { PartialState, CommonNavigationAction, Router } from './types';
|
||||
import {
|
||||
PartialState,
|
||||
CommonNavigationAction,
|
||||
Router,
|
||||
ParamListBase,
|
||||
} from './types';
|
||||
import TabRouter, {
|
||||
TabActions,
|
||||
TabActionType,
|
||||
TabRouterOptions,
|
||||
TabNavigationState,
|
||||
TabActionHelpers,
|
||||
} from './TabRouter';
|
||||
|
||||
export type DrawerActionType =
|
||||
@@ -31,6 +37,25 @@ export type DrawerNavigationState = Omit<
|
||||
history: ({ type: 'route'; key: string } | { type: 'drawer' })[];
|
||||
};
|
||||
|
||||
export type DrawerActionHelpers<
|
||||
ParamList extends ParamListBase
|
||||
> = TabActionHelpers<ParamList> & {
|
||||
/**
|
||||
* Open the drawer sidebar.
|
||||
*/
|
||||
openDrawer(): void;
|
||||
|
||||
/**
|
||||
* Close the drawer sidebar.
|
||||
*/
|
||||
closeDrawer(): void;
|
||||
|
||||
/**
|
||||
* Open the drawer sidebar if closed, or close if opened.
|
||||
*/
|
||||
toggleDrawer(): void;
|
||||
};
|
||||
|
||||
export const DrawerActions = {
|
||||
...TabActions,
|
||||
openDrawer(): DrawerActionType {
|
||||
@@ -46,7 +71,7 @@ export const DrawerActions = {
|
||||
|
||||
const isDrawerOpen = (
|
||||
state: DrawerNavigationState | PartialState<DrawerNavigationState>
|
||||
) => Boolean(state.history?.find(it => it.type === 'drawer'));
|
||||
) => Boolean(state.history?.find((it) => it.type === 'drawer'));
|
||||
|
||||
const openDrawer = (state: DrawerNavigationState): DrawerNavigationState => {
|
||||
if (isDrawerOpen(state)) {
|
||||
@@ -66,7 +91,7 @@ const closeDrawer = (state: DrawerNavigationState): DrawerNavigationState => {
|
||||
|
||||
return {
|
||||
...state,
|
||||
history: state.history.filter(it => it.type !== 'drawer'),
|
||||
history: state.history.filter((it) => it.type !== 'drawer'),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Router,
|
||||
DefaultRouterOptions,
|
||||
Route,
|
||||
ParamListBase,
|
||||
} from './types';
|
||||
|
||||
export type StackActionType =
|
||||
@@ -42,6 +43,42 @@ export type StackNavigationState = NavigationState & {
|
||||
type: 'stack';
|
||||
};
|
||||
|
||||
export type StackActionHelpers<ParamList extends ParamListBase> = {
|
||||
/**
|
||||
* Replace the current route with a new one.
|
||||
*
|
||||
* @param name Route name of the new route.
|
||||
* @param [params] Params object for the new route.
|
||||
*/
|
||||
replace<RouteName extends keyof ParamList>(
|
||||
...args: ParamList[RouteName] extends undefined
|
||||
? [RouteName] | [RouteName, ParamList[RouteName]]
|
||||
: [RouteName, ParamList[RouteName]]
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Push a new screen onto the stack.
|
||||
*
|
||||
* @param name Name of the route for the tab.
|
||||
* @param [params] Params object for the route.
|
||||
*/
|
||||
push<RouteName extends keyof ParamList>(
|
||||
...args: ParamList[RouteName] extends undefined | any
|
||||
? [RouteName] | [RouteName, ParamList[RouteName]]
|
||||
: [RouteName, ParamList[RouteName]]
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Pop a screen from the stack.
|
||||
*/
|
||||
pop(count?: number): void;
|
||||
|
||||
/**
|
||||
* Pop to the first route in the stack, dismissing all other screens.
|
||||
*/
|
||||
popToTop(): void;
|
||||
};
|
||||
|
||||
export const StackActions = {
|
||||
replace(name: string, params?: object): StackActionType {
|
||||
return { type: 'REPLACE', payload: { name, params } };
|
||||
@@ -97,9 +134,9 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
}
|
||||
|
||||
const routes = state.routes
|
||||
.filter(route => routeNames.includes(route.name))
|
||||
.filter((route) => routeNames.includes(route.name))
|
||||
.map(
|
||||
route =>
|
||||
(route) =>
|
||||
({
|
||||
...route,
|
||||
key: route.key || `${route.name}-${shortid()}`,
|
||||
@@ -137,7 +174,7 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
},
|
||||
|
||||
getStateForRouteNamesChange(state, { routeNames, routeParamList }) {
|
||||
const routes = state.routes.filter(route =>
|
||||
const routes = state.routes.filter((route) =>
|
||||
routeNames.includes(route.name)
|
||||
);
|
||||
|
||||
@@ -164,7 +201,7 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
},
|
||||
|
||||
getStateForRouteFocus(state, key) {
|
||||
const index = state.routes.findIndex(r => r.key === key);
|
||||
const index = state.routes.findIndex((r) => r.key === key);
|
||||
|
||||
if (index === -1 || index === state.index) {
|
||||
return state;
|
||||
@@ -183,7 +220,7 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
switch (action.type) {
|
||||
case 'REPLACE': {
|
||||
const index = action.source
|
||||
? state.routes.findIndex(r => r.key === action.source)
|
||||
? state.routes.findIndex((r) => r.key === action.source)
|
||||
: state.index;
|
||||
|
||||
if (index === -1) {
|
||||
@@ -246,7 +283,7 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
case 'POP': {
|
||||
const index =
|
||||
action.target === state.key && action.source
|
||||
? state.routes.findIndex(r => r.key === action.source)
|
||||
? state.routes.findIndex((r) => r.key === action.source)
|
||||
: state.index;
|
||||
|
||||
if (index > 0) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
Router,
|
||||
DefaultRouterOptions,
|
||||
Route,
|
||||
ParamListBase,
|
||||
} from './types';
|
||||
|
||||
export type TabActionType = {
|
||||
@@ -33,6 +34,20 @@ export type TabNavigationState = Omit<NavigationState, 'history'> & {
|
||||
history: { type: 'route'; key: string }[];
|
||||
};
|
||||
|
||||
export type TabActionHelpers<ParamList extends ParamListBase> = {
|
||||
/**
|
||||
* Jump to an existing tab.
|
||||
*
|
||||
* @param name Name of the route for the tab.
|
||||
* @param [params] Params object for the route.
|
||||
*/
|
||||
jumpTo<RouteName extends Extract<keyof ParamList, string>>(
|
||||
...args: ParamList[RouteName] extends undefined | any
|
||||
? [RouteName] | [RouteName, ParamList[RouteName]]
|
||||
: [RouteName, ParamList[RouteName]]
|
||||
): void;
|
||||
};
|
||||
|
||||
const TYPE_ROUTE = 'route' as const;
|
||||
|
||||
export const TabActions = {
|
||||
@@ -78,7 +93,7 @@ const changeIndex = (
|
||||
const currentKey = state.routes[index].key;
|
||||
|
||||
history = state.history
|
||||
.filter(it => (it.type === 'route' ? it.key !== currentKey : false))
|
||||
.filter((it) => (it.type === 'route' ? it.key !== currentKey : false))
|
||||
.concat({ type: TYPE_ROUTE, key: currentKey });
|
||||
} else {
|
||||
history = getRouteHistory(state.routes, index, backBehavior);
|
||||
@@ -109,7 +124,7 @@ export default function TabRouter({
|
||||
? routeNames.indexOf(initialRouteName)
|
||||
: 0;
|
||||
|
||||
const routes = routeNames.map(name => ({
|
||||
const routes = routeNames.map((name) => ({
|
||||
name,
|
||||
key: `${name}-${shortid()}`,
|
||||
params: routeParamList[name],
|
||||
@@ -135,9 +150,9 @@ export default function TabRouter({
|
||||
return state;
|
||||
}
|
||||
|
||||
const routes = routeNames.map(name => {
|
||||
const routes = routeNames.map((name) => {
|
||||
const route = (state as PartialState<TabNavigationState>).routes.find(
|
||||
r => r.name === name
|
||||
(r) => r.name === name
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -169,8 +184,8 @@ export default function TabRouter({
|
||||
routes.length - 1
|
||||
);
|
||||
|
||||
let history = state.history?.filter(it =>
|
||||
routes.find(r => r.key === it.key)
|
||||
let history = state.history?.filter((it) =>
|
||||
routes.find((r) => r.key === it.key)
|
||||
);
|
||||
|
||||
if (!history?.length) {
|
||||
@@ -190,8 +205,8 @@ export default function TabRouter({
|
||||
|
||||
getStateForRouteNamesChange(state, { routeNames, routeParamList }) {
|
||||
const routes = routeNames.map(
|
||||
name =>
|
||||
state.routes.find(r => r.name === name) || {
|
||||
(name) =>
|
||||
state.routes.find((r) => r.name === name) || {
|
||||
name,
|
||||
key: `${name}-${shortid()}`,
|
||||
params: routeParamList[name],
|
||||
@@ -203,8 +218,8 @@ export default function TabRouter({
|
||||
routeNames.indexOf(state.routes[state.index].name)
|
||||
);
|
||||
|
||||
let history = state.history.filter(it =>
|
||||
routes.find(r => r.key === it.key)
|
||||
let history = state.history.filter((it) =>
|
||||
routes.find((r) => r.key === it.key)
|
||||
);
|
||||
|
||||
if (!history.length) {
|
||||
@@ -221,7 +236,7 @@ export default function TabRouter({
|
||||
},
|
||||
|
||||
getStateForRouteFocus(state, key) {
|
||||
const index = state.routes.findIndex(r => r.key === key);
|
||||
const index = state.routes.findIndex((r) => r.key === key);
|
||||
|
||||
if (index === -1 || index === state.index) {
|
||||
return state;
|
||||
@@ -238,11 +253,11 @@ export default function TabRouter({
|
||||
|
||||
if (action.type === 'NAVIGATE' && action.payload.key) {
|
||||
index = state.routes.findIndex(
|
||||
route => route.key === action.payload.key
|
||||
(route) => route.key === action.payload.key
|
||||
);
|
||||
} else {
|
||||
index = state.routes.findIndex(
|
||||
route => route.name === action.payload.name
|
||||
(route) => route.name === action.payload.name
|
||||
);
|
||||
}
|
||||
|
||||
@@ -280,7 +295,7 @@ export default function TabRouter({
|
||||
|
||||
const previousKey = state.history[state.history.length - 2].key;
|
||||
const index = state.routes.findIndex(
|
||||
route => route.key === previousKey
|
||||
(route) => route.key === previousKey
|
||||
);
|
||||
|
||||
if (index === -1) {
|
||||
|
||||
@@ -84,6 +84,22 @@ it('resets state to new state with RESET', () => {
|
||||
expect(result).toEqual({ index: 0, routes });
|
||||
});
|
||||
|
||||
it('adds keys to routes missing keys during RESET', () => {
|
||||
const result = BaseRouter.getStateForAction(
|
||||
STATE,
|
||||
// @ts-ignore
|
||||
CommonActions.reset({
|
||||
...STATE,
|
||||
routes: [...STATE.routes, { name: 'qux' }],
|
||||
})
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
...STATE,
|
||||
routes: [...STATE.routes, { key: 'qux-test', name: 'qux' }],
|
||||
});
|
||||
});
|
||||
|
||||
it("doesn't handle RESET if routes don't match routeNames", () => {
|
||||
const routes = [
|
||||
{ key: 'bar', name: 'bar', params: { fruit: 'orange' } },
|
||||
@@ -114,3 +130,15 @@ it("doesn't handle RESET if routeNames don't match", () => {
|
||||
|
||||
expect(result).toEqual(null);
|
||||
});
|
||||
|
||||
it("doesn't handle RESET if there are no routes", () => {
|
||||
const result = BaseRouter.getStateForAction(
|
||||
STATE,
|
||||
CommonActions.reset({
|
||||
index: 0,
|
||||
routes: [],
|
||||
})
|
||||
);
|
||||
|
||||
expect(result).toEqual(null);
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ export { default as BaseRouter } from './BaseRouter';
|
||||
export {
|
||||
default as StackRouter,
|
||||
StackActions,
|
||||
StackActionHelpers,
|
||||
StackActionType,
|
||||
StackRouterOptions,
|
||||
StackNavigationState,
|
||||
@@ -15,6 +16,7 @@ export {
|
||||
export {
|
||||
default as TabRouter,
|
||||
TabActions,
|
||||
TabActionHelpers,
|
||||
TabActionType,
|
||||
TabRouterOptions,
|
||||
TabNavigationState,
|
||||
@@ -23,6 +25,7 @@ export {
|
||||
export {
|
||||
default as DrawerRouter,
|
||||
DrawerActions,
|
||||
DrawerActionHelpers,
|
||||
DrawerActionType,
|
||||
DrawerRouterOptions,
|
||||
DrawerNavigationState,
|
||||
|
||||
@@ -3,6 +3,68 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.2.4](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.3...@react-navigation/stack@5.2.4) (2020-03-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix swipe not dismissing card in RTL ([043924c](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/043924ca4843b6f02626532cbf4aafc7cad9fab1)), closes [#7841](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7841)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.3](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.2...@react-navigation/stack@5.2.3) (2020-03-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use the correct velocity value in closing animation ([#7836](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7836)) ([adbfedc](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/adbfedcd58d4e3d358c6c9691710bb8e4e0d8afb))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.2](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.1...@react-navigation/stack@5.2.2) (2020-03-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* don't use react-native-screens on web ([b1a65fc](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/b1a65fc73e8603ae2c06ef101a74df31e80bb9b2)), closes [#7485](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7485)
|
||||
* fix blank page if stack was inside display: none before ([49f6fed](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/49f6fed6d3da878e02a9fe9115c05bcf84e332bf))
|
||||
* fix closing stack using inverted gesture. ([#7824](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7824)) ([f24d3a3](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/f24d3a3461ee8a566c25ce7d13f31035b4be2691))
|
||||
* initialize height and width to zero if undefined ([3df65e2](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/3df65e28197db3bb8371059146546d57661c5ba3)), closes [#6789](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/6789)
|
||||
* only dismiss previously focused input on page change. closes [#6918](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/6918) ([b1fe730](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/b1fe73097f3ad58d3ba4a8a9b875276d1d8d220c))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.1](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.0...@react-navigation/stack@5.2.1) (2020-03-17)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.2.0](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.1.1...@react-navigation/stack@5.2.0) (2020-03-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix android header title font weight ([#7720](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7720)) ([0dcaea3](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/0dcaea32428484cdc9b4d56f7bf38f9f1bdf1dee))
|
||||
* fix back gesture cancellation ([#7700](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7700)) ([469d054](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/469d0542c7341dc524a597d70216ba743602a51e)), closes [#6782](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/6782)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add an option to change use a custom card overlay ([#7809](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7809)) ([70029d6](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/70029d6c130f0f47de94b6a6c4cbee6afa12b405))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.1](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.1.0...@react-navigation/stack@5.1.1) (2020-03-03)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/stack",
|
||||
"description": "Stack navigator component for iOS and Android with animated transitions and gestures",
|
||||
"version": "5.1.1",
|
||||
"version": "5.2.4",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -38,18 +38,18 @@
|
||||
"react-native-iphone-x-helper": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.9.3",
|
||||
"@react-native-community/masked-view": "^0.1.6",
|
||||
"@react-navigation/native": "^5.0.9",
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@react-native-community/masked-view": "^0.1.7",
|
||||
"@react-navigation/native": "^5.1.2",
|
||||
"@types/color": "^3.0.1",
|
||||
"@types/react": "^16.9.19",
|
||||
"@types/react-native": "^0.60.30",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-native": "^0.61.22",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-gesture-handler": "^1.5.6",
|
||||
"react-native-safe-area-context": "^0.7.2",
|
||||
"react-native-screens": "^2.0.0-beta.2",
|
||||
"react-native-gesture-handler": "^1.6.0",
|
||||
"react-native-safe-area-context": "^0.7.3",
|
||||
"react-native-screens": "^2.3.0",
|
||||
"typescript": "^3.7.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -59,7 +59,7 @@
|
||||
"react-native": "*",
|
||||
"react-native-gesture-handler": ">= 1.0.0",
|
||||
"react-native-safe-area-context": ">= 0.6.0",
|
||||
"react-native-screens": ">= 2.0.0-alpha.0 || >= 2.0.0-beta.0"
|
||||
"react-native-screens": ">= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0"
|
||||
},
|
||||
"@react-native-community/bob": {
|
||||
"source": "src",
|
||||
|
||||
@@ -40,7 +40,7 @@ function StackNavigator({
|
||||
React.useEffect(
|
||||
() =>
|
||||
navigation.addListener &&
|
||||
navigation.addListener('tabPress', e => {
|
||||
navigation.addListener('tabPress', (e) => {
|
||||
const isFocused = navigation.isFocused();
|
||||
|
||||
// Run the operation in the next frame so we're sure all listeners have been run
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Animated,
|
||||
StyleProp,
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
Route,
|
||||
NavigationHelpers,
|
||||
StackNavigationState,
|
||||
StackActionHelpers,
|
||||
} from '@react-navigation/native';
|
||||
|
||||
export type StackNavigationEventMap = {
|
||||
@@ -40,29 +42,8 @@ export type StackNavigationProp<
|
||||
StackNavigationState,
|
||||
StackNavigationOptions,
|
||||
StackNavigationEventMap
|
||||
> & {
|
||||
/**
|
||||
* Push a new screen onto the stack.
|
||||
*
|
||||
* @param name Name of the route for the tab.
|
||||
* @param [params] Params object for the route.
|
||||
*/
|
||||
push<RouteName extends keyof ParamList>(
|
||||
...args: ParamList[RouteName] extends undefined | any
|
||||
? [RouteName] | [RouteName, ParamList[RouteName]]
|
||||
: [RouteName, ParamList[RouteName]]
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Pop a screen from the stack.
|
||||
*/
|
||||
pop(count?: number): void;
|
||||
|
||||
/**
|
||||
* Pop to the first route in the stack, dismissing all other screens.
|
||||
*/
|
||||
popToTop(): void;
|
||||
};
|
||||
> &
|
||||
StackActionHelpers<ParamList>;
|
||||
|
||||
export type Layout = { width: number; height: number };
|
||||
|
||||
@@ -122,7 +103,7 @@ export type StackHeaderOptions = {
|
||||
/**
|
||||
* Style object for the title component.
|
||||
*/
|
||||
headerTitleStyle?: StyleProp<TextStyle>;
|
||||
headerTitleStyle?: React.ComponentProps<typeof Animated.Text>['style'];
|
||||
/**
|
||||
* Style object for the container of the `headerTitle` component, for example to add padding.
|
||||
* By default, `headerTitleContainerStyle` is with an absolute position style and offsets both `left` and `right`.
|
||||
@@ -279,6 +260,10 @@ export type StackNavigationOptions = StackHeaderOptions &
|
||||
* Defaults to `true` on Android and `false` on iOS.
|
||||
*/
|
||||
cardOverlayEnabled?: boolean;
|
||||
/**
|
||||
* Function that returns a React Element to display as a overlay for the card.
|
||||
*/
|
||||
cardOverlay?: (props: { style: StyleProp<ViewStyle> }) => React.ReactNode;
|
||||
/**
|
||||
* Style object for the card in stack.
|
||||
* You can provide a custom background color to use instead of the default background here.
|
||||
@@ -430,7 +415,7 @@ export type StackHeaderTitleProps = {
|
||||
/**
|
||||
* Style object for the title element.
|
||||
*/
|
||||
style?: StyleProp<TextStyle>;
|
||||
style?: React.ComponentProps<typeof Animated.Text>['style'];
|
||||
};
|
||||
|
||||
export type TransitionSpec =
|
||||
|
||||
@@ -4,7 +4,7 @@ export default function debounce<T extends (...args: any[]) => void>(
|
||||
): T {
|
||||
let timeout: NodeJS.Timeout | number | undefined;
|
||||
|
||||
return function(this: any, ...args) {
|
||||
return function (this: any, ...args) {
|
||||
if (!timeout) {
|
||||
// eslint-disable-next-line babel/no-invalid-this
|
||||
func.apply(this, args);
|
||||
|
||||
@@ -32,13 +32,14 @@ export default class BorderlessButton extends React.Component<Props> {
|
||||
}).start();
|
||||
}
|
||||
|
||||
this.props.onActiveStateChange && this.props.onActiveStateChange(active);
|
||||
this.props.onActiveStateChange?.(active);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { children, style, enabled, ...rest } = this.props;
|
||||
|
||||
return (
|
||||
// @ts-ignore
|
||||
<AnimatedBaseButton
|
||||
{...rest}
|
||||
onActiveStateChange={this.handleActiveStateChange}
|
||||
|
||||
@@ -124,7 +124,7 @@ export default function HeaderContainer({
|
||||
<View
|
||||
onLayout={
|
||||
onContentHeightChange
|
||||
? e =>
|
||||
? (e) =>
|
||||
onContentHeightChange({
|
||||
route: scene.route,
|
||||
height: e.nativeEvent.layout.height,
|
||||
|
||||
@@ -38,7 +38,7 @@ type State = {
|
||||
};
|
||||
|
||||
const warnIfHeaderStylesDefined = (styles: Record<string, any>) => {
|
||||
Object.keys(styles).forEach(styleProp => {
|
||||
Object.keys(styles).forEach((styleProp) => {
|
||||
const value = styles[styleProp];
|
||||
|
||||
if (styleProp === 'position' && value === 'absolute') {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { Animated, StyleSheet, Platform, TextProps } from 'react-native';
|
||||
import { Animated, StyleSheet, Platform } from 'react-native';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
|
||||
type Props = TextProps & {
|
||||
type Props = React.ComponentProps<typeof Animated.Text> & {
|
||||
tintColor?: string;
|
||||
children?: string;
|
||||
};
|
||||
@@ -32,7 +32,8 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
android: {
|
||||
fontSize: 20,
|
||||
fontWeight: '500',
|
||||
fontFamily: 'sans-serif-medium',
|
||||
fontWeight: 'normal',
|
||||
},
|
||||
default: {
|
||||
fontSize: 18,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { TextInput, Keyboard } from 'react-native';
|
||||
import { TextInput } from 'react-native';
|
||||
|
||||
type Props = {
|
||||
enabled: boolean;
|
||||
@@ -54,7 +54,11 @@ export default class KeyboardManager extends React.Component<Props> {
|
||||
|
||||
this.clearKeyboardTimeout();
|
||||
|
||||
Keyboard.dismiss();
|
||||
const input = this.previouslyFocusedTextInput;
|
||||
|
||||
if (input) {
|
||||
TextInput.State.blurTextInput(input);
|
||||
}
|
||||
|
||||
// Cleanup the ID on successful page change
|
||||
this.previouslyFocusedTextInput = null;
|
||||
|
||||
@@ -30,7 +30,7 @@ type Props = {
|
||||
export default function SafeAreaProviderCompat({ children }: Props) {
|
||||
return (
|
||||
<SafeAreaConsumer>
|
||||
{insets => {
|
||||
{(insets) => {
|
||||
if (insets) {
|
||||
// If we already have insets, don't wrap the stack in another safe area provider
|
||||
// This avoids an issue with updates at the cost of potentially incorrect values
|
||||
|
||||
@@ -44,6 +44,7 @@ type Props = ViewProps & {
|
||||
onGestureCanceled?: () => void;
|
||||
onGestureEnd?: () => void;
|
||||
children: React.ReactNode;
|
||||
overlay: (props: { style: StyleProp<ViewStyle> }) => React.ReactNode;
|
||||
overlayEnabled: boolean;
|
||||
shadowEnabled: boolean;
|
||||
gestureEnabled: boolean;
|
||||
@@ -80,6 +81,10 @@ export default class Card extends React.Component<Props> {
|
||||
shadowEnabled: true,
|
||||
gestureEnabled: true,
|
||||
gestureVelocityImpact: GESTURE_VELOCITY_IMPACT,
|
||||
overlay: ({ style }: { style: StyleProp<ViewStyle> }) =>
|
||||
style ? (
|
||||
<Animated.View pointerEvents="none" style={[styles.overlay, style]} />
|
||||
) : null,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
@@ -267,7 +272,9 @@ export default class Card extends React.Component<Props> {
|
||||
}
|
||||
|
||||
const closing =
|
||||
Math.abs(translation + velocity * gestureVelocityImpact) >
|
||||
(translation + velocity) *
|
||||
gestureVelocityImpact *
|
||||
getInvertedMultiplier(gestureDirection) >
|
||||
distance / 2
|
||||
? velocity !== 0 || translation !== 0
|
||||
: false;
|
||||
@@ -409,6 +416,7 @@ export default class Card extends React.Component<Props> {
|
||||
next,
|
||||
layout,
|
||||
insets,
|
||||
overlay,
|
||||
overlayEnabled,
|
||||
shadowEnabled,
|
||||
gestureEnabled,
|
||||
@@ -470,55 +478,54 @@ export default class Card extends React.Component<Props> {
|
||||
: false;
|
||||
|
||||
return (
|
||||
<View pointerEvents="box-none" {...rest}>
|
||||
{overlayEnabled && overlayStyle ? (
|
||||
<CardAnimationContext.Provider value={animationContext}>
|
||||
<View pointerEvents="box-none" {...rest}>
|
||||
{overlayEnabled ? (
|
||||
<View style={StyleSheet.absoluteFill}>
|
||||
{overlay({ style: overlayStyle })}
|
||||
</View>
|
||||
) : null}
|
||||
<Animated.View
|
||||
pointerEvents="none"
|
||||
style={[styles.overlay, overlayStyle]}
|
||||
/>
|
||||
) : null}
|
||||
<Animated.View
|
||||
style={[styles.container, containerStyle, customContainerStyle]}
|
||||
pointerEvents="box-none"
|
||||
>
|
||||
<PanGestureHandler
|
||||
ref={this.gestureRef}
|
||||
enabled={layout.width !== 0 && gestureEnabled}
|
||||
onGestureEvent={handleGestureEvent}
|
||||
onHandlerStateChange={this.handleGestureStateChange}
|
||||
{...this.gestureActivationCriteria()}
|
||||
style={[styles.container, containerStyle, customContainerStyle]}
|
||||
pointerEvents="box-none"
|
||||
>
|
||||
<Animated.View style={[styles.container, cardStyle]}>
|
||||
{shadowEnabled && shadowStyle && !isTransparent ? (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.shadow,
|
||||
gestureDirection === 'horizontal'
|
||||
? [styles.shadowHorizontal, styles.shadowLeft]
|
||||
: gestureDirection === 'horizontal-inverted'
|
||||
? [styles.shadowHorizontal, styles.shadowRight]
|
||||
: gestureDirection === 'vertical'
|
||||
? [styles.shadowVertical, styles.shadowTop]
|
||||
: [styles.shadowVertical, styles.shadowBottom],
|
||||
shadowStyle,
|
||||
]}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
) : null}
|
||||
<View
|
||||
ref={this.contentRef}
|
||||
style={[styles.content, contentStyle]}
|
||||
>
|
||||
<StackGestureRefContext.Provider value={this.gestureRef}>
|
||||
<CardAnimationContext.Provider value={animationContext}>
|
||||
<PanGestureHandler
|
||||
ref={this.gestureRef}
|
||||
enabled={layout.width !== 0 && gestureEnabled}
|
||||
onGestureEvent={handleGestureEvent}
|
||||
onHandlerStateChange={this.handleGestureStateChange}
|
||||
{...this.gestureActivationCriteria()}
|
||||
>
|
||||
<Animated.View style={[styles.container, cardStyle]}>
|
||||
{shadowEnabled && shadowStyle && !isTransparent ? (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.shadow,
|
||||
gestureDirection === 'horizontal'
|
||||
? [styles.shadowHorizontal, styles.shadowLeft]
|
||||
: gestureDirection === 'horizontal-inverted'
|
||||
? [styles.shadowHorizontal, styles.shadowRight]
|
||||
: gestureDirection === 'vertical'
|
||||
? [styles.shadowVertical, styles.shadowTop]
|
||||
: [styles.shadowVertical, styles.shadowBottom],
|
||||
shadowStyle,
|
||||
]}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
) : null}
|
||||
<View
|
||||
ref={this.contentRef}
|
||||
style={[styles.content, contentStyle]}
|
||||
>
|
||||
<StackGestureRefContext.Provider value={this.gestureRef}>
|
||||
{children}
|
||||
</CardAnimationContext.Provider>
|
||||
</StackGestureRefContext.Provider>
|
||||
</View>
|
||||
</Animated.View>
|
||||
</PanGestureHandler>
|
||||
</Animated.View>
|
||||
</View>
|
||||
</StackGestureRefContext.Provider>
|
||||
</View>
|
||||
</Animated.View>
|
||||
</PanGestureHandler>
|
||||
</Animated.View>
|
||||
</View>
|
||||
</CardAnimationContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -532,7 +539,7 @@ const styles = StyleSheet.create({
|
||||
overflow: 'hidden',
|
||||
},
|
||||
overlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
flex: 1,
|
||||
backgroundColor: '#000',
|
||||
},
|
||||
shadow: {
|
||||
|
||||
@@ -19,6 +19,7 @@ type Props = TransitionPreset & {
|
||||
safeAreaInsetRight: number;
|
||||
safeAreaInsetBottom: number;
|
||||
safeAreaInsetLeft: number;
|
||||
cardOverlay?: (props: { style: StyleProp<ViewStyle> }) => React.ReactNode;
|
||||
cardOverlayEnabled?: boolean;
|
||||
cardShadowEnabled?: boolean;
|
||||
cardStyle?: StyleProp<ViewStyle>;
|
||||
@@ -58,6 +59,7 @@ const EPSILON = 0.1;
|
||||
|
||||
function CardContainer({
|
||||
active,
|
||||
cardOverlay,
|
||||
cardOverlayEnabled,
|
||||
cardShadowEnabled,
|
||||
cardStyle,
|
||||
@@ -162,6 +164,7 @@ function CardContainer({
|
||||
closing={closing}
|
||||
onOpen={handleOpen}
|
||||
onClose={handleClose}
|
||||
overlay={cardOverlay}
|
||||
overlayEnabled={cardOverlayEnabled}
|
||||
shadowEnabled={cardShadowEnabled}
|
||||
onTransitionStart={handleTransitionStart}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user