mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-14 22:41:55 +08:00
Compare commits
48 Commits
@react-nav
...
@react-nav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
e071a978e6 | ||
|
|
296c836064 | ||
|
|
09f6808d7d | ||
|
|
5bb0f405ce | ||
|
|
2dfa4f3629 | ||
|
|
cf41288760 | ||
|
|
3677818f63 | ||
|
|
162410843c | ||
|
|
028c2887c6 | ||
|
|
7a44cda136 | ||
|
|
a046db536f | ||
|
|
d115787b1c | ||
|
|
80a337024a | ||
|
|
c19da31240 | ||
|
|
85e9376302 | ||
|
|
a67b49477e | ||
|
|
225cb298b6 | ||
|
|
c8ea4199f4 | ||
|
|
f16700812f | ||
|
|
240ce01822 | ||
|
|
c7dd3a58b1 | ||
|
|
125bd70e49 |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
yarn-*.js binary
|
||||||
2
.github/workflows/expo-preview.yml
vendored
2
.github/workflows/expo-preview.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Get expo link
|
- name: Get expo link
|
||||||
id: expo
|
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
|
- name: Comment on PR
|
||||||
uses: unsplash/comment-on-pr@master
|
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 }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
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."
|
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
|
.gradle
|
||||||
.project
|
.project
|
||||||
.settings
|
.settings
|
||||||
|
.history
|
||||||
|
|
||||||
local.properties
|
local.properties
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/vector-icons": "^10.0.0",
|
"@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",
|
"@types/react-native-restart": "^0.0.0",
|
||||||
"color": "^3.1.2",
|
"color": "^3.1.2",
|
||||||
"expo": "^36.0.2",
|
"expo": "^36.0.2",
|
||||||
@@ -21,22 +21,22 @@
|
|||||||
"react": "~16.9.0",
|
"react": "~16.9.0",
|
||||||
"react-dom": "~16.9.0",
|
"react-dom": "~16.9.0",
|
||||||
"react-native": "~0.61.5",
|
"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-paper": "^3.6.0",
|
||||||
"react-native-reanimated": "^1.7.0",
|
"react-native-reanimated": "^1.7.0",
|
||||||
"react-native-restart": "^0.0.13",
|
"react-native-restart": "^0.0.14",
|
||||||
"react-native-safe-area-context": "^0.7.2",
|
"react-native-safe-area-context": "^0.7.3",
|
||||||
"react-native-screens": "^2.0.0-beta.2",
|
"react-native-screens": "^2.3.0",
|
||||||
"react-native-tab-view": "2.13.0",
|
"react-native-tab-view": "2.13.0",
|
||||||
"react-native-unimodules": "^0.7.0",
|
"react-native-unimodules": "^0.7.0",
|
||||||
"react-native-web": "^0.11.7"
|
"react-native-web": "^0.11.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@expo/webpack-config": "^0.10.12",
|
"@expo/webpack-config": "^0.11.7",
|
||||||
"@types/react": "^16.9.19",
|
"@types/react": "^16.9.23",
|
||||||
"@types/react-native": "^0.60.30",
|
"@types/react-native": "^0.61.22",
|
||||||
"babel-preset-expo": "^8.0.0",
|
"babel-preset-expo": "^8.0.0",
|
||||||
"expo-cli": "^3.11.9",
|
"expo-cli": "^3.13.8",
|
||||||
"typescript": "^3.7.5"
|
"typescript": "^3.7.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,14 @@ import {
|
|||||||
Platform,
|
Platform,
|
||||||
StatusBar,
|
StatusBar,
|
||||||
I18nManager,
|
I18nManager,
|
||||||
|
Dimensions,
|
||||||
|
ScaledSize,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
// eslint-disable-next-line import/no-unresolved
|
||||||
|
import { enableScreens } from 'react-native-screens';
|
||||||
import RNRestart from 'react-native-restart';
|
import RNRestart from 'react-native-restart';
|
||||||
|
import { Updates } from 'expo';
|
||||||
|
import { Asset } from 'expo-asset';
|
||||||
import { MaterialIcons } from '@expo/vector-icons';
|
import { MaterialIcons } from '@expo/vector-icons';
|
||||||
import {
|
import {
|
||||||
Provider as PaperProvider,
|
Provider as PaperProvider,
|
||||||
@@ -17,7 +23,6 @@ import {
|
|||||||
List,
|
List,
|
||||||
Divider,
|
Divider,
|
||||||
} from 'react-native-paper';
|
} from 'react-native-paper';
|
||||||
import { Asset } from 'expo-asset';
|
|
||||||
import {
|
import {
|
||||||
InitialState,
|
InitialState,
|
||||||
useLinking,
|
useLinking,
|
||||||
@@ -49,10 +54,11 @@ import DynamicTabs from './Screens/DynamicTabs';
|
|||||||
import AuthFlow from './Screens/AuthFlow';
|
import AuthFlow from './Screens/AuthFlow';
|
||||||
import CompatAPI from './Screens/CompatAPI';
|
import CompatAPI from './Screens/CompatAPI';
|
||||||
import SettingsItem from './Shared/SettingsItem';
|
import SettingsItem from './Shared/SettingsItem';
|
||||||
import { Updates } from 'expo';
|
|
||||||
|
|
||||||
YellowBox.ignoreWarnings(['Require cycle:', 'Warning: Async Storage']);
|
YellowBox.ignoreWarnings(['Require cycle:', 'Warning: Async Storage']);
|
||||||
|
|
||||||
|
enableScreens();
|
||||||
|
|
||||||
type RootDrawerParamList = {
|
type RootDrawerParamList = {
|
||||||
Root: undefined;
|
Root: undefined;
|
||||||
Another: undefined;
|
Another: undefined;
|
||||||
@@ -192,10 +198,24 @@ export default function App() {
|
|||||||
};
|
};
|
||||||
}, [theme.colors, theme.dark]);
|
}, [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) {
|
if (!isReady) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isLargeScreen = dimensions.width > 900;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PaperProvider theme={paperTheme}>
|
<PaperProvider theme={paperTheme}>
|
||||||
{Platform.OS === 'ios' && (
|
{Platform.OS === 'ios' && (
|
||||||
@@ -212,7 +232,7 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
>
|
>
|
||||||
<Drawer.Navigator>
|
<Drawer.Navigator drawerType={isLargeScreen ? 'permanent' : undefined}>
|
||||||
<Drawer.Screen
|
<Drawer.Screen
|
||||||
name="Root"
|
name="Root"
|
||||||
options={{
|
options={{
|
||||||
@@ -236,13 +256,15 @@ export default function App() {
|
|||||||
name="Home"
|
name="Home"
|
||||||
options={{
|
options={{
|
||||||
title: 'Examples',
|
title: 'Examples',
|
||||||
headerLeft: () => (
|
headerLeft: isLargeScreen
|
||||||
<Appbar.Action
|
? undefined
|
||||||
color={theme.colors.text}
|
: () => (
|
||||||
icon="menu"
|
<Appbar.Action
|
||||||
onPress={() => navigation.toggleDrawer()}
|
color={theme.colors.text}
|
||||||
/>
|
icon="menu"
|
||||||
),
|
onPress={() => navigation.toggleDrawer()}
|
||||||
|
/>
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{({
|
{({
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const error = console.error;
|
const error = console.error;
|
||||||
|
|
||||||
console.error = (...args) =>
|
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(
|
/(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]
|
args[0]
|
||||||
)
|
)
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -20,26 +20,27 @@
|
|||||||
"lint": "eslint --ext '.js,.ts,.tsx' .",
|
"lint": "eslint --ext '.js,.ts,.tsx' .",
|
||||||
"typescript": "tsc --noEmit",
|
"typescript": "tsc --noEmit",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
|
"prerelease": "lerna run clean",
|
||||||
"release": "lerna publish",
|
"release": "lerna publish",
|
||||||
"example": "yarn --cwd example"
|
"example": "yarn --cwd example"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
||||||
"@babel/plugin-proposal-optional-chaining": "^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-flow": "^7.8.3",
|
||||||
"@babel/preset-react": "^7.8.3",
|
"@babel/preset-react": "^7.8.3",
|
||||||
"@babel/preset-typescript": "^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",
|
"@commitlint/config-conventional": "^8.3.4",
|
||||||
"@types/jest": "^25.1.2",
|
"@types/jest": "^25.1.4",
|
||||||
"codecov": "^3.6.5",
|
"codecov": "^3.6.5",
|
||||||
"commitlint": "^8.3.5",
|
"commitlint": "^8.3.5",
|
||||||
"core-js": "^3.6.4",
|
"core-js": "^3.6.4",
|
||||||
"detox": "^15.1.4",
|
"detox": "^16.0.0",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"eslint-config-satya164": "^3.1.5",
|
"eslint-config-satya164": "^3.1.5",
|
||||||
"husky": "^4.2.1",
|
"husky": "^4.2.3",
|
||||||
"jest": "^25.1.0",
|
"jest": "^25.1.0",
|
||||||
"lerna": "^3.20.2",
|
"lerna": "^3.20.2",
|
||||||
"prettier": "^1.19.1",
|
"prettier": "^1.19.1",
|
||||||
|
|||||||
@@ -3,6 +3,64 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.7...@react-navigation/bottom-tabs@5.1.0) (2020-02-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add ability add listeners with listeners prop ([1624108](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/162410843c4f175ae107756de1c3af04d1d47aa7)), closes [#6756](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/issues/6756)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.6...@react-navigation/bottom-tabs@5.0.7) (2020-02-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.0.6](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.5...@react-navigation/bottom-tabs@5.0.6) (2020-02-19)
|
## [5.0.6](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.5...@react-navigation/bottom-tabs@5.0.6) (2020-02-19)
|
||||||
|
|
||||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/bottom-tabs",
|
"name": "@react-navigation/bottom-tabs",
|
||||||
"description": "Bottom tab navigator following iOS design guidelines",
|
"description": "Bottom tab navigator following iOS design guidelines",
|
||||||
"version": "5.0.6",
|
"version": "5.2.2",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -34,22 +34,24 @@
|
|||||||
"react-native-iphone-x-helper": "^1.2.1"
|
"react-native-iphone-x-helper": "^1.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.9.3",
|
"@react-native-community/bob": "^0.10.0",
|
||||||
"@react-navigation/native": "^5.0.6",
|
"@react-navigation/native": "^5.1.1",
|
||||||
"@types/color": "^3.0.1",
|
"@types/color": "^3.0.1",
|
||||||
"@types/react": "^16.9.19",
|
"@types/react": "^16.9.23",
|
||||||
"@types/react-native": "^0.60.30",
|
"@types/react-native": "^0.61.22",
|
||||||
"del-cli": "^3.0.0",
|
"del-cli": "^3.0.0",
|
||||||
"react": "~16.9.0",
|
"react": "~16.9.0",
|
||||||
"react-native": "~0.61.5",
|
"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"
|
"typescript": "^3.7.5"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@react-navigation/native": "^5.0.5",
|
"@react-navigation/native": "^5.0.5",
|
||||||
"react": "*",
|
"react": "*",
|
||||||
"react-native": "*",
|
"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": {
|
"@react-native-community/bob": {
|
||||||
"source": "src",
|
"source": "src",
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ function BottomTabNavigator({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default createNavigatorFactory<
|
export default createNavigatorFactory<
|
||||||
|
TabNavigationState,
|
||||||
BottomTabNavigationOptions,
|
BottomTabNavigationOptions,
|
||||||
|
BottomTabNavigationEventMap,
|
||||||
typeof BottomTabNavigator
|
typeof BottomTabNavigator
|
||||||
>(BottomTabNavigator);
|
>(BottomTabNavigator);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
ParamListBase,
|
ParamListBase,
|
||||||
Descriptor,
|
Descriptor,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
|
TabActionHelpers,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
export type BottomTabNavigationEventMap = {
|
export type BottomTabNavigationEventMap = {
|
||||||
@@ -40,19 +41,8 @@ export type BottomTabNavigationProp<
|
|||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
BottomTabNavigationOptions,
|
BottomTabNavigationOptions,
|
||||||
BottomTabNavigationEventMap
|
BottomTabNavigationEventMap
|
||||||
> & {
|
> &
|
||||||
/**
|
TabActionHelpers<ParamList>;
|
||||||
* 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;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type BottomTabNavigationOptions = {
|
export type BottomTabNavigationOptions = {
|
||||||
/**
|
/**
|
||||||
@@ -184,6 +174,16 @@ export type BottomTabBarOptions = {
|
|||||||
* Whether the label position should adapt to the orientation.
|
* Whether the label position should adapt to the orientation.
|
||||||
*/
|
*/
|
||||||
adaptive?: boolean;
|
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.
|
* Style object for the tab bar container.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
CommonActions,
|
CommonActions,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from '@react-navigation/native';
|
} 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 BottomTabItem from './BottomTabItem';
|
||||||
import { BottomTabBarProps } from '../types';
|
import { BottomTabBarProps } from '../types';
|
||||||
@@ -43,6 +43,7 @@ export default function BottomTabBar({
|
|||||||
keyboardHidesTabBar = false,
|
keyboardHidesTabBar = false,
|
||||||
labelPosition,
|
labelPosition,
|
||||||
labelStyle,
|
labelStyle,
|
||||||
|
safeAreaInsets,
|
||||||
showIcon,
|
showIcon,
|
||||||
showLabel,
|
showLabel,
|
||||||
style,
|
style,
|
||||||
@@ -50,7 +51,12 @@ export default function BottomTabBar({
|
|||||||
}: Props) {
|
}: Props) {
|
||||||
const { colors } = useTheme();
|
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({
|
const [layout, setLayout] = React.useState({
|
||||||
height: 0,
|
height: 0,
|
||||||
width: dimensions.width,
|
width: dimensions.width,
|
||||||
@@ -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 (
|
return (
|
||||||
<SafeAreaConsumer>
|
<Animated.View
|
||||||
{insets => (
|
style={[
|
||||||
<Animated.View
|
styles.tabBar,
|
||||||
style={[
|
{
|
||||||
styles.tabBar,
|
backgroundColor: colors.card,
|
||||||
{
|
borderTopColor: colors.border,
|
||||||
backgroundColor: colors.card,
|
},
|
||||||
borderTopColor: colors.border,
|
keyboardHidesTabBar
|
||||||
},
|
? {
|
||||||
keyboardHidesTabBar
|
// When the keyboard is shown, slide down the tab bar
|
||||||
? {
|
transform: [
|
||||||
// When the keyboard is shown, slide down the tab bar
|
{
|
||||||
transform: [
|
translateY: visible.interpolate({
|
||||||
{
|
inputRange: [0, 1],
|
||||||
translateY: visible.interpolate({
|
outputRange: [layout.height, 0],
|
||||||
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
|
||||||
// Absolutely position the tab bar so that the content is below it
|
position: keyboardShown ? 'absolute' : null,
|
||||||
// This is needed to avoid gap at bottom when the tab bar is hidden
|
}
|
||||||
position: keyboardShown ? 'absolute' : null,
|
: null,
|
||||||
}
|
{
|
||||||
: null,
|
height: DEFAULT_TABBAR_HEIGHT + insets.bottom,
|
||||||
{
|
paddingBottom: insets.bottom,
|
||||||
height: DEFAULT_TABBAR_HEIGHT + (insets ? insets.bottom : 0),
|
paddingHorizontal: Math.max(insets.left, insets.right),
|
||||||
paddingBottom: insets ? insets.bottom : 0,
|
},
|
||||||
},
|
style,
|
||||||
style,
|
]}
|
||||||
]}
|
pointerEvents={keyboardHidesTabBar && keyboardShown ? 'none' : 'auto'}
|
||||||
pointerEvents={keyboardHidesTabBar && keyboardShown ? 'none' : 'auto'}
|
>
|
||||||
>
|
<View style={styles.content} onLayout={handleLayout}>
|
||||||
<View style={styles.content} onLayout={handleLayout}>
|
{routes.map((route, index) => {
|
||||||
{routes.map((route, index) => {
|
const focused = index === state.index;
|
||||||
const focused = index === state.index;
|
const { options } = descriptors[route.key];
|
||||||
const { options } = descriptors[route.key];
|
|
||||||
|
|
||||||
const onPress = () => {
|
const onPress = () => {
|
||||||
const event = navigation.emit({
|
const event = navigation.emit({
|
||||||
type: 'tabPress',
|
type: 'tabPress',
|
||||||
target: route.key,
|
target: route.key,
|
||||||
canPreventDefault: true,
|
canPreventDefault: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!focused && !event.defaultPrevented) {
|
if (!focused && !event.defaultPrevented) {
|
||||||
navigation.dispatch({
|
navigation.dispatch({
|
||||||
...CommonActions.navigate(route.name),
|
...CommonActions.navigate(route.name),
|
||||||
target: state.key,
|
target: state.key,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onLongPress = () => {
|
const onLongPress = () => {
|
||||||
navigation.emit({
|
navigation.emit({
|
||||||
type: 'tabLongPress',
|
type: 'tabLongPress',
|
||||||
target: route.key,
|
target: route.key,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const label =
|
const label =
|
||||||
options.tabBarLabel !== undefined
|
options.tabBarLabel !== undefined
|
||||||
? options.tabBarLabel
|
? options.tabBarLabel
|
||||||
: options.title !== undefined
|
: options.title !== undefined
|
||||||
? options.title
|
? options.title
|
||||||
: route.name;
|
: route.name;
|
||||||
|
|
||||||
const accessibilityLabel =
|
const accessibilityLabel =
|
||||||
options.tabBarAccessibilityLabel !== undefined
|
options.tabBarAccessibilityLabel !== undefined
|
||||||
? options.tabBarAccessibilityLabel
|
? options.tabBarAccessibilityLabel
|
||||||
: typeof label === 'string'
|
: typeof label === 'string'
|
||||||
? `${label}, tab, ${index + 1} of ${routes.length}`
|
? `${label}, tab, ${index + 1} of ${routes.length}`
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavigationContext.Provider
|
<NavigationContext.Provider
|
||||||
key={route.key}
|
key={route.key}
|
||||||
value={descriptors[route.key].navigation}
|
value={descriptors[route.key].navigation}
|
||||||
>
|
>
|
||||||
<NavigationRouteContext.Provider value={route}>
|
<NavigationRouteContext.Provider value={route}>
|
||||||
<BottomTabItem
|
<BottomTabItem
|
||||||
route={route}
|
route={route}
|
||||||
focused={focused}
|
focused={focused}
|
||||||
horizontal={shouldUseHorizontalLabels()}
|
horizontal={shouldUseHorizontalLabels()}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
onLongPress={onLongPress}
|
onLongPress={onLongPress}
|
||||||
accessibilityLabel={accessibilityLabel}
|
accessibilityLabel={accessibilityLabel}
|
||||||
testID={options.tabBarTestID}
|
testID={options.tabBarTestID}
|
||||||
allowFontScaling={allowFontScaling}
|
allowFontScaling={allowFontScaling}
|
||||||
activeTintColor={activeTintColor}
|
activeTintColor={activeTintColor}
|
||||||
inactiveTintColor={inactiveTintColor}
|
inactiveTintColor={inactiveTintColor}
|
||||||
activeBackgroundColor={activeBackgroundColor}
|
activeBackgroundColor={activeBackgroundColor}
|
||||||
inactiveBackgroundColor={inactiveBackgroundColor}
|
inactiveBackgroundColor={inactiveBackgroundColor}
|
||||||
button={options.tabBarButton}
|
button={options.tabBarButton}
|
||||||
icon={options.tabBarIcon}
|
icon={options.tabBarIcon}
|
||||||
label={label}
|
label={label}
|
||||||
showIcon={showIcon}
|
showIcon={showIcon}
|
||||||
showLabel={showLabel}
|
showLabel={showLabel}
|
||||||
labelStyle={labelStyle}
|
labelStyle={labelStyle}
|
||||||
style={tabStyle}
|
style={tabStyle}
|
||||||
/>
|
/>
|
||||||
</NavigationRouteContext.Provider>
|
</NavigationRouteContext.Provider>
|
||||||
</NavigationContext.Provider>
|
</NavigationContext.Provider>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</View>
|
</View>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
)}
|
|
||||||
</SafeAreaConsumer>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Platform, StyleSheet, View } from 'react-native';
|
import { Platform, StyleSheet, View } from 'react-native';
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-unresolved
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import { Screen, screensEnabled } from 'react-native-screens';
|
import { Screen, screensEnabled } from 'react-native-screens';
|
||||||
|
|
||||||
@@ -10,12 +9,14 @@ type Props = {
|
|||||||
style?: any;
|
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> {
|
export default class ResourceSavingScene extends React.Component<Props> {
|
||||||
render() {
|
render() {
|
||||||
if (screensEnabled?.()) {
|
// react-native-screens is buggy on web
|
||||||
|
if (screensEnabled?.() && Platform.OS !== 'web') {
|
||||||
const { isVisible, ...rest } = this.props;
|
const { isVisible, ...rest } = this.props;
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return <Screen active={isVisible ? 1 : 0} {...rest} />;
|
return <Screen active={isVisible ? 1 : 0} {...rest} />;
|
||||||
}
|
}
|
||||||
@@ -24,7 +25,13 @@ export default class ResourceSavingScene extends React.Component<Props> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[styles.container, style, { opacity: isVisible ? 1 : 0 }]}
|
style={[
|
||||||
|
styles.container,
|
||||||
|
Platform.OS === 'web'
|
||||||
|
? { display: isVisible ? 'flex' : 'none' }
|
||||||
|
: null,
|
||||||
|
style,
|
||||||
|
]}
|
||||||
collapsable={false}
|
collapsable={false}
|
||||||
removeClippedSubviews={
|
removeClippedSubviews={
|
||||||
// On iOS, set removeClippedSubviews to true only when not focused
|
// On iOS, set removeClippedSubviews to true only when not focused
|
||||||
|
|||||||
@@ -3,6 +3,57 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.0.7...@react-navigation/compat@5.1.0) (2020-02-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add ability add listeners with listeners prop ([1624108](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/commit/162410843c4f175ae107756de1c3af04d1d47aa7)), closes [#6756](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/issues/6756)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.0.6...@react-navigation/compat@5.0.7) (2020-02-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.0.6](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.0.5...@react-navigation/compat@5.0.6) (2020-02-19)
|
## [5.0.6](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.0.5...@react-navigation/compat@5.0.6) (2020-02-19)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/compat",
|
"name": "@react-navigation/compat",
|
||||||
"description": "Compatibility layer to write navigator definitions in static configuration format",
|
"description": "Compatibility layer to write navigator definitions in static configuration format",
|
||||||
"version": "5.0.6",
|
"version": "5.1.4",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/compat",
|
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/compat",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
@@ -25,9 +25,9 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.9.3",
|
"@react-native-community/bob": "^0.10.0",
|
||||||
"@react-navigation/native": "^5.0.6",
|
"@react-navigation/native": "^5.1.1",
|
||||||
"@types/react": "^16.9.19",
|
"@types/react": "^16.9.23",
|
||||||
"react": "~16.9.0",
|
"react": "~16.9.0",
|
||||||
"typescript": "^3.7.5"
|
"typescript": "^3.7.5"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
TypedNavigator,
|
TypedNavigator,
|
||||||
NavigationProp,
|
NavigationProp,
|
||||||
RouteProp,
|
RouteProp,
|
||||||
|
EventMapBase,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
import CompatScreen from './CompatScreen';
|
import CompatScreen from './CompatScreen';
|
||||||
import ScreenPropsContext from './ScreenPropsContext';
|
import ScreenPropsContext from './ScreenPropsContext';
|
||||||
@@ -15,7 +16,9 @@ import { CompatScreenType, CompatRouteConfig } from './types';
|
|||||||
export default function createCompatNavigatorFactory<
|
export default function createCompatNavigatorFactory<
|
||||||
CreateNavigator extends () => TypedNavigator<
|
CreateNavigator extends () => TypedNavigator<
|
||||||
ParamListBase,
|
ParamListBase,
|
||||||
|
NavigationState,
|
||||||
{},
|
{},
|
||||||
|
EventMapBase,
|
||||||
React.ComponentType<any>
|
React.ComponentType<any>
|
||||||
>
|
>
|
||||||
>(createNavigator: CreateNavigator) {
|
>(createNavigator: CreateNavigator) {
|
||||||
|
|||||||
@@ -22,5 +22,7 @@ function SwitchNavigator(props: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default createCompatNavigatorFactory(
|
export default createCompatNavigatorFactory(
|
||||||
createNavigatorFactory<{}, typeof SwitchNavigator>(SwitchNavigator)
|
createNavigatorFactory<TabNavigationState, {}, {}, typeof SwitchNavigator>(
|
||||||
|
SwitchNavigator
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,6 +3,57 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix links for documentation ([5bb0f40](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/5bb0f405ceb5755d39a0b5b1f2e4ecee0da051bc))
|
||||||
|
* move updating state to useEffect ([2dfa4f3](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/2dfa4f36293a2acb718814f6b2fa79d7c7ddf09c))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.2.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.1.6...@react-navigation/core@5.2.0) (2020-02-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add ability add listeners with listeners prop ([1624108](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/162410843c4f175ae107756de1c3af04d1d47aa7)), closes [#6756](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/6756)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.6](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.1.5...@react-navigation/core@5.1.6) (2020-02-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* avoid emitting focus events twice ([f167008](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/f16700812f3757713b04ca3a860209795b4a6c44)), closes [#6749](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/6749)
|
||||||
|
* preserve screen order with numeric names ([125bd70](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/125bd70e49b708d936a2eee72ba5cb92eacf26a9)), closes [#6900](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/6900)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.1.5](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.1.4...@react-navigation/core@5.1.5) (2020-02-19)
|
## [5.1.5](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.1.4...@react-navigation/core@5.1.5) (2020-02-19)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/core",
|
"name": "@react-navigation/core",
|
||||||
"description": "Core utilities for building navigators",
|
"description": "Core utilities for building navigators",
|
||||||
"version": "5.1.5",
|
"version": "5.2.3",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react",
|
"react",
|
||||||
"react-native",
|
"react-native",
|
||||||
@@ -29,23 +29,23 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-navigation/routers": "^5.0.1",
|
"@react-navigation/routers": "^5.1.1",
|
||||||
"escape-string-regexp": "^2.0.0",
|
"escape-string-regexp": "^2.0.0",
|
||||||
"query-string": "^6.10.1",
|
"query-string": "^6.11.1",
|
||||||
"react-is": "^16.12.0",
|
"react-is": "^16.13.0",
|
||||||
"shortid": "^2.2.15",
|
"shortid": "^2.2.15",
|
||||||
"use-subscription": "^1.3.0"
|
"use-subscription": "^1.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.9.3",
|
"@react-native-community/bob": "^0.10.0",
|
||||||
"@types/react": "^16.9.19",
|
"@types/react": "^16.9.23",
|
||||||
"@types/react-is": "^16.7.1",
|
"@types/react-is": "^16.7.1",
|
||||||
"@types/shortid": "^0.0.29",
|
"@types/shortid": "^0.0.29",
|
||||||
"@types/use-subscription": "^1.0.0",
|
"@types/use-subscription": "^1.0.0",
|
||||||
"del-cli": "^3.0.0",
|
"del-cli": "^3.0.0",
|
||||||
"react": "~16.9.0",
|
"react": "~16.9.0",
|
||||||
"react-native-testing-library": "^1.12.0",
|
"react-native-testing-library": "^1.12.0",
|
||||||
"react-test-renderer": "~16.12.0",
|
"react-test-renderer": "~16.9.0",
|
||||||
"typescript": "^3.7.5"
|
"typescript": "^3.7.5"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ import useSyncState from './useSyncState';
|
|||||||
type State = NavigationState | PartialState<NavigationState> | undefined;
|
type State = NavigationState | PartialState<NavigationState> | undefined;
|
||||||
|
|
||||||
const MISSING_CONTEXT_ERROR =
|
const MISSING_CONTEXT_ERROR =
|
||||||
"We couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'? See https://reactnavigation.org/docs/en/getting-started.html for setup instructions.";
|
"Couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'? See https://reactnavigation.org/docs/getting-started for setup instructions.";
|
||||||
|
|
||||||
const NOT_INITIALIZED_ERROR =
|
const NOT_INITIALIZED_ERROR =
|
||||||
"The 'navigation' object hasn't been initialized yet. This might happen if you don't have a navigator mounted, or if the navigator hasn't finished mounting. See https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html#handling-initialization for more details.";
|
"The 'navigation' object hasn't been initialized yet. This might happen if you don't have a navigator mounted, or if the navigator hasn't finished mounting. See https://reactnavigation.org/docs/navigating-without-navigation-prop#handling-initialization for more details.";
|
||||||
|
|
||||||
export const NavigationStateContext = React.createContext<{
|
export const NavigationStateContext = React.createContext<{
|
||||||
isDefault?: true;
|
isDefault?: true;
|
||||||
@@ -136,6 +136,7 @@ const BaseNavigationContainer = React.forwardRef(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { trackState, trackAction } = useDevTools({
|
const { trackState, trackAction } = useDevTools({
|
||||||
|
enabled: false,
|
||||||
name: '@react-navigation',
|
name: '@react-navigation',
|
||||||
reset,
|
reset,
|
||||||
state,
|
state,
|
||||||
@@ -238,7 +239,7 @@ const BaseNavigationContainer = React.forwardRef(
|
|||||||
hasWarnedForSerialization = true;
|
hasWarnedForSerialization = true;
|
||||||
|
|
||||||
console.warn(
|
console.warn(
|
||||||
"We found non-serializable values in the navigation state, which can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params. If you need to use components with callbacks in your options, you can use 'navigation.setOptions' instead. See https://reactnavigation.org/docs/en/troubleshooting.html#i-get-the-warning-we-found-non-serializable-values-in-the-navigation-state for more details."
|
"Non-serializable values were found in the navigation state, which can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params. If you need to use components with callbacks in your options, you can use 'navigation.setOptions' instead. See https://reactnavigation.org/docs/troubleshooting#i-get-the-warning-non-serializable-values-were-found-in-the-navigation-state for more details."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ type Props = {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MULTIPLE_NAVIGATOR_ERROR = `Another navigator is already registered for this container. You likely have multiple navigators under a single "NavigationContainer" or "Screen". Make sure each navigator is under a separate "Screen" container. See https://reactnavigation.org/docs/en/nesting-navigators.html for a guide on nesting.`;
|
const MULTIPLE_NAVIGATOR_ERROR = `Another navigator is already registered for this container. You likely have multiple navigators under a single "NavigationContainer" or "Screen". Make sure each navigator is under a separate "Screen" container. See https://reactnavigation.org/docs/nesting-navigators for a guide on nesting.`;
|
||||||
|
|
||||||
export const SingleNavigatorContext = React.createContext<
|
export const SingleNavigatorContext = React.createContext<
|
||||||
| {
|
| {
|
||||||
|
|||||||
@@ -10,10 +10,14 @@ import NavigationContext from './NavigationContext';
|
|||||||
import NavigationRouteContext from './NavigationRouteContext';
|
import NavigationRouteContext from './NavigationRouteContext';
|
||||||
import StaticContainer from './StaticContainer';
|
import StaticContainer from './StaticContainer';
|
||||||
import EnsureSingleNavigator from './EnsureSingleNavigator';
|
import EnsureSingleNavigator from './EnsureSingleNavigator';
|
||||||
import { NavigationProp, RouteConfig } from './types';
|
import { NavigationProp, RouteConfig, EventMapBase } from './types';
|
||||||
|
|
||||||
type Props<State extends NavigationState, ScreenOptions extends object> = {
|
type Props<
|
||||||
screen: RouteConfig<ParamListBase, string, ScreenOptions>;
|
State extends NavigationState,
|
||||||
|
ScreenOptions extends object,
|
||||||
|
EventMap extends EventMapBase
|
||||||
|
> = {
|
||||||
|
screen: RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>;
|
||||||
navigation: NavigationProp<ParamListBase, string, State, ScreenOptions>;
|
navigation: NavigationProp<ParamListBase, string, State, ScreenOptions>;
|
||||||
route: Route<string> & {
|
route: Route<string> & {
|
||||||
state?: NavigationState | PartialState<NavigationState>;
|
state?: NavigationState | PartialState<NavigationState>;
|
||||||
@@ -28,14 +32,15 @@ type Props<State extends NavigationState, ScreenOptions extends object> = {
|
|||||||
*/
|
*/
|
||||||
export default function SceneView<
|
export default function SceneView<
|
||||||
State extends NavigationState,
|
State extends NavigationState,
|
||||||
ScreenOptions extends object
|
ScreenOptions extends object,
|
||||||
|
EventMap extends EventMapBase
|
||||||
>({
|
>({
|
||||||
screen,
|
screen,
|
||||||
route,
|
route,
|
||||||
navigation,
|
navigation,
|
||||||
getState,
|
getState,
|
||||||
setState,
|
setState,
|
||||||
}: Props<State, ScreenOptions>) {
|
}: Props<State, ScreenOptions, EventMap>) {
|
||||||
const navigatorKeyRef = React.useRef<string | undefined>();
|
const navigatorKeyRef = React.useRef<string | undefined>();
|
||||||
|
|
||||||
const getKey = React.useCallback(() => navigatorKeyRef.current, []);
|
const getKey = React.useCallback(() => navigatorKeyRef.current, []);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ParamListBase } from '@react-navigation/routers';
|
import { ParamListBase, NavigationState } from '@react-navigation/routers';
|
||||||
import { RouteConfig } from './types';
|
import { RouteConfig, EventMapBase } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Empty component used for specifying route configuration.
|
* Empty component used for specifying route configuration.
|
||||||
@@ -7,8 +7,10 @@ import { RouteConfig } from './types';
|
|||||||
export default function Screen<
|
export default function Screen<
|
||||||
ParamList extends ParamListBase,
|
ParamList extends ParamListBase,
|
||||||
RouteName extends keyof ParamList,
|
RouteName extends keyof ParamList,
|
||||||
ScreenOptions extends object
|
State extends NavigationState,
|
||||||
>(_: RouteConfig<ParamList, RouteName, ScreenOptions>) {
|
ScreenOptions extends object,
|
||||||
|
EventMap extends EventMapBase
|
||||||
|
>(_: RouteConfig<ParamList, RouteName, State, ScreenOptions, EventMap>) {
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ it('throws when getState is accessed without a container', () => {
|
|||||||
const element = <Test />;
|
const element = <Test />;
|
||||||
|
|
||||||
expect(() => render(element).update(element)).toThrowError(
|
expect(() => render(element).update(element)).toThrowError(
|
||||||
"We couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'?"
|
"Couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'?"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ it('throws when setState is accessed without a container', () => {
|
|||||||
const element = <Test />;
|
const element = <Test />;
|
||||||
|
|
||||||
expect(() => render(element).update(element)).toThrowError(
|
expect(() => render(element).update(element)).toThrowError(
|
||||||
"We couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'?"
|
"Couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'?"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -372,7 +372,7 @@ it("doesn't update state if action wasn't handled", () => {
|
|||||||
expect(onStateChange).toBeCalledTimes(0);
|
expect(onStateChange).toBeCalledTimes(0);
|
||||||
|
|
||||||
expect(spy.mock.calls[0][0]).toMatch(
|
expect(spy.mock.calls[0][0]).toMatch(
|
||||||
"The action 'INVALID' with payload 'undefined' was not handled by any navigator."
|
"The action 'INVALID' was not handled by any navigator."
|
||||||
);
|
);
|
||||||
|
|
||||||
spy.mockRestore();
|
spy.mockRestore();
|
||||||
@@ -772,6 +772,60 @@ it('gives access to internal state', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('preserves order of screens in state with non-numeric names', () => {
|
||||||
|
const TestNavigator = (props: any): any => {
|
||||||
|
useNavigationBuilder(MockRouter, props);
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const navigation = React.createRef<NavigationContainerRef>();
|
||||||
|
|
||||||
|
const root = (
|
||||||
|
<BaseNavigationContainer ref={navigation}>
|
||||||
|
<TestNavigator>
|
||||||
|
<Screen name="foo" component={jest.fn()} />
|
||||||
|
<Screen name="bar" component={jest.fn()} />
|
||||||
|
<Screen name="baz" component={jest.fn()} />
|
||||||
|
</TestNavigator>
|
||||||
|
</BaseNavigationContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
render(root);
|
||||||
|
|
||||||
|
expect(navigation.current?.getRootState().routeNames).toEqual([
|
||||||
|
'foo',
|
||||||
|
'bar',
|
||||||
|
'baz',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves order of screens in state with numeric names', () => {
|
||||||
|
const TestNavigator = (props: any): any => {
|
||||||
|
useNavigationBuilder(MockRouter, props);
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const navigation = React.createRef<NavigationContainerRef>();
|
||||||
|
|
||||||
|
const root = (
|
||||||
|
<BaseNavigationContainer ref={navigation}>
|
||||||
|
<TestNavigator>
|
||||||
|
<Screen name="4" component={jest.fn()} />
|
||||||
|
<Screen name="7" component={jest.fn()} />
|
||||||
|
<Screen name="1" component={jest.fn()} />
|
||||||
|
</TestNavigator>
|
||||||
|
</BaseNavigationContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
render(root);
|
||||||
|
|
||||||
|
expect(navigation.current?.getRootState().routeNames).toEqual([
|
||||||
|
'4',
|
||||||
|
'7',
|
||||||
|
'1',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it("throws if navigator doesn't have any screens", () => {
|
it("throws if navigator doesn't have any screens", () => {
|
||||||
const TestNavigator = (props: any) => {
|
const TestNavigator = (props: any) => {
|
||||||
useNavigationBuilder(MockRouter, props);
|
useNavigationBuilder(MockRouter, props);
|
||||||
@@ -1031,7 +1085,7 @@ it('throws descriptive error for invalid screen component', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(() => render(element).update(element)).toThrowError(
|
expect(() => render(element).update(element)).toThrowError(
|
||||||
"Got an invalid value for 'component' prop for the screen 'foo'. It must be a a valid React Component."
|
"Got an invalid value for 'component' prop for the screen 'foo'. It must be a valid React Component."
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -188,10 +188,12 @@ it('fires focus and blur events in nested navigator', () => {
|
|||||||
|
|
||||||
expect(thirdFocusCallback).toBeCalledTimes(0);
|
expect(thirdFocusCallback).toBeCalledTimes(0);
|
||||||
expect(secondFocusCallback).toBeCalledTimes(1);
|
expect(secondFocusCallback).toBeCalledTimes(1);
|
||||||
|
expect(fourthBlurCallback).toBeCalledTimes(0);
|
||||||
|
|
||||||
act(() => parent.current.navigate('nested'));
|
act(() => parent.current.navigate('nested'));
|
||||||
|
|
||||||
expect(firstBlurCallback).toBeCalledTimes(1);
|
expect(firstBlurCallback).toBeCalledTimes(1);
|
||||||
|
expect(secondBlurCallback).toBeCalledTimes(1);
|
||||||
expect(thirdFocusCallback).toBeCalledTimes(0);
|
expect(thirdFocusCallback).toBeCalledTimes(0);
|
||||||
expect(fourthFocusCallback).toBeCalledTimes(1);
|
expect(fourthFocusCallback).toBeCalledTimes(1);
|
||||||
|
|
||||||
@@ -199,6 +201,35 @@ it('fires focus and blur events in nested navigator', () => {
|
|||||||
|
|
||||||
expect(fourthBlurCallback).toBeCalledTimes(1);
|
expect(fourthBlurCallback).toBeCalledTimes(1);
|
||||||
expect(thirdFocusCallback).toBeCalledTimes(1);
|
expect(thirdFocusCallback).toBeCalledTimes(1);
|
||||||
|
|
||||||
|
act(() => parent.current.navigate('first'));
|
||||||
|
|
||||||
|
expect(firstFocusCallback).toBeCalledTimes(2);
|
||||||
|
expect(thirdBlurCallback).toBeCalledTimes(1);
|
||||||
|
|
||||||
|
act(() => parent.current.navigate('nested', { screen: 'fourth' }));
|
||||||
|
|
||||||
|
expect(fourthFocusCallback).toBeCalledTimes(2);
|
||||||
|
expect(thirdBlurCallback).toBeCalledTimes(1);
|
||||||
|
expect(firstBlurCallback).toBeCalledTimes(2);
|
||||||
|
|
||||||
|
act(() => parent.current.navigate('nested', { screen: 'third' }));
|
||||||
|
|
||||||
|
expect(thirdFocusCallback).toBeCalledTimes(2);
|
||||||
|
expect(fourthBlurCallback).toBeCalledTimes(2);
|
||||||
|
|
||||||
|
// Make sure nothing else has changed
|
||||||
|
expect(firstFocusCallback).toBeCalledTimes(2);
|
||||||
|
expect(firstBlurCallback).toBeCalledTimes(2);
|
||||||
|
|
||||||
|
expect(secondFocusCallback).toBeCalledTimes(1);
|
||||||
|
expect(secondBlurCallback).toBeCalledTimes(1);
|
||||||
|
|
||||||
|
expect(thirdFocusCallback).toBeCalledTimes(2);
|
||||||
|
expect(thirdBlurCallback).toBeCalledTimes(1);
|
||||||
|
|
||||||
|
expect(fourthFocusCallback).toBeCalledTimes(2);
|
||||||
|
expect(fourthBlurCallback).toBeCalledTimes(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fires blur event when a route is removed with a delay', async () => {
|
it('fires blur event when a route is removed with a delay', async () => {
|
||||||
@@ -331,7 +362,7 @@ it('fires blur event when a route is removed with a delay', async () => {
|
|||||||
expect(blurCallback).toBeCalledTimes(1);
|
expect(blurCallback).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fires custom events', () => {
|
it('fires custom events added with addListener', () => {
|
||||||
const eventName = 'someSuperCoolEvent';
|
const eventName = 'someSuperCoolEvent';
|
||||||
|
|
||||||
const TestNavigator = React.forwardRef((props: any, ref: any): any => {
|
const TestNavigator = React.forwardRef((props: any, ref: any): any => {
|
||||||
@@ -378,10 +409,13 @@ it('fires custom events', () => {
|
|||||||
expect(secondCallback).toBeCalledTimes(0);
|
expect(secondCallback).toBeCalledTimes(0);
|
||||||
expect(thirdCallback).toBeCalledTimes(0);
|
expect(thirdCallback).toBeCalledTimes(0);
|
||||||
|
|
||||||
|
const target =
|
||||||
|
ref.current.state.routes[ref.current.state.routes.length - 1].key;
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
ref.current.navigation.emit({
|
ref.current.navigation.emit({
|
||||||
type: eventName,
|
type: eventName,
|
||||||
target: ref.current.state.routes[ref.current.state.routes.length - 1].key,
|
target,
|
||||||
data: 42,
|
data: 42,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -391,6 +425,7 @@ it('fires custom events', () => {
|
|||||||
expect(thirdCallback).toBeCalledTimes(1);
|
expect(thirdCallback).toBeCalledTimes(1);
|
||||||
expect(thirdCallback.mock.calls[0][0].type).toBe('someSuperCoolEvent');
|
expect(thirdCallback.mock.calls[0][0].type).toBe('someSuperCoolEvent');
|
||||||
expect(thirdCallback.mock.calls[0][0].data).toBe(42);
|
expect(thirdCallback.mock.calls[0][0].data).toBe(42);
|
||||||
|
expect(thirdCallback.mock.calls[0][0].target).toBe(target);
|
||||||
expect(thirdCallback.mock.calls[0][0].defaultPrevented).toBe(undefined);
|
expect(thirdCallback.mock.calls[0][0].defaultPrevented).toBe(undefined);
|
||||||
expect(thirdCallback.mock.calls[0][0].preventDefault).toBe(undefined);
|
expect(thirdCallback.mock.calls[0][0].preventDefault).toBe(undefined);
|
||||||
|
|
||||||
@@ -398,11 +433,197 @@ it('fires custom events', () => {
|
|||||||
ref.current.navigation.emit({ type: eventName });
|
ref.current.navigation.emit({ type: eventName });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(firstCallback.mock.calls[0][0].target).toBe(undefined);
|
||||||
|
expect(secondCallback.mock.calls[0][0].target).toBe(undefined);
|
||||||
|
expect(thirdCallback.mock.calls[1][0].target).toBe(undefined);
|
||||||
|
|
||||||
expect(firstCallback).toBeCalledTimes(1);
|
expect(firstCallback).toBeCalledTimes(1);
|
||||||
expect(secondCallback).toBeCalledTimes(1);
|
expect(secondCallback).toBeCalledTimes(1);
|
||||||
expect(thirdCallback).toBeCalledTimes(2);
|
expect(thirdCallback).toBeCalledTimes(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("doesn't call same listener multiple times with addListener", () => {
|
||||||
|
const eventName = 'someSuperCoolEvent';
|
||||||
|
|
||||||
|
const TestNavigator = React.forwardRef((props: any, ref: any): any => {
|
||||||
|
const { state, navigation, descriptors } = useNavigationBuilder(
|
||||||
|
MockRouter,
|
||||||
|
props
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useImperativeHandle(ref, () => ({ navigation, state }), [
|
||||||
|
navigation,
|
||||||
|
state,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return state.routes.map(route => descriptors[route.key].render());
|
||||||
|
});
|
||||||
|
|
||||||
|
const callback = jest.fn();
|
||||||
|
|
||||||
|
const Test = ({ navigation }: any) => {
|
||||||
|
React.useEffect(() => navigation.addListener(eventName, callback), [
|
||||||
|
navigation,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ref = React.createRef<any>();
|
||||||
|
|
||||||
|
const element = (
|
||||||
|
<BaseNavigationContainer>
|
||||||
|
<TestNavigator ref={ref}>
|
||||||
|
<Screen name="first" component={Test} />
|
||||||
|
<Screen name="second" component={Test} />
|
||||||
|
<Screen name="third" component={Test} />
|
||||||
|
</TestNavigator>
|
||||||
|
</BaseNavigationContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
render(element);
|
||||||
|
|
||||||
|
expect(callback).toBeCalledTimes(0);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
ref.current.navigation.emit({ type: eventName });
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(callback).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fires custom events added with listeners prop', () => {
|
||||||
|
const eventName = 'someSuperCoolEvent';
|
||||||
|
|
||||||
|
const TestNavigator = React.forwardRef((props: any, ref: any): any => {
|
||||||
|
const { state, navigation } = useNavigationBuilder(MockRouter, props);
|
||||||
|
|
||||||
|
React.useImperativeHandle(ref, () => ({ navigation, state }), [
|
||||||
|
navigation,
|
||||||
|
state,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const firstCallback = jest.fn();
|
||||||
|
const secondCallback = jest.fn();
|
||||||
|
const thirdCallback = jest.fn();
|
||||||
|
|
||||||
|
const ref = React.createRef<any>();
|
||||||
|
|
||||||
|
const element = (
|
||||||
|
<BaseNavigationContainer>
|
||||||
|
<TestNavigator ref={ref}>
|
||||||
|
<Screen
|
||||||
|
name="first"
|
||||||
|
listeners={{ someSuperCoolEvent: firstCallback }}
|
||||||
|
component={jest.fn()}
|
||||||
|
/>
|
||||||
|
<Screen
|
||||||
|
name="second"
|
||||||
|
listeners={{ someSuperCoolEvent: secondCallback }}
|
||||||
|
component={jest.fn()}
|
||||||
|
/>
|
||||||
|
<Screen
|
||||||
|
name="third"
|
||||||
|
listeners={{ someSuperCoolEvent: thirdCallback }}
|
||||||
|
component={jest.fn()}
|
||||||
|
/>
|
||||||
|
</TestNavigator>
|
||||||
|
</BaseNavigationContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
render(element);
|
||||||
|
|
||||||
|
expect(firstCallback).toBeCalledTimes(0);
|
||||||
|
expect(secondCallback).toBeCalledTimes(0);
|
||||||
|
expect(thirdCallback).toBeCalledTimes(0);
|
||||||
|
|
||||||
|
const target =
|
||||||
|
ref.current.state.routes[ref.current.state.routes.length - 1].key;
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
ref.current.navigation.emit({
|
||||||
|
type: eventName,
|
||||||
|
target,
|
||||||
|
data: 42,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(firstCallback).toBeCalledTimes(0);
|
||||||
|
expect(secondCallback).toBeCalledTimes(0);
|
||||||
|
expect(thirdCallback).toBeCalledTimes(1);
|
||||||
|
expect(thirdCallback.mock.calls[0][0].type).toBe('someSuperCoolEvent');
|
||||||
|
expect(thirdCallback.mock.calls[0][0].data).toBe(42);
|
||||||
|
expect(thirdCallback.mock.calls[0][0].target).toBe(target);
|
||||||
|
expect(thirdCallback.mock.calls[0][0].defaultPrevented).toBe(undefined);
|
||||||
|
expect(thirdCallback.mock.calls[0][0].preventDefault).toBe(undefined);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
ref.current.navigation.emit({ type: eventName });
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(firstCallback.mock.calls[0][0].target).toBe(undefined);
|
||||||
|
expect(secondCallback.mock.calls[0][0].target).toBe(undefined);
|
||||||
|
expect(thirdCallback.mock.calls[1][0].target).toBe(undefined);
|
||||||
|
|
||||||
|
expect(firstCallback).toBeCalledTimes(1);
|
||||||
|
expect(secondCallback).toBeCalledTimes(1);
|
||||||
|
expect(thirdCallback).toBeCalledTimes(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("doesn't call same listener multiple times with listeners", () => {
|
||||||
|
const eventName = 'someSuperCoolEvent';
|
||||||
|
|
||||||
|
const TestNavigator = React.forwardRef((props: any, ref: any): any => {
|
||||||
|
const { state, navigation } = useNavigationBuilder(MockRouter, props);
|
||||||
|
|
||||||
|
React.useImperativeHandle(ref, () => ({ navigation, state }), [
|
||||||
|
navigation,
|
||||||
|
state,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const callback = jest.fn();
|
||||||
|
|
||||||
|
const ref = React.createRef<any>();
|
||||||
|
|
||||||
|
const element = (
|
||||||
|
<BaseNavigationContainer>
|
||||||
|
<TestNavigator ref={ref}>
|
||||||
|
<Screen
|
||||||
|
name="first"
|
||||||
|
listeners={{ someSuperCoolEvent: callback }}
|
||||||
|
component={jest.fn()}
|
||||||
|
/>
|
||||||
|
<Screen
|
||||||
|
name="second"
|
||||||
|
listeners={{ someSuperCoolEvent: callback }}
|
||||||
|
component={jest.fn()}
|
||||||
|
/>
|
||||||
|
<Screen
|
||||||
|
name="third"
|
||||||
|
listeners={{ someSuperCoolEvent: callback }}
|
||||||
|
component={jest.fn()}
|
||||||
|
/>
|
||||||
|
</TestNavigator>
|
||||||
|
</BaseNavigationContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
render(element);
|
||||||
|
|
||||||
|
expect(callback).toBeCalledTimes(0);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
ref.current.navigation.emit({ type: eventName });
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(callback).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
it('has option to prevent default', () => {
|
it('has option to prevent default', () => {
|
||||||
expect.assertions(5);
|
expect.assertions(5);
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ it('throws if called outside a navigation context', () => {
|
|||||||
const Test = () => {
|
const Test = () => {
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
expect(() => useNavigation()).toThrow(
|
expect(() => useNavigation()).toThrow(
|
||||||
"We couldn't find a navigation object. Is your component inside a screen in a navigator?"
|
"Couldn't find a navigation object. Is your component inside a screen in a navigator?"
|
||||||
);
|
);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -374,7 +374,7 @@ it('logs error if no navigator handled the action', () => {
|
|||||||
render(element).update(element);
|
render(element).update(element);
|
||||||
|
|
||||||
expect(spy.mock.calls[0][0]).toMatch(
|
expect(spy.mock.calls[0][0]).toMatch(
|
||||||
"The action 'UNKNOWN' with payload 'undefined' was not handled by any navigator."
|
"The action 'UNKNOWN' was not handled by any navigator."
|
||||||
);
|
);
|
||||||
|
|
||||||
spy.mockRestore();
|
spy.mockRestore();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { ParamListBase } from '@react-navigation/routers';
|
import { ParamListBase, NavigationState } from '@react-navigation/routers';
|
||||||
import Screen from './Screen';
|
import Screen from './Screen';
|
||||||
import { TypedNavigator } from './types';
|
import { TypedNavigator, EventMapBase } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Higher order component to create a `Navigator` and `Screen` pair.
|
* Higher order component to create a `Navigator` and `Screen` pair.
|
||||||
@@ -11,17 +11,21 @@ import { TypedNavigator } from './types';
|
|||||||
* @returns Factory method to create a `Navigator` and `Screen` pair.
|
* @returns Factory method to create a `Navigator` and `Screen` pair.
|
||||||
*/
|
*/
|
||||||
export default function createNavigatorFactory<
|
export default function createNavigatorFactory<
|
||||||
|
State extends NavigationState,
|
||||||
ScreenOptions extends object,
|
ScreenOptions extends object,
|
||||||
|
EventMap extends EventMapBase,
|
||||||
NavigatorComponent extends React.ComponentType<any>
|
NavigatorComponent extends React.ComponentType<any>
|
||||||
>(Navigator: NavigatorComponent) {
|
>(Navigator: NavigatorComponent) {
|
||||||
return function<ParamList extends ParamListBase>(): TypedNavigator<
|
return function<ParamList extends ParamListBase>(): TypedNavigator<
|
||||||
ParamList,
|
ParamList,
|
||||||
|
State,
|
||||||
ScreenOptions,
|
ScreenOptions,
|
||||||
|
EventMap,
|
||||||
typeof Navigator
|
typeof Navigator
|
||||||
> {
|
> {
|
||||||
if (arguments[0] !== undefined) {
|
if (arguments[0] !== undefined) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Creating a navigator doesn't take an argument. Maybe you are trying to use React Navigation 4 API with React Navigation 5? See https://reactnavigation.org/docs/en/upgrading-from-4.x.html for migration guide."
|
"Creating a navigator doesn't take an argument. Maybe you are trying to use React Navigation 4 API with React Navigation 5? See https://reactnavigation.org/docs/upgrading-from-4.x for migration guide."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export type EventArg<
|
|||||||
* Type of the event (e.g. `focus`, `blur`)
|
* Type of the event (e.g. `focus`, `blur`)
|
||||||
*/
|
*/
|
||||||
readonly type: EventName;
|
readonly type: EventName;
|
||||||
|
readonly target?: string;
|
||||||
} & (CanPreventDefault extends true
|
} & (CanPreventDefault extends true
|
||||||
? {
|
? {
|
||||||
/**
|
/**
|
||||||
@@ -167,18 +168,6 @@ type NavigationHelpersCommon<
|
|||||||
| { name: RouteName; key?: string; params: ParamList[RouteName] }
|
| { name: RouteName; key?: string; params: ParamList[RouteName] }
|
||||||
): void;
|
): 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.
|
* Reset the navigation state to the provided state.
|
||||||
*
|
*
|
||||||
@@ -360,7 +349,9 @@ export type Descriptor<
|
|||||||
export type RouteConfig<
|
export type RouteConfig<
|
||||||
ParamList extends ParamListBase,
|
ParamList extends ParamListBase,
|
||||||
RouteName extends keyof ParamList,
|
RouteName extends keyof ParamList,
|
||||||
ScreenOptions extends object
|
State extends NavigationState,
|
||||||
|
ScreenOptions extends object,
|
||||||
|
EventMap extends EventMapBase
|
||||||
> = {
|
> = {
|
||||||
/**
|
/**
|
||||||
* Route name of this screen.
|
* Route name of this screen.
|
||||||
@@ -377,6 +368,16 @@ export type RouteConfig<
|
|||||||
navigation: any;
|
navigation: any;
|
||||||
}) => ScreenOptions);
|
}) => ScreenOptions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listeners for this screen.
|
||||||
|
*/
|
||||||
|
listeners?: Partial<
|
||||||
|
{
|
||||||
|
[EventName in keyof (EventMap &
|
||||||
|
EventMapCore<State>)]: EventListenerCallback<EventMap, EventName>;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initial params object for the route.
|
* Initial params object for the route.
|
||||||
*/
|
*/
|
||||||
@@ -420,7 +421,9 @@ export type NavigationContainerRef =
|
|||||||
|
|
||||||
export type TypedNavigator<
|
export type TypedNavigator<
|
||||||
ParamList extends ParamListBase,
|
ParamList extends ParamListBase,
|
||||||
|
State extends NavigationState,
|
||||||
ScreenOptions extends object,
|
ScreenOptions extends object,
|
||||||
|
EventMap extends EventMapBase,
|
||||||
Navigator extends React.ComponentType<any>
|
Navigator extends React.ComponentType<any>
|
||||||
> = {
|
> = {
|
||||||
/**
|
/**
|
||||||
@@ -451,6 +454,6 @@ export type TypedNavigator<
|
|||||||
* Component used for specifying route configuration.
|
* Component used for specifying route configuration.
|
||||||
*/
|
*/
|
||||||
Screen: <RouteName extends keyof ParamList>(
|
Screen: <RouteName extends keyof ParamList>(
|
||||||
_: RouteConfig<ParamList, RouteName, ScreenOptions>
|
_: RouteConfig<ParamList, RouteName, State, ScreenOptions, EventMap>
|
||||||
) => null;
|
) => null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,11 +13,24 @@ import NavigationBuilderContext, {
|
|||||||
} from './NavigationBuilderContext';
|
} from './NavigationBuilderContext';
|
||||||
import { NavigationEventEmitter } from './useEventEmitter';
|
import { NavigationEventEmitter } from './useEventEmitter';
|
||||||
import useNavigationCache from './useNavigationCache';
|
import useNavigationCache from './useNavigationCache';
|
||||||
import { Descriptor, NavigationHelpers, RouteConfig, RouteProp } from './types';
|
import {
|
||||||
|
Descriptor,
|
||||||
|
NavigationHelpers,
|
||||||
|
RouteConfig,
|
||||||
|
RouteProp,
|
||||||
|
EventMapBase,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
type Options<State extends NavigationState, ScreenOptions extends object> = {
|
type Options<
|
||||||
|
State extends NavigationState,
|
||||||
|
ScreenOptions extends object,
|
||||||
|
EventMap extends EventMapBase
|
||||||
|
> = {
|
||||||
state: State;
|
state: State;
|
||||||
screens: Record<string, RouteConfig<ParamListBase, string, ScreenOptions>>;
|
screens: Record<
|
||||||
|
string,
|
||||||
|
RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>
|
||||||
|
>;
|
||||||
navigation: NavigationHelpers<ParamListBase>;
|
navigation: NavigationHelpers<ParamListBase>;
|
||||||
screenOptions?:
|
screenOptions?:
|
||||||
| ScreenOptions
|
| ScreenOptions
|
||||||
@@ -49,7 +62,8 @@ type Options<State extends NavigationState, ScreenOptions extends object> = {
|
|||||||
*/
|
*/
|
||||||
export default function useDescriptors<
|
export default function useDescriptors<
|
||||||
State extends NavigationState,
|
State extends NavigationState,
|
||||||
ScreenOptions extends object
|
ScreenOptions extends object,
|
||||||
|
EventMap extends EventMapBase
|
||||||
>({
|
>({
|
||||||
state,
|
state,
|
||||||
screens,
|
screens,
|
||||||
@@ -64,7 +78,7 @@ export default function useDescriptors<
|
|||||||
onRouteFocus,
|
onRouteFocus,
|
||||||
router,
|
router,
|
||||||
emitter,
|
emitter,
|
||||||
}: Options<State, ScreenOptions>) {
|
}: Options<State, ScreenOptions, EventMap>) {
|
||||||
const [options, setOptions] = React.useState<Record<string, object>>({});
|
const [options, setOptions] = React.useState<Record<string, object>>({});
|
||||||
const { trackAction } = React.useContext(NavigationBuilderContext);
|
const { trackAction } = React.useContext(NavigationBuilderContext);
|
||||||
|
|
||||||
@@ -133,6 +147,7 @@ export default function useDescriptors<
|
|||||||
: screen.options({
|
: screen.options({
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
route,
|
route,
|
||||||
|
// @ts-ignore
|
||||||
navigation,
|
navigation,
|
||||||
})),
|
})),
|
||||||
// The options set via `navigation.setOptions`
|
// The options set via `navigation.setOptions`
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
type State = NavigationState | PartialState<NavigationState> | undefined;
|
type State = NavigationState | PartialState<NavigationState> | undefined;
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
|
enabled: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
reset: (state: NavigationState) => void;
|
reset: (state: NavigationState) => void;
|
||||||
state: State;
|
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>();
|
const devToolsRef = React.useRef<DevTools>();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
enabled &&
|
||||||
process.env.NODE_ENV !== 'production' &&
|
process.env.NODE_ENV !== 'production' &&
|
||||||
global.__REDUX_DEVTOOLS_EXTENSION__ &&
|
global.__REDUX_DEVTOOLS_EXTENSION__ &&
|
||||||
devToolsRef.current === undefined
|
devToolsRef.current === undefined
|
||||||
|
|||||||
@@ -5,12 +5,20 @@ export type NavigationEventEmitter = EventEmitter<Record<string, any>> & {
|
|||||||
create: (target: string) => EventConsumer<Record<string, any>>;
|
create: (target: string) => EventConsumer<Record<string, any>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Listeners = ((data: any) => void)[];
|
type Listeners = ((e: any) => void)[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook to manage the event system used by the navigator to notify screens of various events.
|
* Hook to manage the event system used by the navigator to notify screens of various events.
|
||||||
*/
|
*/
|
||||||
export default function useEventEmitter(): NavigationEventEmitter {
|
export default function useEventEmitter(
|
||||||
|
listen?: (e: any) => void
|
||||||
|
): NavigationEventEmitter {
|
||||||
|
const listenRef = React.useRef(listen);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
listenRef.current = listen;
|
||||||
|
});
|
||||||
|
|
||||||
const listeners = React.useRef<Record<string, Record<string, Listeners>>>({});
|
const listeners = React.useRef<Record<string, Record<string, Listeners>>>({});
|
||||||
|
|
||||||
const create = React.useCallback((target: string) => {
|
const create = React.useCallback((target: string) => {
|
||||||
@@ -60,7 +68,9 @@ export default function useEventEmitter(): NavigationEventEmitter {
|
|||||||
const callbacks =
|
const callbacks =
|
||||||
target !== undefined
|
target !== undefined
|
||||||
? items[target] && items[target].slice()
|
? items[target] && items[target].slice()
|
||||||
: ([] as Listeners).concat(...Object.keys(items).map(t => items[t]));
|
: ([] as Listeners)
|
||||||
|
.concat(...Object.keys(items).map(t => items[t]))
|
||||||
|
.filter((cb, i, self) => self.lastIndexOf(cb) === i);
|
||||||
|
|
||||||
const event: EventArg<any, any, any> = {
|
const event: EventArg<any, any, any> = {
|
||||||
get type() {
|
get type() {
|
||||||
@@ -68,8 +78,18 @@ export default function useEventEmitter(): NavigationEventEmitter {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (target !== undefined) {
|
||||||
|
Object.defineProperty(event, 'target', {
|
||||||
|
enumerable: true,
|
||||||
|
get() {
|
||||||
|
return target;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (data !== undefined) {
|
if (data !== undefined) {
|
||||||
Object.defineProperty(event, 'data', {
|
Object.defineProperty(event, 'data', {
|
||||||
|
enumerable: true,
|
||||||
get() {
|
get() {
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
@@ -81,11 +101,13 @@ export default function useEventEmitter(): NavigationEventEmitter {
|
|||||||
|
|
||||||
Object.defineProperties(event, {
|
Object.defineProperties(event, {
|
||||||
defaultPrevented: {
|
defaultPrevented: {
|
||||||
|
enumerable: true,
|
||||||
get() {
|
get() {
|
||||||
return defaultPrevented;
|
return defaultPrevented;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
preventDefault: {
|
preventDefault: {
|
||||||
|
enumerable: true,
|
||||||
value() {
|
value() {
|
||||||
defaultPrevented = true;
|
defaultPrevented = true;
|
||||||
},
|
},
|
||||||
@@ -93,6 +115,8 @@ export default function useEventEmitter(): NavigationEventEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listenRef.current?.(event);
|
||||||
|
|
||||||
callbacks?.forEach(cb => cb(event));
|
callbacks?.forEach(cb => cb(event));
|
||||||
|
|
||||||
return event as any;
|
return event as any;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export default function useFocusEffect(effect: EffectCallback) {
|
|||||||
' fetchData();\n' +
|
' fetchData();\n' +
|
||||||
' }, [someId])\n' +
|
' }, [someId])\n' +
|
||||||
'};\n\n' +
|
'};\n\n' +
|
||||||
'See usage guide: https://reactnavigation.org/docs/en/use-focus-effect.html';
|
'See usage guide: https://reactnavigation.org/docs/use-focus-effect';
|
||||||
} else {
|
} else {
|
||||||
message += ` You returned: '${JSON.stringify(destroy)}'`;
|
message += ` You returned: '${JSON.stringify(destroy)}'`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,17 +21,19 @@ export default function useFocusEvents({ state, emitter }: Options) {
|
|||||||
// Coz the child screen can't be focused if the parent screen is out of focus
|
// Coz the child screen can't be focused if the parent screen is out of focus
|
||||||
React.useEffect(
|
React.useEffect(
|
||||||
() =>
|
() =>
|
||||||
navigation?.addListener('focus', () =>
|
navigation?.addListener('focus', () => {
|
||||||
emitter.emit({ type: 'focus', target: currentFocusedKey })
|
lastFocusedKeyRef.current = currentFocusedKey;
|
||||||
),
|
emitter.emit({ type: 'focus', target: currentFocusedKey });
|
||||||
|
}),
|
||||||
[currentFocusedKey, emitter, navigation]
|
[currentFocusedKey, emitter, navigation]
|
||||||
);
|
);
|
||||||
|
|
||||||
React.useEffect(
|
React.useEffect(
|
||||||
() =>
|
() =>
|
||||||
navigation?.addListener('blur', () =>
|
navigation?.addListener('blur', () => {
|
||||||
emitter.emit({ type: 'blur', target: currentFocusedKey })
|
lastFocusedKeyRef.current = undefined;
|
||||||
),
|
emitter.emit({ type: 'blur', target: currentFocusedKey });
|
||||||
|
}),
|
||||||
[currentFocusedKey, emitter, navigation]
|
[currentFocusedKey, emitter, navigation]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -60,14 +62,7 @@ export default function useFocusEvents({ state, emitter }: Options) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
emitter.emit({
|
emitter.emit({ type: 'focus', target: currentFocusedKey });
|
||||||
type: 'focus',
|
emitter.emit({ type: 'blur', target: lastFocusedKey });
|
||||||
target: currentFocusedKey,
|
|
||||||
});
|
|
||||||
|
|
||||||
emitter.emit({
|
|
||||||
type: 'blur',
|
|
||||||
target: lastFocusedKey,
|
|
||||||
});
|
|
||||||
}, [currentFocusedKey, emitter, navigation]);
|
}, [currentFocusedKey, emitter, navigation]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function useNavigation<
|
|||||||
|
|
||||||
if (navigation === undefined) {
|
if (navigation === undefined) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"We couldn't find a navigation object. Is your component inside a screen in a navigator?"
|
"Couldn't find a navigation object. Is your component inside a screen in a navigator?"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
DefaultNavigatorOptions,
|
DefaultNavigatorOptions,
|
||||||
RouteConfig,
|
RouteConfig,
|
||||||
PrivateValueStore,
|
PrivateValueStore,
|
||||||
|
EventMapBase,
|
||||||
} from './types';
|
} from './types';
|
||||||
import useStateGetters from './useStateGetters';
|
import useStateGetters from './useStateGetters';
|
||||||
import useOnGetState from './useOnGetState';
|
import useOnGetState from './useOnGetState';
|
||||||
@@ -55,18 +56,28 @@ const isArrayEqual = (a: any[], b: any[]) =>
|
|||||||
*
|
*
|
||||||
* @param children React Elements to extract the config from.
|
* @param children React Elements to extract the config from.
|
||||||
*/
|
*/
|
||||||
const getRouteConfigsFromChildren = <ScreenOptions extends object>(
|
const getRouteConfigsFromChildren = <
|
||||||
|
State extends NavigationState,
|
||||||
|
ScreenOptions extends object,
|
||||||
|
EventMap extends EventMapBase
|
||||||
|
>(
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
) => {
|
) => {
|
||||||
const configs = React.Children.toArray(children).reduce<
|
const configs = React.Children.toArray(children).reduce<
|
||||||
RouteConfig<ParamListBase, string, ScreenOptions>[]
|
RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>[]
|
||||||
>((acc, child) => {
|
>((acc, child) => {
|
||||||
if (React.isValidElement(child)) {
|
if (React.isValidElement(child)) {
|
||||||
if (child.type === Screen) {
|
if (child.type === Screen) {
|
||||||
// We can only extract the config from `Screen` elements
|
// We can only extract the config from `Screen` elements
|
||||||
// If something else was rendered, it's probably a bug
|
// If something else was rendered, it's probably a bug
|
||||||
acc.push(
|
acc.push(
|
||||||
child.props as RouteConfig<ParamListBase, string, ScreenOptions>
|
child.props as RouteConfig<
|
||||||
|
ParamListBase,
|
||||||
|
string,
|
||||||
|
State,
|
||||||
|
ScreenOptions,
|
||||||
|
EventMap
|
||||||
|
>
|
||||||
);
|
);
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
@@ -75,7 +86,9 @@ const getRouteConfigsFromChildren = <ScreenOptions extends object>(
|
|||||||
// When we encounter a fragment, we need to dive into its children to extract the configs
|
// When we encounter a fragment, we need to dive into its children to extract the configs
|
||||||
// This is handy to conditionally define a group of screens
|
// This is handy to conditionally define a group of screens
|
||||||
acc.push(
|
acc.push(
|
||||||
...getRouteConfigsFromChildren<ScreenOptions>(child.props.children)
|
...getRouteConfigsFromChildren<State, ScreenOptions, EventMap>(
|
||||||
|
child.props.children
|
||||||
|
)
|
||||||
);
|
);
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
@@ -116,7 +129,7 @@ const getRouteConfigsFromChildren = <ScreenOptions extends object>(
|
|||||||
|
|
||||||
if (component !== undefined && !isValidElementType(component)) {
|
if (component !== undefined && !isValidElementType(component)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Got an invalid value for 'component' prop for the screen '${name}'. It must be a a valid React Component.`
|
`Got an invalid value for 'component' prop for the screen '${name}'. It must be a valid React Component.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,20 +190,29 @@ export default function useNavigationBuilder<
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const screens = getRouteConfigsFromChildren<ScreenOptions>(children).reduce<
|
const routeConfigs = getRouteConfigsFromChildren<
|
||||||
Record<string, RouteConfig<ParamListBase, string, ScreenOptions>>
|
State,
|
||||||
>((acc, curr) => {
|
ScreenOptions,
|
||||||
if (curr.name in acc) {
|
EventMap
|
||||||
|
>(children);
|
||||||
|
|
||||||
|
const screens = routeConfigs.reduce<
|
||||||
|
Record<
|
||||||
|
string,
|
||||||
|
RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>
|
||||||
|
>
|
||||||
|
>((acc, config) => {
|
||||||
|
if (config.name in acc) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${curr.name}')`
|
`A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${config.name}')`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
acc[curr.name] = curr;
|
acc[config.name] = config;
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
const routeNames = Object.keys(screens);
|
const routeNames = routeConfigs.map(config => config.name);
|
||||||
const routeParamList = routeNames.reduce<Record<string, object | undefined>>(
|
const routeParamList = routeNames.reduce<Record<string, object | undefined>>(
|
||||||
(acc, curr) => {
|
(acc, curr) => {
|
||||||
const { initialParams } = screens[curr];
|
const { initialParams } = screens[curr];
|
||||||
@@ -311,12 +333,14 @@ export default function useNavigationBuilder<
|
|||||||
: state;
|
: state;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state !== nextState) {
|
const shouldUpdate = state !== nextState;
|
||||||
// If the state needs to be updated, we'll schedule an update with React
|
|
||||||
// setState in render seems hacky, but that's how React docs implement getDerivedPropsFromState
|
React.useEffect(() => {
|
||||||
// https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-getderivedstatefromprops
|
if (shouldUpdate) {
|
||||||
setState(nextState);
|
// If the state needs to be updated, we'll schedule an update with React
|
||||||
}
|
setState(nextState);
|
||||||
|
}
|
||||||
|
}, [nextState, setState, shouldUpdate]);
|
||||||
|
|
||||||
// The up-to-date state will come in next render, but we don't need to wait for it
|
// The up-to-date state will come in next render, but we don't need to wait for it
|
||||||
// We can't use the outdated state since the screens have changed, which will cause error due to mismatched config
|
// We can't use the outdated state since the screens have changed, which will cause error due to mismatched config
|
||||||
@@ -343,7 +367,35 @@ export default function useNavigationBuilder<
|
|||||||
: (initializedStateRef.current as State);
|
: (initializedStateRef.current as State);
|
||||||
}, [getCurrentState, isStateInitialized]);
|
}, [getCurrentState, isStateInitialized]);
|
||||||
|
|
||||||
const emitter = useEventEmitter();
|
const emitter = useEventEmitter(e => {
|
||||||
|
let routeNames = [];
|
||||||
|
|
||||||
|
if (e.target) {
|
||||||
|
const name = state.routes.find(route => route.key === e.target)?.name;
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
routeNames.push(name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
routeNames.push(...Object.keys(screens));
|
||||||
|
}
|
||||||
|
|
||||||
|
const listeners = ([] as (((e: any) => void) | undefined)[])
|
||||||
|
.concat(
|
||||||
|
...routeNames.map(name => {
|
||||||
|
const { listeners } = screens[name];
|
||||||
|
|
||||||
|
return listeners
|
||||||
|
? Object.keys(listeners)
|
||||||
|
.filter(type => type === e.type)
|
||||||
|
.map(type => listeners[type])
|
||||||
|
: undefined;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
|
||||||
|
|
||||||
|
listeners.forEach(listener => listener?.(e));
|
||||||
|
});
|
||||||
|
|
||||||
useFocusEvents({ state, emitter });
|
useFocusEvents({ state, emitter });
|
||||||
|
|
||||||
@@ -399,7 +451,7 @@ export default function useNavigationBuilder<
|
|||||||
getStateForRoute,
|
getStateForRoute,
|
||||||
});
|
});
|
||||||
|
|
||||||
const descriptors = useDescriptors<State, ScreenOptions>({
|
const descriptors = useDescriptors<State, ScreenOptions, EventMap>({
|
||||||
state,
|
state,
|
||||||
screens,
|
screens,
|
||||||
navigation,
|
navigation,
|
||||||
|
|||||||
@@ -36,18 +36,45 @@ export default function useNavigationHelpers<
|
|||||||
const parentNavigationHelpers = React.useContext(NavigationContext);
|
const parentNavigationHelpers = React.useContext(NavigationContext);
|
||||||
|
|
||||||
return React.useMemo(() => {
|
return React.useMemo(() => {
|
||||||
const dispatch = (action: Action | ((state: State) => Action)) => {
|
const dispatch = (op: Action | ((state: State) => Action)) => {
|
||||||
const payload =
|
const action = typeof op === 'function' ? op(getState()) : op;
|
||||||
typeof action === 'function' ? action(getState()) : action;
|
|
||||||
|
|
||||||
const handled = onAction(payload);
|
const handled = onAction(action);
|
||||||
|
|
||||||
if (!handled && process.env.NODE_ENV !== 'production') {
|
if (!handled && process.env.NODE_ENV !== 'production') {
|
||||||
console.error(
|
const payload: Record<string, any> | undefined = action.payload;
|
||||||
`The action '${payload.type}' with payload '${JSON.stringify(
|
|
||||||
payload.payload
|
let message = `The action '${action.type}'${
|
||||||
)}' was not handled by any navigator. If you are trying to navigate to a screen, check if the screen exists in your navigator. If you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/en/nesting-navigators.html#navigating-to-a-screen-in-a-nested-navigator.`
|
payload ? ` with payload ${JSON.stringify(action.payload)}` : ''
|
||||||
);
|
} was not handled by any navigator.`;
|
||||||
|
|
||||||
|
switch (action.type) {
|
||||||
|
case 'NAVIGATE':
|
||||||
|
case 'PUSH':
|
||||||
|
case 'REPLACE':
|
||||||
|
case 'JUMP_TO':
|
||||||
|
if (payload?.name) {
|
||||||
|
message += `\n\nDo you have a screen named '${payload.name}'?\n\nIf you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator.`;
|
||||||
|
} else {
|
||||||
|
message += `\n\nYou need to pass the name of the screen to navigate to.\n\nSee https://reactnavigation.org/docs/navigation-actions for usage.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'GO_BACK':
|
||||||
|
case 'POP':
|
||||||
|
case 'POP_TO_TOP':
|
||||||
|
message += `\n\nIs there any screen to go back to?`;
|
||||||
|
break;
|
||||||
|
case 'OPEN_DRAWER':
|
||||||
|
case 'CLOSE_DRAWER':
|
||||||
|
case 'TOGGLE_DRAWER':
|
||||||
|
message += `\n\nIs your screen inside a Drawer navigator?`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
message += `\n\nThis is a development-only warning and won't be shown in production.`;
|
||||||
|
|
||||||
|
console.error(message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function useRoute<
|
|||||||
|
|
||||||
if (route === undefined) {
|
if (route === undefined) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"We couldn't find a route object. Is your component inside a screen in a navigator?"
|
"Couldn't find a route object. Is your component inside a screen in a navigator?"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,77 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.7...@react-navigation/drawer@5.1.0) (2020-02-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add ability add listeners with listeners prop ([1624108](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/162410843c4f175ae107756de1c3af04d1d47aa7)), closes [#6756](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/6756)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.6...@react-navigation/drawer@5.0.7) (2020-02-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.0.6](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.5...@react-navigation/drawer@5.0.6) (2020-02-19)
|
## [5.0.6](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.5...@react-navigation/drawer@5.0.6) (2020-02-19)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/drawer",
|
"name": "@react-navigation/drawer",
|
||||||
"description": "Drawer navigator component with animated transitions and gesturess",
|
"description": "Drawer navigator component with animated transitions and gesturess",
|
||||||
"version": "5.0.6",
|
"version": "5.3.2",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -39,17 +39,17 @@
|
|||||||
"react-native-iphone-x-helper": "^1.2.1"
|
"react-native-iphone-x-helper": "^1.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.9.3",
|
"@react-native-community/bob": "^0.10.0",
|
||||||
"@react-navigation/native": "^5.0.6",
|
"@react-navigation/native": "^5.1.1",
|
||||||
"@types/react": "^16.9.19",
|
"@types/react": "^16.9.23",
|
||||||
"@types/react-native": "^0.60.30",
|
"@types/react-native": "^0.61.22",
|
||||||
"del-cli": "^3.0.0",
|
"del-cli": "^3.0.0",
|
||||||
"react": "~16.9.0",
|
"react": "~16.9.0",
|
||||||
"react-native": "~0.61.5",
|
"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-reanimated": "^1.7.0",
|
||||||
"react-native-safe-area-context": "^0.7.2",
|
"react-native-safe-area-context": "^0.7.3",
|
||||||
"react-native-screens": "^2.0.0-beta.2",
|
"react-native-screens": "^2.3.0",
|
||||||
"typescript": "^3.7.5"
|
"typescript": "^3.7.5"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
"react-native-gesture-handler": ">= 1.0.0",
|
"react-native-gesture-handler": ">= 1.0.0",
|
||||||
"react-native-reanimated": ">= 1.0.0",
|
"react-native-reanimated": ">= 1.0.0",
|
||||||
"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"
|
"react-native-screens": ">= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0"
|
||||||
},
|
},
|
||||||
"@react-native-community/bob": {
|
"@react-native-community/bob": {
|
||||||
"source": "src",
|
"source": "src",
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ function DrawerNavigator({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default createNavigatorFactory<
|
export default createNavigatorFactory<
|
||||||
|
DrawerNavigationState,
|
||||||
DrawerNavigationOptions,
|
DrawerNavigationOptions,
|
||||||
|
DrawerNavigationEventMap,
|
||||||
typeof DrawerNavigator
|
typeof DrawerNavigator
|
||||||
>(DrawerNavigator);
|
>(DrawerNavigator);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Descriptor,
|
Descriptor,
|
||||||
NavigationHelpers,
|
NavigationHelpers,
|
||||||
DrawerNavigationState,
|
DrawerNavigationState,
|
||||||
|
DrawerActionHelpers,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
import { PanGestureHandler } from 'react-native-gesture-handler';
|
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.
|
* - `front`: Traditional drawer which covers the screen with a overlay behind it.
|
||||||
* - `back`: The drawer is revealed behind the screen on swipe.
|
* - `back`: The drawer is revealed behind the screen on swipe.
|
||||||
* - `slide`: Both the screen and the drawer slide on swipe to reveal the drawer.
|
* - `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.
|
* How far from the edge of the screen the swipe gesture should activate.
|
||||||
*/
|
*/
|
||||||
@@ -190,22 +192,8 @@ export type DrawerNavigationProp<
|
|||||||
DrawerNavigationState,
|
DrawerNavigationState,
|
||||||
DrawerNavigationOptions,
|
DrawerNavigationOptions,
|
||||||
DrawerNavigationEventMap
|
DrawerNavigationEventMap
|
||||||
> & {
|
> &
|
||||||
/**
|
DrawerActionHelpers<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 type DrawerDescriptor = Descriptor<
|
export type DrawerDescriptor = Descriptor<
|
||||||
ParamListBase,
|
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 * as React from 'react';
|
||||||
import { useNavigation, ParamListBase } from '@react-navigation/native';
|
import DrawerOpenContext from './DrawerOpenContext';
|
||||||
import { DrawerNavigationProp } from '../types';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook to detect if the drawer is open in a parent navigator.
|
* Hook to detect if the drawer is open in a parent navigator.
|
||||||
*/
|
*/
|
||||||
export default function useIsDrawerOpen() {
|
export default function useIsDrawerOpen() {
|
||||||
const navigation = useNavigation();
|
const isDrawerOpen = React.useContext(DrawerOpenContext);
|
||||||
|
|
||||||
let drawer = navigation as DrawerNavigationProp<ParamListBase>;
|
if (typeof isDrawerOpen !== 'boolean') {
|
||||||
|
throw new Error(
|
||||||
// The screen might be inside another navigator such as stack nested in drawer
|
"Couldn't find a drawer. Is your component inside a drawer navigator?"
|
||||||
// We need to find the closest drawer navigator and add the listener there
|
);
|
||||||
while (drawer && drawer.dangerouslyGetState().type !== 'drawer') {
|
|
||||||
drawer = drawer.dangerouslyGetParent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
return isDrawerOpen;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ const SPRING_CONFIG = {
|
|||||||
restSpeedThreshold: 0.01,
|
restSpeedThreshold: 0.01,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ANIMATED_ONE = new Animated.Value(1);
|
||||||
|
|
||||||
type Binary = 0 | 1;
|
type Binary = 0 | 1;
|
||||||
|
|
||||||
type Renderer = (props: { progress: Animated.Node<number> }) => React.ReactNode;
|
type Renderer = (props: { progress: Animated.Node<number> }) => React.ReactNode;
|
||||||
@@ -79,7 +81,7 @@ type Props = {
|
|||||||
onGestureRef?: (ref: PanGestureHandler | null) => void;
|
onGestureRef?: (ref: PanGestureHandler | null) => void;
|
||||||
gestureEnabled: boolean;
|
gestureEnabled: boolean;
|
||||||
drawerPosition: 'left' | 'right';
|
drawerPosition: 'left' | 'right';
|
||||||
drawerType: 'front' | 'back' | 'slide';
|
drawerType: 'front' | 'back' | 'slide' | 'permanent';
|
||||||
keyboardDismissMode: 'none' | 'on-drag';
|
keyboardDismissMode: 'none' | 'on-drag';
|
||||||
swipeEdgeWidth: number;
|
swipeEdgeWidth: number;
|
||||||
swipeDistanceThreshold?: number;
|
swipeDistanceThreshold?: number;
|
||||||
@@ -125,6 +127,12 @@ export default class DrawerView extends React.PureComponent<Props> {
|
|||||||
statusBarAnimation: 'slide',
|
statusBarAnimation: 'slide',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if (Platform.OS === 'web') {
|
||||||
|
document?.body?.addEventListener?.('keyup', this.handleEscape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: Props) {
|
componentDidUpdate(prevProps: Props) {
|
||||||
const {
|
const {
|
||||||
open,
|
open,
|
||||||
@@ -180,8 +188,22 @@ export default class DrawerView extends React.PureComponent<Props> {
|
|||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.toggleStatusBar(false);
|
this.toggleStatusBar(false);
|
||||||
this.handleEndInteraction();
|
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 = () => {
|
private handleEndInteraction = () => {
|
||||||
if (this.interactionHandle !== undefined) {
|
if (this.interactionHandle !== undefined) {
|
||||||
InteractionManager.clearInteractionHandle(this.interactionHandle);
|
InteractionManager.clearInteractionHandle(this.interactionHandle);
|
||||||
@@ -544,6 +566,7 @@ export default class DrawerView extends React.PureComponent<Props> {
|
|||||||
gestureHandlerProps,
|
gestureHandlerProps,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
const isOpen = drawerType === 'permanent' ? true : open;
|
||||||
const isRight = drawerPosition === 'right';
|
const isRight = drawerPosition === 'right';
|
||||||
|
|
||||||
const contentTranslateX = drawerType === 'front' ? 0 : this.translateX;
|
const contentTranslateX = drawerType === 'front' ? 0 : this.translateX;
|
||||||
@@ -569,8 +592,10 @@ export default class DrawerView extends React.PureComponent<Props> {
|
|||||||
const hitSlop = isRight
|
const hitSlop = isRight
|
||||||
? // Extend hitSlop to the side of the screen when drawer is closed
|
? // 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
|
// This lets the user drag the drawer from the side of the screen
|
||||||
{ right: 0, width: open ? undefined : swipeEdgeWidth }
|
{ right: 0, width: isOpen ? undefined : swipeEdgeWidth }
|
||||||
: { left: 0, width: open ? undefined : swipeEdgeWidth };
|
: { left: 0, width: isOpen ? undefined : swipeEdgeWidth };
|
||||||
|
|
||||||
|
const progress = drawerType === 'permanent' ? ANIMATED_ONE : this.progress;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanGestureHandler
|
<PanGestureHandler
|
||||||
@@ -580,62 +605,83 @@ export default class DrawerView extends React.PureComponent<Props> {
|
|||||||
onGestureEvent={this.handleGestureEvent}
|
onGestureEvent={this.handleGestureEvent}
|
||||||
onHandlerStateChange={this.handleGestureStateChange}
|
onHandlerStateChange={this.handleGestureStateChange}
|
||||||
hitSlop={hitSlop}
|
hitSlop={hitSlop}
|
||||||
enabled={gestureEnabled}
|
enabled={drawerType !== 'permanent' && gestureEnabled}
|
||||||
{...gestureHandlerProps}
|
{...gestureHandlerProps}
|
||||||
>
|
>
|
||||||
<Animated.View
|
<Animated.View
|
||||||
onLayout={this.handleContainerLayout}
|
onLayout={this.handleContainerLayout}
|
||||||
style={styles.main}
|
style={[
|
||||||
|
styles.main,
|
||||||
|
{
|
||||||
|
flexDirection:
|
||||||
|
drawerType === 'permanent' && !isRight ? 'row-reverse' : 'row',
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Animated.View
|
<Animated.View
|
||||||
style={[
|
style={[
|
||||||
styles.content,
|
styles.content,
|
||||||
{
|
drawerType !== 'permanent' && {
|
||||||
transform: [{ translateX: contentTranslateX }],
|
transform: [{ translateX: contentTranslateX }],
|
||||||
},
|
},
|
||||||
sceneContainerStyle as any,
|
sceneContainerStyle as any,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
accessibilityElementsHidden={open}
|
accessibilityElementsHidden={isOpen}
|
||||||
importantForAccessibility={open ? 'no-hide-descendants' : 'auto'}
|
importantForAccessibility={
|
||||||
|
isOpen ? 'no-hide-descendants' : 'auto'
|
||||||
|
}
|
||||||
style={styles.content}
|
style={styles.content}
|
||||||
>
|
>
|
||||||
{renderSceneContent({ progress: this.progress })}
|
{renderSceneContent({ progress })}
|
||||||
</View>
|
</View>
|
||||||
<TapGestureHandler
|
{// Disable overlay if sidebar is permanent
|
||||||
enabled={gestureEnabled}
|
drawerType === 'permanent' ? null : (
|
||||||
onHandlerStateChange={this.handleTapStateChange}
|
<TapGestureHandler
|
||||||
>
|
enabled={gestureEnabled}
|
||||||
<Overlay progress={this.progress} style={overlayStyle} />
|
onHandlerStateChange={this.handleTapStateChange}
|
||||||
</TapGestureHandler>
|
>
|
||||||
|
<Overlay progress={progress} style={overlayStyle} />
|
||||||
|
</TapGestureHandler>
|
||||||
|
)}
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
<Animated.Code
|
{drawerType === 'permanent' ? null : (
|
||||||
exec={block([
|
<Animated.Code
|
||||||
onChange(this.manuallyTriggerSpring, [
|
exec={block([
|
||||||
cond(eq(this.manuallyTriggerSpring, TRUE), [
|
onChange(this.manuallyTriggerSpring, [
|
||||||
set(this.nextIsOpen, FALSE),
|
cond(eq(this.manuallyTriggerSpring, TRUE), [
|
||||||
call([], () => (this.currentOpenValue = false)),
|
set(this.nextIsOpen, FALSE),
|
||||||
|
call([], () => (this.currentOpenValue = false)),
|
||||||
|
]),
|
||||||
]),
|
]),
|
||||||
]),
|
])}
|
||||||
])}
|
/>
|
||||||
/>
|
)}
|
||||||
<Animated.View
|
<Animated.View
|
||||||
accessibilityViewIsModal={open}
|
accessibilityViewIsModal={isOpen}
|
||||||
removeClippedSubviews={Platform.OS !== 'ios'}
|
removeClippedSubviews={Platform.OS !== 'ios'}
|
||||||
onLayout={this.handleDrawerLayout}
|
onLayout={this.handleDrawerLayout}
|
||||||
style={[
|
style={[
|
||||||
styles.container,
|
styles.container,
|
||||||
isRight ? { right: offset } : { left: offset },
|
drawerType === 'permanent'
|
||||||
{
|
? // Without this, the `left`/`right` values don't get reset
|
||||||
transform: [{ translateX: drawerTranslateX }],
|
isRight
|
||||||
opacity: this.drawerOpacity,
|
? { right: 0 }
|
||||||
zIndex: drawerType === 'back' ? -1 : 0,
|
: { left: 0 }
|
||||||
},
|
: [
|
||||||
|
styles.nonPermanent,
|
||||||
|
{
|
||||||
|
transform: [{ translateX: drawerTranslateX }],
|
||||||
|
opacity: this.drawerOpacity,
|
||||||
|
},
|
||||||
|
isRight ? { right: offset } : { left: offset },
|
||||||
|
{ zIndex: drawerType === 'back' ? -1 : 0 },
|
||||||
|
],
|
||||||
drawerStyle as any,
|
drawerStyle as any,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{renderDrawerContent({ progress: this.progress })}
|
{renderDrawerContent({ progress })}
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
</PanGestureHandler>
|
</PanGestureHandler>
|
||||||
@@ -646,11 +692,13 @@ export default class DrawerView extends React.PureComponent<Props> {
|
|||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
backgroundColor: 'white',
|
backgroundColor: 'white',
|
||||||
|
maxWidth: '100%',
|
||||||
|
},
|
||||||
|
nonPermanent: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
width: '80%',
|
width: '80%',
|
||||||
maxWidth: '100%',
|
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import {
|
|||||||
DrawerNavigationHelpers,
|
DrawerNavigationHelpers,
|
||||||
DrawerContentComponentProps,
|
DrawerContentComponentProps,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
import DrawerOpenContext from '../utils/DrawerOpenContext';
|
||||||
import DrawerPositionContext from '../utils/DrawerPositionContext';
|
import DrawerPositionContext from '../utils/DrawerPositionContext';
|
||||||
|
|
||||||
type Props = DrawerNavigationConfig & {
|
type Props = DrawerNavigationConfig & {
|
||||||
@@ -88,15 +89,17 @@ export default function DrawerView({
|
|||||||
sceneContainerStyle,
|
sceneContainerStyle,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const [loaded, setLoaded] = React.useState([state.index]);
|
const [loaded, setLoaded] = React.useState([state.index]);
|
||||||
const [drawerWidth, setDrawerWidth] = React.useState(() =>
|
const [drawerWidth, setDrawerWidth] = React.useState(() => {
|
||||||
getDefaultDrawerWidth(Dimensions.get('window'))
|
const { height = 0, width = 0 } = Dimensions.get('window');
|
||||||
);
|
|
||||||
|
return getDefaultDrawerWidth({ height, width });
|
||||||
|
});
|
||||||
|
|
||||||
const drawerGestureRef = React.useRef<PanGestureHandler>(null);
|
const drawerGestureRef = React.useRef<PanGestureHandler>(null);
|
||||||
|
|
||||||
const { colors } = useTheme();
|
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(() => {
|
const handleDrawerOpen = React.useCallback(() => {
|
||||||
navigation.dispatch({
|
navigation.dispatch({
|
||||||
@@ -203,36 +206,48 @@ export default function DrawerView({
|
|||||||
<GestureHandlerWrapper style={styles.content}>
|
<GestureHandlerWrapper style={styles.content}>
|
||||||
<SafeAreaProviderCompat>
|
<SafeAreaProviderCompat>
|
||||||
<DrawerGestureContext.Provider value={drawerGestureRef}>
|
<DrawerGestureContext.Provider value={drawerGestureRef}>
|
||||||
<Drawer
|
<DrawerOpenContext.Provider value={isDrawerOpen}>
|
||||||
open={isDrawerOpen}
|
<Drawer
|
||||||
gestureEnabled={gestureEnabled}
|
open={isDrawerOpen}
|
||||||
onOpen={handleDrawerOpen}
|
gestureEnabled={gestureEnabled}
|
||||||
onClose={handleDrawerClose}
|
onOpen={handleDrawerOpen}
|
||||||
onGestureRef={ref => {
|
onClose={handleDrawerClose}
|
||||||
// @ts-ignore
|
onGestureRef={ref => {
|
||||||
drawerGestureRef.current = ref;
|
// @ts-ignore
|
||||||
}}
|
drawerGestureRef.current = ref;
|
||||||
gestureHandlerProps={gestureHandlerProps}
|
}}
|
||||||
drawerType={drawerType}
|
gestureHandlerProps={gestureHandlerProps}
|
||||||
drawerPosition={drawerPosition}
|
drawerType={drawerType}
|
||||||
sceneContainerStyle={[
|
drawerPosition={drawerPosition}
|
||||||
{ backgroundColor: colors.background },
|
sceneContainerStyle={[
|
||||||
sceneContainerStyle,
|
{ backgroundColor: colors.background },
|
||||||
]}
|
sceneContainerStyle,
|
||||||
drawerStyle={[
|
]}
|
||||||
{ width: drawerWidth, backgroundColor: colors.card },
|
drawerStyle={[
|
||||||
drawerStyle,
|
{ width: drawerWidth, backgroundColor: colors.card },
|
||||||
]}
|
drawerType === 'permanent' &&
|
||||||
overlayStyle={{ backgroundColor: overlayColor }}
|
(drawerPosition === 'left'
|
||||||
swipeEdgeWidth={edgeWidth}
|
? {
|
||||||
swipeDistanceThreshold={minSwipeDistance}
|
borderRightColor: colors.border,
|
||||||
hideStatusBar={hideStatusBar}
|
borderRightWidth: StyleSheet.hairlineWidth,
|
||||||
statusBarAnimation={statusBarAnimation}
|
}
|
||||||
renderDrawerContent={renderNavigationView}
|
: {
|
||||||
renderSceneContent={renderContent}
|
borderLeftColor: colors.border,
|
||||||
keyboardDismissMode={keyboardDismissMode}
|
borderLeftWidth: StyleSheet.hairlineWidth,
|
||||||
drawerPostion={drawerPosition}
|
}),
|
||||||
/>
|
drawerStyle,
|
||||||
|
]}
|
||||||
|
overlayStyle={{ backgroundColor: overlayColor }}
|
||||||
|
swipeEdgeWidth={edgeWidth}
|
||||||
|
swipeDistanceThreshold={minSwipeDistance}
|
||||||
|
hideStatusBar={hideStatusBar}
|
||||||
|
statusBarAnimation={statusBarAnimation}
|
||||||
|
renderDrawerContent={renderNavigationView}
|
||||||
|
renderSceneContent={renderContent}
|
||||||
|
keyboardDismissMode={keyboardDismissMode}
|
||||||
|
drawerPostion={drawerPosition}
|
||||||
|
/>
|
||||||
|
</DrawerOpenContext.Provider>
|
||||||
</DrawerGestureContext.Provider>
|
</DrawerGestureContext.Provider>
|
||||||
</SafeAreaProviderCompat>
|
</SafeAreaProviderCompat>
|
||||||
</GestureHandlerWrapper>
|
</GestureHandlerWrapper>
|
||||||
|
|||||||
@@ -9,21 +9,29 @@ type Props = {
|
|||||||
style?: any;
|
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> {
|
export default class ResourceSavingScene extends React.Component<Props> {
|
||||||
render() {
|
render() {
|
||||||
if (screensEnabled?.()) {
|
// react-native-screens is buggy on web
|
||||||
|
if (screensEnabled?.() && Platform.OS !== 'web') {
|
||||||
const { isVisible, ...rest } = this.props;
|
const { isVisible, ...rest } = this.props;
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return <Screen active={isVisible ? 1 : 0} {...rest} />;
|
return <Screen active={isVisible ? 1 : 0} {...rest} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { isVisible, children, style, ...rest } = this.props;
|
const { isVisible, children, style, ...rest } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[styles.container, style]}
|
style={[
|
||||||
|
styles.container,
|
||||||
|
Platform.OS === 'web'
|
||||||
|
? { display: isVisible ? 'flex' : 'none' }
|
||||||
|
: null,
|
||||||
|
style,
|
||||||
|
]}
|
||||||
collapsable={false}
|
collapsable={false}
|
||||||
removeClippedSubviews={
|
removeClippedSubviews={
|
||||||
// On iOS, set removeClippedSubviews to true only when not focused
|
// On iOS, set removeClippedSubviews to true only when not focused
|
||||||
|
|||||||
@@ -3,6 +3,57 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.7...@react-navigation/material-bottom-tabs@5.1.0) (2020-02-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add ability add listeners with listeners prop ([1624108](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/commit/162410843c4f175ae107756de1c3af04d1d47aa7)), closes [#6756](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/issues/6756)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.6...@react-navigation/material-bottom-tabs@5.0.7) (2020-02-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.0.6](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.5...@react-navigation/material-bottom-tabs@5.0.6) (2020-02-19)
|
## [5.0.6](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.5...@react-navigation/material-bottom-tabs@5.0.6) (2020-02-19)
|
||||||
|
|
||||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/material-bottom-tabs",
|
"name": "@react-navigation/material-bottom-tabs",
|
||||||
"description": "Integration for bottom navigation component from react-native-paper",
|
"description": "Integration for bottom navigation component from react-native-paper",
|
||||||
"version": "5.0.6",
|
"version": "5.1.4",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -35,10 +35,10 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.9.3",
|
"@react-native-community/bob": "^0.10.0",
|
||||||
"@react-navigation/native": "^5.0.6",
|
"@react-navigation/native": "^5.1.1",
|
||||||
"@types/react": "^16.9.19",
|
"@types/react": "^16.9.23",
|
||||||
"@types/react-native": "^0.60.30",
|
"@types/react-native": "^0.61.22",
|
||||||
"@types/react-native-vector-icons": "^6.4.5",
|
"@types/react-native-vector-icons": "^6.4.5",
|
||||||
"del-cli": "^3.0.0",
|
"del-cli": "^3.0.0",
|
||||||
"react": "~16.9.0",
|
"react": "~16.9.0",
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ function MaterialBottomTabNavigator({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default createNavigatorFactory<
|
export default createNavigatorFactory<
|
||||||
|
TabNavigationState,
|
||||||
MaterialBottomTabNavigationOptions,
|
MaterialBottomTabNavigationOptions,
|
||||||
|
MaterialBottomTabNavigationEventMap,
|
||||||
typeof MaterialBottomTabNavigator
|
typeof MaterialBottomTabNavigator
|
||||||
>(MaterialBottomTabNavigator);
|
>(MaterialBottomTabNavigator);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
NavigationProp,
|
NavigationProp,
|
||||||
NavigationHelpers,
|
NavigationHelpers,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
|
TabActionHelpers,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
export type MaterialBottomTabNavigationEventMap = {
|
export type MaterialBottomTabNavigationEventMap = {
|
||||||
@@ -28,19 +29,8 @@ export type MaterialBottomTabNavigationProp<
|
|||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
MaterialBottomTabNavigationOptions,
|
MaterialBottomTabNavigationOptions,
|
||||||
MaterialBottomTabNavigationEventMap
|
MaterialBottomTabNavigationEventMap
|
||||||
> & {
|
> &
|
||||||
/**
|
TabActionHelpers<ParamList>;
|
||||||
* 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;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type MaterialBottomTabNavigationOptions = {
|
export type MaterialBottomTabNavigationOptions = {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,6 +3,57 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.0.7...@react-navigation/material-top-tabs@5.1.0) (2020-02-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add ability add listeners with listeners prop ([1624108](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/commit/162410843c4f175ae107756de1c3af04d1d47aa7)), closes [#6756](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/issues/6756)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.0.6...@react-navigation/material-top-tabs@5.0.7) (2020-02-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.0.6](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.0.5...@react-navigation/material-top-tabs@5.0.6) (2020-02-19)
|
## [5.0.6](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.0.5...@react-navigation/material-top-tabs@5.0.6) (2020-02-19)
|
||||||
|
|
||||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/material-top-tabs",
|
"name": "@react-navigation/material-top-tabs",
|
||||||
"description": "Integration for the animated tab view component from react-native-tab-view",
|
"description": "Integration for the animated tab view component from react-native-tab-view",
|
||||||
"version": "5.0.6",
|
"version": "5.1.4",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -38,14 +38,14 @@
|
|||||||
"color": "^3.1.2"
|
"color": "^3.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.9.3",
|
"@react-native-community/bob": "^0.10.0",
|
||||||
"@react-navigation/native": "^5.0.6",
|
"@react-navigation/native": "^5.1.1",
|
||||||
"@types/react": "^16.9.19",
|
"@types/react": "^16.9.23",
|
||||||
"@types/react-native": "^0.60.30",
|
"@types/react-native": "^0.61.22",
|
||||||
"del-cli": "^3.0.0",
|
"del-cli": "^3.0.0",
|
||||||
"react": "~16.9.0",
|
"react": "~16.9.0",
|
||||||
"react-native": "~0.61.5",
|
"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-reanimated": "^1.7.0",
|
||||||
"react-native-tab-view": "^2.13.0",
|
"react-native-tab-view": "^2.13.0",
|
||||||
"typescript": "^3.7.5"
|
"typescript": "^3.7.5"
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ function MaterialTopTabNavigator({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default createNavigatorFactory<
|
export default createNavigatorFactory<
|
||||||
|
TabNavigationState,
|
||||||
MaterialTopTabNavigationOptions,
|
MaterialTopTabNavigationOptions,
|
||||||
|
MaterialTopTabNavigationEventMap,
|
||||||
typeof MaterialTopTabNavigator
|
typeof MaterialTopTabNavigator
|
||||||
>(MaterialTopTabNavigator);
|
>(MaterialTopTabNavigator);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Route,
|
Route,
|
||||||
NavigationProp,
|
NavigationProp,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
|
TabActionHelpers,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
export type MaterialTopTabNavigationEventMap = {
|
export type MaterialTopTabNavigationEventMap = {
|
||||||
@@ -42,19 +43,8 @@ export type MaterialTopTabNavigationProp<
|
|||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
MaterialTopTabNavigationOptions,
|
MaterialTopTabNavigationOptions,
|
||||||
MaterialTopTabNavigationEventMap
|
MaterialTopTabNavigationEventMap
|
||||||
> & {
|
> &
|
||||||
/**
|
TabActionHelpers<ParamList>;
|
||||||
* 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;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type MaterialTopTabNavigationOptions = {
|
export type MaterialTopTabNavigationOptions = {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,6 +3,57 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.0.8](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.0.7...@react-navigation/native@5.0.8) (2020-02-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/native
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.0.6...@react-navigation/native@5.0.7) (2020-02-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/native
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.0.6](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.0.5...@react-navigation/native@5.0.6) (2020-02-19)
|
## [5.0.6](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.0.5...@react-navigation/native@5.0.6) (2020-02-19)
|
||||||
|
|
||||||
**Note:** Version bump only for package @react-navigation/native
|
**Note:** Version bump only for package @react-navigation/native
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/native",
|
"name": "@react-navigation/native",
|
||||||
"description": "React Native integration for React Navigation",
|
"description": "React Native integration for React Navigation",
|
||||||
"version": "5.0.6",
|
"version": "5.1.1",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native",
|
"react-native",
|
||||||
"react-navigation",
|
"react-navigation",
|
||||||
@@ -31,12 +31,12 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-navigation/core": "^5.1.5"
|
"@react-navigation/core": "^5.2.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.9.3",
|
"@react-native-community/bob": "^0.10.0",
|
||||||
"@types/react": "^16.9.19",
|
"@types/react": "^16.9.23",
|
||||||
"@types/react-native": "^0.60.30",
|
"@types/react-native": "^0.61.22",
|
||||||
"del-cli": "^3.0.0",
|
"del-cli": "^3.0.0",
|
||||||
"react": "~16.9.0",
|
"react": "~16.9.0",
|
||||||
"react-native": "~0.61.5",
|
"react-native": "~0.61.5",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const DefaultTheme: Theme = {
|
|||||||
background: 'rgb(242, 242, 242)',
|
background: 'rgb(242, 242, 242)',
|
||||||
card: 'rgb(255, 255, 255)',
|
card: 'rgb(255, 255, 255)',
|
||||||
text: 'rgb(28, 28, 30)',
|
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 =
|
type ScrollableView =
|
||||||
| { scrollToTop(): void }
|
| { scrollToTop(): void }
|
||||||
| { scrollTo(options: ScrollOptions): void }
|
| { scrollTo(options: ScrollOptions): void }
|
||||||
| { scrollToOffset(options: ScrollOptions): void }
|
| { scrollToOffset(options: { offset?: number; animated?: boolean }): void }
|
||||||
| { scrollResponderScrollTo(options: ScrollOptions): void };
|
| { scrollResponderScrollTo(options: ScrollOptions): void };
|
||||||
|
|
||||||
type ScrollableWrapper =
|
type ScrollableWrapper =
|
||||||
| { getScrollResponder(): ScrollableView }
|
| { getScrollResponder(): React.ReactNode }
|
||||||
| { getNode(): ScrollableView }
|
| { getNode(): ScrollableView }
|
||||||
| 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
|
// 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
|
// This is necessary to know if preventDefault() has been called
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const scrollable = getScrollableNode(ref);
|
const scrollable = getScrollableNode(ref) as ScrollableWrapper;
|
||||||
|
|
||||||
if (isFocused && isFirst && scrollable && !e.defaultPrevented) {
|
if (isFocused && isFirst && scrollable && !e.defaultPrevented) {
|
||||||
if ('scrollToTop' in scrollable) {
|
if ('scrollToTop' in scrollable) {
|
||||||
@@ -87,7 +87,7 @@ export default function useScrollToTop(
|
|||||||
} else if ('scrollTo' in scrollable) {
|
} else if ('scrollTo' in scrollable) {
|
||||||
scrollable.scrollTo({ y: 0, animated: true });
|
scrollable.scrollTo({ y: 0, animated: true });
|
||||||
} else if ('scrollToOffset' in scrollable) {
|
} else if ('scrollToOffset' in scrollable) {
|
||||||
scrollable.scrollToOffset({ y: 0, animated: true });
|
scrollable.scrollToOffset({ offset: 0, animated: true });
|
||||||
} else if ('scrollResponderScrollTo' in scrollable) {
|
} else if ('scrollResponderScrollTo' in scrollable) {
|
||||||
scrollable.scrollResponderScrollTo({ y: 0, animated: true });
|
scrollable.scrollResponderScrollTo({ y: 0, animated: true });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,52 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix links for documentation ([5bb0f40](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/commit/5bb0f405ceb5755d39a0b5b1f2e4ecee0da051bc))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* make reset bubble up ([09f6808](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/commit/09f6808d7d43c70b2c502151f9f20fad03972886))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.0.3](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.0.2...@react-navigation/routers@5.0.3) (2020-02-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/routers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.0.2](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.0.1...@react-navigation/routers@5.0.2) (2020-02-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* tweak error message for navigate ([c8ea419](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/commit/c8ea4199f4b19a58d5e409cfcc96e587fe354a9a))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.0.1](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.0.0-alpha.33...@react-navigation/routers@5.0.1) (2020-02-10)
|
## [5.0.1](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.0.0-alpha.33...@react-navigation/routers@5.0.1) (2020-02-10)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/routers",
|
"name": "@react-navigation/routers",
|
||||||
"description": "Routers to help build custom navigators",
|
"description": "Routers to help build custom navigators",
|
||||||
"version": "5.0.1",
|
"version": "5.1.1",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react",
|
"react",
|
||||||
"react-native",
|
"react-native",
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
"shortid": "^2.2.15"
|
"shortid": "^2.2.15"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.9.3",
|
"@react-native-community/bob": "^0.10.0",
|
||||||
"del-cli": "^3.0.0",
|
"del-cli": "^3.0.0",
|
||||||
"typescript": "^3.7.5"
|
"typescript": "^3.7.5"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -29,8 +29,29 @@ const BaseRouter = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'RESET':
|
case 'RESET': {
|
||||||
return action.payload as PartialState<State>;
|
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)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextState;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -41,11 +41,11 @@ export function navigate(...args: any): Action {
|
|||||||
if (typeof args[0] === 'string') {
|
if (typeof args[0] === 'string') {
|
||||||
return { type: 'NAVIGATE', payload: { name: args[0], params: args[1] } };
|
return { type: 'NAVIGATE', payload: { name: args[0], params: args[1] } };
|
||||||
} else {
|
} else {
|
||||||
const payload = args[0];
|
const payload = args[0] || {};
|
||||||
|
|
||||||
if (!payload.hasOwnProperty('key') && !payload.hasOwnProperty('name')) {
|
if (!payload.hasOwnProperty('key') && !payload.hasOwnProperty('name')) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'While calling navigate with an object as the argument, you need to specify name or key'
|
'You need to specify name or key when calling navigate with an object as the argument. See https://reactnavigation.org/docs/navigation-actions#navigate for usage.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import shortid from 'shortid';
|
import shortid from 'shortid';
|
||||||
import { PartialState, CommonNavigationAction, Router } from './types';
|
import {
|
||||||
|
PartialState,
|
||||||
|
CommonNavigationAction,
|
||||||
|
Router,
|
||||||
|
ParamListBase,
|
||||||
|
} from './types';
|
||||||
import TabRouter, {
|
import TabRouter, {
|
||||||
TabActions,
|
TabActions,
|
||||||
TabActionType,
|
TabActionType,
|
||||||
TabRouterOptions,
|
TabRouterOptions,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
|
TabActionHelpers,
|
||||||
} from './TabRouter';
|
} from './TabRouter';
|
||||||
|
|
||||||
export type DrawerActionType =
|
export type DrawerActionType =
|
||||||
@@ -31,6 +37,25 @@ export type DrawerNavigationState = Omit<
|
|||||||
history: ({ type: 'route'; key: string } | { type: 'drawer' })[];
|
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 = {
|
export const DrawerActions = {
|
||||||
...TabActions,
|
...TabActions,
|
||||||
openDrawer(): DrawerActionType {
|
openDrawer(): DrawerActionType {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
Router,
|
Router,
|
||||||
DefaultRouterOptions,
|
DefaultRouterOptions,
|
||||||
Route,
|
Route,
|
||||||
|
ParamListBase,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
export type StackActionType =
|
export type StackActionType =
|
||||||
@@ -42,6 +43,42 @@ export type StackNavigationState = NavigationState & {
|
|||||||
type: 'stack';
|
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 = {
|
export const StackActions = {
|
||||||
replace(name: string, params?: object): StackActionType {
|
replace(name: string, params?: object): StackActionType {
|
||||||
return { type: 'REPLACE', payload: { name, params } };
|
return { type: 'REPLACE', payload: { name, params } };
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Router,
|
Router,
|
||||||
DefaultRouterOptions,
|
DefaultRouterOptions,
|
||||||
Route,
|
Route,
|
||||||
|
ParamListBase,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
export type TabActionType = {
|
export type TabActionType = {
|
||||||
@@ -33,6 +34,20 @@ export type TabNavigationState = Omit<NavigationState, 'history'> & {
|
|||||||
history: { type: 'route'; key: string }[];
|
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;
|
const TYPE_ROUTE = 'route' as const;
|
||||||
|
|
||||||
export const TabActions = {
|
export const TabActions = {
|
||||||
|
|||||||
@@ -83,3 +83,46 @@ it('resets state to new state with RESET', () => {
|
|||||||
|
|
||||||
expect(result).toEqual({ index: 0, routes });
|
expect(result).toEqual({ index: 0, routes });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("doesn't handle RESET if routes don't match routeNames", () => {
|
||||||
|
const routes = [
|
||||||
|
{ key: 'bar', name: 'bar', params: { fruit: 'orange' } },
|
||||||
|
{ key: 'baz', name: 'baz' },
|
||||||
|
{ key: 'qux', name: 'quz' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = BaseRouter.getStateForAction(
|
||||||
|
STATE,
|
||||||
|
CommonActions.reset({
|
||||||
|
index: 0,
|
||||||
|
routes,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("doesn't handle RESET if routeNames don't match", () => {
|
||||||
|
const result = BaseRouter.getStateForAction(
|
||||||
|
STATE,
|
||||||
|
CommonActions.reset({
|
||||||
|
...STATE,
|
||||||
|
// @ts-ignore
|
||||||
|
routeNames: ['ten'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ import * as CommonActions from '../CommonActions';
|
|||||||
it('throws if NAVIGATE is called without key or name', () => {
|
it('throws if NAVIGATE is called without key or name', () => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(() => CommonActions.navigate({})).toThrowError(
|
expect(() => CommonActions.navigate({})).toThrowError(
|
||||||
'While calling navigate with an object as the argument, you need to specify name or key'
|
'You need to specify name or key when calling navigate with an object as the argument.'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export { default as BaseRouter } from './BaseRouter';
|
|||||||
export {
|
export {
|
||||||
default as StackRouter,
|
default as StackRouter,
|
||||||
StackActions,
|
StackActions,
|
||||||
|
StackActionHelpers,
|
||||||
StackActionType,
|
StackActionType,
|
||||||
StackRouterOptions,
|
StackRouterOptions,
|
||||||
StackNavigationState,
|
StackNavigationState,
|
||||||
@@ -15,6 +16,7 @@ export {
|
|||||||
export {
|
export {
|
||||||
default as TabRouter,
|
default as TabRouter,
|
||||||
TabActions,
|
TabActions,
|
||||||
|
TabActionHelpers,
|
||||||
TabActionType,
|
TabActionType,
|
||||||
TabRouterOptions,
|
TabRouterOptions,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
@@ -23,6 +25,7 @@ export {
|
|||||||
export {
|
export {
|
||||||
default as DrawerRouter,
|
default as DrawerRouter,
|
||||||
DrawerActions,
|
DrawerActions,
|
||||||
|
DrawerActionHelpers,
|
||||||
DrawerActionType,
|
DrawerActionType,
|
||||||
DrawerRouterOptions,
|
DrawerRouterOptions,
|
||||||
DrawerNavigationState,
|
DrawerNavigationState,
|
||||||
|
|||||||
@@ -3,6 +3,102 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* ignore back button press if screen isn't focused. closes [#7673](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7673) ([296c836](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/296c836064447e055a88e43cfbbf5f9de93838f0))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.0.9...@react-navigation/stack@5.1.0) (2020-02-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add ability add listeners with listeners prop ([1624108](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/162410843c4f175ae107756de1c3af04d1d47aa7)), closes [#6756](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/6756)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.0.9](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.0.8...@react-navigation/stack@5.0.9) (2020-02-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* enhance border radius in modals on new iPhones ([#6945](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/6945)) ([80a3370](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/80a337024abc53537ff4a63916cea38bb4f374bf))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.0.8](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.0.7...@react-navigation/stack@5.0.8) (2020-02-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix transparent header on Android ([a67b494](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/a67b49477eb500c81fedcd73bbd8102901a95170))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.0.6...@react-navigation/stack@5.0.7) (2020-02-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* debounce back button by default in stack header ([c7dd3a5](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/c7dd3a58b18d7a267d94009d459944c251ea74c1))
|
||||||
|
* make sure header is visibile to accessibility tools on iOS ([240ce01](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/240ce01822febac2c1aa324c01e43fdc88a235a0))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.0.6](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.0.5...@react-navigation/stack@5.0.6) (2020-02-19)
|
## [5.0.6](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.0.5...@react-navigation/stack@5.0.6) (2020-02-19)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/stack",
|
"name": "@react-navigation/stack",
|
||||||
"description": "Stack navigator component for iOS and Android with animated transitions and gestures",
|
"description": "Stack navigator component for iOS and Android with animated transitions and gestures",
|
||||||
"version": "5.0.6",
|
"version": "5.2.2",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -38,18 +38,18 @@
|
|||||||
"react-native-iphone-x-helper": "^1.2.1"
|
"react-native-iphone-x-helper": "^1.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.9.3",
|
"@react-native-community/bob": "^0.10.0",
|
||||||
"@react-native-community/masked-view": "^0.1.6",
|
"@react-native-community/masked-view": "^0.1.7",
|
||||||
"@react-navigation/native": "^5.0.6",
|
"@react-navigation/native": "^5.1.1",
|
||||||
"@types/color": "^3.0.1",
|
"@types/color": "^3.0.1",
|
||||||
"@types/react": "^16.9.19",
|
"@types/react": "^16.9.23",
|
||||||
"@types/react-native": "^0.60.30",
|
"@types/react-native": "^0.61.22",
|
||||||
"del-cli": "^3.0.0",
|
"del-cli": "^3.0.0",
|
||||||
"react": "~16.9.0",
|
"react": "~16.9.0",
|
||||||
"react-native": "~0.61.5",
|
"react-native": "~0.61.5",
|
||||||
"react-native-gesture-handler": "^1.5.6",
|
"react-native-gesture-handler": "^1.6.0",
|
||||||
"react-native-safe-area-context": "^0.7.2",
|
"react-native-safe-area-context": "^0.7.3",
|
||||||
"react-native-screens": "^2.0.0-beta.2",
|
"react-native-screens": "^2.3.0",
|
||||||
"typescript": "^3.7.5"
|
"typescript": "^3.7.5"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
"react-native": "*",
|
"react-native": "*",
|
||||||
"react-native-gesture-handler": ">= 1.0.0",
|
"react-native-gesture-handler": ">= 1.0.0",
|
||||||
"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"
|
"react-native-screens": ">= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0"
|
||||||
},
|
},
|
||||||
"@react-native-community/bob": {
|
"@react-native-community/bob": {
|
||||||
"source": "src",
|
"source": "src",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Animated } from 'react-native';
|
import { Animated } from 'react-native';
|
||||||
|
import { isIphoneX } from 'react-native-iphone-x-helper';
|
||||||
import conditional from '../utils/conditional';
|
import conditional from '../utils/conditional';
|
||||||
import {
|
import {
|
||||||
StackCardInterpolationProps,
|
StackCardInterpolationProps,
|
||||||
@@ -152,8 +153,8 @@ export function forModalPresentationIOS({
|
|||||||
? 0
|
? 0
|
||||||
: index === 0
|
: index === 0
|
||||||
? progress.interpolate({
|
? progress.interpolate({
|
||||||
inputRange: [0, 1, 2],
|
inputRange: [0, 1, 1.0001, 2],
|
||||||
outputRange: [0, 0, 10],
|
outputRange: [0, 0, isIphoneX() ? 38 : 0, 10],
|
||||||
})
|
})
|
||||||
: 10;
|
: 10;
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ function StackNavigator({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default createNavigatorFactory<
|
export default createNavigatorFactory<
|
||||||
|
StackNavigationState,
|
||||||
StackNavigationOptions,
|
StackNavigationOptions,
|
||||||
|
StackNavigationEventMap,
|
||||||
typeof StackNavigator
|
typeof StackNavigator
|
||||||
>(StackNavigator);
|
>(StackNavigator);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import * as React from 'react';
|
||||||
import {
|
import {
|
||||||
Animated,
|
Animated,
|
||||||
StyleProp,
|
StyleProp,
|
||||||
@@ -13,6 +14,7 @@ import {
|
|||||||
Route,
|
Route,
|
||||||
NavigationHelpers,
|
NavigationHelpers,
|
||||||
StackNavigationState,
|
StackNavigationState,
|
||||||
|
StackActionHelpers,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
export type StackNavigationEventMap = {
|
export type StackNavigationEventMap = {
|
||||||
@@ -40,29 +42,8 @@ export type StackNavigationProp<
|
|||||||
StackNavigationState,
|
StackNavigationState,
|
||||||
StackNavigationOptions,
|
StackNavigationOptions,
|
||||||
StackNavigationEventMap
|
StackNavigationEventMap
|
||||||
> & {
|
> &
|
||||||
/**
|
StackActionHelpers<ParamList>;
|
||||||
* 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 type Layout = { width: number; height: number };
|
export type Layout = { width: number; height: number };
|
||||||
|
|
||||||
@@ -122,7 +103,7 @@ export type StackHeaderOptions = {
|
|||||||
/**
|
/**
|
||||||
* Style object for the title component.
|
* 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.
|
* 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`.
|
* 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.
|
* Defaults to `true` on Android and `false` on iOS.
|
||||||
*/
|
*/
|
||||||
cardOverlayEnabled?: boolean;
|
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.
|
* Style object for the card in stack.
|
||||||
* You can provide a custom background color to use instead of the default background here.
|
* 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 object for the title element.
|
||||||
*/
|
*/
|
||||||
style?: StyleProp<TextStyle>;
|
style?: React.ComponentProps<typeof Animated.Text>['style'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TransitionSpec =
|
export type TransitionSpec =
|
||||||
|
|||||||
17
packages/stack/src/utils/debounce.tsx
Normal file
17
packages/stack/src/utils/debounce.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
export default function debounce<T extends (...args: any[]) => void>(
|
||||||
|
func: T,
|
||||||
|
duration: number
|
||||||
|
): T {
|
||||||
|
let timeout: NodeJS.Timeout | number | undefined;
|
||||||
|
|
||||||
|
return function(this: any, ...args) {
|
||||||
|
if (!timeout) {
|
||||||
|
// eslint-disable-next-line babel/no-invalid-this
|
||||||
|
func.apply(this, args);
|
||||||
|
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
timeout = undefined;
|
||||||
|
}, duration);
|
||||||
|
}
|
||||||
|
} as T;
|
||||||
|
}
|
||||||
@@ -32,13 +32,14 @@ export default class BorderlessButton extends React.Component<Props> {
|
|||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.onActiveStateChange && this.props.onActiveStateChange(active);
|
this.props.onActiveStateChange?.(active);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { children, style, enabled, ...rest } = this.props;
|
const { children, style, enabled, ...rest } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
// @ts-ignore
|
||||||
<AnimatedBaseButton
|
<AnimatedBaseButton
|
||||||
{...rest}
|
{...rest}
|
||||||
onActiveStateChange={this.handleActiveStateChange}
|
onActiveStateChange={this.handleActiveStateChange}
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ import * as React from 'react';
|
|||||||
import { StackActions } from '@react-navigation/native';
|
import { StackActions } from '@react-navigation/native';
|
||||||
|
|
||||||
import HeaderSegment from './HeaderSegment';
|
import HeaderSegment from './HeaderSegment';
|
||||||
import { StackHeaderProps, StackHeaderTitleProps } from '../../types';
|
|
||||||
import HeaderTitle from './HeaderTitle';
|
import HeaderTitle from './HeaderTitle';
|
||||||
|
import debounce from '../../utils/debounce';
|
||||||
|
import { StackHeaderProps, StackHeaderTitleProps } from '../../types';
|
||||||
|
|
||||||
export default React.memo(function Header(props: StackHeaderProps) {
|
export default React.memo(function Header(props: StackHeaderProps) {
|
||||||
const {
|
const {
|
||||||
@@ -40,6 +41,18 @@ export default React.memo(function Header(props: StackHeaderProps) {
|
|||||||
: previous.route.name;
|
: previous.route.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const goBack = React.useCallback(
|
||||||
|
debounce(() => {
|
||||||
|
if (navigation.isFocused() && navigation.canGoBack()) {
|
||||||
|
navigation.dispatch({
|
||||||
|
...StackActions.pop(),
|
||||||
|
source: scene.route.key,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 50),
|
||||||
|
[navigation, scene.route.key]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HeaderSegment
|
<HeaderSegment
|
||||||
{...options}
|
{...options}
|
||||||
@@ -53,18 +66,7 @@ export default React.memo(function Header(props: StackHeaderProps) {
|
|||||||
? (props: StackHeaderTitleProps) => <HeaderTitle {...props} />
|
? (props: StackHeaderTitleProps) => <HeaderTitle {...props} />
|
||||||
: options.headerTitle
|
: options.headerTitle
|
||||||
}
|
}
|
||||||
onGoBack={
|
onGoBack={previous ? goBack : undefined}
|
||||||
previous
|
|
||||||
? () => {
|
|
||||||
if (navigation.canGoBack()) {
|
|
||||||
navigation.dispatch({
|
|
||||||
...StackActions.pop(),
|
|
||||||
source: scene.route.key,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
styleInterpolator={styleInterpolator}
|
styleInterpolator={styleInterpolator}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -137,7 +137,9 @@ export default function HeaderContainer({
|
|||||||
isFocused ? 'auto' : 'no-hide-descendants'
|
isFocused ? 'auto' : 'no-hide-descendants'
|
||||||
}
|
}
|
||||||
style={
|
style={
|
||||||
mode === 'float' || options.headerTransparent
|
// Avoid positioning the focused header absolutely
|
||||||
|
// Otherwise accessibility tools don't seem to be able to find it
|
||||||
|
(mode === 'float' && !isFocused) || options.headerTransparent
|
||||||
? styles.header
|
? styles.header
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as React from 'react';
|
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';
|
import { useTheme } from '@react-navigation/native';
|
||||||
|
|
||||||
type Props = TextProps & {
|
type Props = React.ComponentProps<typeof Animated.Text> & {
|
||||||
tintColor?: string;
|
tintColor?: string;
|
||||||
children?: string;
|
children?: string;
|
||||||
};
|
};
|
||||||
@@ -32,7 +32,8 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
android: {
|
android: {
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: '500',
|
fontFamily: 'sans-serif-medium',
|
||||||
|
fontWeight: 'normal',
|
||||||
},
|
},
|
||||||
default: {
|
default: {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { TextInput, Keyboard } from 'react-native';
|
import { TextInput } from 'react-native';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
@@ -54,7 +54,11 @@ export default class KeyboardManager extends React.Component<Props> {
|
|||||||
|
|
||||||
this.clearKeyboardTimeout();
|
this.clearKeyboardTimeout();
|
||||||
|
|
||||||
Keyboard.dismiss();
|
const input = this.previouslyFocusedTextInput;
|
||||||
|
|
||||||
|
if (input) {
|
||||||
|
TextInput.State.blurTextInput(input);
|
||||||
|
}
|
||||||
|
|
||||||
// Cleanup the ID on successful page change
|
// Cleanup the ID on successful page change
|
||||||
this.previouslyFocusedTextInput = null;
|
this.previouslyFocusedTextInput = null;
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ type Props = ViewProps & {
|
|||||||
onGestureCanceled?: () => void;
|
onGestureCanceled?: () => void;
|
||||||
onGestureEnd?: () => void;
|
onGestureEnd?: () => void;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
overlay: (props: { style: StyleProp<ViewStyle> }) => React.ReactNode;
|
||||||
overlayEnabled: boolean;
|
overlayEnabled: boolean;
|
||||||
shadowEnabled: boolean;
|
shadowEnabled: boolean;
|
||||||
gestureEnabled: boolean;
|
gestureEnabled: boolean;
|
||||||
@@ -80,6 +81,10 @@ export default class Card extends React.Component<Props> {
|
|||||||
shadowEnabled: true,
|
shadowEnabled: true,
|
||||||
gestureEnabled: true,
|
gestureEnabled: true,
|
||||||
gestureVelocityImpact: GESTURE_VELOCITY_IMPACT,
|
gestureVelocityImpact: GESTURE_VELOCITY_IMPACT,
|
||||||
|
overlay: ({ style }: { style: StyleProp<ViewStyle> }) =>
|
||||||
|
style ? (
|
||||||
|
<Animated.View pointerEvents="none" style={[styles.overlay, style]} />
|
||||||
|
) : null,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -266,9 +271,16 @@ export default class Card extends React.Component<Props> {
|
|||||||
velocity = nativeEvent.velocityX;
|
velocity = nativeEvent.velocityX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
gestureDirection === 'horizontal-inverted' ||
|
||||||
|
gestureDirection === 'vertical-inverted'
|
||||||
|
) {
|
||||||
|
translation *= -1;
|
||||||
|
velocity *= -1;
|
||||||
|
}
|
||||||
|
|
||||||
const closing =
|
const closing =
|
||||||
Math.abs(translation + velocity * gestureVelocityImpact) >
|
translation + velocity * gestureVelocityImpact > distance / 2
|
||||||
distance / 2
|
|
||||||
? velocity !== 0 || translation !== 0
|
? velocity !== 0 || translation !== 0
|
||||||
: false;
|
: false;
|
||||||
|
|
||||||
@@ -409,6 +421,7 @@ export default class Card extends React.Component<Props> {
|
|||||||
next,
|
next,
|
||||||
layout,
|
layout,
|
||||||
insets,
|
insets,
|
||||||
|
overlay,
|
||||||
overlayEnabled,
|
overlayEnabled,
|
||||||
shadowEnabled,
|
shadowEnabled,
|
||||||
gestureEnabled,
|
gestureEnabled,
|
||||||
@@ -470,55 +483,54 @@ export default class Card extends React.Component<Props> {
|
|||||||
: false;
|
: false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View pointerEvents="box-none" {...rest}>
|
<CardAnimationContext.Provider value={animationContext}>
|
||||||
{overlayEnabled && overlayStyle ? (
|
<View pointerEvents="box-none" {...rest}>
|
||||||
|
{overlayEnabled ? (
|
||||||
|
<View style={StyleSheet.absoluteFill}>
|
||||||
|
{overlay({ style: overlayStyle })}
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
<Animated.View
|
<Animated.View
|
||||||
pointerEvents="none"
|
style={[styles.container, containerStyle, customContainerStyle]}
|
||||||
style={[styles.overlay, overlayStyle]}
|
pointerEvents="box-none"
|
||||||
/>
|
|
||||||
) : 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()}
|
|
||||||
>
|
>
|
||||||
<Animated.View style={[styles.container, cardStyle]}>
|
<PanGestureHandler
|
||||||
{shadowEnabled && shadowStyle && !isTransparent ? (
|
ref={this.gestureRef}
|
||||||
<Animated.View
|
enabled={layout.width !== 0 && gestureEnabled}
|
||||||
style={[
|
onGestureEvent={handleGestureEvent}
|
||||||
styles.shadow,
|
onHandlerStateChange={this.handleGestureStateChange}
|
||||||
gestureDirection === 'horizontal'
|
{...this.gestureActivationCriteria()}
|
||||||
? [styles.shadowHorizontal, styles.shadowLeft]
|
>
|
||||||
: gestureDirection === 'horizontal-inverted'
|
<Animated.View style={[styles.container, cardStyle]}>
|
||||||
? [styles.shadowHorizontal, styles.shadowRight]
|
{shadowEnabled && shadowStyle && !isTransparent ? (
|
||||||
: gestureDirection === 'vertical'
|
<Animated.View
|
||||||
? [styles.shadowVertical, styles.shadowTop]
|
style={[
|
||||||
: [styles.shadowVertical, styles.shadowBottom],
|
styles.shadow,
|
||||||
shadowStyle,
|
gestureDirection === 'horizontal'
|
||||||
]}
|
? [styles.shadowHorizontal, styles.shadowLeft]
|
||||||
pointerEvents="none"
|
: gestureDirection === 'horizontal-inverted'
|
||||||
/>
|
? [styles.shadowHorizontal, styles.shadowRight]
|
||||||
) : null}
|
: gestureDirection === 'vertical'
|
||||||
<View
|
? [styles.shadowVertical, styles.shadowTop]
|
||||||
ref={this.contentRef}
|
: [styles.shadowVertical, styles.shadowBottom],
|
||||||
style={[styles.content, contentStyle]}
|
shadowStyle,
|
||||||
>
|
]}
|
||||||
<StackGestureRefContext.Provider value={this.gestureRef}>
|
pointerEvents="none"
|
||||||
<CardAnimationContext.Provider value={animationContext}>
|
/>
|
||||||
|
) : null}
|
||||||
|
<View
|
||||||
|
ref={this.contentRef}
|
||||||
|
style={[styles.content, contentStyle]}
|
||||||
|
>
|
||||||
|
<StackGestureRefContext.Provider value={this.gestureRef}>
|
||||||
{children}
|
{children}
|
||||||
</CardAnimationContext.Provider>
|
</StackGestureRefContext.Provider>
|
||||||
</StackGestureRefContext.Provider>
|
</View>
|
||||||
</View>
|
</Animated.View>
|
||||||
</Animated.View>
|
</PanGestureHandler>
|
||||||
</PanGestureHandler>
|
</Animated.View>
|
||||||
</Animated.View>
|
</View>
|
||||||
</View>
|
</CardAnimationContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -532,7 +544,7 @@ const styles = StyleSheet.create({
|
|||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
},
|
},
|
||||||
overlay: {
|
overlay: {
|
||||||
...StyleSheet.absoluteFillObject,
|
flex: 1,
|
||||||
backgroundColor: '#000',
|
backgroundColor: '#000',
|
||||||
},
|
},
|
||||||
shadow: {
|
shadow: {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type Props = TransitionPreset & {
|
|||||||
safeAreaInsetRight: number;
|
safeAreaInsetRight: number;
|
||||||
safeAreaInsetBottom: number;
|
safeAreaInsetBottom: number;
|
||||||
safeAreaInsetLeft: number;
|
safeAreaInsetLeft: number;
|
||||||
|
cardOverlay?: (props: { style: StyleProp<ViewStyle> }) => React.ReactNode;
|
||||||
cardOverlayEnabled?: boolean;
|
cardOverlayEnabled?: boolean;
|
||||||
cardShadowEnabled?: boolean;
|
cardShadowEnabled?: boolean;
|
||||||
cardStyle?: StyleProp<ViewStyle>;
|
cardStyle?: StyleProp<ViewStyle>;
|
||||||
@@ -58,6 +59,7 @@ const EPSILON = 0.1;
|
|||||||
|
|
||||||
function CardContainer({
|
function CardContainer({
|
||||||
active,
|
active,
|
||||||
|
cardOverlay,
|
||||||
cardOverlayEnabled,
|
cardOverlayEnabled,
|
||||||
cardShadowEnabled,
|
cardShadowEnabled,
|
||||||
cardStyle,
|
cardStyle,
|
||||||
@@ -162,6 +164,7 @@ function CardContainer({
|
|||||||
closing={closing}
|
closing={closing}
|
||||||
onOpen={handleOpen}
|
onOpen={handleOpen}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
|
overlay={cardOverlay}
|
||||||
overlayEnabled={cardOverlayEnabled}
|
overlayEnabled={cardOverlayEnabled}
|
||||||
shadowEnabled={cardShadowEnabled}
|
shadowEnabled={cardShadowEnabled}
|
||||||
onTransitionStart={handleTransitionStart}
|
onTransitionStart={handleTransitionStart}
|
||||||
|
|||||||
@@ -37,14 +37,6 @@ type GestureValues = {
|
|||||||
[key: string]: Animated.Value;
|
[key: string]: Animated.Value;
|
||||||
};
|
};
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
const maybeExpoVersion = global.Expo?.Constants.manifest.sdkVersion.split(
|
|
||||||
'.'
|
|
||||||
)[0];
|
|
||||||
const isInsufficientExpoVersion = maybeExpoVersion
|
|
||||||
? Number(maybeExpoVersion) <= 36
|
|
||||||
: maybeExpoVersion === 'UNVERSIONED';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
mode: StackCardMode;
|
mode: StackCardMode;
|
||||||
insets: EdgeInsets;
|
insets: EdgeInsets;
|
||||||
@@ -82,37 +74,24 @@ type State = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const EPSILON = 0.01;
|
const EPSILON = 0.01;
|
||||||
const FAR_FAR_AWAY = 9000;
|
|
||||||
|
|
||||||
const dimensions = Dimensions.get('window');
|
|
||||||
const layout = { width: dimensions.width, height: dimensions.height };
|
|
||||||
|
|
||||||
const MaybeScreenContainer = ({
|
const MaybeScreenContainer = ({
|
||||||
enabled,
|
enabled,
|
||||||
style,
|
|
||||||
...rest
|
...rest
|
||||||
}: ViewProps & {
|
}: ViewProps & {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) => {
|
}) => {
|
||||||
if (enabled && screensEnabled()) {
|
if (enabled && screensEnabled()) {
|
||||||
return <ScreenContainer style={style} {...rest} />;
|
return <ScreenContainer {...rest} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <View {...rest} />;
|
||||||
<View
|
|
||||||
collapsable={!enabled}
|
|
||||||
removeClippedSubviews={Platform.OS !== 'ios' && enabled}
|
|
||||||
style={[style, { overflow: 'hidden' }]}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const MaybeScreen = ({
|
const MaybeScreen = ({
|
||||||
enabled,
|
enabled,
|
||||||
active,
|
active,
|
||||||
style,
|
|
||||||
...rest
|
...rest
|
||||||
}: ViewProps & {
|
}: ViewProps & {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
@@ -121,39 +100,10 @@ const MaybeScreen = ({
|
|||||||
}) => {
|
}) => {
|
||||||
if (enabled && screensEnabled()) {
|
if (enabled && screensEnabled()) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return <Screen active={active} style={style} {...rest} />;
|
return <Screen active={active} {...rest} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <View {...rest} />;
|
||||||
<Animated.View
|
|
||||||
style={[
|
|
||||||
style,
|
|
||||||
{
|
|
||||||
overflow: 'hidden',
|
|
||||||
// Position the screen offscreen to take advantage of offscreen perf optimization
|
|
||||||
// https://facebook.github.io/react-native/docs/view#removeclippedsubviews
|
|
||||||
// This can be useful if screens is not enabled
|
|
||||||
// It's buggy on iOS, so we don't enable it there
|
|
||||||
top:
|
|
||||||
enabled && typeof active === 'number' && !active ? FAR_FAR_AWAY : 0,
|
|
||||||
transform: [
|
|
||||||
{
|
|
||||||
// If the `active` prop is animated node, we can't use the `left` property due to native driver
|
|
||||||
// So we use `translateY` instead
|
|
||||||
translateY:
|
|
||||||
enabled && typeof active !== 'number'
|
|
||||||
? active.interpolate({
|
|
||||||
inputRange: [0, 1],
|
|
||||||
outputRange: [FAR_FAR_AWAY, 0],
|
|
||||||
})
|
|
||||||
: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const FALLBACK_DESCRIPTOR = Object.freeze({ options: {} });
|
const FALLBACK_DESCRIPTOR = Object.freeze({ options: {} });
|
||||||
@@ -207,7 +157,16 @@ const getProgressFromGesture = (
|
|||||||
layout: Layout,
|
layout: Layout,
|
||||||
descriptor?: StackDescriptor
|
descriptor?: StackDescriptor
|
||||||
) => {
|
) => {
|
||||||
const distance = getDistanceFromOptions(mode, layout, descriptor);
|
const distance = getDistanceFromOptions(
|
||||||
|
mode,
|
||||||
|
{
|
||||||
|
// Make sure that we have a non-zero distance, otherwise there will be incorrect progress
|
||||||
|
// This causes blank screen on web if it was previously inside container with display: none
|
||||||
|
width: Math.max(1, layout.width),
|
||||||
|
height: Math.max(1, layout.height),
|
||||||
|
},
|
||||||
|
descriptor
|
||||||
|
);
|
||||||
|
|
||||||
if (distance > 0) {
|
if (distance > 0) {
|
||||||
return gesture.interpolate({
|
return gesture.interpolate({
|
||||||
@@ -337,19 +296,25 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
state: State = {
|
constructor(props: Props) {
|
||||||
routes: [],
|
super(props);
|
||||||
scenes: [],
|
|
||||||
gestures: {},
|
const { height = 0, width = 0 } = Dimensions.get('window');
|
||||||
layout,
|
|
||||||
descriptors: this.props.descriptors,
|
this.state = {
|
||||||
// Used when card's header is null and mode is float to make transition
|
routes: [],
|
||||||
// between screens with headers and those without headers smooth.
|
scenes: [],
|
||||||
// This is not a great heuristic here. We don't know synchronously
|
gestures: {},
|
||||||
// on mount what the header height is so we have just used the most
|
layout: { height, width },
|
||||||
// common cases here.
|
descriptors: this.props.descriptors,
|
||||||
headerHeights: {},
|
// Used when card's header is null and mode is float to make transition
|
||||||
};
|
// between screens with headers and those without headers smooth.
|
||||||
|
// This is not a great heuristic here. We don't know synchronously
|
||||||
|
// on mount what the header height is so we have just used the most
|
||||||
|
// common cases here.
|
||||||
|
headerHeights: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private handleLayout = (e: LayoutChangeEvent) => {
|
private handleLayout = (e: LayoutChangeEvent) => {
|
||||||
const { height, width } = e.nativeEvent.layout;
|
const { height, width } = e.nativeEvent.layout;
|
||||||
@@ -448,11 +413,9 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
left = insets.left,
|
left = insets.left,
|
||||||
} = focusedOptions.safeAreaInsets || {};
|
} = focusedOptions.safeAreaInsets || {};
|
||||||
|
|
||||||
// Screens is buggy on iOS, so we don't enable it there
|
// Screens is buggy on iOS and web, so we only enable it on Android
|
||||||
// For modals, usually we want the screen underneath to be visible, so also disable it there
|
// For modals, usually we want the screen underneath to be visible, so also disable it there
|
||||||
const isScreensEnabled =
|
const isScreensEnabled = Platform.OS === 'android' && mode !== 'modal';
|
||||||
Platform.OS !== 'ios' &&
|
|
||||||
(isInsufficientExpoVersion ? mode !== 'modal' : true);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
@@ -466,26 +429,13 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
const gesture = gestures[route.key];
|
const gesture = gestures[route.key];
|
||||||
const scene = scenes[index];
|
const scene = scenes[index];
|
||||||
|
|
||||||
// Display current screen and a screen beneath.
|
const isScreenActive = scene.progress.next
|
||||||
let isScreenActive: Animated.AnimatedInterpolation | 0 | 1 =
|
? scene.progress.next.interpolate({
|
||||||
index >= self.length - 2 ? 1 : 0;
|
inputRange: [0, 1 - EPSILON, 1],
|
||||||
|
outputRange: [1, 1, 0],
|
||||||
if (isInsufficientExpoVersion) {
|
extrapolate: 'clamp',
|
||||||
isScreenActive =
|
})
|
||||||
index === self.length - 1
|
: 1;
|
||||||
? 1
|
|
||||||
: Platform.OS === 'android'
|
|
||||||
? scene.progress.next
|
|
||||||
? scene.progress.next.interpolate({
|
|
||||||
inputRange: [0, 1 - EPSILON, 1],
|
|
||||||
outputRange: [1, 1, 0],
|
|
||||||
extrapolate: 'clamp',
|
|
||||||
})
|
|
||||||
: 1
|
|
||||||
: index === self.length - 2
|
|
||||||
? 1
|
|
||||||
: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
safeAreaInsets,
|
safeAreaInsets,
|
||||||
@@ -493,6 +443,7 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
headerTransparent,
|
headerTransparent,
|
||||||
cardShadowEnabled,
|
cardShadowEnabled,
|
||||||
cardOverlayEnabled,
|
cardOverlayEnabled,
|
||||||
|
cardOverlay,
|
||||||
cardStyle,
|
cardStyle,
|
||||||
animationEnabled,
|
animationEnabled,
|
||||||
gestureResponseDistance,
|
gestureResponseDistance,
|
||||||
@@ -590,6 +541,7 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
safeAreaInsetRight={safeAreaInsetRight}
|
safeAreaInsetRight={safeAreaInsetRight}
|
||||||
safeAreaInsetBottom={safeAreaInsetBottom}
|
safeAreaInsetBottom={safeAreaInsetBottom}
|
||||||
safeAreaInsetLeft={safeAreaInsetLeft}
|
safeAreaInsetLeft={safeAreaInsetLeft}
|
||||||
|
cardOverlay={cardOverlay}
|
||||||
cardOverlayEnabled={cardOverlayEnabled}
|
cardOverlayEnabled={cardOverlayEnabled}
|
||||||
cardShadowEnabled={cardShadowEnabled}
|
cardShadowEnabled={cardShadowEnabled}
|
||||||
cardStyle={cardStyle}
|
cardStyle={cardStyle}
|
||||||
|
|||||||
Reference in New Issue
Block a user