Compare commits

...

83 Commits

Author SHA1 Message Date
satyajit.happy
8318c49331 chore: publish
- @react-navigation/bottom-tabs@5.0.0-alpha.11
 - @react-navigation/compat@5.0.0-alpha.5
 - @react-navigation/core@5.0.0-alpha.12
 - @react-navigation/drawer@5.0.0-alpha.11
 - @react-navigation/example@5.0.0-alpha.9
 - @react-navigation/material-bottom-tabs@5.0.0-alpha.10
 - @react-navigation/material-top-tabs@5.0.0-alpha.9
 - @react-navigation/native@5.0.0-alpha.9
 - @react-navigation/routers@5.0.0-alpha.9
 - @react-navigation/stack@5.0.0-alpha.21
2019-10-03 19:47:41 +02:00
satyajit.happy
ece6e3899e fix: add missing React import 2019-10-03 19:07:56 +02:00
satyajit.happy
da944ccef9 fix: fix header buttons not clickable on Android. fixes #108 2019-10-03 19:07:21 +02:00
satyajit.happy
bc3586ae3e fix: keep the routes we are closing or replacing 2019-10-03 18:44:18 +02:00
satyajit.happy
19be2b4bbc fix: don't throw when switching navigator. fixes #91 2019-10-03 17:51:13 +02:00
satyajit.happy
a8851b730d chore: upgrade deps 2019-10-03 17:35:24 +02:00
Michał Osadnik
7a5bcb446b feat: add a getRootState method (#119) 2019-10-01 23:17:28 +02:00
satyajit.happy
1345a8fec6 chore: upgrade eslint config 2019-09-28 11:58:21 +02:00
satyajit.happy
7393464515 fix: don't merge state with existing state during reset. fixes #111 2019-09-28 11:49:45 +02:00
osdnk
9810a06061 chore: publish
- @react-navigation/bottom-tabs@5.0.0-alpha.10
 - @react-navigation/compat@5.0.0-alpha.4
 - @react-navigation/core@5.0.0-alpha.11
 - @react-navigation/drawer@5.0.0-alpha.10
 - @react-navigation/example@5.0.0-alpha.8
 - @react-navigation/material-bottom-tabs@5.0.0-alpha.9
 - @react-navigation/material-top-tabs@5.0.0-alpha.8
 - @react-navigation/routers@5.0.0-alpha.8
 - @react-navigation/stack@5.0.0-alpha.20
2019-09-27 17:30:35 +02:00
osdnk
655a220137 fix: close drawer on navigate 2019-09-27 17:29:59 +02:00
satyajit.happy
e61f5941d7 feat: add a method to reset root navigator state 2019-09-26 16:34:34 +02:00
satyajit.happy
8b78d617c0 feat: export some more type aliases 2019-09-25 23:09:58 +02:00
Dulmandakh
1fd5a868a3 feat: export BottomTabBar props type (#109) 2019-09-25 16:41:40 +02:00
satyajit.happy
1153d5575b fix: fire blur event when a route is removed with a delay
closes #110
2019-09-24 20:27:54 +02:00
osdnk
76de32f441 chore: publish
- @react-navigation/stack@5.0.0-alpha.19
2019-09-23 18:26:26 +02:00
osdnk
4ee19bcdb9 fix: vertical gesture in stack 2019-09-23 18:25:06 +02:00
satyajit.happy
06cc8b9142 chore: publish
- @react-navigation/compat@5.0.0-alpha.3
 - @react-navigation/stack@5.0.0-alpha.18
2019-09-23 17:26:27 +02:00
satyajit.happy
2f66556b10 fix: fix header rendered behind card. closes #108 2019-09-23 17:24:43 +02:00
Dulmandakh
f39813626a feat(stack): use Animated.Text for header title (#105) 2019-09-21 17:55:11 +02:00
Dulmandakh
089390ce87 feat(stack): use Animated.View for header background (#106) 2019-09-21 17:54:21 +02:00
Dulmandakh
d09c9ae643 refactor(stack): convert Header component to function (#104) 2019-09-21 17:53:44 +02:00
satyajit.happy
8920da68a7 fix: throw when wrapping a compat navigatofor compat 2019-09-19 14:57:31 +02:00
satyajit.happy
98a9487cda chore: publish
- @react-navigation/bottom-tabs@5.0.0-alpha.9
 - @react-navigation/compat@5.0.0-alpha.2
 - @react-navigation/core@5.0.0-alpha.10
 - @react-navigation/stack@5.0.0-alpha.17
2019-09-17 18:44:55 +02:00
satyajit.happy
ff502820c7 feat: add createSwitchNavigator 2019-09-17 18:32:07 +02:00
satyajit.happy
30e510d123 fix: provide navigation prop in header 2019-09-17 18:13:42 +02:00
satyajit.happy
114a5dc888 feat: add withNavigation and withNavigationFocus 2019-09-17 17:57:01 +02:00
Ryan Stelly
b4bbf9b0c3 fix: add fallbacks for non-web modules
closes #95, #96
2019-09-17 16:58:17 +02:00
satyajit.happy
98f0de088f refactor: make dispatch accept thunks 2019-09-17 13:05:45 +02:00
satyajit.happy
2537145d81 chore: publish
- @react-navigation/bottom-tabs@5.0.0-alpha.8
 - @react-navigation/compat@5.0.0-alpha.1
 - @react-navigation/core@5.0.0-alpha.9
 - @react-navigation/drawer@5.0.0-alpha.9
 - @react-navigation/example@5.0.0-alpha.7
 - @react-navigation/material-bottom-tabs@5.0.0-alpha.8
 - @react-navigation/native@5.0.0-alpha.8
 - @react-navigation/stack@5.0.0-alpha.16
2019-09-16 23:45:20 +02:00
Michał Osadnik
6af8400421 fix: don't remove route if animation isn't finished when dragging (#100) 2019-09-16 21:02:49 +02:00
satyajit.happy
d83d3de697 fix: support legacy goBack method 2019-09-16 16:04:31 +02:00
satyajit.happy
88a560aacb fix: fix dispatching compat actions 2019-09-16 15:21:07 +02:00
satyajit.happy
e0f28a432d feat: compatibility layer 2019-09-16 15:16:28 +02:00
satyajit.happy
438692d661 chore: fix running the example app 2019-09-16 14:59:54 +02:00
satyajit.happy
849d952703 feat: make deep link handling more flexible
This adds ability to specify a custom config to control how to convert between state and path.

Example:

```js
{
  Chat: {
    path: 'chat/:author/:id',
    parse: { id: Number }
  }
}
```

The above config can parse a path matching the provided pattern: `chat/jane/42` to a valid state:

```js
{
  routes: [
    {
      name: 'Chat',
      params: { author: 'jane', id: 42 },
    },
  ],
}
```

This makes it much easier to control the parsing without having to specify a custom function.
2019-09-16 13:20:19 +02:00
satyajit.happy
17045f5b6d refactor: share fallback descriptor 2019-09-14 16:05:19 +02:00
Brent Vatne
d16c20cd39 feat: make example run as bare react-native project as well (#85) 2019-09-08 18:10:44 +02:00
Janic Duplessis
9563a27284 feat: integrate InterationManager in stack 2019-09-08 17:01:06 +02:00
satyajit.happy
f57a91c417 fix: tweak android q animations 2019-09-08 12:40:38 +02:00
satyajit.happy
8e8b7eef36 chore: add issue and pull request templates 2019-09-07 15:42:35 +02:00
satyajit.happy
44020452fb chore: publish
- @react-navigation/core@5.0.0-alpha.8
 - @react-navigation/stack@5.0.0-alpha.15
2019-09-04 11:56:16 +02:00
satyajit.happy
ceaf18edd6 chore: remove commented out code 2019-09-04 11:55:37 +02:00
satyajit.happy
faaf855717 refactor: separate navigation and route context 2019-09-04 11:54:20 +02:00
satyajit.happy
196cce0803 feat: add approximate android Q transition 2019-09-04 11:36:00 +02:00
satyajit.happy
a022860317 chore: publish
- @react-navigation/stack@5.0.0-alpha.14
2019-09-03 16:43:26 +02:00
satyajit.happy
167d58ce27 fix: change order of attaching nodes in card exec 2019-09-03 16:41:11 +02:00
Michal Osadnik
798a905d0a chore: publish
- @react-navigation/stack@5.0.0-alpha.13
2019-09-01 16:56:21 +01:00
Michal Osadnik
aa6313c0e9 feat: useForeground if possible in stack header backButton 2019-09-01 16:56:00 +01:00
Michal Osadnik
2563924f53 chore: publish
- @react-navigation/stack@5.0.0-alpha.12
2019-09-01 15:43:59 +01:00
Michal Osadnik
55ec815247 fix: stack with gesture enabled 2019-09-01 12:46:55 +01:00
satyajit.happy
c7a79a62fa fix: defer running the animation to next frame 2019-09-01 12:43:03 +02:00
Michal Osadnik
5c6e85cec8 chore: publish
- @react-navigation/stack@5.0.0-alpha.11
2019-09-01 02:17:06 +01:00
Michal Osadnik
3f853d458f feat: optimizations in stack 2019-09-01 02:09:48 +01:00
satyajit.happy
06b3e6bda7 chore: publish
- @react-navigation/core@5.0.0-alpha.7
 - @react-navigation/example@5.0.0-alpha.6
 - @react-navigation/native@5.0.0-alpha.7
2019-08-31 23:23:10 +02:00
satyajit.happy
3c840bbae3 fix: fix navigation object changing too often 2019-08-31 23:21:27 +02:00
satyajit.happy
d4ad9d48f9 refactor: make descriptor.options a getter 2019-08-31 23:10:30 +02:00
satyajit.happy
a0e9784d98 chore: publish
- @react-navigation/core@5.0.0-alpha.6
 - @react-navigation/example@5.0.0-alpha.5
 - @react-navigation/native@5.0.0-alpha.6
2019-08-31 23:01:28 +02:00
satyajit.happy
eff0c0464f feat: support function in screenOptions 2019-08-31 23:00:27 +02:00
satyajit.happy
fe9ba2bf71 refactor: rename NativeContainer -> NavigationNativeContainer 2019-08-31 21:02:48 +02:00
Michał Osadnik
b0a3756d18 feat: add useRoute (#89) 2019-08-31 15:56:04 +01:00
satyajit.happy
4e0ebb05f9 chore: publish
- @react-navigation/bottom-tabs@5.0.0-alpha.7
 - @react-navigation/drawer@5.0.0-alpha.8
 - @react-navigation/example@5.0.0-alpha.4
 - @react-navigation/material-bottom-tabs@5.0.0-alpha.7
 - @react-navigation/material-top-tabs@5.0.0-alpha.7
 - @react-navigation/routers@5.0.0-alpha.7
 - @react-navigation/stack@5.0.0-alpha.10
2019-08-31 10:22:57 +02:00
satyajit.happy
bf0b408238 refactor: change signature of interpolation props 2019-08-31 10:21:36 +02:00
Satyajit Sahoo
1b2983eaa9 fix: handle route names change when all routes are removed (#86) 2019-08-30 19:15:54 +01:00
satyajit.happy
b38ee1c162 chore: publish
- @react-navigation/core@5.0.0-alpha.5
 - @react-navigation/drawer@5.0.0-alpha.7
 - @react-navigation/stack@5.0.0-alpha.9
2019-08-30 15:34:34 +02:00
satyajit.happy
fdc24d2523 fix: rename contentContainerStyle to sceneContainerStyle for drawer 2019-08-30 15:30:18 +02:00
satyajit.happy
6a8242c31a fix: properly set animated node on gestureEnabled change 2019-08-29 19:55:42 +02:00
satyajit.happy
3ad2e6ebcf fix: change interpolated style when idle to avoid messing up reanimated 2019-08-29 16:12:22 +02:00
satyajit.happy
5c8d183d68 chore: rename BaseActions -> CommonActions 2019-08-29 13:10:36 +02:00
Michal Osadnik
f1b976b68c chore: publish
- @react-navigation/bottom-tabs@5.0.0-alpha.6
 - @react-navigation/core@5.0.0-alpha.4
 - @react-navigation/drawer@5.0.0-alpha.6
 - @react-navigation/example@5.0.0-alpha.3
 - @react-navigation/material-bottom-tabs@5.0.0-alpha.6
 - @react-navigation/material-top-tabs@5.0.0-alpha.6
 - @react-navigation/native@5.0.0-alpha.5
 - @react-navigation/routers@5.0.0-alpha.6
 - @react-navigation/stack@5.0.0-alpha.8
2019-08-29 10:40:13 +01:00
Michał Osadnik
6b75cbaaa6 feat: handle navigating with both with both key and name (#83) 2019-08-29 09:46:51 +02:00
satyajit.happy
c951027ebb fix: handle both null and undefined in useScrollToTop 2019-08-29 08:33:42 +02:00
Janic Duplessis
cdbf1e97f9 feat: handle animated component wrappers in useScrollToTop (#81) 2019-08-28 23:13:00 +01:00
Janic Duplessis
56a2ee99f9 chore: add prettier config to package.json so it is picked up by editors (#82) 2019-08-28 23:12:29 +01:00
satyajit.happy
a9d4813b47 fix: allow making params optional. fixes #80 2019-08-28 22:05:48 +02:00
satyajit.happy
74c3377b63 chore: fix typo in example 2019-08-28 21:48:58 +02:00
satyajit.happy
8c1acc33c6 fix: fix gestures not working in stack 2019-08-28 21:41:15 +02:00
satyajit.happy
d72a96d1ef chore: add example for pop to top on tab press 2019-08-28 21:28:18 +02:00
satyajit.happy
f9e8c7e80f feat: handle more methods in useScrollToTop 2019-08-28 21:17:48 +02:00
satyajit.happy
9245c79990 feat: export NavigationContext 2019-08-28 15:52:48 +02:00
satyajit.happy
ea4c753d0a refactor: rename 'timing' option to 'animation' in transition spec 2019-08-28 15:48:17 +02:00
satyajit.happy
01196d7b48 docs: add JSDoc for transition configurations 2019-08-28 15:41:11 +02:00
Janic Duplessis
b4a5c3c35e fix: types path (#75) 2019-08-28 11:36:37 +01:00
179 changed files with 8731 additions and 3153 deletions

View File

@@ -6,6 +6,7 @@
"@react-navigation/core",
"@react-navigation/native",
"@react-navigation/routers",
"@react-navigation/compat",
"@react-navigation/stack",
"@react-navigation/drawer",
"@react-navigation/bottom-tabs",

40
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,40 @@
## The issue tracker is reserved for bug reports only.
If you have a question, feature request, or an idea for improving the library or its related tools, please try one of the following resources:
- [Read the documentation](https://reactnavigation.org/)
- [Post an issue to the website repository if you'd like to see a documentation change](http://github.com/react-navigation/website)
- [Post a feature request to Canny](https://react-navigation.canny.io/feature-requests)
- [Write a RFC if you have ideas for how to implement a feature request](https://github.com/react-navigation/rfcs)
- [Get help on Discord chat (#react-navigation on Reactiflux)](https://discord.gg/4xEK3nD) or [on StackOverflow](https://stackoverflow.com/questions/tagged/react-navigation)
- Search for your issue - it may have already been answered or even fixed in the development branch. However, if you find that an old, closed issue still persists in the latest version, you should open a new issue.
Bugs with react-navigation must be reproducible *without any external libraries that operate on it*. This means that if you are attempting to use Redux or MobX with it and you think you have found a bug, you must be able to reproduce it without Redux or MobX in this report.
---
### Current Behavior
- What code are you running and what is happening?
- Include a screenshot if it makes sense.
### Expected Behavior
- What do you expect should be happening?
- Include a screenshot if it makes sense.
### How to reproduce
- You must provide a way to reproduce the problem. If you are having an issue with your machine or build tools, the issue belongs on another repository as that is outside of the scope of React Navigation.
- Either re-create the bug on [Snack](https://snack.expo.io) or link to a GitHub repository with code that reproduces the bug.
- Explain how to run the example app and any steps that we need to take to reproduce the issue from the example app.
### Your Environment
| software | version
| ---------------- | -------
| react-navigation |
| react-native |
| expo |
| node |
| npm or yarn |

17
.github/PULL_REQUEST.md vendored Normal file
View File

@@ -0,0 +1,17 @@
Please provide enough information so that others can review your pull request:
## Motivation
Explain the **motivation** for making this change. What existing problem does the pull request solve?
## Test plan
Demonstrate the code is solid. Example: the exact commands you ran and their output, screenshots / videos if the pull request changes UI.
Make sure you test on both platforms if your change affects both platforms.
The code must pass tests.
## Code formatting
Look around. Match the style of the rest of the codebase. Run `yarn lint --fix` before committing.

7
.gitignore vendored
View File

@@ -3,17 +3,22 @@
.vscode
.idea
.expo
.gradle
/coverage/
/types/
/dist/
/lib/
/packages/**/lib/
node_modules/
web-build/
web-report/
xcuserdata
generated
lib
build
npm-debug.*
*.log

View File

@@ -1,14 +0,0 @@
{
"git": {
"commitMessage": "chore: release %s",
"tagName": "v%s"
},
"github": {
"release": true
},
"plugins": {
"@release-it/conventional-changelog": {
"preset": "angular"
}
}
}

View File

@@ -1,5 +1,3 @@
/* eslint-disable import/no-commonjs */
module.exports = {
presets: [
[

View File

@@ -1,5 +1,3 @@
/* eslint-disable import/no-commonjs */
module.exports = {
extends: ['@commitlint/config-conventional'],
};

View File

@@ -23,27 +23,27 @@
},
"devDependencies": {
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@babel/preset-env": "^7.6.2",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.3.3",
"@babel/runtime": "^7.5.5",
"@commitlint/config-conventional": "^8.0.0",
"@babel/preset-typescript": "^7.6.0",
"@babel/runtime": "^7.6.2",
"@commitlint/config-conventional": "^8.2.0",
"@types/jest": "^24.0.13",
"codecov": "^3.5.0",
"commitlint": "^8.0.0",
"core-js": "^3.1.4",
"eslint": "^5.16.0",
"eslint-config-satya164": "^2.4.1",
"husky": "^2.4.0",
"codecov": "^3.6.1",
"commitlint": "^8.2.0",
"core-js": "^3.2.1",
"eslint": "^6.5.1",
"eslint-config-satya164": "^3.1.2",
"husky": "^3.0.8",
"jest": "^24.8.0",
"lerna": "^3.16.4",
"prettier": "^1.18.2",
"typescript": "^3.5.3"
"typescript": "^3.6.3"
},
"resolutions": {
"react": "16.8.3",
"react-native": "https://github.com/expo/react-native/archive/sdk-34.0.0.tar.gz",
"react-native-safe-area-view": "0.14.6"
"react-native": "0.59.10",
"react-native-safe-area-view": "0.14.7"
},
"husky": {
"hooks": {
@@ -64,5 +64,10 @@
"@react-navigation/([^/]+)": "<rootDir>/packages/$1/src"
}
},
"name": "react-navigation"
"prettier": {
"tabWidth": 2,
"useTabs": false,
"singleQuote": true,
"trailingComma": "es5"
}
}

View File

@@ -3,6 +3,68 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.11](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.10...@react-navigation/bottom-tabs@5.0.0-alpha.11) (2019-10-03)
**Note:** Version bump only for package @react-navigation/bottom-tabs
# [5.0.0-alpha.10](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.9...@react-navigation/bottom-tabs@5.0.0-alpha.10) (2019-09-27)
### Features
* export BottomTabBar props type ([#109](https://github.com/react-navigation/navigation-ex/issues/109)) ([1fd5a86](https://github.com/react-navigation/navigation-ex/commit/1fd5a86))
* export some more type aliases ([8b78d61](https://github.com/react-navigation/navigation-ex/commit/8b78d61))
# [5.0.0-alpha.9](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.8...@react-navigation/bottom-tabs@5.0.0-alpha.9) (2019-09-17)
### Bug Fixes
* provide navigation prop in header ([30e510d](https://github.com/react-navigation/navigation-ex/commit/30e510d))
# [5.0.0-alpha.8](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.7...@react-navigation/bottom-tabs@5.0.0-alpha.8) (2019-09-16)
### Features
* make example run as bare react-native project as well ([#85](https://github.com/react-navigation/navigation-ex/issues/85)) ([d16c20c](https://github.com/react-navigation/navigation-ex/commit/d16c20c))
# [5.0.0-alpha.7](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.6...@react-navigation/bottom-tabs@5.0.0-alpha.7) (2019-08-31)
**Note:** Version bump only for package @react-navigation/bottom-tabs
# [5.0.0-alpha.6](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.5...@react-navigation/bottom-tabs@5.0.0-alpha.6) (2019-08-29)
### Bug Fixes
* allow making params optional. fixes [#80](https://github.com/react-navigation/navigation-ex/issues/80) ([a9d4813](https://github.com/react-navigation/navigation-ex/commit/a9d4813))
* types path ([#75](https://github.com/react-navigation/navigation-ex/issues/75)) ([b4a5c3c](https://github.com/react-navigation/navigation-ex/commit/b4a5c3c))
# [5.0.0-alpha.5](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.4...@react-navigation/bottom-tabs@5.0.0-alpha.5) (2019-08-28)
**Note:** Version bump only for package @react-navigation/bottom-tabs

View File

@@ -10,7 +10,7 @@
"android",
"tab"
],
"version": "5.0.0-alpha.5",
"version": "5.0.0-alpha.11",
"license": "MIT",
"repository": {
"type": "git",
@@ -20,7 +20,7 @@
"main": "lib/commonjs/index.js",
"react-native": "src/index.tsx",
"module": "lib/module/index.js",
"types": "lib/typescript/bottom-tabssrc/index.d.ts",
"types": "lib/typescript/bottom-tabs/src/index.d.ts",
"files": [
"src",
"lib"
@@ -33,17 +33,17 @@
"clean": "del lib"
},
"dependencies": {
"@react-navigation/routers": "^5.0.0-alpha.5",
"react-native-safe-area-view": "^0.14.6"
"@react-navigation/routers": "^5.0.0-alpha.9",
"react-native-safe-area-view": "^0.14.7"
},
"devDependencies": {
"@react-native-community/bob": "^0.7.0",
"@types/react": "^16.8.24",
"@types/react-native": "^0.60.2",
"del-cli": "^2.0.0",
"@types/react": "^16.9.4",
"@types/react-native": "^0.60.17",
"del-cli": "^3.0.0",
"react": "16.8.3",
"react-native": "^0.59.8",
"typescript": "^3.5.3"
"react-native": "0.59.10",
"typescript": "^3.6.3"
},
"peerDependencies": {
"@react-navigation/core": "^5.0.0-alpha.0",

View File

@@ -13,4 +13,9 @@ export { default as BottomTabBar } from './views/BottomTabBar';
/**
* Types
*/
export { BottomTabNavigationOptions, BottomTabNavigationProp } from './types';
export {
BottomTabNavigationOptions,
BottomTabNavigationProp,
BottomTabBarProps,
BottomTabBarOptions,
} from './types';

View File

@@ -53,8 +53,8 @@ export type BottomTabNavigationProp<
* @param [params] Params object for the route.
*/
jumpTo<RouteName extends Extract<keyof ParamList, string>>(
...args: ParamList[RouteName] extends void
? [RouteName]
...args: ParamList[RouteName] extends (undefined | any)
? [RouteName] | [RouteName, ParamList[RouteName]]
: [RouteName, ParamList[RouteName]]
): void;
};
@@ -199,6 +199,7 @@ export type BottomTabBarOptions = {
export type BottomTabBarProps = BottomTabBarOptions & {
state: TabNavigationState;
descriptors: BottomTabDescriptorMap;
navigation: NavigationHelpers<ParamListBase>;
onTabPress: (props: { route: Route<string> }) => void;
onTabLongPress: (props: { route: Route<string> }) => void;
@@ -231,5 +232,7 @@ export type BottomTabBarProps = BottomTabBarOptions & {
tintColor: string;
horizontal: boolean;
}) => React.ReactNode;
activeTintColor: string;
inactiveTintColor: string;
safeAreaInset?: React.ComponentProps<typeof SafeAreaView>['forceInset'];
};

View File

@@ -9,7 +9,7 @@ import {
Dimensions,
} from 'react-native';
import SafeAreaView from 'react-native-safe-area-view';
import { Route } from '@react-navigation/core';
import { Route, NavigationContext } from '@react-navigation/core';
import TabBarIcon from './TabBarIcon';
import TouchableWithoutFeedbackWrapper from './TouchableWithoutFeedbackWrapper';
@@ -23,8 +23,6 @@ type State = {
};
type Props = BottomTabBarProps & {
activeTintColor: string;
inactiveTintColor: string;
safeAreaInset: React.ComponentProps<typeof SafeAreaView>['forceInset'];
};
@@ -261,6 +259,7 @@ export default class TabBarBottom extends React.Component<Props, State> {
render() {
const {
state,
descriptors,
keyboardHidesTabBar,
activeBackgroundColor,
inactiveBackgroundColor,
@@ -291,8 +290,7 @@ export default class TabBarBottom extends React.Component<Props, State> {
style={[
styles.container,
keyboardHidesTabBar
? // eslint-disable-next-line react-native/no-inline-styles
{
? {
// When the keyboard is shown, slide down the tab bar
transform: [
{
@@ -337,26 +335,30 @@ export default class TabBarBottom extends React.Component<Props, State> {
getButtonComponent({ route }) || TouchableWithoutFeedbackWrapper;
return (
<ButtonComponent
<NavigationContext.Provider
key={route.key}
onPress={() => onTabPress({ route })}
onLongPress={() => onTabLongPress({ route })}
testID={testID}
accessibilityLabel={accessibilityLabel}
accessibilityRole={accessibilityRole}
accessibilityStates={accessibilityStates}
style={[
styles.tab,
{ backgroundColor },
this.shouldUseHorizontalLabels()
? styles.tabLandscape
: styles.tabPortrait,
tabStyle,
]}
value={descriptors[route.key].navigation}
>
{this.renderIcon(scene)}
{this.renderLabel(scene)}
</ButtonComponent>
<ButtonComponent
onPress={() => onTabPress({ route })}
onLongPress={() => onTabLongPress({ route })}
testID={testID}
accessibilityLabel={accessibilityLabel}
accessibilityRole={accessibilityRole}
accessibilityStates={accessibilityStates}
style={[
styles.tab,
{ backgroundColor },
this.shouldUseHorizontalLabels()
? styles.tabLandscape
: styles.tabPortrait,
tabStyle,
]}
>
{this.renderIcon(scene)}
{this.renderLabel(scene)}
</ButtonComponent>
</NavigationContext.Provider>
);
})}
</SafeAreaView>

View File

@@ -177,6 +177,7 @@ export default class BottomTabView extends React.Component<Props, State> {
<TabBarComponent
{...tabBarOptions}
state={state}
descriptors={descriptors}
navigation={navigation}
onTabPress={this.handleTabPress}
onTabLongPress={this.handleTabLongPress}

View File

@@ -24,12 +24,7 @@ export default class ResourceSavingScene extends React.Component<Props> {
return (
<View
style={[
styles.container,
style,
// eslint-disable-next-line react-native/no-inline-styles
{ opacity: isVisible ? 1 : 0 },
]}
style={[styles.container, style, { opacity: isVisible ? 1 : 0 }]}
collapsable={false}
removeClippedSubviews={
// On iOS, set removeClippedSubviews to true only when not focused

View File

@@ -0,0 +1,61 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.5](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/compat@5.0.0-alpha.4...@react-navigation/compat@5.0.0-alpha.5) (2019-10-03)
**Note:** Version bump only for package @react-navigation/compat
# [5.0.0-alpha.4](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/compat@5.0.0-alpha.3...@react-navigation/compat@5.0.0-alpha.4) (2019-09-27)
**Note:** Version bump only for package @react-navigation/compat
# [5.0.0-alpha.3](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/compat@5.0.0-alpha.2...@react-navigation/compat@5.0.0-alpha.3) (2019-09-23)
### Bug Fixes
* throw when wrapping a compat navigatofor compat ([8920da6](https://github.com/react-navigation/navigation-ex/commit/8920da6))
# [5.0.0-alpha.2](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/compat@5.0.0-alpha.1...@react-navigation/compat@5.0.0-alpha.2) (2019-09-17)
### Bug Fixes
* provide navigation prop in header ([30e510d](https://github.com/react-navigation/navigation-ex/commit/30e510d))
### Features
* add createSwitchNavigator ([ff50282](https://github.com/react-navigation/navigation-ex/commit/ff50282))
* add withNavigation and withNavigationFocus ([114a5dc](https://github.com/react-navigation/navigation-ex/commit/114a5dc))
# 5.0.0-alpha.1 (2019-09-16)
### Bug Fixes
* fix dispatching compat actions ([88a560a](https://github.com/react-navigation/navigation-ex/commit/88a560a))
* support legacy goBack method ([d83d3de](https://github.com/react-navigation/navigation-ex/commit/d83d3de))
### Features
* compatibility layer ([e0f28a4](https://github.com/react-navigation/navigation-ex/commit/e0f28a4))

28
packages/compat/README.md Normal file
View File

@@ -0,0 +1,28 @@
# `@react-navigation/compat`
Compatibility layer to write navigator definitions in static configuration format.
## Installation
Open a Terminal in your project's folder and run,
```sh
yarn add @react-navigation/core @react-navigation/compat
```
## Usage
```js
import { createCompatNavigatorFactory } from '@react-navigation/compat';
import { createStackNavigator } from '@react-navigation/stack';
const RootStack = createCompatNavigatorFactory(createStackNavigator)(
{
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen },
},
{
initialRouteName: 'Profile',
}
);
```

View File

@@ -0,0 +1,47 @@
{
"name": "@react-navigation/compat",
"description": "Compatibility layer to write navigator definitions in static configuration format",
"version": "5.0.0-alpha.5",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/react-navigation/navigation-ex.git",
"directory": "packages/compat"
},
"main": "lib/commonjs/index.js",
"react-native": "src/index.tsx",
"module": "lib/module/index.js",
"types": "lib/typescript/compat/src/index.d.ts",
"files": [
"src",
"lib"
],
"publishConfig": {
"access": "public"
},
"scripts": {
"prepare": "bob build",
"clean": "del lib"
},
"dependencies": {
"@react-navigation/routers": "^5.0.0-alpha.9"
},
"devDependencies": {
"@types/react": "^16.9.4",
"react": "^16.8.3",
"typescript": "^3.6.3"
},
"peerDependencies": {
"@react-navigation/core": "^5.0.0-alpha.0",
"react": "^16.8.3"
},
"@react-native-community/bob": {
"source": "src",
"output": "lib",
"targets": [
"commonjs",
"module",
"typescript"
]
}
}

View File

@@ -0,0 +1,30 @@
import * as React from 'react';
import {
NavigationProp,
ParamListBase,
RouteProp,
} from '@react-navigation/core';
import ScreenPropsContext from './ScreenPropsContext';
import createCompatNavigationProp from './createCompatNavigationProp';
type Props<ParamList extends ParamListBase> = {
navigation: NavigationProp<ParamList>;
route: RouteProp<ParamList, string>;
component: React.ComponentType<any>;
};
function ScreenComponent<ParamList extends ParamListBase>(
props: Props<ParamList>
) {
const navigation = React.useMemo(
() =>
createCompatNavigationProp(props.navigation as any, props.route as any),
[props.navigation, props.route]
);
const screenProps = React.useContext(ScreenPropsContext);
return <props.component navigation={navigation} screenProps={screenProps} />;
}
export default React.memo(ScreenComponent);

View File

@@ -0,0 +1,13 @@
import { DrawerActions, DrawerActionType } from '@react-navigation/routers';
export function openDrawer(): DrawerActionType {
return DrawerActions.openDrawer();
}
export function closeDrawer(): DrawerActionType {
return DrawerActions.closeDrawer();
}
export function toggleDrawer(): DrawerActionType {
return DrawerActions.toggleDrawer();
}

View File

@@ -0,0 +1,48 @@
import { CommonActions, NavigationState } from '@react-navigation/core';
export function navigate({
routeName,
params,
key,
action,
}: {
routeName: string;
params?: object;
key?: string;
action?: never;
}): CommonActions.Action {
if (action !== undefined) {
throw new Error(
'Sub-actions are not supported for `navigate`. Remove the `action` key from the options.'
);
}
return CommonActions.navigate({
name: routeName,
key: key,
params: params,
});
}
export function back(options?: { key?: null | string }) {
return options && options.key != null
? (state: NavigationState) => ({
...CommonActions.goBack(),
source: options.key,
target: state.key,
})
: CommonActions.goBack();
}
export function setParams({
params,
key,
}: {
params: object;
key?: string;
}): CommonActions.Action {
return {
...CommonActions.setParams(params),
...(key !== undefined ? { source: key } : null),
};
}

View File

@@ -0,0 +1,3 @@
import * as React from 'react';
export default React.createContext<unknown>(undefined);

View File

@@ -0,0 +1,68 @@
import { CommonActions } from '@react-navigation/core';
import { StackActions, StackActionType } from '@react-navigation/routers';
export function reset(): CommonActions.Action {
throw new Error(
'The legacy `reset` action is not supported. Use the new reset API by accessing the original navigation object at `navigation.original`.'
);
}
export function replace({
routeName,
params,
key,
newKey,
action,
}: {
routeName: string;
params?: object;
key?: string;
newKey?: string;
action?: never;
}): CommonActions.Action {
if (action !== undefined) {
throw new Error(
'Sub-actions are not supported for `replace`. Remove the `action` key from the options.'
);
}
return {
type: 'REPLACE',
payload: {
name: routeName,
key: newKey,
params,
},
...(key !== undefined ? { source: key } : null),
};
}
export function push({
routeName,
params,
action,
}: {
routeName: string;
params?: object;
action?: never;
}): StackActionType {
if (action !== undefined) {
throw new Error(
'Sub-actions are not supported for `push`. Remove the `action` key from the options.'
);
}
return StackActions.push(routeName, params);
}
export function pop({ n = 1 }: { n: number }): StackActionType {
return StackActions.pop(n);
}
export function popToTop(): StackActionType {
return StackActions.popToTop();
}
export function dismiss(): CommonActions.Action {
throw new Error('The legacy `dismiss` action is not supported.');
}

View File

@@ -0,0 +1,18 @@
import { TabActions, TabActionType } from '@react-navigation/routers';
export function jumpTo({
routeName,
key,
}: {
routeName: string;
key?: string;
}): TabActionType {
if (key === undefined) {
return TabActions.jumpTo(routeName);
} else {
return {
...TabActions.jumpTo(routeName),
target: key,
};
}
}

View File

@@ -0,0 +1,189 @@
import {
NavigationState,
PartialState,
ParamListBase,
NavigationProp,
RouteProp,
} from '@react-navigation/core';
import * as helpers from './helpers';
import { CompatNavigationProp } from './types';
type EventName = 'willFocus' | 'willBlur' | 'didFocus' | 'didBlur' | 'refocus';
const focusSubscriptions = new WeakMap<() => void, () => void>();
const blurSubscriptions = new WeakMap<() => void, () => void>();
const refocusSubscriptions = new WeakMap<() => void, () => void>();
export default function createCompatNavigationProp<
NavigationPropType extends NavigationProp<ParamListBase>,
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
infer P
>
? P
: ParamListBase
>(
navigation: NavigationPropType,
state:
| (RouteProp<ParamList, keyof ParamList> & {
state?: NavigationState | PartialState<NavigationState>;
})
| NavigationState
| PartialState<NavigationState>
): CompatNavigationProp<NavigationPropType> {
return {
...navigation,
...Object.entries(helpers).reduce<{
[key: string]: (...args: any[]) => void;
}>((acc, [name, method]: [string, Function]) => {
if (name in navigation) {
acc[name] = (...args: any[]) => navigation.dispatch(method(...args));
}
return acc;
}, {}),
original: navigation,
addListener(type: EventName, callback: () => void) {
let unsubscribe: () => void;
switch (type) {
case 'willFocus':
unsubscribe = navigation.addListener('focus', callback);
break;
case 'willBlur':
unsubscribe = navigation.addListener('blur', callback);
break;
case 'didFocus': {
const listener = () => {
if (navigation.isFocused()) {
callback();
}
};
// @ts-ignore
unsubscribe = navigation.addListener('transitionEnd', listener);
focusSubscriptions.set(callback, unsubscribe);
break;
}
case 'didBlur': {
const listener = () => {
if (!navigation.isFocused()) {
callback();
}
};
// @ts-ignore
unsubscribe = navigation.addListener('transitionEnd', listener);
blurSubscriptions.set(callback, unsubscribe);
break;
}
case 'refocus': {
const listener = () => {
if (navigation.isFocused()) {
callback();
}
};
// @ts-ignore
unsubscribe = navigation.addListener('tabPress', listener);
refocusSubscriptions.set(callback, unsubscribe);
break;
}
default:
// @ts-ignore
unsubscribe = navigation.addListener(type, callback);
}
const subscription = () => unsubscribe();
subscription.remove = unsubscribe;
return subscription;
},
removeListener(type: EventName, callback: () => void) {
switch (type) {
case 'willFocus':
navigation.removeListener('focus', callback);
break;
case 'willBlur':
navigation.removeListener('blur', callback);
break;
case 'didFocus': {
const unsubscribe = focusSubscriptions.get(callback);
unsubscribe && unsubscribe();
break;
}
case 'didBlur': {
const unsubscribe = blurSubscriptions.get(callback);
unsubscribe && unsubscribe();
break;
}
case 'refocus': {
const unsubscribe = refocusSubscriptions.get(callback);
unsubscribe && unsubscribe();
break;
}
default:
// @ts-ignore
navigation.removeListener(type, callback);
}
},
state: {
...state,
// @ts-ignore
routeName: state.name,
get index() {
// @ts-ignore
if (state.index !== undefined) {
// @ts-ignore
return state.index;
}
console.warn(
"Accessing child navigation state for a route is not safe and won't work correctly."
);
// @ts-ignore
return state.state ? state.state.index : undefined;
},
get routes() {
// @ts-ignore
if (state.routes !== undefined) {
// @ts-ignore
return state.routes;
}
console.warn(
"Accessing child navigation state for a route is not safe and won't work correctly."
);
// @ts-ignore
return state.state ? state.state.routes : undefined;
},
},
getParam<T extends keyof ParamList>(
paramName: T,
defaultValue: ParamList[T]
): ParamList[T] {
// @ts-ignore
const params = state.params;
if (params && paramName in params) {
return params[paramName];
}
return defaultValue;
},
dangerouslyGetParent() {
const parent = navigation.dangerouslyGetParent();
if (parent) {
return createCompatNavigationProp(
parent,
navigation.dangerouslyGetState()
);
}
return undefined;
},
} as any;
}

View File

@@ -0,0 +1,171 @@
import * as React from 'react';
import {
NavigationState,
PartialState,
ParamListBase,
TypedNavigator,
NavigationProp,
RouteProp,
} from '@react-navigation/core';
import CompatScreen from './CompatScreen';
import ScreenPropsContext from './ScreenPropsContext';
import createCompatNavigationProp from './createCompatNavigationProp';
import { CompatScreenType, CompatRouteConfig } from './types';
export default function createCompatNavigatorFactory<
CreateNavigator extends () => TypedNavigator<
ParamListBase,
{},
React.ComponentType<any>
>
>(createNavigator: CreateNavigator) {
// @ts-ignore
if (createNavigator.isCompat) {
throw new Error(
`The navigator is already in compat mode. You don't need to wrap it in 'createCompatNavigatorFactory'.`
);
}
const createCompatNavigator = <
NavigationPropType extends NavigationProp<any, any, any, any, any>,
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
infer P
>
? P
: ParamListBase,
ScreenOptions extends {} = NavigationPropType extends NavigationProp<
any,
any,
any,
infer O
>
? O
: {},
NavigationConfig extends {} = React.ComponentProps<
ReturnType<CreateNavigator>['Navigator']
>
>(
routeConfig: CompatRouteConfig<NavigationPropType>,
navigationConfig: Partial<Omit<NavigationConfig, 'screenOptions'>> & {
order?: Array<Extract<keyof ParamList, string>>;
defaultNavigationOptions?: ScreenOptions;
navigationOptions?: { [key: string]: any };
} = {}
) => {
const Pair = createNavigator();
const {
order,
defaultNavigationOptions,
navigationOptions: parentNavigationOptions,
...restConfig
} = navigationConfig;
const routeNames = order !== undefined ? order : Object.keys(routeConfig);
function Navigator({ screenProps }: { screenProps?: unknown }) {
const screens = React.useMemo(
() =>
routeNames.map(name => {
let getScreenComponent: () => CompatScreenType<NavigationPropType>;
let initialParams;
const routeConfigItem = routeConfig[name];
if ('getScreen' in routeConfigItem) {
getScreenComponent = routeConfigItem.getScreen;
initialParams = routeConfigItem.params;
} else if ('screen' in routeConfigItem) {
getScreenComponent = () => routeConfigItem.screen;
initialParams = routeConfigItem.params;
} else {
getScreenComponent = () => routeConfigItem;
}
const screenOptions = ({
navigation,
route,
}: {
navigation: NavigationPropType;
route: RouteProp<ParamList, keyof ParamList> & {
state?: NavigationState | PartialState<NavigationState>;
};
}) => {
// @ts-ignore
const routeNavigationOptions = routeConfigItem.navigationOptions;
const screenNavigationOptions = getScreenComponent()
.navigationOptions;
if (
routeNavigationOptions == null &&
screenNavigationOptions == null
) {
return undefined;
}
const options =
typeof routeNavigationOptions === 'function' ||
typeof screenNavigationOptions === 'function'
? {
navigation: createCompatNavigationProp<
NavigationPropType
>(navigation, route),
navigationOptions: defaultNavigationOptions || {},
screenProps,
}
: {};
return {
...(typeof routeNavigationOptions === 'function'
? routeNavigationOptions(options)
: routeNavigationOptions),
...(typeof screenNavigationOptions === 'function'
? (screenNavigationOptions as (o: any) => ScreenOptions)(
options
)
: screenNavigationOptions),
} as ScreenOptions;
};
return (
<Pair.Screen
key={name}
name={name}
initialParams={initialParams}
options={screenOptions}
>
{({ navigation, route }) => (
<CompatScreen
navigation={navigation}
route={route}
component={getScreenComponent()}
/>
)}
</Pair.Screen>
);
}),
[screenProps]
);
return (
<ScreenPropsContext.Provider value={screenProps}>
<Pair.Navigator
{...(restConfig as NavigationConfig)}
screenOptions={defaultNavigationOptions}
>
{screens}
</Pair.Navigator>
</ScreenPropsContext.Provider>
);
}
Navigator.navigationOtions = parentNavigationOptions;
return Navigator;
};
createCompatNavigator.isCompat = true;
return createCompatNavigator;
}

View File

@@ -0,0 +1,28 @@
import {
useNavigationBuilder,
createNavigator,
DefaultNavigatorOptions,
} from '@react-navigation/core';
import {
TabRouter,
TabRouterOptions,
TabNavigationState,
} from '@react-navigation/routers';
import createCompatNavigatorFactory from './createCompatNavigatorFactory';
type Props = DefaultNavigatorOptions<{}> & TabRouterOptions;
function SwitchNavigator(props: Props) {
const { state, descriptors } = useNavigationBuilder<
TabNavigationState,
TabRouterOptions,
{},
{}
>(TabRouter, props);
return descriptors[state.routes[state.index].key].render();
}
export default createCompatNavigatorFactory(
createNavigator<{}, typeof SwitchNavigator>(SwitchNavigator)
);

View File

@@ -0,0 +1,86 @@
import * as NavigationActions from './NavigationActions';
import * as StackActions from './StackActions';
import * as SwitchActions from './SwitchActions';
import * as DrawerActions from './DrawerActions';
type NavigateActionPayload = Parameters<typeof NavigationActions.navigate>['0'];
type NavigateActionType = ReturnType<typeof NavigationActions.navigate>;
export function navigate(
routeName: string,
params?: object,
action?: never
): NavigateActionType;
export function navigate(options: NavigateActionPayload): NavigateActionType;
export function navigate(
options: string | NavigateActionPayload,
params?: object,
action?: never
): NavigateActionType {
if (typeof options === 'string') {
return NavigationActions.navigate({
routeName: options,
params,
action,
});
}
return NavigationActions.navigate(options);
}
export function goBack(fromKey?: null | string) {
return NavigationActions.back({ key: fromKey });
}
export function setParams(params: object) {
return NavigationActions.setParams({ params });
}
export function reset() {
return StackActions.reset();
}
export function replace(routeName: string, params?: object, action?: never) {
return StackActions.replace({
routeName,
params,
action,
});
}
export function push(routeName: string, params?: object, action?: never) {
return StackActions.push({
routeName,
params,
action,
});
}
export function pop(n: number) {
return StackActions.pop(typeof n === 'number' ? { n } : n);
}
export function popToTop() {
return StackActions.popToTop();
}
export function dismiss() {
return StackActions.dismiss();
}
export function jumpTo(routeName: string) {
return SwitchActions.jumpTo({ routeName });
}
export function openDrawer() {
return DrawerActions.openDrawer();
}
export function closeDrawer() {
return DrawerActions.closeDrawer();
}
export function toggleDrawer() {
return DrawerActions.toggleDrawer();
}

View File

@@ -0,0 +1,21 @@
import * as NavigationActions from './NavigationActions';
import * as StackActions from './StackActions';
import * as DrawerActions from './DrawerActions';
import * as SwitchActions from './SwitchActions';
export { NavigationActions, StackActions, DrawerActions, SwitchActions };
export {
default as createCompatNavigatorFactory,
} from './createCompatNavigatorFactory';
export {
default as createCompatNavigationProp,
} from './createCompatNavigationProp';
export { default as createSwitchNavigator } from './createSwitchNavigator';
export { default as withNavigation } from './withNavigation';
export { default as withNavigationFocus } from './withNavigationFocus';
export * from './types';

View File

@@ -0,0 +1,79 @@
import { ParamListBase, NavigationProp, Route } from '@react-navigation/core';
import * as helpers from './helpers';
export type CompatNavigationProp<
NavigationPropType extends NavigationProp<ParamListBase>,
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
infer P
>
? P
: ParamListBase,
RouteName extends Extract<keyof ParamList, string> = Extract<
NavigationPropType extends NavigationProp<any, infer R> ? R : string,
string
>
> = Omit<NavigationPropType, keyof typeof helpers> &
{
[method in Extract<keyof NavigationPropType, keyof typeof helpers>]: (
...args: Parameters<typeof helpers[method]>
) => void;
} & {
state: Route<RouteName> & {
routeName: RouteName;
};
getParam<T extends keyof ParamList[RouteName]>(
paramName: T,
defaultValue?: ParamList[RouteName][T]
): ParamList[RouteName][T];
dangerouslyGetParent<
T = NavigationProp<ParamListBase> | undefined
>(): T extends NavigationProp<ParamListBase>
? CompatNavigationProp<T>
: undefined;
};
export type CompatNavigationOptions<
NavigationPropType extends NavigationProp<ParamListBase>,
ScreenOptions extends {} = NavigationPropType extends NavigationProp<
any,
any,
any,
infer O
>
? O
: {}
> =
| ((options: {
navigation: CompatNavigationProp<NavigationPropType>;
navigationOptions: Partial<ScreenOptions>;
screenProps: unknown;
}) => ScreenOptions)
| ScreenOptions;
export type CompatScreenType<
NavigationPropType extends NavigationProp<ParamListBase>
> = React.ComponentType<{
navigation: CompatNavigationProp<NavigationPropType>;
screenProps: unknown;
}> & {
navigationOptions?: CompatNavigationOptions<NavigationPropType>;
};
export type CompatRouteConfig<
NavigationPropType extends NavigationProp<ParamListBase>,
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
infer P
>
? P
: ParamListBase
> = {
[RouteName in keyof ParamList]:
| React.ComponentType<any>
| ((
| { screen: React.ComponentType<any> }
| { getScreen(): React.ComponentType<any> }
) & {
navigationOptions?: CompatNavigationOptions<NavigationPropType>;
params?: ParamList[RouteName];
});
};

View File

@@ -0,0 +1,25 @@
import * as React from 'react';
import {
useNavigation,
useRoute,
NavigationProp,
ParamListBase,
} from '@react-navigation/core';
import createCompatNavigationProp from './createCompatNavigationProp';
import { CompatNavigationProp } from './types';
export default function useCompatNavigation<
T extends NavigationProp<ParamListBase>
>() {
const navigation = useNavigation();
const route = useRoute();
return React.useMemo(
() =>
createCompatNavigationProp(
navigation,
route as any
) as CompatNavigationProp<T>,
[navigation, route]
);
}

View File

@@ -0,0 +1,33 @@
import * as React from 'react';
import { NavigationProp, ParamListBase } from '@react-navigation/core';
import useCompatNavigation from './useCompatNavigation';
import { CompatNavigationProp } from './types';
type InjectedProps<T extends NavigationProp<ParamListBase>> = {
navigation: CompatNavigationProp<T>;
};
export default function withNavigation<
T extends NavigationProp<ParamListBase>,
P extends InjectedProps<T>,
C extends React.ComponentType<P>
>(Comp: C) {
const WrappedComponent = ({
onRef,
...rest
}: Exclude<P, InjectedProps<T>> & {
onRef?: C extends React.ComponentClass<any>
? React.Ref<InstanceType<C>>
: never;
}): React.ReactElement => {
const navigation = useCompatNavigation<T>();
// @ts-ignore
return <Comp ref={onRef} navigation={navigation} {...rest} />;
};
WrappedComponent.displayName = `withNavigation(${Comp.displayName ||
Comp.name})`;
return WrappedComponent;
}

View File

@@ -0,0 +1,30 @@
import * as React from 'react';
import { useIsFocused } from '@react-navigation/core';
type InjectedProps = {
isFocused: boolean;
};
export default function withNavigationFocus<
P extends InjectedProps,
C extends React.ComponentType<P>
>(Comp: C) {
const WrappedComponent = ({
onRef,
...rest
}: Exclude<P, InjectedProps> & {
onRef?: C extends React.ComponentClass<any>
? React.Ref<InstanceType<C>>
: never;
}): React.ReactElement => {
const isFocused = useIsFocused();
// @ts-ignore
return <Comp ref={onRef} isFocused={isFocused} {...rest} />;
};
WrappedComponent.displayName = `withNavigationFocus(${Comp.displayName ||
Comp.name})`;
return WrappedComponent;
}

View File

@@ -0,0 +1,3 @@
{
"extends": "../../tsconfig"
}

View File

@@ -3,6 +3,122 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.12](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.11...@react-navigation/core@5.0.0-alpha.12) (2019-10-03)
### Bug Fixes
* don't merge state with existing state during reset. fixes [#111](https://github.com/react-navigation/navigation-ex/issues/111) ([7393464](https://github.com/react-navigation/navigation-ex/commit/7393464))
* don't throw when switching navigator. fixes [#91](https://github.com/react-navigation/navigation-ex/issues/91) ([19be2b4](https://github.com/react-navigation/navigation-ex/commit/19be2b4))
### Features
* add a getRootState method ([#119](https://github.com/react-navigation/navigation-ex/issues/119)) ([7a5bcb4](https://github.com/react-navigation/navigation-ex/commit/7a5bcb4))
# [5.0.0-alpha.11](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.10...@react-navigation/core@5.0.0-alpha.11) (2019-09-27)
### Bug Fixes
* fire blur event when a route is removed with a delay ([1153d55](https://github.com/react-navigation/navigation-ex/commit/1153d55)), closes [#110](https://github.com/react-navigation/navigation-ex/issues/110)
### Features
* add a method to reset root navigator state ([e61f594](https://github.com/react-navigation/navigation-ex/commit/e61f594))
# [5.0.0-alpha.10](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.9...@react-navigation/core@5.0.0-alpha.10) (2019-09-17)
**Note:** Version bump only for package @react-navigation/core
# [5.0.0-alpha.9](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.8...@react-navigation/core@5.0.0-alpha.9) (2019-09-16)
### Features
* compatibility layer ([e0f28a4](https://github.com/react-navigation/navigation-ex/commit/e0f28a4))
* make deep link handling more flexible ([849d952](https://github.com/react-navigation/navigation-ex/commit/849d952))
* make example run as bare react-native project as well ([#85](https://github.com/react-navigation/navigation-ex/issues/85)) ([d16c20c](https://github.com/react-navigation/navigation-ex/commit/d16c20c))
# [5.0.0-alpha.8](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.7...@react-navigation/core@5.0.0-alpha.8) (2019-09-04)
**Note:** Version bump only for package @react-navigation/core
# [5.0.0-alpha.7](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.5...@react-navigation/core@5.0.0-alpha.7) (2019-08-31)
### Bug Fixes
* fix navigation object changing too often ([3c840bb](https://github.com/react-navigation/navigation-ex/commit/3c840bb))
### Features
* add useRoute ([#89](https://github.com/react-navigation/navigation-ex/issues/89)) ([b0a3756](https://github.com/react-navigation/navigation-ex/commit/b0a3756))
* support function in screenOptions ([eff0c04](https://github.com/react-navigation/navigation-ex/commit/eff0c04))
# [5.0.0-alpha.6](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.5...@react-navigation/core@5.0.0-alpha.6) (2019-08-31)
### Features
* add useRoute ([#89](https://github.com/react-navigation/navigation-ex/issues/89)) ([b0a3756](https://github.com/react-navigation/navigation-ex/commit/b0a3756))
* support function in screenOptions ([eff0c04](https://github.com/react-navigation/navigation-ex/commit/eff0c04))
# [5.0.0-alpha.5](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.4...@react-navigation/core@5.0.0-alpha.5) (2019-08-30)
**Note:** Version bump only for package @react-navigation/core
# [5.0.0-alpha.4](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.3...@react-navigation/core@5.0.0-alpha.4) (2019-08-29)
### Bug Fixes
* allow making params optional. fixes [#80](https://github.com/react-navigation/navigation-ex/issues/80) ([a9d4813](https://github.com/react-navigation/navigation-ex/commit/a9d4813))
### Features
* export NavigationContext ([9245c79](https://github.com/react-navigation/navigation-ex/commit/9245c79))
* handle navigating with both with both key and name ([#83](https://github.com/react-navigation/navigation-ex/issues/83)) ([6b75cba](https://github.com/react-navigation/navigation-ex/commit/6b75cba))
# [5.0.0-alpha.3](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.2...@react-navigation/core@5.0.0-alpha.3) (2019-08-27)

View File

@@ -6,7 +6,7 @@
"react-native",
"react-navigation"
],
"version": "5.0.0-alpha.3",
"version": "5.0.0-alpha.12",
"license": "MIT",
"repository": {
"type": "git",
@@ -29,19 +29,21 @@
"clean": "del lib"
},
"dependencies": {
"shortid": "^2.2.14",
"use-subscription": "^1.0.0"
"escape-string-regexp": "^2.0.0",
"query-string": "^6.8.3",
"shortid": "^2.2.15",
"use-subscription": "^1.1.1"
},
"devDependencies": {
"@babel/core": "^7.4.5",
"@babel/core": "^7.6.2",
"@react-native-community/bob": "^0.7.0",
"@types/react": "^16.8.19",
"@types/react": "^16.9.4",
"@types/shortid": "^0.0.29",
"del-cli": "^2.0.0",
"del-cli": "^3.0.0",
"react": "^16.8.3",
"react-native-testing-library": "^1.9.1",
"react-test-renderer": "^16.8.3",
"typescript": "^3.5.3"
"react-test-renderer": "16.8.3",
"typescript": "^3.6.3"
},
"peerDependencies": {
"react": "^16.8.3"

View File

@@ -1,5 +1,5 @@
import shortid from 'shortid';
import { CommonAction, NavigationState } from './types';
import { CommonAction, NavigationState, PartialState } from './types';
/**
* Base router object that can be used when writing custom routers.
@@ -9,7 +9,7 @@ const BaseRouter = {
getStateForAction<State extends NavigationState>(
state: State,
action: CommonAction
): State | null {
): State | PartialState<State> | null {
switch (action.type) {
case 'REPLACE': {
const index = action.source
@@ -20,14 +20,16 @@ const BaseRouter = {
return null;
}
const { name, key, params } = action.payload;
return {
...state,
routes: state.routes.map((route, i) =>
i === index
? {
key: `${action.payload.name}-${shortid()}`,
name: action.payload.name,
params: action.payload.params,
key: key !== undefined ? key : `${name}-${shortid()}`,
name,
params,
}
: route
),
@@ -54,12 +56,7 @@ const BaseRouter = {
}
case 'RESET':
return {
...state,
...action.payload,
key: state.key,
routeNames: state.routeNames,
};
return action.payload as PartialState<State>;
default:
return null;

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-cycle
import { NavigationState, PartialState } from './types';
export type Action =
@@ -9,14 +10,14 @@ export type Action =
| {
type: 'NAVIGATE';
payload:
| { name: string; key?: undefined; params?: object }
| { key: string; name?: undefined; params?: object };
| { key: string; name?: undefined; params?: object }
| { name: string; key?: string; params?: object };
source?: string;
target?: string;
}
| {
type: 'REPLACE';
payload: { name: string; params?: object };
payload: { name: string; key?: string; params?: object };
source?: string;
target?: string;
}
@@ -38,7 +39,9 @@ export function goBack(): Action {
}
export function navigate(
route: { key: string; params?: object } | { name: string; params?: object }
route:
| { key: string; params?: object }
| { name: string; key?: string; params?: object }
): Action;
export function navigate(name: string, params?: object): Action;
export function navigate(...args: any): Action {
@@ -47,12 +50,9 @@ export function navigate(...args: any): Action {
} else {
const payload = args[0];
if (
(payload.hasOwnProperty('key') && payload.hasOwnProperty('name')) ||
(!payload.hasOwnProperty('key') && !payload.hasOwnProperty('name'))
) {
if (!payload.hasOwnProperty('key') && !payload.hasOwnProperty('name')) {
throw new Error(
'While calling navigate with an object as the argument, you need to specify either name or key'
'While calling navigate with an object as the argument, you need to specify name or key'
);
}

View File

@@ -18,25 +18,30 @@ export const SingleNavigatorContext = React.createContext<
* Component which ensures that there's only one navigator nested under it.
*/
export default function EnsureSingleNavigator({ children }: Props) {
const [currentKey, setCurrentKey] = React.useState<string | undefined>();
const navigatorKeyRef = React.useRef<string | undefined>();
const value = React.useMemo(
() => ({
register(key: string) {
const currentKey = navigatorKeyRef.current;
if (currentKey !== undefined && key !== currentKey) {
throw new Error(MULTIPLE_NAVIGATOR_ERROR);
}
setCurrentKey(key);
navigatorKeyRef.current = key;
},
unregister(key: string) {
if (currentKey !== undefined && key !== currentKey) {
throw new Error(MULTIPLE_NAVIGATOR_ERROR);
const currentKey = navigatorKeyRef.current;
if (key !== currentKey) {
return;
}
setCurrentKey(undefined);
navigatorKeyRef.current = undefined;
},
}),
[currentKey]
[]
);
return (

View File

@@ -1,5 +1,10 @@
import * as React from 'react';
import { NavigationAction, NavigationHelpers, ParamListBase } from './types';
import {
NavigationAction,
NavigationHelpers,
NavigationState,
ParamListBase,
} from './types';
export type ChildActionListener = (
action: NavigationAction,
@@ -14,6 +19,8 @@ export type FocusedNavigationListener = <T>(
callback: FocusedNavigationCallback<T>
) => { handled: boolean; result: T };
export type NavigatorStateGetter = () => NavigationState;
/**
* Context which holds the required helpers needed to build nested navigators.
*/
@@ -25,6 +32,7 @@ const NavigationBuilderContext = React.createContext<{
addActionListener?: (listener: ChildActionListener) => void;
addFocusedListener?: (listener: FocusedNavigationListener) => void;
onRouteFocus?: (key: string) => void;
addStateGetter?: (key: string, getter: NavigatorStateGetter) => void;
trackAction: (action: NavigationAction) => void;
}>({
trackAction: () => undefined,

View File

@@ -2,8 +2,10 @@ import * as React from 'react';
import * as CommonActions from './CommonActions';
import EnsureSingleNavigator from './EnsureSingleNavigator';
import NavigationBuilderContext from './NavigationBuilderContext';
import ResetRootContext from './ResetRootContext';
import useFocusedListeners from './useFocusedListeners';
import useDevTools from './useDevTools';
import useStateGetters from './useStateGetters';
import {
Route,
@@ -23,7 +25,9 @@ const MISSING_CONTEXT_ERROR =
export const NavigationStateContext = React.createContext<{
state?: NavigationState | PartialState<NavigationState>;
getState: () => NavigationState | PartialState<NavigationState> | undefined;
setState: (state: NavigationState | undefined) => void;
setState: (
state: NavigationState | PartialState<NavigationState> | undefined
) => void;
key?: string;
performTransaction: (action: () => void) => void;
}>({
@@ -86,46 +90,6 @@ const Container = React.forwardRef(function NavigationContainer(
getPartialState(initialState)
);
const { listeners, addListener: addFocusedListener } = useFocusedListeners();
const dispatch = (action: NavigationAction) => {
listeners[0](navigation => navigation.dispatch(action));
};
const canGoBack = () => {
const { result, handled } = listeners[0](navigation =>
navigation.canGoBack()
);
if (handled) {
return result;
} else {
return false;
}
};
React.useImperativeHandle(ref, () => ({
...(Object.keys(CommonActions) as Array<keyof typeof CommonActions>).reduce<
any
>((acc, name) => {
acc[name] = (...args: any[]) =>
dispatch(
// eslint-disable-next-line import/namespace
CommonActions[name](
// @ts-ignore
...args
)
);
return acc;
}, {}),
resetRoot: (state: PartialState<NavigationState> | NavigationState) => {
trackAction('@@RESET_ROOT');
setNavigationState(state);
},
dispatch,
canGoBack,
}));
const navigationStateRef = React.useRef<State>();
const transactionStateRef = React.useRef<State | null>(null);
const isTransactionActiveRef = React.useRef<boolean>(false);
@@ -143,12 +107,66 @@ const Container = React.forwardRef(function NavigationContainer(
state,
});
const { listeners, addListener: addFocusedListener } = useFocusedListeners();
const { getStateForRoute, addStateGetter } = useStateGetters();
const dispatch = (
action: NavigationAction | ((state: NavigationState) => NavigationAction)
) => {
listeners[0](navigation => navigation.dispatch(action));
};
const canGoBack = () => {
const { result, handled } = listeners[0](navigation =>
navigation.canGoBack()
);
if (handled) {
return result;
} else {
return false;
}
};
const resetRoot = React.useCallback(
(state: PartialState<NavigationState> | NavigationState) => {
trackAction('@@RESET_ROOT');
setNavigationState(state);
},
[trackAction]
);
const getRootState = () => {
return getStateForRoute('root');
};
React.useImperativeHandle(ref, () => ({
...(Object.keys(CommonActions) as Array<keyof typeof CommonActions>).reduce<
any
>((acc, name) => {
acc[name] = (...args: any[]) =>
dispatch(
CommonActions[name](
// @ts-ignore
...args
)
);
return acc;
}, {}),
resetRoot,
dispatch,
canGoBack,
getRootState,
}));
const builderContext = React.useMemo(
() => ({
addFocusedListener,
addStateGetter,
trackAction,
}),
[addFocusedListener, trackAction]
[addFocusedListener, trackAction, addStateGetter]
);
const performTransaction = React.useCallback((callback: () => void) => {
@@ -218,7 +236,9 @@ const Container = React.forwardRef(function NavigationContainer(
return (
<NavigationBuilderContext.Provider value={builderContext}>
<NavigationStateContext.Provider value={context}>
<EnsureSingleNavigator>{children}</EnsureSingleNavigator>
<ResetRootContext.Provider value={resetRoot}>
<EnsureSingleNavigator>{children}</EnsureSingleNavigator>
</ResetRootContext.Provider>
</NavigationStateContext.Provider>
</NavigationBuilderContext.Provider>
);

View File

@@ -0,0 +1,11 @@
import * as React from 'react';
import { Route } from './types';
/**
* Context which holds the route prop for a screen.
*/
const NavigationContext = React.createContext<Route<string> | undefined>(
undefined
);
export default NavigationContext;

View File

@@ -0,0 +1,15 @@
import * as React from 'react';
import { NavigationState, PartialState } from './types';
/**
* Context which holds the method to reset root navigator state.
*/
const ResetRootContext = React.createContext<
(state: PartialState<NavigationState> | NavigationState) => void
>(() => {
throw new Error(
"We couldn't find a way to reset root state. Have you wrapped your app with 'NavigationContainer'?"
);
});
export default ResetRootContext;

View File

@@ -1,6 +1,7 @@
import * as React from 'react';
import { NavigationStateContext } from './NavigationContainer';
import NavigationContext from './NavigationContext';
import NavigationRouteContext from './NavigationRouteContext';
import StaticContainer from './StaticContainer';
import EnsureSingleNavigator from './EnsureSingleNavigator';
import {
@@ -18,8 +19,8 @@ type Props<State extends NavigationState, ScreenOptions extends object> = {
route: Route<string> & {
state?: NavigationState | PartialState<NavigationState>;
};
getState: () => NavigationState;
setState: (state: NavigationState) => void;
getState: () => State;
setState: (state: State) => void;
};
/**
@@ -46,7 +47,7 @@ export default function SceneView<
}, [getState, route.key]);
const setCurrentState = React.useCallback(
(child: NavigationState | undefined) => {
(child: NavigationState | PartialState<NavigationState> | undefined) => {
const state = getState();
setState({
@@ -56,7 +57,7 @@ export default function SceneView<
),
});
},
[getState, route, setState]
[getState, route.key, setState]
);
const context = React.useMemo(
@@ -78,23 +79,25 @@ export default function SceneView<
return (
<NavigationContext.Provider value={navigation}>
<NavigationStateContext.Provider value={context}>
<EnsureSingleNavigator>
<StaticContainer
name={screen.name}
// @ts-ignore
render={screen.component || screen.children}
navigation={navigation}
route={route}
>
{'component' in screen && screen.component !== undefined ? (
<screen.component navigation={navigation} route={route} />
) : 'children' in screen && screen.children !== undefined ? (
screen.children({ navigation, route })
) : null}
</StaticContainer>
</EnsureSingleNavigator>
</NavigationStateContext.Provider>
<NavigationRouteContext.Provider value={route}>
<NavigationStateContext.Provider value={context}>
<EnsureSingleNavigator>
<StaticContainer
name={screen.name}
// @ts-ignore
render={screen.component || screen.children}
navigation={navigation}
route={route}
>
{'component' in screen && screen.component !== undefined ? (
<screen.component navigation={navigation} route={route} />
) : 'children' in screen && screen.children !== undefined ? (
screen.children({ navigation, route })
) : null}
</StaticContainer>
</EnsureSingleNavigator>
</NavigationStateContext.Provider>
</NavigationRouteContext.Provider>
</NavigationContext.Provider>
);
}

View File

@@ -125,15 +125,5 @@ it('resets state to new state with RESET', () => {
})
);
expect(result).toEqual({ ...STATE, index: 0, routes });
});
it('ignores key and routeNames when resetting with RESET', () => {
const result = BaseRouter.getStateForAction(
STATE,
// @ts-ignore
CommonActions.reset({ index: 2, key: 'foo', routeNames: ['test'] })
);
expect(result).toEqual({ ...STATE, index: 2 });
expect(result).toEqual({ index: 0, routes });
});

View File

@@ -7,42 +7,7 @@ import MockRouter, { MockRouterKey } from './__fixtures__/MockRouter';
beforeEach(() => (MockRouterKey.current = 0));
it('throws if NAVIGATE dispatched with both key and name', () => {
const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return descriptors[state.routes[state.index].key].render();
};
const FooScreen = (props: any) => {
React.useEffect(() => {
props.navigation.navigate({ key: '1', name: '2' });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return null;
};
const onStateChange = jest.fn();
const element = (
<NavigationContainer onStateChange={onStateChange}>
<TestNavigator initialRouteName="foo">
<Screen
name="foo"
component={FooScreen}
initialParams={{ count: 10 }}
/>
</TestNavigator>
</NavigationContainer>
);
expect(() => render(element).update(element)).toThrowError(
'While calling navigate with an object as the argument, you need to specify either name or key'
);
});
it('throws if NAVIGATE dispatched neither both key nor name', () => {
it('throws if NAVIGATE dispatched neither key nor name', () => {
const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
@@ -73,6 +38,6 @@ it('throws if NAVIGATE dispatched neither both key nor name', () => {
);
expect(() => render(element).update(element)).toThrowError(
'While calling navigate with an object as the argument, you need to specify either name or key'
'While calling navigate with an object as the argument, you need to specify name or key'
);
});

View File

@@ -321,3 +321,56 @@ it('handle resetting state with ref', () => {
expect(onStateChange).toBeCalledTimes(1);
expect(onStateChange).lastCalledWith(state);
});
it('handle getRootState', () => {
const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return descriptors[state.routes[state.index].key].render();
};
const ref = React.createRef<NavigationContainerRef>();
const element = (
<NavigationContainer ref={ref}>
<TestNavigator initialRouteName="foo">
<Screen name="foo">
{() => (
<TestNavigator>
<Screen name="qux" component={() => null} />
<Screen name="lex" component={() => null} />
</TestNavigator>
)}
</Screen>
<Screen name="bar" component={() => null} />
</TestNavigator>
</NavigationContainer>
);
render(element);
let state;
if (ref.current) {
state = ref.current.getRootState();
}
expect(state).toEqual({
index: 0,
key: '7',
routeNames: ['foo', 'bar'],
routes: [
{
key: 'foo',
name: 'foo',
state: {
index: 0,
key: '8',
routeNames: ['qux', 'lex'],
routes: [{ key: 'qux', name: 'qux' }, { key: 'lex', name: 'lex' }],
stale: false,
},
},
{ key: 'bar', name: 'bar' },
],
stale: false,
});
});

View File

@@ -1,6 +1,6 @@
import getPathFromState from '../getPathFromState';
it('converts path string to initial state', () => {
it('converts state to path string', () => {
expect(
getPathFromState({
routes: [
@@ -12,11 +12,12 @@ it('converts path string to initial state', () => {
{ name: 'boo' },
{
name: 'bar',
params: { fruit: 'apple' },
state: {
routes: [
{
name: 'baz qux',
params: { author: 'jane & co', valid: true },
params: { author: 'jane', valid: true },
},
],
},
@@ -26,9 +27,50 @@ it('converts path string to initial state', () => {
},
],
})
).toMatchInlineSnapshot(
`"/foo/bar/baz%20qux?author=%22jane%20%26%20co%22&valid=true"`
);
).toMatchInlineSnapshot(`"/foo/bar/baz%20qux?author=jane&valid=true"`);
});
it('converts state to path string with config', () => {
expect(
getPathFromState(
{
routes: [
{
name: 'Foo',
state: {
index: 1,
routes: [
{ name: 'boo' },
{
name: 'Bar',
params: { fruit: 'apple', type: 'sweet', avaliable: false },
state: {
routes: [
{
name: 'Baz',
params: { author: 'Jane', valid: true, id: 10 },
},
],
},
},
],
},
},
],
},
{
Foo: 'few',
Bar: 'bar/:type/:fruit',
Baz: {
path: 'baz/:author',
stringify: {
author: author => author.toLowerCase(),
id: id => `x${id}`,
},
},
}
)
).toMatchInlineSnapshot(`"/few/bar/sweet/apple/baz/jane?id=x10&valid=true"`);
});
it('handles route without param', () => {

View File

@@ -2,9 +2,7 @@ import getStateFromPath from '../getStateFromPath';
it('converts path string to initial state', () => {
expect(
getStateFromPath(
'foo/bar/baz%20qux?author=%22jane%20%26%20co%22&valid=true'
)
getStateFromPath('foo/bar/baz%20qux?author=jane%20%26%20co&valid=true')
).toEqual({
routes: [
{
@@ -17,7 +15,55 @@ it('converts path string to initial state', () => {
routes: [
{
name: 'baz qux',
params: { author: 'jane & co', valid: true },
params: { author: 'jane & co', valid: 'true' },
},
],
},
},
],
},
},
],
});
});
it('converts path string to initial state with config', () => {
expect(
getStateFromPath(
'/few/bar/sweet/apple/baz/jane?count=10&answer=42&valid=true',
{
Foo: 'few',
Bar: 'bar/:type/:fruit',
Baz: {
path: 'baz/:author',
parse: {
author: (author: string) =>
author.replace(/^\w/, c => c.toUpperCase()),
count: Number,
valid: Boolean,
},
},
}
)
).toEqual({
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Bar',
params: { fruit: 'apple', type: 'sweet' },
state: {
routes: [
{
name: 'Baz',
params: {
author: 'Jane',
count: 10,
answer: '42',
valid: true,
},
},
],
},
@@ -38,7 +84,7 @@ it('handles leading slash when converting', () => {
routes: [
{
name: 'bar',
params: { count: 42 },
params: { count: '42' },
},
],
},
@@ -56,7 +102,7 @@ it('handles ending slash when converting', () => {
routes: [
{
name: 'bar',
params: { count: 42 },
params: { count: '42' },
},
],
},

View File

@@ -323,7 +323,7 @@ it('cleans up state when the navigator unmounts', () => {
expect(onStateChange).lastCalledWith(undefined);
});
it('allows arbitrary state updates by dispatching a function', () => {
it('allows state updates by dispatching a function returning an action', () => {
const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
@@ -332,11 +332,11 @@ it('allows arbitrary state updates by dispatching a function', () => {
const FooScreen = (props: any) => {
React.useEffect(() => {
props.navigation.dispatch((state: any) => ({
...state,
routes: state.routes.slice().reverse(),
index: 1,
}));
props.navigation.dispatch((state: NavigationState) =>
state.index === 0
? { type: 'NAVIGATE', payload: { name: state.routeNames[1] } }
: { type: 'NOOP' }
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -364,7 +364,7 @@ it('allows arbitrary state updates by dispatching a function', () => {
index: 1,
key: '0',
routeNames: ['foo', 'bar'],
routes: [{ key: 'bar', name: 'bar' }, { key: 'foo', name: 'foo' }],
routes: [{ key: 'foo', name: 'foo' }, { key: 'bar', name: 'bar' }],
});
});
@@ -655,6 +655,7 @@ it('throws when a React Element is not the direct children', () => {
);
});
// eslint-disable-next-line jest/expect-expect
it("doesn't throw when direct children is Screen or empty element", () => {
const TestNavigator = (props: any) => {
useNavigationBuilder(MockRouter, props);
@@ -694,3 +695,30 @@ it('throws when multiple screens with same name are defined', () => {
"A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named 'foo')"
);
});
it('switches rendered navigators', () => {
const TestNavigator = (props: any) => {
useNavigationBuilder(MockRouter, props);
return null;
};
const root = render(
<NavigationContainer>
<TestNavigator key="a">
<Screen name="foo" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
);
expect(() =>
root.update(
<NavigationContainer>
<TestNavigator key="b">
<Screen name="foo" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
)
).not.toThrowError(
'Another navigator is already registered for this container.'
);
});

View File

@@ -46,11 +46,320 @@ it('sets options with options prop as an object', () => {
</NavigationContainer>
);
expect(root).toMatchInlineSnapshot(`
<main>
<h1>
Hello world
</h1>
<div>
Test screen
</div>
</main>
`);
});
it('sets options with options prop as a fuction', () => {
const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder<
NavigationState,
any,
{ title?: string },
any
>(MockRouter, props);
const { render, options } = descriptors[state.routes[state.index].key];
return (
<main>
<h1>{options.title}</h1>
<div>{render()}</div>
</main>
);
};
const TestScreen = (): any => 'Test screen';
const root = render(
<NavigationContainer>
<TestNavigator>
<Screen
name="foo"
component={TestScreen}
options={({ route }: any) => ({ title: route.params.author })}
initialParams={{ author: 'Jane' }}
/>
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
);
expect(root).toMatchInlineSnapshot(`
<main>
<h1>
Jane
</h1>
<div>
Test screen
</div>
</main>
`);
});
it('sets options with screenOptions prop as an object', () => {
const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder<
NavigationState,
any,
{ title?: string },
any
>(MockRouter, props);
return (
<>
{state.routes.map(route => {
const { render, options } = descriptors[route.key];
return (
<main key={route.key}>
<h1>{options.title}</h1>
<div>{render()}</div>
</main>
);
})}
</>
);
};
const TestScreenA = (): any => 'Test screen A';
const TestScreenB = (): any => 'Test screen B';
const root = render(
<NavigationContainer>
<TestNavigator screenOptions={{ title: 'Hello world' }}>
<Screen name="foo" component={TestScreenA} />
<Screen name="bar" component={TestScreenB} />
</TestNavigator>
</NavigationContainer>
);
expect(root).toMatchInlineSnapshot(`
Array [
<main>
<h1>
Hello world
</h1>
<div>
Test screen A
</div>
</main>,
<main>
<h1>
Hello world
</h1>
<div>
Test screen B
</div>
</main>,
]
`);
});
it('sets options with screenOptions prop as a fuction', () => {
const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder<
NavigationState,
any,
{ title?: string },
any
>(MockRouter, props);
return (
<>
{state.routes.map(route => {
const { render, options } = descriptors[route.key];
return (
<main key={route.key}>
<h1>{options.title}</h1>
<div>{render()}</div>
</main>
);
})}
</>
);
};
const TestScreenA = (): any => 'Test screen A';
const TestScreenB = (): any => 'Test screen B';
const root = render(
<NavigationContainer>
<TestNavigator
screenOptions={({ route }: any) => ({
title: `${route.name}: ${route.params.author || route.params.fruit}`,
})}
>
<Screen
name="foo"
component={TestScreenA}
initialParams={{ author: 'Jane' }}
/>
<Screen
name="bar"
component={TestScreenB}
initialParams={{ fruit: 'Apple' }}
/>
</TestNavigator>
</NavigationContainer>
);
expect(root).toMatchInlineSnapshot(`
Array [
<main>
<h1>
foo: Jane
</h1>
<div>
Test screen A
</div>
</main>,
<main>
<h1>
bar: Apple
</h1>
<div>
Test screen B
</div>
</main>,
]
`);
});
it('sets initial options with setOptions', () => {
const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder<
NavigationState,
any,
{
title?: string;
color?: string;
},
any
>(MockRouter, props);
const { render, options } = descriptors[state.routes[state.index].key];
return (
<main>
<h1 color={options.color}>{options.title}</h1>
<div>{render()}</div>
</main>
);
};
const TestScreen = ({ navigation }: any): any => {
navigation.setOptions({
title: 'Hello world',
});
return 'Test screen';
};
const root = render(
<NavigationContainer>
<TestNavigator>
<Screen name="foo" options={{ color: 'blue' }}>
{props => <TestScreen {...props} />}
</Screen>
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
);
expect(root).toMatchInlineSnapshot(`
<main>
<h1
color="blue"
>
Hello world
</h1>
<div>
Test screen
</div>
</main>
`);
});
it('updates options with setOptions', () => {
const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder<
NavigationState,
any,
any,
any
>(MockRouter, props);
const { render, options } = descriptors[state.routes[state.index].key];
return (
<main>
<h1 color={options.color}>{options.title}</h1>
<p>{options.description}</p>
<caption>{options.author}</caption>
<div>{render()}</div>
</main>
);
};
const TestScreen = ({ navigation }: any): any => {
navigation.setOptions({
title: 'Hello world',
description: 'Something here',
});
React.useEffect(() => {
const timer = setTimeout(() =>
navigation.setOptions({
title: 'Hello again',
author: 'Jane',
})
);
return () => clearTimeout(timer);
});
return 'Test screen';
};
const element = (
<NavigationContainer>
<TestNavigator>
<Screen name="foo" options={{ color: 'blue' }}>
{props => <TestScreen {...props} />}
</Screen>
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
);
const root = render(element);
act(() => jest.runAllTimers());
root.update(element);
expect(root).toMatchInlineSnapshot(`
<main>
<h1>
Hello world
<h1
color="blue"
>
Hello again
</h1>
<p>
Something here
</p>
<caption>
Jane
</caption>
<div>
Test screen
</div>
@@ -295,180 +604,3 @@ it('returns true for canGoBack when parent router handles GO_BACK', () => {
expect(result).toBe(false);
});
it('sets options with options prop as a fuction', () => {
const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder<
NavigationState,
any,
{ title?: string },
any
>(MockRouter, props);
const { render, options } = descriptors[state.routes[state.index].key];
return (
<main>
<h1>{options.title}</h1>
<div>{render()}</div>
</main>
);
};
const TestScreen = (): any => 'Test screen';
const root = render(
<NavigationContainer>
<TestNavigator>
<Screen
name="foo"
component={TestScreen}
options={({ route }: any) => ({ title: route.params.author })}
initialParams={{ author: 'Jane' }}
/>
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
);
expect(root).toMatchInlineSnapshot(`
<main>
<h1>
Jane
</h1>
<div>
Test screen
</div>
</main>
`);
});
it('sets initial options with setOptions', () => {
const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder<
NavigationState,
any,
{
title?: string;
color?: string;
},
any
>(MockRouter, props);
const { render, options } = descriptors[state.routes[state.index].key];
return (
<main>
<h1 color={options.color}>{options.title}</h1>
<div>{render()}</div>
</main>
);
};
const TestScreen = ({ navigation }: any): any => {
navigation.setOptions({
title: 'Hello world',
});
return 'Test screen';
};
const root = render(
<NavigationContainer>
<TestNavigator>
<Screen name="foo" options={{ color: 'blue' }}>
{props => <TestScreen {...props} />}
</Screen>
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
);
expect(root).toMatchInlineSnapshot(`
<main>
<h1
color="blue"
>
Hello world
</h1>
<div>
Test screen
</div>
</main>
`);
});
it('updates options with setOptions', () => {
const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder<
NavigationState,
any,
any,
any
>(MockRouter, props);
const { render, options } = descriptors[state.routes[state.index].key];
return (
<main>
<h1 color={options.color}>{options.title}</h1>
<p>{options.description}</p>
<caption>{options.author}</caption>
<div>{render()}</div>
</main>
);
};
const TestScreen = ({ navigation }: any): any => {
navigation.setOptions({
title: 'Hello world',
description: 'Something here',
});
React.useEffect(() => {
const timer = setTimeout(() =>
navigation.setOptions({
title: 'Hello again',
author: 'Jane',
})
);
return () => clearTimeout(timer);
});
return 'Test screen';
};
const element = (
<NavigationContainer>
<TestNavigator>
<Screen name="foo" options={{ color: 'blue' }}>
{props => <TestScreen {...props} />}
</Screen>
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
);
const root = render(element);
act(() => jest.runAllTimers());
root.update(element);
expect(root).toMatchInlineSnapshot(`
<main>
<h1
color="blue"
>
Hello again
</h1>
<p>
Something here
</p>
<caption>
Jane
</caption>
<div>
Test screen
</div>
</main>
`);
});

View File

@@ -4,6 +4,7 @@ import useNavigationBuilder from '../useNavigationBuilder';
import NavigationContainer from '../NavigationContainer';
import Screen from '../Screen';
import MockRouter from './__fixtures__/MockRouter';
import { Router, NavigationState } from '../types';
it('fires focus and blur events in root navigator', () => {
const TestNavigator = React.forwardRef((props: any, ref: any): any => {
@@ -200,6 +201,131 @@ it('fires focus and blur events in nested navigator', () => {
expect(thirdFocusCallback).toBeCalledTimes(1);
});
it('fires blur event when a route is removed with a delay', async () => {
const TestRouter = (options: any): Router<NavigationState, any> => {
const router = MockRouter(options);
return {
...router,
getInitialState({ routeNames, routeParamList }) {
const initialRouteName =
options.initialRouteName !== undefined
? options.initialRouteName
: routeNames[0];
return {
stale: false,
key: 'stack',
index: 0,
routeNames,
routes: [
{
key: initialRouteName,
name: initialRouteName,
params: routeParamList[initialRouteName],
},
],
};
},
getStateForAction(state, action) {
switch (action.type) {
case 'PUSH':
return {
...state,
index: state.index + 1,
routes: [...state.routes, action.payload],
};
case 'POP': {
const routes = state.routes.slice(0, -1);
return {
...state,
index: routes.length - 1,
routes,
};
}
default:
return router.getStateForAction(state, action);
}
},
actionCreators: {
push(payload) {
return { type: 'PUSH', payload };
},
pop() {
return { type: 'POP' };
},
},
};
};
const TestNavigator = React.forwardRef((props: any, ref: any): any => {
const { state, navigation, descriptors } = useNavigationBuilder(
TestRouter,
props
);
React.useImperativeHandle(ref, () => navigation, [navigation]);
const [previous, dispatch] = React.useReducer(
(state, action) => {
return { ...state, ...action };
},
{ routes: state.routes, descriptors }
);
React.useEffect(() => {
dispatch({ routes: state.routes, descriptors });
}, [descriptors, state.routes]);
return previous.routes.map((route: any) =>
previous.descriptors[route.key].render()
);
});
const blurCallback = jest.fn();
const First = () => null;
const Second = ({ navigation }: any) => {
React.useEffect(() => navigation.addListener('blur', blurCallback), [
navigation,
]);
return null;
};
const navigation = React.createRef<any>();
const element = (
<NavigationContainer>
<TestNavigator ref={navigation}>
<Screen name="first" component={First} />
<Screen name="second" component={Second} />
</TestNavigator>
</NavigationContainer>
);
render(element);
act(() =>
navigation.current.push({
name: 'second',
key: 'second',
})
);
expect(blurCallback).toBeCalledTimes(0);
act(() => navigation.current.pop());
expect(blurCallback).toBeCalledTimes(1);
});
it('fires custom events', () => {
const eventName = 'someSuperCoolEvent';

View File

@@ -296,6 +296,7 @@ it("action doesn't bubble if target is specified", () => {
expect(onStateChange).not.toBeCalled();
});
// eslint-disable-next-line jest/expect-expect
it("doesn't crash if no navigator handled the action", () => {
const TestRouter = MockRouter;

View File

@@ -0,0 +1,34 @@
import * as React from 'react';
import { render } from 'react-native-testing-library';
import useNavigationBuilder from '../useNavigationBuilder';
import useRoute from '../useRoute';
import NavigationContainer from '../NavigationContainer';
import Screen from '../Screen';
import MockRouter from './__fixtures__/MockRouter';
import { RouteProp } from '../types';
it('gets route prop from context', () => {
expect.assertions(1);
const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return state.routes.map(route => descriptors[route.key].render());
};
const Test = () => {
const route = useRoute<RouteProp<{ sample: { x: string } }, 'sample'>>();
expect(route && route.params && route.params.x).toEqual(1);
return null;
};
render(
<NavigationContainer>
<TestNavigator>
<Screen name="foo" component={Test} initialParams={{ x: 1 }} />
</TestNavigator>
</NavigationContainer>
);
});

View File

@@ -1,14 +1,45 @@
import queryString from 'query-string';
import { NavigationState, PartialState, Route } from './types';
type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>;
type StringifyConfig = { [key: string]: (value: any) => string };
type Options = {
[routeName: string]: string | { path: string; stringify?: StringifyConfig };
};
/**
* Utility to serialize a navigation state object to a path string.
*
* Example:
* ```js
* getPathFromState(
* {
* routes: [
* {
* name: 'Chat',
* params: { author: 'Jane', id: 42 },
* },
* ],
* },
* {
* Chat: {
* path: 'chat/:author/:id',
* stringify: { author: author => author.toLowerCase() }
* }
* }
* )
* ```
*
* @param state Navigation state to serialize.
* @param options Extra options to fine-tune how to serialize the path.
* @returns Path representing the state, e.g. /foo/bar?count=42.
*/
export default function getPathFromState(state: State): string {
export default function getPathFromState(
state: State,
options: Options = {}
): string {
let path = '/';
let current: State | undefined = state;
@@ -19,24 +50,51 @@ export default function getPathFromState(state: State): string {
state?: State | undefined;
};
path += encodeURIComponent(route.name);
const config =
options[route.name] !== undefined
? (options[route.name] as { stringify?: StringifyConfig }).stringify
: undefined;
const params = route.params
? // Stringify all of the param values before we use them
Object.entries(route.params).reduce<{
[key: string]: string;
}>((acc, [key, value]) => {
acc[key] = config && config[key] ? config[key](value) : String(value);
return acc;
}, {})
: undefined;
if (options[route.name] !== undefined) {
const pattern =
typeof options[route.name] === 'string'
? (options[route.name] as string)
: (options[route.name] as { path: string }).path;
path += pattern
.split('/')
.map(p => {
const name = p.replace(/^:/, '');
// If the path has a pattern for a param, put the param in the path
if (params && name in params && p.startsWith(':')) {
const value = params[name];
// Remove the used value from the params object since we'll use the rest for query string
delete params[name];
return encodeURIComponent(value);
}
return encodeURIComponent(p);
})
.join('/');
} else {
path += encodeURIComponent(route.name);
}
if (route.state) {
path += '/';
} else if (route.params) {
const query = [];
for (const param in route.params) {
const value = (route.params as { [key: string]: any })[param];
query.push(
`${encodeURIComponent(param)}=${encodeURIComponent(
JSON.stringify(value)
)}`
);
}
path += `?${query.join('&')}`;
} else if (params) {
path += `?${queryString.stringify(params)}`;
}
current = route.state;

View File

@@ -1,50 +1,149 @@
import escape from 'escape-string-regexp';
import queryString from 'query-string';
import { NavigationState, PartialState } from './types';
type ParseConfig = { [key: string]: (value: string) => any };
type Options = {
[routeName: string]: string | { path: string; parse?: ParseConfig };
};
/**
* Utility to parse a path string to initial state object accepted by the container.
* This is useful for deep linking when we need to handle the incoming URL.
*
* Example:
* ```js
* getStateFromPath(
* '/chat/jane/42',
* {
* Chat: {
* path: 'chat/:author/:id',
* parse: { id: Number }
* }
* }
* )
* ```
* @param path Path string to parse and convert, e.g. /foo/bar?count=42.
* @param options Extra options to fine-tune how to parse the path.
*/
export default function getStateFromPath(
path: string
path: string,
options: Options = {}
): PartialState<NavigationState> | undefined {
const parts = path.split('?');
const segments = parts[0].split('/').filter(Boolean);
const query = parts[1] ? parts[1].split('&') : undefined;
// Create a normalized config array which will be easier to use
const routeConfig = Object.keys(options).map(key => {
const pattern =
typeof options[key] === 'string'
? (options[key] as string)
: (options[key] as { path: string }).path;
// Create a regex from the provided path pattern
// With the pattern, we can match segements containing params and extract them
const match = new RegExp(
'^' + escape(pattern).replace(/:[a-z0-9]+/gi, '([^/]+)') + '/?'
);
return {
match,
pattern,
routeName: key,
// @ts-ignore
parse: options[key].parse,
};
});
let result: PartialState<NavigationState> | undefined;
let current: PartialState<NavigationState> | undefined;
while (segments.length) {
let remaining = path
.replace(/[/]+/, '/') // Replace multiple slash (//) with single ones
.replace(/^\//, '') // Remove extra leading slash
.replace(/\?.*/, ''); // Remove query params which we will handle later
while (remaining) {
let routeName;
let params;
// Go through all configs, and see if the next path segment matches our regex
for (const config of routeConfig) {
const match = remaining.match(config.match);
// If our regex matches, we need to extract params from the path
if (match) {
routeName = config.routeName;
const paramPatterns = config.pattern
.split('/')
.filter(p => p.startsWith(':'));
if (paramPatterns.length) {
params = paramPatterns.reduce<{ [key: string]: any }>((acc, p, i) => {
const key = p.replace(/^:/, '');
const value = match[i + 1]; // The param segments start from index 1 in the regex match result
acc[key] =
config.parse && config.parse[key]
? config.parse[key](value)
: value;
return acc;
}, {});
}
// Remove the matched segment from the remaining path
remaining = remaining.replace(match[0], '');
break;
}
}
// If we hadn't matched any segments earlier, use the path as route name
if (routeName === undefined) {
const segments = remaining.split('/');
routeName = decodeURIComponent(segments[0]);
segments.shift();
remaining = segments.join('/');
}
const state = {
routes: [{ name: decodeURIComponent(segments[0]) }],
routes: [{ name: routeName, params }],
};
if (current) {
// The state should be nested inside the route we parsed before
current.routes[0].state = state;
} else {
result = state;
}
current = state;
segments.shift();
}
if (current == null || result == null) {
return undefined;
}
const query = path.split('?')[1];
if (query) {
const params = query.reduce<{ [key: string]: any }>((acc, curr) => {
const [key, value] = curr.split('=');
const route = current.routes[0];
acc[decodeURIComponent(key)] = JSON.parse(decodeURIComponent(value));
const params = queryString.parse(query);
const config = options[route.name]
? (options[route.name] as { parse?: ParseConfig }).parse
: undefined;
return acc;
}, {});
if (config) {
Object.keys(params).forEach(name => {
if (config[name] && typeof params[name] === 'string') {
params[name] = config[name](params[name] as string);
}
});
}
current.routes[0].params = params;
route.params = { ...route.params, ...params };
}
return result;

View File

@@ -6,8 +6,12 @@ export { default as BaseRouter } from './BaseRouter';
export { default as NavigationContainer } from './NavigationContainer';
export { default as createNavigator } from './createNavigator';
export { default as NavigationContext } from './NavigationContext';
export { default as NavigationRouteContext } from './NavigationRouteContext';
export { default as useNavigationBuilder } from './useNavigationBuilder';
export { default as useNavigation } from './useNavigation';
export { default as useRoute } from './useRoute';
export { default as useFocusEffect } from './useFocusEffect';
export { default as useIsFocused } from './useIsFocused';

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-cycle
import * as CommonActions from './CommonActions';
import * as React from 'react';
@@ -96,7 +97,12 @@ export type DefaultNavigatorOptions<
/**
* Default options for all screens under this navigator.
*/
screenOptions?: ScreenOptions;
screenOptions?:
| ScreenOptions
| ((props: {
route: RouteProp<ParamListBase, string>;
navigation: any;
}) => ScreenOptions);
};
export type RouterFactory<
@@ -165,7 +171,10 @@ export type Router<
* @param state State object to apply the action on.
* @param action Action object to apply.
*/
getStateForAction(state: State, action: Action): State | null;
getStateForAction(
state: State,
action: Action
): State | PartialState<State> | null;
/**
* Whether the action should also change focus in parent navigator
@@ -265,7 +274,9 @@ type NavigationHelpersCommon<
*
* @param action Action object or update function.
*/
dispatch(action: NavigationAction | ((state: State) => State)): void;
dispatch(
action: NavigationAction | ((state: State) => NavigationAction)
): void;
/**
* Navigate to a route in current navigation tree.
@@ -274,8 +285,8 @@ type NavigationHelpersCommon<
* @param [params] Params object for the route.
*/
navigate<RouteName extends keyof ParamList>(
...args: ParamList[RouteName] extends undefined
? [RouteName] | [RouteName, undefined]
...args: ParamList[RouteName] extends (undefined | any)
? [RouteName] | [RouteName, ParamList[RouteName]]
: [RouteName, ParamList[RouteName]]
): void;
@@ -287,7 +298,7 @@ type NavigationHelpersCommon<
navigate<RouteName extends keyof ParamList>(
route:
| { key: string; params?: ParamList[RouteName] }
| { name: RouteName; params: ParamList[RouteName] }
| { name: RouteName; key?: string; params: ParamList[RouteName] }
): void;
/**
@@ -298,7 +309,7 @@ type NavigationHelpersCommon<
*/
replace<RouteName extends keyof ParamList>(
...args: ParamList[RouteName] extends undefined
? [RouteName] | [RouteName, undefined]
? [RouteName] | [RouteName, ParamList[RouteName]]
: [RouteName, ParamList[RouteName]]
): void;
@@ -309,6 +320,13 @@ type NavigationHelpersCommon<
*/
reset(state: PartialState<State> | State): void;
/**
* Reset the navigation state of the root navigator to the provided state.
*
* @param state Navigation state object.
*/
resetRoot(state: PartialState<NavigationState> | NavigationState): void;
/**
* Go back to the previous route in history.
*/
@@ -382,14 +400,13 @@ export type NavigationProp<
* It can be useful to decide whether to display a back button in a stack.
*/
isFirstRouteInParent(): boolean;
/**
* Returns the parent navigator, if any. Reason why the function is called
* dangerouslyGetParent is to warn developers against overusing it to eg. get parent
* of parent and other hard-to-follow patterns.
*/
dangerouslyGetParent():
| NavigationProp<ParamListBase, string, any, any>
| undefined;
dangerouslyGetParent<T = NavigationProp<ParamListBase> | undefined>(): T;
/**
* Returns the navigator's state. Reason why the function is called
@@ -521,6 +538,7 @@ export type NavigationContainerRef =
* @param state Navigation state object.
*/
resetRoot(state: PartialState<NavigationState> | NavigationState): void;
getRootState(): NavigationState;
}
| undefined
| null;
@@ -534,11 +552,24 @@ export type TypedNavigator<
* Navigator component which manages the child screens.
*/
Navigator: React.ComponentType<
React.ComponentProps<Navigator> & {
Omit<
React.ComponentProps<Navigator>,
'initialRouteName' | 'screenOptions'
> & {
/**
* Route to focus on initial render.
* Name of the route to focus by on initial render.
* If not specified, usually the first route is used.
*/
initialRouteName?: keyof ParamList;
/**
* Default options for all screens under this navigator.
*/
screenOptions?:
| ScreenOptions
| ((props: {
route: RouteProp<ParamList, keyof ParamList>;
navigation: any;
}) => ScreenOptions);
}
>;
/**

View File

@@ -3,6 +3,7 @@ import SceneView from './SceneView';
import NavigationBuilderContext, {
ChildActionListener,
FocusedNavigationListener,
NavigatorStateGetter,
} from './NavigationBuilderContext';
import { NavigationEventEmitter } from './useEventEmitter';
import useNavigationCache from './useNavigationCache';
@@ -13,24 +14,31 @@ import {
NavigationState,
ParamListBase,
RouteConfig,
RouteProp,
Router,
} from './types';
type Options<ScreenOptions extends object> = {
state: NavigationState;
type Options<State extends NavigationState, ScreenOptions extends object> = {
state: State;
screens: { [key: string]: RouteConfig<ParamListBase, string, ScreenOptions> };
navigation: NavigationHelpers<ParamListBase>;
screenOptions?: ScreenOptions;
screenOptions?:
| ScreenOptions
| ((props: {
route: RouteProp<ParamListBase, string>;
navigation: any;
}) => ScreenOptions);
onAction: (
action: NavigationAction,
visitedNavigators?: Set<string>
) => boolean;
getState: () => NavigationState;
setState: (state: NavigationState) => void;
getState: () => State;
setState: (state: State) => void;
addActionListener: (listener: ChildActionListener) => void;
addFocusedListener: (listener: FocusedNavigationListener) => void;
addStateGetter: (key: string, getter: NavigatorStateGetter) => void;
onRouteFocus: (key: string) => void;
router: Router<NavigationState, NavigationAction>;
router: Router<State, NavigationAction>;
emitter: NavigationEventEmitter;
};
@@ -55,10 +63,11 @@ export default function useDescriptors<
setState,
addActionListener,
addFocusedListener,
addStateGetter,
onRouteFocus,
router,
emitter,
}: Options<ScreenOptions>) {
}: Options<State, ScreenOptions>) {
const [options, setOptions] = React.useState<{ [key: string]: object }>({});
const { trackAction } = React.useContext(NavigationBuilderContext);
@@ -68,6 +77,7 @@ export default function useDescriptors<
onAction,
addActionListener,
addFocusedListener,
addStateGetter,
onRouteFocus,
trackAction,
}),
@@ -77,6 +87,7 @@ export default function useDescriptors<
addActionListener,
addFocusedListener,
onRouteFocus,
addStateGetter,
trackAction,
]
);
@@ -93,13 +104,15 @@ export default function useDescriptors<
return state.routes.reduce(
(acc, route) => {
const screen = screens[route.name];
const navigation = navigations[route.key];
acc[route.key] = {
navigation,
render() {
return (
<NavigationBuilderContext.Provider key={route.key} value={context}>
<SceneView
navigation={navigations[route.key]}
navigation={navigation}
route={route}
screen={screen}
getState={getState}
@@ -108,22 +121,30 @@ export default function useDescriptors<
</NavigationBuilderContext.Provider>
);
},
options: {
// The default `screenOptions` passed to the navigator
...screenOptions,
// The `options` prop passed to `Screen` elements
...(typeof screen.options === 'object' || screen.options == null
? screen.options
: screen.options({
// @ts-ignore
route,
navigation: navigations[route.key],
})),
// The options set via `navigation.setOptions`
...options[route.key],
get options() {
return {
// The default `screenOptions` passed to the navigator
...(typeof screenOptions === 'object' || screenOptions == null
? screenOptions
: screenOptions({
// @ts-ignore
route,
navigation,
})),
// The `options` prop passed to `Screen` elements
...(typeof screen.options === 'object' || screen.options == null
? screen.options
: screen.options({
// @ts-ignore
route,
navigation,
})),
// The options set via `navigation.setOptions`
...options[route.key],
};
},
navigation: navigations[route.key],
};
return acc;
},
{} as {

View File

@@ -18,6 +18,7 @@ type DevTools = {
};
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace NodeJS {
interface Global {
__REDUX_DEVTOOLS_EXTENSION__:

View File

@@ -57,19 +57,19 @@ export default function useFocusEvents({ state, emitter }: Options) {
return;
}
state.routes.forEach((route, i) => {
if (
lastFocusedKey === undefined ||
(route.key !== lastFocusedKey && route.key !== currentFocusedKey)
) {
// Only fire events after mount, or if focus state of this route changed
return;
}
if (lastFocusedKey === undefined) {
// Only fire events after initial mount
return;
}
emitter.emit({
type: i === state.index ? 'focus' : 'blur',
target: route.key,
});
emitter.emit({
type: 'focus',
target: currentFocusedKey,
});
}, [currentFocusedKey, emitter, navigation, state.index, state.routes]);
emitter.emit({
type: 'blur',
target: lastFocusedKey,
});
}, [currentFocusedKey, emitter, navigation]);
}

View File

@@ -23,6 +23,8 @@ import {
PrivateValueStore,
NavigationAction,
} from './types';
import useStateGetters from './useStateGetters';
import useOnGetState from './useOnGetState';
// This is to make TypeScript compiler happy
// eslint-disable-next-line babel/no-unused-expressions
@@ -227,6 +229,8 @@ export default function useNavigationBuilder<
addListener: addFocusedListener,
} = useFocusedListeners();
const { getStateForRoute, addStateGetter } = useStateGetters();
const onAction = useOnAction({
router,
getState,
@@ -245,7 +249,6 @@ export default function useNavigationBuilder<
const navigation = useNavigationHelpers<State, NavigationAction, EventMap>({
onAction,
getState,
setState,
emitter,
router,
});
@@ -255,6 +258,11 @@ export default function useNavigationBuilder<
focusedListeners,
});
useOnGetState({
getState,
getStateForRoute,
});
const descriptors = useDescriptors<State, ScreenOptions>({
state,
screens,
@@ -266,6 +274,7 @@ export default function useNavigationBuilder<
onRouteFocus,
addActionListener,
addFocusedListener,
addStateGetter,
router,
emitter,
});

View File

@@ -12,15 +12,15 @@ import {
Router,
} from './types';
type Options = {
state: NavigationState;
getState: () => NavigationState;
type Options<State extends NavigationState> = {
state: State;
getState: () => State;
navigation: NavigationHelpers<ParamListBase> &
Partial<NavigationProp<ParamListBase, string, any, any, any>>;
setOptions: (
cb: (options: { [key: string]: object }) => { [key: string]: object }
) => void;
router: Router<NavigationState, NavigationAction>;
router: Router<State, NavigationAction>;
emitter: NavigationEventEmitter;
};
@@ -39,7 +39,14 @@ type NavigationCache<
export default function useNavigationCache<
State extends NavigationState,
ScreenOptions extends object
>({ state, getState, navigation, setOptions, router, emitter }: Options) {
>({
state,
getState,
navigation,
setOptions,
router,
emitter,
}: Options<State>) {
// Cache object which holds navigation objects for each screen
// We use `React.useMemo` instead of `React.useRef` coz we want to invalidate it when deps change
// In reality, these deps will rarely change, if ever
@@ -70,13 +77,17 @@ export default function useNavigationCache<
const { emit, ...rest } = navigation;
const dispatch = (
action: NavigationAction | ((state: State) => State)
) =>
action: NavigationAction | ((state: State) => NavigationAction)
) => {
const payload =
typeof action === 'function' ? action(getState()) : action;
navigation.dispatch(
typeof action === 'object' && action != null
? { source: route.key, ...action }
: action
typeof payload === 'object' && payload != null
? { source: route.key, ...payload }
: payload
);
};
const helpers = Object.keys(actions).reduce(
(acc, name) => {
@@ -91,8 +102,8 @@ export default function useNavigationCache<
...rest,
...helpers,
...emitter.create(route.key),
dangerouslyGetParent: () => parentNavigation,
dangerouslyGetState: getState as () => State,
dangerouslyGetParent: () => parentNavigation as any,
dangerouslyGetState: getState,
dispatch,
setOptions: (options: object) =>
setOptions(o => ({

View File

@@ -1,6 +1,7 @@
import * as React from 'react';
import * as CommonActions from './CommonActions';
import NavigationContext from './NavigationContext';
import ResetRootContext from './ResetRootContext';
import { NavigationStateContext } from './NavigationContainer';
import { NavigationEventEmitter } from './useEventEmitter';
import {
@@ -23,7 +24,6 @@ type Options<State extends NavigationState, Action extends NavigationAction> = {
visitedNavigators?: Set<string>
) => boolean;
getState: () => State;
setState: (state: State) => void;
emitter: NavigationEventEmitter;
router: Router<State, Action>;
};
@@ -36,18 +36,18 @@ export default function useNavigationHelpers<
State extends NavigationState,
Action extends NavigationAction,
EventMap extends { [key: string]: any }
>({ onAction, getState, setState, emitter, router }: Options<State, Action>) {
>({ onAction, getState, emitter, router }: Options<State, Action>) {
const resetRoot = React.useContext(ResetRootContext);
const parentNavigationHelpers = React.useContext(NavigationContext);
const { performTransaction } = React.useContext(NavigationStateContext);
return React.useMemo(() => {
const dispatch = (action: Action | ((state: State) => State)) =>
const dispatch = (action: Action | ((state: State) => Action)) =>
performTransaction(() => {
if (typeof action === 'function') {
setState(action(getState()));
} else {
onAction(action);
}
const payload =
typeof action === 'function' ? action(getState()) : action;
onAction(payload);
});
const actions = {
@@ -67,6 +67,7 @@ export default function useNavigationHelpers<
return {
...parentNavigationHelpers,
...helpers,
resetRoot,
dispatch,
emit: emitter.emit,
isFocused: parentNavigationHelpers
@@ -85,9 +86,9 @@ export default function useNavigationHelpers<
router,
getState,
parentNavigationHelpers,
resetRoot,
emitter.emit,
performTransaction,
setState,
onAction,
]);
}

View File

@@ -2,13 +2,18 @@ import * as React from 'react';
import NavigationBuilderContext, {
ChildActionListener,
} from './NavigationBuilderContext';
import { NavigationAction, NavigationState, Router } from './types';
import {
NavigationAction,
NavigationState,
PartialState,
Router,
} from './types';
type Options = {
router: Router<NavigationState, NavigationAction>;
key?: string;
getState: () => NavigationState;
setState: (state: NavigationState) => void;
setState: (state: NavigationState | PartialState<NavigationState>) => void;
listeners: ChildActionListener[];
};

View File

@@ -0,0 +1,31 @@
import * as React from 'react';
import NavigationBuilderContext from './NavigationBuilderContext';
import { NavigationState } from './types';
import NavigationRouteContext from './NavigationRouteContext';
export default function useOnGetState({
getStateForRoute,
getState,
}: {
getStateForRoute: (routeName: string) => NavigationState | undefined;
getState: () => NavigationState;
}) {
const { addStateGetter } = React.useContext(NavigationBuilderContext);
const route = React.useContext(NavigationRouteContext);
const key = route ? route.key : 'root';
const getter = React.useCallback(() => {
const state = getState();
return {
...state,
routes: state.routes.map(route => ({
...route,
state: getStateForRoute(route.key),
})),
};
}, [getState, getStateForRoute]);
React.useEffect(() => {
return addStateGetter && addStateGetter(key, getter);
}, [addStateGetter, getter, key]);
}

View File

@@ -0,0 +1,22 @@
import * as React from 'react';
import NavigationRouteContext from './NavigationRouteContext';
import { ParamListBase, RouteProp } from './types';
/**
* Hook to access the route prop of the parent screen anywhere.
*
* @returns Route prop of the parent screen.
*/
export default function useRoute<
T extends RouteProp<ParamListBase, string>
>(): T {
const route = React.useContext(NavigationRouteContext);
if (route === undefined) {
throw new Error(
"We couldn't find a route object. Is your component inside a navigator?"
);
}
return route as T;
}

View File

@@ -0,0 +1,35 @@
import * as React from 'react';
import { NavigatorStateGetter } from './NavigationBuilderContext';
/**
* Hook which lets child navigators add getters to be called for obtaining rehydrated state.
*/
export default function useStateGetters() {
const stateGetters = React.useRef<Record<string, NavigatorStateGetter>>({});
const getStateForRoute = React.useCallback(
routeKey =>
stateGetters.current[routeKey] === undefined
? undefined
: stateGetters.current[routeKey](),
[stateGetters]
);
const addStateGetter = React.useCallback(
(key: string, getter: NavigatorStateGetter) => {
stateGetters.current[key] = getter;
return () => {
// @ts-ignore
stateGetters.current[key] = undefined;
};
},
[]
);
return {
getStateForRoute,
addStateGetter,
};
}

View File

@@ -3,6 +3,63 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.11](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.10...@react-navigation/drawer@5.0.0-alpha.11) (2019-10-03)
**Note:** Version bump only for package @react-navigation/drawer
# [5.0.0-alpha.10](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.9...@react-navigation/drawer@5.0.0-alpha.10) (2019-09-27)
### Features
* export some more type aliases ([8b78d61](https://github.com/react-navigation/navigation-ex/commit/8b78d61))
# [5.0.0-alpha.9](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.8...@react-navigation/drawer@5.0.0-alpha.9) (2019-09-16)
### Features
* make example run as bare react-native project as well ([#85](https://github.com/react-navigation/navigation-ex/issues/85)) ([d16c20c](https://github.com/react-navigation/navigation-ex/commit/d16c20c))
# [5.0.0-alpha.8](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.7...@react-navigation/drawer@5.0.0-alpha.8) (2019-08-31)
**Note:** Version bump only for package @react-navigation/drawer
# [5.0.0-alpha.7](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.6...@react-navigation/drawer@5.0.0-alpha.7) (2019-08-30)
### Bug Fixes
* rename contentContainerStyle to sceneContainerStyle for drawer ([fdc24d2](https://github.com/react-navigation/navigation-ex/commit/fdc24d2))
# [5.0.0-alpha.6](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.5...@react-navigation/drawer@5.0.0-alpha.6) (2019-08-29)
**Note:** Version bump only for package @react-navigation/drawer
# [5.0.0-alpha.5](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.4...@react-navigation/drawer@5.0.0-alpha.5) (2019-08-28)
**Note:** Version bump only for package @react-navigation/drawer

View File

@@ -11,7 +11,7 @@
"material",
"drawer"
],
"version": "5.0.0-alpha.5",
"version": "5.0.0-alpha.11",
"license": "MIT",
"repository": {
"type": "git",
@@ -34,20 +34,20 @@
"clean": "del lib"
},
"dependencies": {
"@react-navigation/routers": "^5.0.0-alpha.5",
"react-native-safe-area-view": "^0.14.6"
"@react-navigation/routers": "^5.0.0-alpha.9",
"react-native-safe-area-view": "^0.14.7"
},
"devDependencies": {
"@react-native-community/bob": "^0.7.0",
"@types/react": "^16.8.24",
"@types/react-native": "^0.60.2",
"del-cli": "^2.0.0",
"@types/react": "^16.9.4",
"@types/react-native": "^0.60.17",
"del-cli": "^3.0.0",
"react": "16.8.3",
"react-native": "^0.59.8",
"react-native": "0.59.10",
"react-native-gesture-handler": "^1.3.0",
"react-native-reanimated": "^1.1.0",
"react-native-reanimated": "^1.3.0",
"react-native-screens": "^1.0.0-alpha.22",
"typescript": "^3.5.3"
"typescript": "^3.6.3"
},
"peerDependencies": {
"@react-navigation/core": "^5.0.0-alpha.0",

View File

@@ -20,4 +20,8 @@ export { default as DrawerGestureContext } from './utils/DrawerGestureContext';
/**
* Types
*/
export { DrawerNavigationOptions, DrawerNavigationProp } from './types';
export {
DrawerNavigationOptions,
DrawerNavigationProp,
DrawerNavigationItemsProps,
} from './types';

View File

@@ -75,7 +75,7 @@ export type DrawerNavigationConfig = {
lazy: boolean;
/**
* Whether a screen should be unmounted when navigating away from it.
* Defaults to `false`..
* Defaults to `false`.
*/
unmountInactiveRoutes?: boolean;
/**
@@ -87,7 +87,10 @@ export type DrawerNavigationConfig = {
* Options for the content component which will be passed as props.
*/
contentOptions?: object;
contentContainerStyle?: StyleProp<ViewStyle>;
/**
* Style object for the component wrapping the screen content.
*/
sceneContainerStyle?: StyleProp<ViewStyle>;
style?: StyleProp<ViewStyle>;
};

View File

@@ -88,7 +88,7 @@ type Props = {
statusBarAnimation: 'slide' | 'none' | 'fade';
overlayStyle?: StyleProp<ViewStyle>;
drawerStyle?: StyleProp<ViewStyle>;
contentContainerStyle?: StyleProp<ViewStyle>;
sceneContainerStyle?: StyleProp<ViewStyle>;
renderDrawerContent: Renderer;
renderSceneContent: Renderer;
gestureHandlerProps?: React.ComponentProps<typeof PanGestureHandler>;
@@ -484,7 +484,7 @@ export default class DrawerView extends React.PureComponent<Props> {
drawerPosition,
drawerType,
swipeEdgeWidth,
contentContainerStyle,
sceneContainerStyle,
drawerStyle,
overlayStyle,
onGestureRef,
@@ -534,7 +534,7 @@ export default class DrawerView extends React.PureComponent<Props> {
{
transform: [{ translateX: contentTranslateX }],
},
contentContainerStyle as any,
sceneContainerStyle as any,
]}
>
{renderSceneContent({ progress: this.progress })}
@@ -578,7 +578,6 @@ export default class DrawerView extends React.PureComponent<Props> {
style={[
styles.container,
right ? { right: offset } : { left: offset },
// eslint-disable-next-line react-native/no-inline-styles
{
transform: [{ translateX: drawerTranslateX }],
opacity: this.drawerOpacity,

View File

@@ -163,7 +163,6 @@ export default class DrawerView extends React.PureComponent<Props, State> {
key={route.key}
style={[
StyleSheet.absoluteFill,
// eslint-disable-next-line react-native/no-inline-styles
{ opacity: isFocused ? 1 : 0 },
]}
isVisible={isFocused}
@@ -190,7 +189,7 @@ export default class DrawerView extends React.PureComponent<Props, State> {
drawerPosition,
drawerBackgroundColor,
overlayColor,
contentContainerStyle,
sceneContainerStyle,
edgeWidth,
minSwipeDistance,
hideStatusBar,
@@ -222,7 +221,7 @@ export default class DrawerView extends React.PureComponent<Props, State> {
gestureHandlerProps={gestureHandlerProps}
drawerType={drawerType}
drawerPosition={drawerPosition}
contentContainerStyle={contentContainerStyle}
sceneContainerStyle={sceneContainerStyle}
drawerStyle={{
backgroundColor: drawerBackgroundColor || 'white',
width: this.state.drawerWidth,

1
packages/example/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
ios/Pods

View File

@@ -3,6 +3,75 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.9](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.8...@react-navigation/example@5.0.0-alpha.9) (2019-10-03)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.8](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.7...@react-navigation/example@5.0.0-alpha.8) (2019-09-27)
### Bug Fixes
* close drawer on navigate ([655a220](https://github.com/react-navigation/navigation-ex/commit/655a220))
# [5.0.0-alpha.7](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.6...@react-navigation/example@5.0.0-alpha.7) (2019-09-16)
### Features
* compatibility layer ([e0f28a4](https://github.com/satya164/navigation-ex/commit/e0f28a4))
* make example run as bare react-native project as well ([#85](https://github.com/satya164/navigation-ex/issues/85)) ([d16c20c](https://github.com/satya164/navigation-ex/commit/d16c20c))
# [5.0.0-alpha.6](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.4...@react-navigation/example@5.0.0-alpha.6) (2019-08-31)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.5](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.4...@react-navigation/example@5.0.0-alpha.5) (2019-08-31)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.4](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.3...@react-navigation/example@5.0.0-alpha.4) (2019-08-31)
### Bug Fixes
* handle route names change when all routes are removed ([#86](https://github.com/satya164/navigation-ex/issues/86)) ([1b2983e](https://github.com/satya164/navigation-ex/commit/1b2983e))
# [5.0.0-alpha.3](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.2...@react-navigation/example@5.0.0-alpha.3) (2019-08-29)
### Features
* handle more methods in useScrollToTop ([f9e8c7e](https://github.com/react-navigation/navigation-ex/commit/f9e8c7e))
# [5.0.0-alpha.2](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.1...@react-navigation/example@5.0.0-alpha.2) (2019-08-27)

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="ReactNavigationExample" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="java-gradle" name="Java-Gradle">
<configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
<option name="BUILDABLE" value="false" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -0,0 +1,65 @@
# To learn about Buck see [Docs](https://buckbuild.com/).
# To run your application with Buck:
# - install Buck
# - `npm start` - to start the packager
# - `cd android`
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
# - `buck install -r android/app` - compile, install and run application
#
lib_deps = []
for jarfile in glob(['libs/*.jar']):
name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')]
lib_deps.append(':' + name)
prebuilt_jar(
name = name,
binary_jar = jarfile,
)
for aarfile in glob(['libs/*.aar']):
name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')]
lib_deps.append(':' + name)
android_prebuilt_aar(
name = name,
aar = aarfile,
)
android_library(
name = "all-libs",
exported_deps = lib_deps,
)
android_library(
name = "app-code",
srcs = glob([
"src/main/java/**/*.java",
]),
deps = [
":all-libs",
":build_config",
":res",
],
)
android_build_config(
name = "build_config",
package = "com.reactnavigationexample",
)
android_resource(
name = "res",
package = "com.reactnavigationexample",
res = "src/main/res",
)
android_binary(
name = "app",
keystore = "//android/keystores:debug",
manifest = "src/main/AndroidManifest.xml",
package_type = "debug",
deps = [
":app-code",
],
)

View File

@@ -0,0 +1,179 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":app" />
<option name="LAST_SUCCESSFUL_SYNC_AGP_VERSION" value="3.3.0" />
<option name="LAST_KNOWN_AGP_VERSION" value="3.3.0" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<afterSyncTasks>
<task>generateDebugSources</task>
</afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res;file://$MODULE_DIR$/build/generated/res/rs/debug;file://$MODULE_DIR$/build/generated/res/resValues/debug" />
<option name="TEST_RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes" />
<output-test url="file://$MODULE_DIR$/build/intermediates/javac/debugUnitTest/compileDebugUnitTestJavaWithJavac/classes" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/aidl_source_output_dir/debug/compileDebugAidl/out" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/renderscript_source_output_dir/debug/compileDebugRenderscript/out" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/react/debug" type="java-resource" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/aidl_source_output_dir/debugAndroidTest/compileDebugAndroidTestAidl/out" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/renderscript_source_output_dir/debugAndroidTest/compileDebugAndroidTestRenderscript/out" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/test/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="jdk" jdkName="Android API 28 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Gradle: com.android.support:collections:28.0.0@jar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.1.1@jar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.core:common:1.1.1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-annotations:28.0.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.infer.annotation:infer-annotation:0.11.2@jar" level="project" />
<orderEntry type="library" name="Gradle: javax.inject:javax.inject:1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2@jar" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp-urlconnection:3.12.1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.12.1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.15.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.device.yearclass:yearclass:2.1.0@jar" level="project" />
<orderEntry type="library" name="Gradle: commons-codec:commons-codec:1.10@jar" level="project" />
<orderEntry type="library" name="Gradle: commons-io:commons-io:1.4@jar" level="project" />
<orderEntry type="library" name="Gradle: com.github.bumptech.glide:disklrucache:4.9.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.github.bumptech.glide:annotations:4.9.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.parse.bolts:bolts-tasks:1.4.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.react:react-native:0.59.10@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:appcompat-v7:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.github.bumptech.glide:glide:4.9.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-location:16.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-base:16.0.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-places-placereport:16.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-tasks:16.0.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-basement:16.0.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-v4:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-fragment:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:customtabs:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-core-ui:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-core-utils:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:loader:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-media-compat:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:viewpager:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:coordinatorlayout:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:drawerlayout:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:slidingpanelayout:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:customview:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:swiperefreshlayout:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:asynclayoutinflater:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-compat:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:versionedparcelable:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:cursoradapter:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime:1.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:documentfile:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:localbroadcastmanager:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:print:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel:1.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.github.bumptech.glide:gifdecoder:4.9.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:interpolator:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata:1.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core:1.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.core:runtime:1.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:fresco:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-okhttp3:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:drawee:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-base:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.soloader:soloader:0.6.0@aar" level="project" />
<orderEntry type="library" name="Gradle: io.nlopez.smartlocation:library:3.2.11@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:fbcore:1.10.0@aar" level="project" />
<orderEntry type="module" module-name="react-native-reanimated" />
<orderEntry type="module" module-name="react-native-gesture-handler" />
<orderEntry type="module" module-name="expo-permissions" />
<orderEntry type="module" module-name="unimodules-core" />
<orderEntry type="module" module-name="unimodules-react-native-adapter" />
<orderEntry type="module" module-name="expo-app-loader-provider" />
<orderEntry type="module" module-name="expo-constants" />
<orderEntry type="module" module-name="expo-file-system" />
<orderEntry type="module" module-name="expo-font" />
<orderEntry type="module" module-name="expo-keep-awake" />
<orderEntry type="module" module-name="expo-linear-gradient" />
<orderEntry type="module" module-name="expo-location" />
<orderEntry type="module" module-name="expo-sqlite" />
<orderEntry type="module" module-name="expo-web-browser" />
<orderEntry type="module" module-name="unimodules-barcode-scanner-interface" />
<orderEntry type="module" module-name="unimodules-camera-interface" />
<orderEntry type="module" module-name="unimodules-constants-interface" />
<orderEntry type="module" module-name="unimodules-face-detector-interface" />
<orderEntry type="module" module-name="unimodules-file-system-interface" />
<orderEntry type="module" module-name="unimodules-font-interface" />
<orderEntry type="module" module-name="unimodules-image-loader-interface" />
<orderEntry type="module" module-name="unimodules-permissions-interface" />
<orderEntry type="module" module-name="unimodules-sensors-interface" />
<orderEntry type="module" module-name="unimodules-task-manager-interface" />
</component>
</module>

View File

@@ -0,0 +1,155 @@
apply plugin: "com.android.application"
import com.android.build.OutputFile
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation
* entryFile: "index.android.js",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
project.ext.react = [
entryFile: "index.js"
]
apply from: '../../node_modules/react-native-unimodules/gradle.groovy'
apply from: "../../node_modules/react-native/react.gradle"
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
applicationId "com.reactnavigationexample"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
}
buildTypes {
release {
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
def versionCodes = ["armeabi-v7a":1, "x86":2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
}
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation project(':react-native-reanimated')
implementation project(':react-native-gesture-handler')
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation "com.facebook.react:react-native:+" // From node_modules
addUnimodulesDependencies()
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
into 'libs'
}

View File

@@ -0,0 +1,19 @@
"""Helper definitions to glob .aar and .jar targets"""
def create_aar_targets(aarfiles):
for aarfile in aarfiles:
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
lib_deps.append(":" + name)
android_prebuilt_aar(
name = name,
aar = aarfile,
)
def create_jar_targets(jarfiles):
for jarfile in jarfiles:
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
lib_deps.append(":" + name)
prebuilt_jar(
name = name,
binary_jar = jarfile,
)

View File

@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@@ -0,0 +1,48 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.reactnavigationexample">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<!-- OPTIONAL PERMISSIONS, REMOVE WHATEVER YOU DO NOT NEED -->
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<!-- These require runtime permissions on M -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<!-- END OPTIONAL PERMISSIONS -->
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>

View File

@@ -0,0 +1,28 @@
package com.reactnavigationexample;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "ReactNavigationExample";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected ReactRootView createRootView() {
return new RNGestureHandlerEnabledRootView(MainActivity.this);
}
};
}
}

View File

@@ -0,0 +1,64 @@
package com.reactnavigationexample;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.swmansion.reanimated.ReanimatedPackage;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import com.reactnavigationexample.generated.BasePackageList;
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
import org.unimodules.adapters.react.ReactAdapterPackage;
import org.unimodules.adapters.react.ModuleRegistryAdapter;
import org.unimodules.adapters.react.ReactModuleRegistryProvider;
import org.unimodules.core.interfaces.Package;
import org.unimodules.core.interfaces.SingletonModule;
import expo.modules.constants.ConstantsPackage;
import expo.modules.permissions.PermissionsPackage;
import expo.modules.filesystem.FileSystemPackage;
import java.util.Arrays;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider(
new BasePackageList().getPackageList(),
Arrays.<SingletonModule>asList()
);
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new ReanimatedPackage(),
new RNGestureHandlerPackage(),
new ModuleRegistryAdapter(mModuleRegistryProvider)
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">React Navigation Example</string>
</resources>

View File

@@ -0,0 +1,8 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
</style>
</resources>

View File

@@ -0,0 +1,39 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
buildToolsVersion = "28.0.3"
minSdkVersion = 21
compileSdkVersion = 28
targetSdkVersion = 27
supportLibVersion = "28.0.0"
}
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenLocal()
google()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
}
}
task wrapper(type: Wrapper) {
gradleVersion = '4.7'
distributionUrl = distributionUrl.replace("bin", "all")
}

View File

@@ -0,0 +1,18 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More