Compare commits

..

256 Commits
2.0.2 ... 3.0.7

Author SHA1 Message Date
Brent Vatne
d0b4ba40e9 Release 3.0.7 2018-12-08 18:31:35 -08:00
Brent Vatne
1be5c7ae35 Release 3.0.6 2018-12-06 18:26:54 -08:00
Brent Vatne
bfa68ded1e Release 3.0.5 2018-12-03 16:26:04 -08:00
Brent Vatne
2d3f159c2a Release 3.0.4 2018-11-30 13:14:53 -08:00
Brent Vatne
e0fb642490 Release 3.0.3 2018-11-30 12:24:46 -08:00
Brent Vatne
d341162731 Release 3.0.2 2018-11-27 13:59:37 -08:00
Paul-Henri Koeck
4eec187d66 Fix for NavigationPlayground red box (duplicate native module) (#5294)
Solves https://github.com/react-navigation/react-navigation/issues/5293
2018-11-27 11:09:41 -08:00
Brent Vatne
03be0324a3 Release 3.0.1 2018-11-26 14:18:03 -08:00
Brent Vatne
55f326960a Release 3.0.0 2018-11-26 11:50:25 -08:00
Yu Watanabe
0641bdd656 ./src/createNavigationContainer -> @react-navigation/native/src/createAppContainer on NavigationTestUtils.js (#5266)
* ./src/createNavigationContainer deprecated , so change.

* changelog
2018-11-27 01:07:51 +07:00
Ashoat Tevosyan
087831ab71 [flow] Update libdef for 3.0 (#5234)
1. `navigationOptions` in `RouteConfig`s is now `defaultNavigationOptions`
2. `create*Navigator` no longer return a container, they return a navigator
3. Introducing `createAppContainer`

Closes #4722 by including https://github.com/flow-typed/flow-typed/pull/2522
2018-11-15 17:09:37 +01:00
Brent Vatne
23f7895792 Release 3.0.0-rc.5 2018-11-07 16:53:10 -08:00
Anne Klapwijk
cfa1eb2eff [flow] Restrict ParamName to string (#5200) 2018-11-06 09:09:18 -05:00
Brent Vatne
e34c724106 Release 3.0.0-rc.4 2018-11-05 13:02:40 -08:00
Brent Vatne
93a754a3d5 Release 3.0.0-rc.3 2018-11-03 20:07:56 -07:00
Brent Vatne
dcadbfcfa0 Release 3.0.0-rc.2 2018-11-03 14:31:15 -07:00
Brent Vatne
a393818875 Release 3.0.0-rc.1 with explicit dependency on react-native-screens 2018-11-01 16:27:11 -07:00
Brent Vatne
f16ca93521 Remove codecov badge (lives on other subrepos now) 2018-11-01 08:25:48 -07:00
Brent Vatne
b3ab5ce23c Release 3.0.0-rc.0 2018-10-31 19:27:42 -07:00
Brent Vatne
b4da1a63ce Update react-navigation-drawer and release 3.0.0-alpha.21 2018-10-31 18:21:46 -07:00
Brent Vatne
7bf6404733 Bump drawer/tabs versions, release 3.0.0-alpha.20 2018-10-31 16:49:23 -07:00
Brent Vatne
334be6021a Bump react-navigation-stack, core, and release new version 2018-10-31 15:55:26 -07:00
Brent Vatne
4254d46694 Clean up SafeAreaView in example 2018-10-31 15:54:29 -07:00
Brent Vatne
a00784ae6e Release 3.0.0-alpha.27 2018-10-30 13:25:33 -07:00
Eric Vicenti
22f3422293 chore: release 3.0.0-alpha.15 2018-10-29 20:19:26 -07:00
Eric Vicenti
4502061a10 bump versions, export context 2018-10-29 20:15:41 -07:00
Brent Vatne
5a8ebc806c Release 3.0.0-alpha.14 2018-10-26 10:57:24 -07:00
Brent Vatne
7942eecb4e Bump react-navigation-stack version 2018-10-26 10:57:09 -07:00
Brent Vatne
6b3d6c1399 Bump to 3.0.0-alpha.13 2018-10-26 07:28:53 -07:00
Ashoat Tevosyan
adcb2e5b4a [flow] Fix type of getCurrentNavigation (#5173)
As of 2.18 we seem to be okay returning `null` here.
2018-10-25 14:14:57 -07:00
Brent Vatne
352e703ea9 Release 3.0.0-alpha.12 2018-10-23 17:08:07 -07:00
Brent Vatne
308d38015d Bump core for default params 2018-10-23 17:01:43 -07:00
Brent Vatne
f34095e71a Re-export tab bar views from react-navigation 2018-10-23 14:54:00 -07:00
Brent Vatne
000f8afe8f Release 3.0.0-alpha.11 2018-10-23 11:53:15 -07:00
Brent Vatne
77c30d543b Bump @react-navigation/native and re-export scroll related modules 2018-10-23 11:52:21 -07:00
Brent Vatne
93ce7f4e9e Release 3.0.0-alpha.10 2018-10-22 21:01:39 -07:00
Brent Vatne
e456d29104 Release 3.0.0-alpha.9 2018-10-22 19:50:33 -07:00
Brent Vatne
eb54371306 Bump dependency versions 2018-10-22 19:50:33 -07:00
Brent Vatne
a2e053d31c Bump react-navigation-stack 2018-10-22 19:50:33 -07:00
Yao Hui Chua
af18e1c672 Fix typo in MainScreen (#5162) 2018-10-22 11:55:11 -07:00
Gregory Benner
0c4845f2ff [flow] typecheck getParam to actually check that key and fallback are valid (#5074)
* Update flow type definition for getParam to actually check key and
fallback are valid

* add changelog entry for `getParam` flow type changes

* Return "any" in `getParam` if params are not defined in the navigation state

* use void in place of any
2018-10-19 09:57:34 -07:00
Brent Vatne
0c9014e52f Bump versions 2018-10-17 15:35:57 -07:00
Brent Vatne
156f55fe34 chore: bump stack version 2018-10-17 13:20:34 -07:00
Brent Vatne
46be835351 chore: release 3.0.0-alpha.6 2018-10-16 21:29:34 -07:00
Brent Vatne
89b01f2211 Bump dependency versions 2018-10-16 21:29:01 -07:00
Brent Vatne
ffae3efe42 Use RNGH buttons 2018-10-16 13:21:56 -07:00
Brent Vatne
ccfbaedb9f Remove ReduxExample and SafeAreaExample 2018-10-15 15:27:42 -07:00
Brent Vatne
84de94d961 Fix tests 2018-10-15 14:19:32 -07:00
Brent Vatne
550d4fb676 Update for new alpha versions of navigators, release new alpha 2018-10-15 14:15:01 -07:00
Satyajit Sahoo
5acc07b9b0 chore: use release-it for automated release (#5115) 2018-10-15 11:49:48 -07:00
Eric Vicenti
d34f883180 cleanup dependencies, SafeAreaView moved to native 2018-10-11 23:02:38 -07:00
Eric Vicenti
89af1a6917 rename to createAppContainer, other deprecations
- rename createNavigationContainer to createAppContainer
- deprecate Transitioner import
- clean up old deprecated exports without create prefix
- rearrange
2018-10-11 22:40:21 -07:00
Eric Vicenti
402b201618 Fix playground 2018-10-11 20:58:39 -07:00
Eric Vicenti
df1728e13b Copy native support to @react-navigation/native 2018-10-11 20:47:20 -07:00
Eric Vicenti
b331dea1a1 Extract core codebase to @react-navigation/core 2018-10-11 14:48:58 -07:00
Tien Pham
3e827e4061 Fix unexpected route switching after the transition is complete (#5113) 2018-10-11 13:49:48 -07:00
Brent Vatne
4c168a7f73 Use dummy navigator instead of stack 2018-10-11 13:10:00 -07:00
Brent Vatne
200f596461 Add RNGH as a dependency of react-navigation 2018-10-11 12:53:49 -07:00
Brent Vatne
9e1982d8a8 Bump navigator dependencies to alpha versions 2018-10-11 12:48:47 -07:00
Brent Vatne
51d791d301 navigationOptions in navigator config is renamed to defaultNavigationOptions.
The new meaning of navigationOptions in navigator config is the navigation options for that navigator, not for screens inside of it.
2018-10-11 12:46:54 -07:00
Serge Lebedev
07afa55265 [flow] Mark key in StackActions.replace as optional (#5073) 2018-10-10 11:27:00 -07:00
Eric Vicenti
3ac5f412b7 Introduce getActiveChildNavigationOptions (#5080)
This is a utility to allow navigationOptions definition functions to access the navigation options of the active child route, which allows people to replicate the v1 behavior of deep nav configuration.
2018-10-09 15:37:57 -07:00
Ashoat Tevosyan
70a2c3b97c [flow] Update StackViewConfig to match recent changes (#5067) 2018-10-09 11:56:28 -07:00
Julian Paas
4bd6f17b46 Removes drawer actions from react-navigation-web (#4549)
* Removes drawer actions from react-navigation-web

* Lint fix
2018-10-03 15:44:28 -07:00
Nicolas Charpentier
9824e90b9f Add disableRouteNamePaths option to router configs (#4824)
* Add routeNameAsPathDisabled prop to router config

* Rename option to disableRouteNamePaths

* Update PathHandling-test.js
2018-10-03 10:11:34 -07:00
Badr Rahal
eae992467b Removed unused imports and const declarations (#5053) 2018-10-01 12:12:36 -07:00
Brent Vatne
6b4d92ca4d Release 2.17.0 2018-09-25 18:56:44 -07:00
Vojtech Novak
41d3c97cea Update CHANGELOG.md (#5023) 2018-09-24 09:36:22 -07:00
Vojtech Novak
ab3e053338 add dangerouslyGetParent() to flow typings (#5014)
* add dangerouslyGetParent() to flow typings

* Update CHANGELOG.md
2018-09-20 16:44:49 -07:00
Brent Vatne
b14262c2ef Release 2.16.0 2018-09-19 16:46:05 -07:00
Eric Vicenti
03d9133a7d Custom Tabs UI Example (#4996)
* Custom Tabs UI Example

* Clean up and add SafeAreaView
2018-09-19 21:15:34 +02:00
Brent Vatne
d0835351bd Release 2.15.0 2018-09-19 11:27:30 -07:00
Brent Vatne
f892526e7b Update react-native-safe-area-view to 0.11.0 2018-09-19 11:23:51 -07:00
Brent Vatne
1afdb799fc Release 2.14.2 2018-09-14 15:30:56 +02:00
Brent Vatne
83d36dcf7c Release 2.14.1 2018-09-14 13:03:47 +02:00
Brent Vatne
aa94038190 Update react-navigation-stack 2018-09-14 12:59:30 +02:00
Brent Vatne
0b698ae5d6 Update playground to expo sdk 30 and add commented out screens option in App.js 2018-09-14 11:48:34 +02:00
Brent Vatne
dd3ce66120 Release 2.14.0 2018-09-12 16:47:03 +02:00
Brent Vatne
82754d41d9 Bump react-navigation-stack to 0.4.0 2018-09-12 16:40:13 +02:00
Thibault Malbranche
9d54ec68dd Bump Safe-Area-View to remove circular dependency. (#4980)
* Bump Safe-Area-View to remove circular dependency.

React Native 0.57 now adds a yellowbox warning due to this package, bumping to solve

* Update CHANGELOG.md

* Update CHANGELOG.md
2018-09-12 16:39:22 +02:00
Brent Vatne
460754fde1 Release 2.13.0 2018-09-06 15:07:39 -07:00
Brent Vatne
ffd1865485 Bump react-navigation-stack and react-navigation-tabs 2018-09-06 15:06:34 -07:00
Tom Spencer
50320bf0d9 Add missing types (#4836)
* Add missing types

* Added missing types to DrawerActions
2018-09-04 10:44:02 -07:00
Thibaut
74a04c3ce5 Add missing StackNavigatorConfig option (#4917) 2018-09-04 10:42:46 -07:00
Rodolfo Silva
54d0d5180d Update CHANGELOG.md (#4923)
Change dependency version
2018-09-04 10:42:27 -07:00
Cameron Knight
14eb5a1e75 Pin create-react-context at v0.2.2 (#4950)
Because `create-react-context` updated its license from MIT (read: Open
Source) to a more-restrictive, non-Open Source license without
appropriately bumping its major version in violation of semver, the
`create-react-context` version within `react-navigation` necessarily
needs to have its version pinned to a version of `create-react-context`
before the license change.

v0.2.2 is the most recent version of `create-react-context` that still
retains an Open Source license.

Note: Another solution to this would be to require `react` `16.3.x` or
higher, which includes the official `React.createContext` API without
need to rely on a third-party library.

Closes #4934
2018-09-04 10:41:42 -07:00
Rodrigo Bermúdez Schettino
222c77a360 CHANGELOG: Remove duplicate entry (#4909)
Issue link was missing in removed entry. Left the one with better format.
2018-08-27 12:00:04 -07:00
Brent Vatne
39316fc339 Fix typo in issue template 2018-08-25 15:50:55 -07:00
Brent Vatne
27eb73cc14 Release 2.12.1 2018-08-23 16:29:12 -07:00
Brent Vatne
f01b4896e6 Release 2.12.0 2018-08-22 17:17:54 -07:00
Brent Vatne
556c31626e Update react-navigation-stack, export HeaderStyleInterpolator, and add example of using it to playground 2018-08-22 17:16:39 -07:00
Brent Vatne
b6bca3ed2e Fix drawer config in Drawer + Tabs example 2018-08-22 16:04:11 -07:00
Nicolas Charpentier
0c56b21b46 Remove duplicate private key (#4819)
A package can't be more private than that, so let's remove duplicates.
2018-08-21 16:13:22 -07:00
Kevin Coleman
912c7ca076 Bump react-native-safe-area-view dep to 0.9.0 (#4810)
https://github.com/react-navigation/react-navigation/issues/3992
2018-08-07 10:23:51 -07:00
Brent Vatne
73c76f1e4b Extract stack to react-navigation-stack (#4809)
* Extract stack to react-navigation-stack

* Update CHANGELOG

* Fix tests
2018-08-03 18:47:46 -07:00
Brent Vatne
d746a587b0 Release 2.11.2 2018-08-03 15:45:27 -07:00
Brent Vatne
dee03c839a Undo pathUtils rename 2018-08-03 15:33:38 -07:00
Brent Vatne
2104bf1a04 Release 2.11.1 2018-08-03 14:48:49 -07:00
Brent Vatne
4e2a409dca Fix a couple of exports and export a few more modules 2018-08-03 14:47:43 -07:00
Brent Vatne
51bfe8dd19 Release 2.11.0 2018-08-03 14:17:24 -07:00
Brent Vatne
04a4512c1b Export modules useful for moving stack navigator outside of core 2018-08-03 14:16:57 -07:00
Brent Vatne
4a5da86ce0 Release 2.10.0 2018-08-02 13:03:34 -07:00
Brent Vatne
a118122aed Update react-navigation-[tabs&drawer] 2018-08-02 13:02:01 -07:00
Justin Parker
a94f89ffe1 Fixes bug where null doesn't work in routerOptions paths object for deeplinking (#4791)
* Add test for handling null path set on router

* Allow null paths on the router config

Previously if you specified `null` in the router `paths`, the logic would actually fall back to the `path` specified on the route, which is especially bad if the latter is undefined, because then the path would be set as the default of the routeName.
2018-08-01 01:12:49 -07:00
Brent Vatne
9d77fd6d54 Add NavigationTestUtils and release 2.9.3 2018-07-26 15:49:34 -07:00
Brent Vatne
13cf4497ee Release 2.9.2 2018-07-25 11:52:39 -07:00
Tim Wang
9175118383 Export StackViewTransitionConfigs (#4761)
* export StackViewTransitionConfigs

* Add changelogs
2018-07-25 11:50:07 -07:00
Simone D'Avico
6fc21250ec Fix typo in header transition preset check (uitkit -> uikit) (#4757) 2018-07-25 11:49:24 -07:00
Yevhen
714d5eab6b Error when building with haul: ref to pathToRegexp.compile (#4658)
Error in source code that cause problem on react native using haul https://puu.sh/AT1uZ/158623d5a4.png
2018-07-25 20:28:05 +02:00
Brent Vatne
67233dc9ef Release 2.9.1 2018-07-24 10:49:56 -07:00
Brent Vatne
b0443c1861 Move more logs behind debug flag in stack playground 2018-07-24 10:47:59 -07:00
Brent Vatne
c0b637df52 Fix title offfset calculation 2018-07-24 10:47:02 -07:00
Brent Vatne
9a82706fba Fix snapshots 2018-07-20 14:44:26 -07:00
Brent Vatne
d973a26edb Release 2.9.0 2018-07-20 14:33:27 -07:00
Brent Vatne
852e7e1974 Respect custom background color in header wrapper 2018-07-20 14:30:38 -07:00
Brent Vatne
cd3707d64b Add headerLayoutPreset, add config for back button title visibility and make it have reasonable defaults, better back button ripple on Android (#4588) 2018-07-20 14:12:39 -07:00
Brent Vatne
3c36db455f Release 2.8.0 2018-07-19 15:48:09 -07:00
Brent Vatne
ec52c884c5 Update NavigationPlayground to Expo SDK 28 2018-07-19 15:46:24 -07:00
Brent Vatne
c4b3f25a0f Cleanup unused descriptors and handle the case where we might expect to have a descriptor but do not (#4723) 2018-07-19 13:16:38 -07:00
Eric Vicenti
93642e16e7 Fix createNavigator leak of old descriptors 2018-07-19 12:41:14 -07:00
Eric Vicenti
1a76556290 Fix leak in createNavigator
Previous descriptors had been retained because this binding caused `this.prevState` to remain referenced. This binds the component getter to null instead.
2018-07-19 12:21:22 -07:00
Reza Ghorbani
12b21f052e added header container styles to be customized (#4331) 2018-07-18 15:14:08 -07:00
Brent Vatne
c1f07dc167 Release 2.7.0 2018-07-17 15:50:18 -07:00
Brent Vatne
bc04b31d01 Add border for transparent header in example 2018-07-17 15:43:22 -07:00
Eric Vicenti
35307c70be Improve empty path and param handling (#4671)
* Overhaul Path handling

* Another test for deep link
2018-07-17 13:51:20 -07:00
Brent Vatne
7e3f4f3bec Fix tests 2018-07-17 13:49:12 -07:00
Brent Vatne
cbd0958e6f Remove unnecessary style array 2018-07-17 13:36:16 -07:00
Brent Vatne
cab4d71a5e Fix edge case where route was in nav state but never actually graduated to scene 2018-07-17 13:36:16 -07:00
Kenza Iraki
108ac0e2a9 Set borderBottom to transparent and of size 0 if headerTransparent is true (#4701) 2018-07-16 16:32:14 -07:00
Brent Vatne
fa4fdb9c57 Fix onTransitionStart config not being invoked with keyboard aware navigator, and use prop over config when available 2018-07-16 15:13:25 -07:00
Brent Vatne
ebdd2da79f Pull onTransitionStart from navigationConfig, fixes #4100 2018-07-16 14:55:34 -07:00
Matteo Codogno
1fe11c100e Fix #4608 - remove header left component only when a headerLeft optio… (#4679)
* Fix #4608 - remove header left component only when a headerLeft option is not specified

* Update CHANGELOG.md
2018-07-12 11:42:56 -07:00
Eric Vicenti
c4b84f1d66 Fix container referene to startup state data 2018-07-12 11:01:11 -07:00
Michael Lefkowitz
69f394be5b Feat/allow keyless replace (#4636)
* allow key to be undefined on StackNavigation.replace method

* added tests for replace action w/out key

* fix typo

* updated changelog

* updated teests for clarity

* added length check on routes to safely fallthrough to search
2018-07-10 23:41:42 -07:00
Eric Vicenti
316e4991ac Add enableURLHandling to navigation container 2018-07-10 14:47:57 -07:00
Ashoat Tevosyan
805064cb5e [flow] Make NavigationRoute types exact (#4667)
React Native 0.56 introduces Flow 0.75, which makes it impossible to refine `NavigationRoute` based on the presence of `index` or `routes` properties.

This PR turns `NavigationStateRoute` and `NavigationLeafRoute` into exact types, which addresses this issue.
2018-07-10 13:59:09 -07:00
Dariusz Łuksza
8f199980cb Fix changelog (#4651)
Fixes 2.6.1 header link
2018-07-06 20:21:00 -07:00
Brent Vatne
37ca6a92ca Release 2.6.2 2018-07-06 10:44:30 -07:00
Brent Vatne
980e0409dc Temporarily remove warnings on vertical padding in header 2018-07-06 10:42:32 -07:00
Brent Vatne
a00ba5918a Default to 0 elevation on transparent header 2018-07-05 15:17:08 -07:00
Brent Vatne
ad6b25cff9 Fix 2.6.1 changelog 2018-07-05 15:07:17 -07:00
Brent Vatne
a69b67d6d2 Release 2.6.1 2018-07-05 15:03:14 -07:00
Brent Vatne
dc436e4d01 Warn for more invalid header styles 2018-07-05 15:02:48 -07:00
Brent Vatne
fe95bdeee6 Fix regression for shadow in header on Android 2018-07-05 14:53:41 -07:00
Brent Vatne
525528e38f Release 2.6.0 2018-07-04 13:04:00 -07:00
liuqiang1357
9f5f3d994c Fix bug: params not be passed to navigator inside SwitchNavigator (#4306) 2018-07-04 12:28:01 -07:00
Eric Vicenti
e8c1833053 Fix stack router child router delegation priority (#4635)
Stack router had some aggressive logic for deferring to inactive child routers. The child router behavior should come after all of the appropriate stack actions, with the exception of the active child router.

This was causing issues such as https://github.com/react-navigation/react-navigation/issues/4623 , where inactive tab navigators would handle the back action, and cause the stack to attempt to  pop back to it.
2018-07-03 21:18:25 -07:00
Eric Vicenti
0921889f7a Avoid crash when calling isFocused on old route (#4634) 2018-07-03 12:03:54 -07:00
Sébastien Lorber
1951a3ac46 Add <NavigationEvents/> component (#4188)
* add NavigationEvents

* expose TabsWithNavigationEvents in lib entrypoints

* Add NavigationEvents example in playground

* Add NavigationEvents example in playground

* Add NavigationEvents tests

* Add NavigationEvents Flow declarations

* remove useless NavigationEvents constructor

* NavigationEvents => make tests more readable by avoiding beforeEach callback

* fix flow test error by adding <any, any> to React.Component
2018-06-29 07:34:11 -07:00
Eric Vicenti
4e384f8057 Routers: Deep Linking Overhaul (#4590)
* deep linking overhaul

* clean up PlatformHelpers

this had previously been required for old versions of react native and react-native-web
2018-06-29 07:27:12 -07:00
Eric Vicenti
3d06d19d6a clean up PlatformHelpers (#4586)
this had previously been required for old versions of react native and react-native-web
2018-06-28 10:02:52 -07:00
Brent Vatne
30ef5ef72b Release 2.5.5 2018-06-27 18:12:59 -07:00
Brent Vatne
c7fff52408 Delegate to child routers for more than just the top screen in the stack (#4587)
* Delegate to child routers for more than just the top screen in the stack. Fixes #4185

* Add CHANGELOG entry
2018-06-27 17:37:30 -07:00
Brent Vatne
bc01a4cd57 Throw error in development mode when title is not a string 2018-06-27 17:35:29 -07:00
Brent Vatne
cad3d70aed Update react-navigation-drawer to 0.4.3 2018-06-27 17:27:58 -07:00
Brent Vatne
bb5719f438 Throw an error when header is invalid 2018-06-27 17:22:00 -07:00
Brent Vatne
3dd3f5b804 Release 2.5.4 2018-06-27 15:24:34 -07:00
Julian Paas
3d8d5a0634 Adds getNavigation to web exports (#4551) 2018-06-27 13:34:45 -07:00
Brent Vatne
54448ed070 Prevent flicker in header when header is null on initial mount (when using default header sizes) (#4577)
* Prevent flicker in header in most common cases. Fixes https://github.com/react-navigation/react-navigation/issues/4264

* Update snapshots
2018-06-26 13:27:43 -07:00
Julian Paas
369ac2b568 Adds SwitchNavigator to react-navigation-web (#4550)
* Adds SwitchNavigator to react-navigation-web
2018-06-26 11:24:23 -07:00
Vojtech Novak
3dc592f679 Update ISSUE_TEMPLATE.md (#4575) 2018-06-25 15:41:56 -07:00
Brent Vatne
4f93200c91 Release 2.5.3 2018-06-25 14:37:26 -07:00
Brent Vatne
665736d754 Hoist navigation action creators for router above those for child router 2018-06-25 14:33:01 -07:00
Brent Vatne
5598c3e28f Update changelog 2018-06-23 11:03:38 -07:00
Brent Vatne
cde6e845cd Release 2.5.2 2018-06-23 11:02:14 -07:00
Brent Vatne
fb8c712ad8 Update react-navigation-drawer to 0.4.2 to fix toggle regression 2018-06-23 11:01:47 -07:00
Brent Vatne
350b7e0aed Release 2.5.1 2018-06-22 13:21:22 -07:00
Brent Vatne
de112565d3 Fix name of prop, should be lastTransitionProps instead of prevTransitionProps in StackViewLayout
- Fixes #4542
2018-06-22 13:18:26 -07:00
Brent Vatne
acdd515c13 Update example app.json 2018-06-22 12:50:50 -07:00
Rodrigo Bermúdez Schettino
452a6d2004 Improve changelog format (#4559)
Bug fixes should be listed in the "Fixed" section instead of "Changed" according to keepachangelog.
2018-06-22 10:41:58 -07:00
Brent Vatne
08c8031a71 Release 2.5.0 2018-06-22 10:32:11 -07:00
Eric Vicenti
608365266a @ericvicenti/universe (#4493)
* Isolate modules for uncontainerized navigators

* Clean up prop-types

* Fix warnings and web import friendlyness

* strip a flow

* Standalone provider/consumer navigation context

* export shallowEqual as module

* address various lint

# Conflicts:
#	src/navigators/createStackNavigator.js

* Get tests to pass
2018-06-22 10:20:27 -07:00
Rodrigo Bermúdez Schettino
247fba56e6 Fix typo in Pull Request Template (#4558)
Also link to the "Unreleased" section in CHANGELOG.
2018-06-22 10:19:41 -07:00
Brent Vatne
060f5dcecf Update PR template for changelog 2018-06-22 08:25:38 -07:00
Rodrigo Bermúdez Schettino
fdec05c87a Create CHANGELOG.md (#4544)
List all changes between versions to notify about notable changes in releases.
2018-06-22 08:22:15 -07:00
Brent Vatne
76da804574 Fixes #4491 2018-06-21 15:24:52 -07:00
Brent Vatne
dde091848a Release 2.4.1 2018-06-20 15:35:07 -07:00
Brent Vatne
824fa32416 Improve playground on Android 2018-06-20 14:20:00 -07:00
Brent Vatne
c518e7f36c Improve example 2018-06-20 13:51:34 -07:00
Brent Vatne
1cfe01dbdb Release 2.4.0 2018-06-20 13:38:05 -07:00
Brent Vatne
e62a9050fd Fix withDefaultValue 2018-06-20 13:37:33 -07:00
Christophe Hurpeau
310b909ba8 createNavigationContainer: rethrow the error instead of creating a new one (#4533)
Creating a new error makes the stack unreadable in sentry, because the stack is stringified when the error is cast to string to create a new one.
Is there another reason to do that ?
Alternative possible solution would be to add a method to be able to handle the error ourselves ?
2018-06-20 13:03:02 -07:00
Eric Vicenti
aebe8a5c23 Fix isTransitioning on nested navigate (#4520)
This bug wasnt apparent until we fixed the transitioner to fully respect isTransitioning. The router did not handle this properly until now.

Enhanced a test to verify this in the future
2018-06-20 12:17:14 -07:00
Vladislav Shabanov
e1df2c6c4a Make headers of non-standard heights work correctly in StackNavigator when header is null (Fixes #4208) (#4353)
* Measure header height dynamically

* Add comment

* StacksOverTopTabs playground example
2018-06-15 08:10:43 -07:00
Ashoat Tevosyan
fa86718a24 Revert "Export getNavigationActionCreators (#4258)" (#4495)
This reverts commit 1e7d8d55c3.
2018-06-14 11:34:38 -07:00
Ashoat Tevosyan
c8e5673183 [flow] Type getNavigation (#4505)
Unrelated change in here: typing `tabBarOnPress`'s `defaultHandler`. Wasn't sure if I should make a separate PR for a single unrelated line, let me know if you'd prefer that going forward.
2018-06-14 11:33:50 -07:00
Ashoat Tevosyan
b312a5e307 [ReduxExample] Update to use react-navigation-redux-helpers@2.0.0 (#4504) 2018-06-14 11:15:11 -07:00
Brent Vatne
ee6a6c53b1 Fix incorrect methods being called in createKeyboardAwareNavigator 2018-06-13 14:50:03 -07:00
Eric Vicenti
8edec88341 Export getNavigation as public API, test redux (#4489)
This unblocks the fixing of react-navigation-redux-helpers to unbreak redux support in 2.3

https://github.com/react-navigation/react-navigation-redux-helpers/pull/37

All redux users will need to change their code to reflect the changes made here to `ReduxExample/AppNavigator`
2018-06-12 11:59:09 -07:00
Brent Vatne
b8d6d4253d Release 2.3.1 2018-06-12 11:32:08 -07:00
Eric Vicenti
0adb1ba9f1 Export getNavigation as public API, test redux (#4488)
This unblocks the fixing of react-navigation-redux-helpers to unbreak redux support in 2.3

https://github.com/react-navigation/react-navigation-redux-helpers/pull/37

All redux users will need to change their code to reflect the changes made here to `ReduxExample/AppNavigator`
2018-06-12 11:30:23 -07:00
Brent Vatne
d3ef3d1271 Release 2.3.0 2018-06-11 15:54:41 -07:00
k-murakami0609
89a24bdc12 fix broken link to Common mistakes (#4468) 2018-06-10 10:41:18 -07:00
Brent Vatne
128a95b496 Release 2.3.0-beta.1 2018-06-09 14:18:09 -07:00
Brent Vatne
470eaf3b08 Make the actions available on the navigator navigation prop the same as its children (#4467)
* Make the actions available on the navigator navigation prop the same as its children

* Fix test and improve code clarity
2018-06-09 14:13:21 -07:00
Brent Vatne
c91e8206a5 Fix ScenesReducer test 2018-06-08 17:44:32 -07:00
Brent Vatne
da283915f8 Release 2.3.0-beta.0 2018-06-08 17:36:42 -07:00
Brent Vatne
3031e7bd80 Update header when screenProps change (Fixes #4271) 2018-06-08 17:35:17 -07:00
Brent Vatne
98a4f26f26 Bump react-navigation-drawer. Fixes #4416 2018-06-08 16:27:36 -07:00
Brent Vatne
b7f5435c93 Fix withNavigation in non-floating headers 2018-06-08 15:48:21 -07:00
Brent Vatne
21ef4fcb82 Return result of dispatch from action helpers 2018-06-08 14:33:01 -07:00
Eric Vicenti
5f64a2a9cb Refactor redo, child navigation to navigation prop (#4453) 2018-06-08 14:20:07 -07:00
Brent Vatne
992d0fb267 Release 2.2.5 2018-06-08 09:40:39 -07:00
ensecoz
6f41379ed1 extract correct property for onTransitionEnd (#4459) 2018-06-08 09:35:36 -07:00
Tom Klaver
267af01e72 throw readable error when wrong navigation prop is passed (#4455)
* add warning when passing wrong navigation prop: https://github.com/react-navigation/react-navigation/issues/3598

* add test for throwing when passing wrong navigation prop

* wrong nav prop error: update snapshots, throw => throw new, update docs reference url
2018-06-08 09:31:05 -07:00
Arthur Levoyer
b68c3a755d Add missing tests about DeepLinking (#4451)
* Fix #1950 (Deep link not working when nesting have more than two levels.)

* Fix test
2018-06-07 11:37:57 -07:00
Brent Vatne
7345634493 Release 2.2.4 2018-06-07 11:33:14 -07:00
Brent Vatne
6517169119 Re-compute options when screenProps change 2018-06-07 11:31:31 -07:00
Brent Vatne
ea5d14a720 Release 2.2.3 2018-06-06 21:13:00 -07:00
Brent Vatne
313d0726a8 Add some logs to NavigationPlayground around helper dispatch return value
This is a poor substitute for tests
2018-06-06 21:12:18 -07:00
Brent Vatne
b52f153747 Return the result of dispatch from helpers. Fixes #4445 2018-06-06 21:01:51 -07:00
Brent Vatne
44621005ff Release 2.2.2 2018-06-06 14:34:59 -07:00
Brent Vatne
bf58364c3d Bump react-navigation-drawer 2018-06-06 14:34:42 -07:00
Brent Vatne
b55053cde6 Release 2.2.1 2018-06-06 13:24:50 -07:00
Brent Vatne
9abb2644a9 Bump react-navigation-tabs and rebuild yarn.lock 2018-06-06 13:24:29 -07:00
Brent Vatne
6a946d6ab7 Revert "Refactor, move child navigation to navigation prop (#4425)"
This reverts commit ba62509ff4.
2018-06-06 13:19:08 -07:00
陈然
395abe5200 Change 'React' import writing, Prevent warnings in old version React (#4428) 2018-06-06 12:51:09 -07:00
Eric Vicenti
ba62509ff4 Refactor, move child navigation to navigation prop (#4425)
* Refactor, move child navigation to navigation prop

This fixes our descriptor caching issue, and unblocks explicit nested navigation options.

As a side effect, the following APIs are introduced:

- navigation.getChildNavigation(routeKey) , which is useful for explicitly getting children config info
- navigation.router, access to the static router

- router.childRouters[routeName] , an optional way to access the children routers directly. If childRouters are not provided in a router, we will fall back on getComponentForRouteName(routeName).router, which is the previous external API for this (although it may be slower because it will require the whole screen component).

* supporting tests and top level actions

* cleanup
2018-06-06 12:50:30 -07:00
Brent Vatne
45391db7d9 Move all drawer code into react-navigation-drawer (#4435)
* Move all drawer code into react-navigation-drawer

* Remove react-native-drawer-layout-polyfill from package.json
2018-06-06 12:11:28 -07:00
Brent Vatne
7f86362e86 Release 2.2.0 2018-06-05 12:21:09 -07:00
Brent Vatne
99605737e9 Bump react-navigation-tabs to latest 2018-06-05 12:20:47 -07:00
Serge Lebedev
842f5eb7b2 [flow] Fix NavigationScreenComponent's navigationOptions (#4317) 2018-06-05 12:19:12 -07:00
Richárd Biró
183ea82416 Pass getCustomActionCreators to Drawer route (#4433) 2018-06-05 11:12:03 -07:00
Rodrigo Bermúdez Schettino
108a6504a7 Use consistent naming in README (#4427)
Use the same casing (camel case) as in the README's title for the License section.
2018-06-05 10:55:20 -07:00
Eric Vicenti
f92d671746 Fix getCustomActionCreators in drawer navigator 2018-06-05 10:51:05 -07:00
Brent Vatne
e0c4a8f7d3 Release 2.1.0 2018-06-04 14:04:00 -07:00
Rodrigo Bermúdez Schettino
bc881c8aa1 Update deprecated StackNavigator usage (#4389)
Use createStackNavigator instead.
2018-06-04 13:59:55 -07:00
Ashoat Tevosyan
118c19dcce [flow] Update NavigationActions and introduce StackActions and DrawerActions (#4345) 2018-06-04 13:58:14 -07:00
James Bechet
01b43974e6 fix swipe to go back by providing the index (#4409) 2018-06-04 13:55:26 -07:00
Brent Vatne
2f90899620 Cache descriptors in createNavigator (#4424) 2018-06-04 13:54:09 -07:00
Eric Vicenti
6cc86f66e1 Fix stack router state change without isTransitioning change (#4368) 2018-06-04 12:47:40 -07:00
Eric Vicenti
4be99b6645 Transitioner: Fix instantaneous transitions (#4404)
* Transitioner: Fix instantaneous transitions

The transitioner will always perform an animation on state change. Instead we should check if the navigation state is requesting a transition with isTransitioning. If not, we follow a similar codepath to transitioning, without any animation.

* Update .eslintrc
2018-06-04 12:46:08 -07:00
Eric Vicenti
80016b7218 Fix race condition in DrawerView (#4392) 2018-06-03 13:35:55 -07:00
Eric Vicenti
f555a9ec9a Fix drawer router logic to close drawer (#4394) 2018-06-03 13:35:30 -07:00
Brent Vatne
05cbd85d5c Temporarily stop running flow on navigation playground due to internal react-native flow errors 2018-06-01 15:02:12 -07:00
Hunkyo Jung
51965eac38 Pass getCustomActionCreators to StackRouter (#4366) 2018-05-31 19:21:11 -07:00
Brent Vatne
a3956bf3ce Update tab snapshots 2018-05-31 13:53:00 -07:00
Brent Vatne
ce24c66b5a Disable react/no-deprecated to pass CI without breaking old versions of React 2018-05-31 13:47:28 -07:00
Brent Vatne
5467f0e22d Release 2.0.4 2018-05-25 23:05:54 -07:00
Ashoat Tevosyan
1e7d8d55c3 Export getNavigationActionCreators (#4258)
* Export getNavigationActionCreators

`react-navigation-redux-helpers` generally needs to duplicate whatever `createNavigationCreator` does. As of `react-navigation@2.0`, that includes using `getNavigationActionCreators` to construct the top-level `NavigationScreenProp`.

This PR isn't strictly needed, as it's possible to import from the file directly. But that's a brittle approach, and not particularly discoverable for anybody attempting to use a "non-stateful" navigator with their state management library of choice.

* Further refinements after writing the new helper

* Final refinement of NavigationScreenProp's relationship with _DefaultActionCreators

* Resolve merge conflicts
2018-05-25 22:59:11 -07:00
Brent Vatne
1d2ce862c2 Fix Stacks over Tabs example 2018-05-25 16:07:55 -07:00
Louis Lagrange
d778479e4a Fix drawer router initial state (#4219)
* Fix drawer router initial state

* Add test

* Be concise
2018-05-25 16:01:53 -07:00
Sébastien BARBIER
352dae50e1 Apply drawerlockmode from screen options to DrawerLayout component (#4202)
Fix #4201
2018-05-25 15:50:39 -07:00
Louis Lagrange
61385cae59 Export missing Drawer components (#4221) 2018-05-25 15:49:33 -07:00
Brent Vatne
aa3c13891e Bump some versions 2018-05-25 15:26:25 -07:00
Andrei Xavier de Oliveira Calazans
9696d7220d fix(create-nav-container): pass up error on catch (#4298) 2018-05-25 15:13:18 -07:00
Louis Lagrange
2b83b44816 Remove obsolete DrawerScreen (#4222) 2018-05-25 14:45:45 -07:00
Louis Lagrange
ec749023ed [Web] Fix header height margin (#4206) 2018-05-25 14:44:46 -07:00
Sébastien Lorber
adc9389eb3 Add CircleCI Badge to README (#4318)
Displaying a badge is helpful for users but also contributors (for example to know why my upstream-rebased PR is not passing tests)
2018-05-25 14:41:27 -07:00
Sébastien Lorber
54d143fee2 Fix Codecov link -> react-community vs react-navigation (#4319) 2018-05-25 14:39:51 -07:00
Louis Lagrange
d50e74d0c7 Fix NavigatorContainer test (fixes CI) (#4327) 2018-05-24 17:43:58 +02:00
Nicolas Charpentier
22926c5230 Optimize images (#4251) 2018-05-23 16:54:23 -07:00
Ashoat Tevosyan
f6c47a6c66 [examples] Update ReduxExample (#4267)
Using two new utils:
1. `createNavigationPropConstructor`, which is now preferred over `createReduxBoundAddListener`.
2. `initializeListeners`, which is necessary to trigger the event listeners with the initial state.
2018-05-23 10:20:14 -07:00
Ashoat Tevosyan
046a9f8930 [flow] Make StackRouter action creators optional and add DrawerRouter ones (#4257)
This will force all Flow users to do runtime assertions if they want to use these action creators. Open to any better solutions.
2018-05-23 10:18:35 -07:00
Ashoat Tevosyan
72f17538c2 [flow] Fix tabBarOnPress type for react-navigation@2.0 (#4256) 2018-05-23 10:18:11 -07:00
Ashoat Tevosyan
550001b053 [flow] Fixes for v2 libdef (#4230)
* [flow] Remove "any" type from NavigationComponent

"any" cripples the typechecker, so it's best to avoid. It was introduced in #3392, but I don't think the intention was to keep it there.

* [flow] Remove `any` type from `createNavigator` return

And use objects with spread sub-types instead of unions for `React$ComponentType` type param
2018-05-23 10:17:45 -07:00
166 changed files with 7716 additions and 29310 deletions

View File

@@ -7,18 +7,18 @@
"prettier/react"
],
"parser": "babel-eslint",
"plugins": [
"react",
"prettier"
],
"plugins": ["react", "prettier"],
"env": {
"jasmine": true
},
"rules": {
"prettier/prettier": ["error", {
"trailingComma": "es5",
"singleQuote": true
}],
"prettier/prettier": [
"error",
{
"trailingComma": "es5",
"singleQuote": true
}
],
"no-underscore-dangle": "off",
"no-use-before-define": "off",
@@ -27,17 +27,15 @@
"no-plusplus": "off",
"no-class-assign": "off",
"no-duplicate-imports": "off",
"import/extensions": "off",
"import/no-extraneous-dependencies": "off",
"import/no-unresolved": "off",
"react/jsx-filename-extension": [
"off", { "extensions": [".js", ".jsx"] }
],
"react/jsx-filename-extension": ["off", { "extensions": [".js", ".jsx"] }],
"react/sort-comp": "off",
"react/prefer-stateless-function": "off",
"react/no-deprecated": "off",
"react/forbid-prop-types": "warn",
"react/prop-types": "off",

View File

@@ -25,7 +25,7 @@ Bugs with react-navigation must be reproducible *without any external libraries
### 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 repoistory as that is outside of the scope of Rect Navigation.
- 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.

View File

@@ -1,17 +1,21 @@
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?
Prefer **small pull requests**. These are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise split it.
## Test plan
**Test plan (required)**
Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI.
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**
## Code formatting
Look around. Match the style of the rest of the codebase.
Look around. Match the style of the rest of the codebase. Run `yarn format` before committing.
## Changelog
Add an entry under the "Unreleased" heading in [CHANGELOG.md](https://github.com/react-navigation/react-navigation/blob/master/CHANGELOG.md#unreleased) which explains your change.

15
.release-it.json Normal file
View File

@@ -0,0 +1,15 @@
{
"increment": "conventional:angular",
"changelogCommand": "conventional-changelog -p angular | tail -n +3",
"safeBump": false,
"src": {
"commitMessage": "chore: release %s",
"tagName": "v%s"
},
"npm": {
"publish": true
},
"github": {
"release": true
}
}

86
CHANGELOG.md Normal file
View File

@@ -0,0 +1,86 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [3.0.7] - [2018-12-08](https://github.com/react-navigation/react-navigation/releases/tag/3.0.7)
## Changed
- Optimize stack gesture to avoid a setState and reduce unnecessary Animated node creation (https://github.com/react-navigation/react-navigation-stack/pull/70)
## [3.0.6] - [2018-12-06](https://github.com/react-navigation/react-navigation/releases/tag/3.0.6)
## Fixes
- Fix drawer accessibility label when drawer label is not a string
## [3.0.5] - [2018-12-03](https://github.com/react-navigation/react-navigation/releases/tag/3.0.5)
## Fixes
- Fix crash in rare case where onNavigationStateChange on container leads to setState and container has screenProps (https://github.com/react-navigation/react-navigation/issues/5301)
- Expose underlaying ScrollView methods to NavigationAwareScrollable (https://github.com/react-navigation/react-navigation-native/pull/8)
## [3.0.4] - [2018-11-30](https://github.com/react-navigation/react-navigation/releases/tag/3.0.4)
## Changed
- Lock dependencies to exact versions
## Fixes
- Fix crash when screenInterpolator is null (https://github.com/react-navigation/react-navigation-stack/issues/64)
- Fix renderPager override (https://github.com/react-navigation/react-navigation-tabs/pull/70)
## Added
- Accessibility labels on drawer items (https://github.com/react-navigation/react-navigation-drawer/pull/30)
## [3.0.3] - [2018-11-30](https://github.com/react-navigation/react-navigation/releases/tag/3.0.3)
## Fixes
- Fix bug where if you navigate immediately when the navigator is first mounted the stack could get in an invalid state.
- Transparent stack card factors in header height now, even though you probably won't want to use this.
- Fix bug where shadow was still rendered on transparent stack
- Fix gestureResponseDistance custom values being ignored for modal stack
## [3.0.2] - [2018-11-27](https://github.com/react-navigation/react-navigation/releases/tag/3.0.2)
## Fixes
- Fix `drawerLockMode` on drawer navigator
- Fix RTL support in drawer navigator
## [3.0.1] - [2018-11-26](https://github.com/react-navigation/react-navigation/releases/tag/3.0.1)
## Fixes
- fix NavigationTestUtils.js deprecated file import.
- Update `getParam` flow typings to check `key` and `fallback` arguments, as well as return the correct type automatically.
- Fix regression in backgroundColor on cardStyle for stack navigator.
## [3.0.0] - [2018-11-17](https://github.com/react-navigation/react-navigation/releases/tag/3.0.0)
- Changes between the latest 2.x release and 3.0.0 are listed on the blog at https://reactnavigation.org/blog/2018/11/17/react-navigation-3.0.html
# [Previous major versions]
- [2.x](https://github.com/react-navigation/react-navigation/blob/2.x/CHANGELOG.md)
[Unreleased]: https://github.com/react-navigation/react-navigation/compare/3.0.7...HEAD
[3.0.7]: https://github.com/react-navigation/react-navigation/compare/3.0.6...3.0.7
[3.0.6]: https://github.com/react-navigation/react-navigation/compare/3.0.5...3.0.6
[3.0.5]: https://github.com/react-navigation/react-navigation/compare/3.0.4...3.0.5
[3.0.4]: https://github.com/react-navigation/react-navigation/compare/3.0.3...3.0.4
[3.0.3]: https://github.com/react-navigation/react-navigation/compare/3.0.2...3.0.3
[3.0.2]: https://github.com/react-navigation/react-navigation/compare/3.0.1...3.0.2
[3.0.1]: https://github.com/react-navigation/react-navigation/compare/3.0.0...3.0.1
[3.0.0]: https://github.com/react-navigation/react-navigation/compare/2.x...3.0.0

7
NavigationTestUtils.js Normal file
View File

@@ -0,0 +1,7 @@
import { _TESTING_ONLY_reset_container_count } from '@react-navigation/native/src/createAppContainer';
export default {
resetInternalState: () => {
_TESTING_ONLY_reset_container_count();
},
};

View File

@@ -1,6 +1,6 @@
# React Navigation
[![npm version](https://badge.fury.io/js/react-navigation.svg)](https://badge.fury.io/js/react-navigation) [![codecov](https://codecov.io/gh/react-community/react-navigation/branch/master/graph/badge.svg)](https://codecov.io/gh/react-community/react-navigation) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://reactnavigation.org/docs/contributing.html)
[![npm version](https://badge.fury.io/js/react-navigation.svg)](https://badge.fury.io/js/react-navigation) [![CircleCI badge](https://circleci.com/gh/react-navigation/react-navigation/tree/master.svg?style=shield)](https://circleci.com/gh/react-navigation/react-navigation/tree/master) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://reactnavigation.org/docs/contributing.html)
React Navigation is born from the React Native community's need for an extensible yet easy-to-use navigation solution based on Javascript.
@@ -55,4 +55,4 @@ This library has adopted a Code of Conduct that we expect project participants t
## License
React-navigation is licensed under the [BSD 2-clause "Simplified" License](https://github.com/react-community/react-navigation/blob/master/LICENSE).
React Navigation is licensed under the [BSD 2-clause "Simplified" License](https://github.com/react-community/react-navigation/blob/master/LICENSE).

View File

@@ -55,8 +55,6 @@ module.system=haste
emoji=true
experimental.strict_type_args=true
munge_underscores=true
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
@@ -77,7 +75,5 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
unsafe.enable_getters_and_setters=true
[version]
^0.61.0
^0.67.0

View File

@@ -1,2 +1,9 @@
import { Platform } from 'react-native';
import { useScreens } from 'react-native-screens';
if (Platform.OS === 'android') {
// useScreens();
}
import App from './js/App';
export default App;

View File

@@ -11,16 +11,16 @@
"splash": {
"image": "./assets/icons/splash.png"
},
"sdkVersion": "26.0.0",
"entryPoint": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
"packagerOpts": {
"assetExts": [
"ttf",
"mp4"
]
},
"sdkVersion": "30.0.0",
"assetBundlePatterns": [
"**/*"
],
"ios": {
"bundleIdentifier": "com.reactnavigation.example",
"supportsTablet": true
},
"android": {
"package": "com.reactnavigation.example"
}
}
}
}

View File

@@ -11,12 +11,20 @@ import {
Platform,
ScrollView,
StyleSheet,
TouchableOpacity,
Text,
StatusBar,
View,
} from 'react-native';
import { SafeAreaView, createStackNavigator } from 'react-navigation';
import {
RectButton,
NativeViewGestureHandler,
} from 'react-native-gesture-handler';
import {
createAppContainer,
SafeAreaView,
createStackNavigator,
} from 'react-navigation';
import { Assets as StackAssets } from 'react-navigation-stack';
import CustomTabs from './CustomTabs';
import CustomTransitioner from './CustomTransitioner';
@@ -26,6 +34,7 @@ import TabsInDrawer from './TabsInDrawer';
import ModalStack from './ModalStack';
import StacksInTabs from './StacksInTabs';
import StacksOverTabs from './StacksOverTabs';
import StacksOverTopTabs from './StacksOverTopTabs';
import StacksWithKeys from './StacksWithKeys';
import InactiveStack from './InactiveStack';
import StackWithCustomHeaderBackImage from './StackWithCustomHeaderBackImage';
@@ -33,10 +42,14 @@ import SimpleStack from './SimpleStack';
import StackWithHeaderPreset from './StackWithHeaderPreset';
import StackWithTranslucentHeader from './StackWithTranslucentHeader';
import SimpleTabs from './SimpleTabs';
import CustomTabUI from './CustomTabUI';
import SwitchWithStacks from './SwitchWithStacks';
import TabsWithNavigationFocus from './TabsWithNavigationFocus';
import TabsWithNavigationEvents from './TabsWithNavigationEvents';
import KeyboardHandlingExample from './KeyboardHandlingExample';
process.env.REACT_NAV_LOGGING = true;
const ExampleInfo = {
SimpleStack: {
name: 'Stack Example',
@@ -105,6 +118,10 @@ const ExampleInfo = {
name: 'Stacks over Tabs',
description: 'Nested stack navigation that pushes on top of tabs',
},
StacksOverTopTabs: {
name: 'Stacks with non-standard header height',
description: 'Tab navigator in stack with custom header heights',
},
StacksWithKeys: {
name: 'Link in Stack with keys',
description: 'Use keys to link between screens',
@@ -121,11 +138,20 @@ const ExampleInfo = {
name: 'withNavigationFocus',
description: 'Receive the focus prop to know when a screen is focused',
},
TabsWithNavigationEvents: {
name: 'NavigationEvents',
description:
'Declarative NavigationEvents component to subscribe to navigation events',
},
KeyboardHandlingExample: {
name: 'Keyboard Handling Example',
description:
'Demo automatic handling of keyboard showing/hiding inside StackNavigator',
},
CustomTabUI: {
name: 'Custom Tabs UI',
description: 'Render additional views around a Tab navigator',
},
};
const ExampleRoutes = {
@@ -137,7 +163,12 @@ const ExampleRoutes = {
// screen: MultipleDrawer,
// },
StackWithCustomHeaderBackImage: StackWithCustomHeaderBackImage,
StackWithHeaderPreset: StackWithHeaderPreset,
...Platform.select({
ios: {
StackWithHeaderPreset: StackWithHeaderPreset,
},
android: {},
}),
StackWithTranslucentHeader: StackWithTranslucentHeader,
TabsInDrawer: TabsInDrawer,
CustomTabs: CustomTabs,
@@ -145,7 +176,9 @@ const ExampleRoutes = {
ModalStack: ModalStack,
StacksWithKeys: StacksWithKeys,
StacksInTabs: StacksInTabs,
CustomTabUI: CustomTabUI,
StacksOverTabs: StacksOverTabs,
StacksOverTopTabs: StacksOverTopTabs,
LinkStack: {
screen: SimpleStack,
path: 'people/Jordan',
@@ -155,6 +188,7 @@ const ExampleRoutes = {
path: 'settings',
},
TabsWithNavigationFocus,
TabsWithNavigationEvents,
KeyboardHandlingExample,
// This is commented out because it's rarely useful
// InactiveStack,
@@ -169,12 +203,7 @@ class MainScreen extends React.Component<any, State> {
};
componentDidMount() {
Asset.fromModule(
require('react-navigation/src/views/assets/back-icon-mask.png')
).downloadAsync();
Asset.fromModule(
require('react-navigation/src/views/assets/back-icon.png')
).downloadAsync();
Asset.loadAsync(StackAssets);
}
render() {
@@ -216,69 +245,72 @@ class MainScreen extends React.Component<any, State> {
return (
<View style={{ flex: 1 }}>
<Animated.ScrollView
style={{ flex: 1 }}
scrollEventThrottle={1}
onScroll={Animated.event(
[
{
nativeEvent: { contentOffset: { y: this.state.scrollY } },
},
],
{ useNativeDriver: true }
)}
>
<Animated.View
style={[
styles.backgroundUnderlay,
{
transform: [
{ scale: backgroundScale },
{ translateY: backgroundTranslateY },
],
},
]}
/>
<Animated.View
style={{ opacity, transform: [{ scale }, { translateY }] }}
<NativeViewGestureHandler>
<Animated.ScrollView
style={{ flex: 1, backgroundColor: '#eee' }}
scrollEventThrottle={1}
onScroll={Animated.event(
[
{
nativeEvent: { contentOffset: { y: this.state.scrollY } },
},
],
{ useNativeDriver: true }
)}
>
<SafeAreaView
style={styles.bannerContainer}
forceInset={{ top: 'always', bottom: 'never' }}
<Animated.View
style={[
styles.backgroundUnderlay,
{
transform: [
{ scale: backgroundScale },
{ translateY: backgroundTranslateY },
],
},
]}
/>
<Animated.View
style={{ opacity, transform: [{ scale }, { translateY }] }}
>
<View style={styles.banner}>
<Image
source={require('./assets/NavLogo.png')}
style={styles.bannerImage}
/>
<Text style={styles.bannerTitle}>
React Navigation Examples
</Text>
</View>
</SafeAreaView>
</Animated.View>
<SafeAreaView
style={styles.bannerContainer}
forceInset={{ top: 'always', bottom: 'never' }}
>
<View style={styles.banner}>
<Image
source={require('./assets/NavLogo.png')}
style={styles.bannerImage}
/>
<Text style={styles.bannerTitle}>
React Navigation Examples
</Text>
</View>
</SafeAreaView>
</Animated.View>
<SafeAreaView forceInset={{ bottom: 'always', horizontal: 'never' }}>
<View style={{ backgroundColor: '#fff' }}>
{Object.keys(ExampleRoutes).map((routeName: string) => (
<TouchableOpacity
key={routeName}
onPress={() => {
let route = ExampleRoutes[routeName];
if (route.screen || route.path || route.params) {
const { path, params, screen } = route;
const { router } = screen;
const action =
path && router.getActionForPathAndParams(path, params);
navigation.navigate(routeName, {}, action);
} else {
navigation.navigate(routeName);
}
}}
>
<SafeAreaView
style={styles.itemContainer}
forceInset={{ veritcal: 'never', bottom: 'never' }}
<SafeAreaView
forceInset={{ top: 'never', bottom: 'always' }}
style={{ backgroundColor: '#eee' }}
>
<View style={{ backgroundColor: '#fff' }}>
{Object.keys(ExampleRoutes).map((routeName: string) => (
<RectButton
key={routeName}
underlayColor="#ccc"
activeOpacity={0.3}
onPress={() => {
let route = ExampleRoutes[routeName];
if (route.screen || route.path || route.params) {
const { path, params, screen } = route;
const { router } = screen;
const action =
path &&
router.getActionForPathAndParams(path, params);
navigation.navigate(routeName, {}, action);
} else {
navigation.navigate(routeName);
}
}}
>
<View style={styles.item}>
<Text style={styles.title}>
@@ -288,12 +320,12 @@ class MainScreen extends React.Component<any, State> {
{ExampleInfo[routeName].description}
</Text>
</View>
</SafeAreaView>
</TouchableOpacity>
))}
</View>
</SafeAreaView>
</Animated.ScrollView>
</RectButton>
))}
</View>
</SafeAreaView>
</Animated.ScrollView>
</NativeViewGestureHandler>
<StatusBar barStyle="light-content" />
<Animated.View
style={[styles.statusBarUnderlay, { opacity: underlayOpacity }]}
@@ -303,34 +335,37 @@ class MainScreen extends React.Component<any, State> {
}
}
const AppNavigator = createStackNavigator(
{
...ExampleRoutes,
Index: {
screen: MainScreen,
const AppNavigator = createAppContainer(
createStackNavigator(
{
...ExampleRoutes,
Index: {
screen: MainScreen,
},
},
},
{
initialRouteName: 'Index',
headerMode: 'none',
{
initialRouteName: 'Index',
headerMode: 'none',
/*
* Use modal on iOS because the card mode comes from the right,
* which conflicts with the drawer example gesture
*/
mode: Platform.OS === 'ios' ? 'modal' : 'card',
}
/*
* Use modal on iOS because the card mode comes from the right,
* which conflicts with the drawer example gesture
*/
mode: Platform.OS === 'ios' ? 'modal' : 'card',
}
)
);
export default AppNavigator;
export default class App extends React.Component {
render() {
return <AppNavigator /* persistenceKey="if-you-want-it" */ />;
}
}
const styles = StyleSheet.create({
item: {
paddingHorizontal: 16,
paddingVertical: 12,
},
itemContainer: {
backgroundColor: '#fff',
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#ddd',
},

View File

@@ -0,0 +1,129 @@
import React from 'react';
import {
LayoutAnimation,
View,
StyleSheet,
StatusBar,
Text,
} from 'react-native';
import { SafeAreaView, createMaterialTopTabNavigator } from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
import { Button } from './commonComponents/ButtonWithMargin';
class MyHomeScreen extends React.Component {
static navigationOptions = {
tabBarLabel: 'Home',
tabBarIcon: ({ tintColor, focused, horizontal }) => (
<Ionicons
name={focused ? 'ios-home' : 'ios-home-outline'}
size={horizontal ? 20 : 26}
style={{ color: tintColor }}
/>
),
};
render() {
const { navigation } = this.props;
return (
<SafeAreaView forceInset={{ horizontal: 'always', top: 'always' }}>
<Text>Home Screen</Text>
<Button
onPress={() => navigation.navigate('Home')}
title="Go to home tab"
/>
<Button onPress={() => navigation.goBack(null)} title="Go back" />
</SafeAreaView>
);
}
}
class ReccomendedScreen extends React.Component {
static navigationOptions = {
tabBarLabel: 'Reccomended',
tabBarIcon: ({ tintColor, focused, horizontal }) => (
<Ionicons
name={focused ? 'ios-people' : 'ios-people-outline'}
size={horizontal ? 20 : 26}
style={{ color: tintColor }}
/>
),
};
render() {
const { navigation } = this.props;
return (
<SafeAreaView forceInset={{ horizontal: 'always', top: 'always' }}>
<Text>Reccomended Screen</Text>
<Button
onPress={() => navigation.navigate('Home')}
title="Go to home tab"
/>
<Button onPress={() => navigation.goBack(null)} title="Go back" />
</SafeAreaView>
);
}
}
class FeaturedScreen extends React.Component {
static navigationOptions = ({ navigation }) => ({
tabBarLabel: 'Featured',
tabBarIcon: ({ tintColor, focused, horizontal }) => (
<Ionicons
name={focused ? 'ios-star' : 'ios-star-outline'}
size={horizontal ? 20 : 26}
style={{ color: tintColor }}
/>
),
});
render() {
const { navigation } = this.props;
return (
<SafeAreaView forceInset={{ horizontal: 'always', top: 'always' }}>
<Text>Featured Screen</Text>
<Button
onPress={() => navigation.navigate('Home')}
title="Go to home tab"
/>
<Button onPress={() => navigation.goBack(null)} title="Go back" />
</SafeAreaView>
);
}
}
const SimpleTabs = createMaterialTopTabNavigator({
Home: MyHomeScreen,
Reccomended: ReccomendedScreen,
Featured: FeaturedScreen,
});
class TabNavigator extends React.Component {
static router = SimpleTabs.router;
componentWillUpdate() {
LayoutAnimation.easeInEaseOut();
}
render() {
const { navigation } = this.props;
const { routes, index } = navigation.state;
const activeRoute = routes[index];
let bottom = null;
if (activeRoute.routeName !== 'Home') {
bottom = (
<View style={{ height: 50, borderTopWidth: StyleSheet.hairlineWidth }}>
<Button title="Check out" onPress={() => {}} />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<StatusBar barStyle="default" />
<SafeAreaView
style={{ flex: 1 }}
forceInset={{ horizontal: 'always', top: 'always' }}
>
<SimpleTabs navigation={navigation} />
</SafeAreaView>
{bottom}
</View>
);
}
}
export default TabNavigator;

View File

@@ -9,15 +9,11 @@ import {
StyleSheet,
StatusBar,
Text,
TouchableOpacity,
View,
} from 'react-native';
import {
createNavigator,
createNavigationContainer,
SafeAreaView,
TabRouter,
} from 'react-navigation';
import { BorderlessButton } from 'react-native-gesture-handler';
import { createNavigator, SafeAreaView, TabRouter } from 'react-navigation';
import { createAppContainer } from 'react-navigation';
import SampleText from './SampleText';
import { Button } from './commonComponents/ButtonWithMargin';
@@ -53,13 +49,13 @@ const CustomTabBar = ({ navigation }) => {
return (
<SafeAreaView style={styles.tabContainer}>
{routes.map(route => (
<TouchableOpacity
<BorderlessButton
onPress={() => navigation.navigate(route.routeName)}
style={styles.tab}
key={route.routeName}
>
<Text>{route.routeName}</Text>
</TouchableOpacity>
</BorderlessButton>
))}
</SafeAreaView>
);
@@ -98,7 +94,7 @@ const CustomTabRouter = TabRouter(
}
);
const CustomTabs = createNavigationContainer(
const CustomTabs = createAppContainer(
createNavigator(CustomTabView, CustomTabRouter, {})
);

View File

@@ -9,10 +9,10 @@ import {
View,
} from 'react-native';
import {
createAppContainer,
Transitioner,
SafeAreaView,
StackRouter,
createNavigationContainer,
createNavigator,
} from 'react-navigation';
import SampleText from './SampleText';
@@ -100,7 +100,7 @@ const CustomRouter = StackRouter({
Settings: { screen: MySettingsScreen },
});
const CustomTransitioner = createNavigationContainer(
const CustomTransitioner = createAppContainer(
createNavigator(CustomNavigationView, CustomRouter, {})
);

View File

@@ -22,7 +22,7 @@ const MyNavScreen = ({ navigation, banner }) => (
onPress={() => navigation.navigate('Email')}
title="Open other screen"
/>
<Button onPress={() => navigation.goBack(null)} title="Go back" />
<Button onPress={() => navigation.navigate('Index')} title="Go back" />
</SafeAreaView>
<StatusBar barStyle="default" />
</ScrollView>
@@ -46,33 +46,39 @@ DraftsScreen.navigationOptions = {
headerTitle: 'Drafts',
};
const InboxStack = createStackNavigator({
Inbox: { screen: InboxScreen },
Email: { screen: EmailScreen },
});
const InboxStack = createStackNavigator(
{
Inbox: { screen: InboxScreen },
Email: { screen: EmailScreen },
},
{
navigationOptions: {
drawerLabel: 'Inbox',
drawerIcon: ({ tintColor }) => (
<MaterialIcons
name="move-to-inbox"
size={24}
style={{ color: tintColor }}
/>
),
},
}
);
InboxStack.navigationOptions = {
drawerLabel: 'Inbox',
drawerIcon: ({ tintColor }) => (
<MaterialIcons
name="move-to-inbox"
size={24}
style={{ color: tintColor }}
/>
),
};
const DraftsStack = createStackNavigator({
Drafts: { screen: DraftsScreen },
Email: { screen: EmailScreen },
});
DraftsStack.navigationOptions = {
drawerLabel: 'Drafts',
drawerIcon: ({ tintColor }) => (
<MaterialIcons name="drafts" size={24} style={{ color: tintColor }} />
),
};
const DraftsStack = createStackNavigator(
{
Drafts: { screen: DraftsScreen },
Email: { screen: EmailScreen },
},
{
navigationOptions: {
drawerLabel: 'Drafts',
drawerIcon: ({ tintColor }) => (
<MaterialIcons name="drafts" size={24} style={{ color: tintColor }} />
),
},
}
);
const DrawerExample = createDrawerNavigator(
{
@@ -85,6 +91,7 @@ const DrawerExample = createDrawerNavigator(
screen: DraftsStack,
},
},
{
initialRouteName: 'Drafts',
contentOptions: {

View File

@@ -70,7 +70,7 @@ const ProfileNavigator = createStackNavigator(
},
},
{
navigationOptions: {
defaultNavigationOptions: {
headerLeft: null,
},
mode: 'modal',
@@ -97,7 +97,7 @@ const ModalStack = createStackNavigator(
HeaderTest: { screen: MyHeaderTestScreen },
},
{
navigationOptions: {
defaultNavigationOptions: {
header: null,
},
mode: 'modal',

View File

@@ -10,16 +10,22 @@ import type {
} from 'react-navigation';
import * as React from 'react';
import { ScrollView, StatusBar } from 'react-native';
import { Platform, ScrollView, StatusBar } from 'react-native';
import {
createStackNavigator,
SafeAreaView,
withNavigation,
NavigationActions,
StackActions,
} from 'react-navigation';
import invariant from 'invariant';
import SampleText from './SampleText';
import { Button } from './commonComponents/ButtonWithMargin';
import { HeaderButtons } from './commonComponents/HeaderButtons';
const DEBUG = false;
type MyNavScreenProps = {
navigation: NavigationScreenProp<NavigationState>,
banner: React.Node,
@@ -48,25 +54,55 @@ const MyBackButtonWithNavigation = withNavigation(MyBackButton);
class MyNavScreen extends React.Component<MyNavScreenProps> {
render() {
const { navigation, banner } = this.props;
const { push, replace, popToTop, pop, dismiss } = navigation;
invariant(
push && replace && popToTop && pop && dismiss,
'missing action creators for StackNavigator'
);
return (
<SafeAreaView>
<SafeAreaView forceInset={{ top: 'never' }}>
<SampleText>{banner}</SampleText>
<Button
onPress={() => navigation.push('Profile', { name: 'Jane' })}
onPress={() => push('Profile', { name: 'Jane' })}
title="Push a profile screen"
/>
<Button
onPress={() =>
navigation.dispatch(
StackActions.reset({
index: 0,
actions: [
NavigationActions.navigate({
routeName: 'Photos',
params: { name: 'Jane' },
}),
],
})
)
}
title="Reset photos"
/>
<Button
onPress={() => navigation.navigate('Photos', { name: 'Jane' })}
title="Navigate to a photos screen"
/>
<Button
onPress={() => navigation.replace('Profile', { name: 'Lucy' })}
onPress={() => replace('Profile', { name: 'Lucy' })}
title="Replace with profile"
/>
<Button onPress={() => navigation.popToTop()} title="Pop to top" />
<Button onPress={() => navigation.pop()} title="Pop" />
<Button onPress={() => navigation.goBack()} title="Go back" />
<Button onPress={() => navigation.dismiss()} title="Dismiss" />
<Button onPress={() => popToTop()} title="Pop to top" />
<Button onPress={() => pop()} title="Pop" />
<Button
onPress={() => {
if (navigation.goBack()) {
console.log('goBack handled');
} else {
console.log('goBack unhandled');
}
}}
title="Go back"
/>
<Button onPress={() => dismiss()} title="Dismiss" />
<StatusBar barStyle="default" />
</SafeAreaView>
);
@@ -99,16 +135,16 @@ class MyHomeScreen extends React.Component<MyHomeScreenProps> {
this._s3.remove();
}
_onWF = a => {
console.log('_willFocus HomeScreen', a);
DEBUG && console.log('_willFocus HomeScreen', a);
};
_onDF = a => {
console.log('_didFocus HomeScreen', a);
DEBUG && console.log('_didFocus HomeScreen', a);
};
_onWB = a => {
console.log('_willBlur HomeScreen', a);
DEBUG && console.log('_willBlur HomeScreen', a);
};
_onDB = a => {
console.log('_didBlur HomeScreen', a);
DEBUG && console.log('_didBlur HomeScreen', a);
};
render() {
@@ -143,16 +179,16 @@ class MyPhotosScreen extends React.Component<MyPhotosScreenProps> {
this._s3.remove();
}
_onWF = a => {
console.log('_willFocus PhotosScreen', a);
DEBUG && console.log('_willFocus PhotosScreen', a);
};
_onDF = a => {
console.log('_didFocus PhotosScreen', a);
DEBUG && console.log('_didFocus PhotosScreen', a);
};
_onWB = a => {
console.log('_willBlur PhotosScreen', a);
DEBUG && console.log('_willBlur PhotosScreen', a);
};
_onDB = a => {
console.log('_didBlur PhotosScreen', a);
DEBUG && console.log('_didBlur PhotosScreen', a);
};
render() {
@@ -197,18 +233,23 @@ MyProfileScreen.navigationOptions = props => {
};
};
const SimpleStack = createStackNavigator({
Home: {
screen: MyHomeScreen,
const SimpleStack = createStackNavigator(
{
Home: {
screen: MyHomeScreen,
},
Profile: {
path: 'people/:name',
screen: MyProfileScreen,
},
Photos: {
path: 'photos/:name',
screen: MyPhotosScreen,
},
},
Profile: {
path: 'people/:name',
screen: MyProfileScreen,
},
Photos: {
path: 'photos/:name',
screen: MyPhotosScreen,
},
});
{
// headerLayoutPreset: 'center',
}
);
export default SimpleStack;

View File

@@ -8,26 +8,57 @@ import type {
} from 'react-navigation';
import React from 'react';
import { Platform, ScrollView, StatusBar, View } from 'react-native';
import { SafeAreaView, createBottomTabNavigator } from 'react-navigation';
import { Animated, Platform, Text, StatusBar, View } from 'react-native';
import {
ScrollView,
FlatList,
SafeAreaView,
createBottomTabNavigator,
withNavigation,
} from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
import SampleText from './SampleText';
import { Button } from './commonComponents/ButtonWithMargin';
const TEXT = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla a hendrerit dui, id consectetur nulla. Curabitur mattis sapien nunc, quis dignissim eros venenatis sit amet. Praesent rutrum dapibus diam quis eleifend. Donec vulputate quis purus sed vulputate. Fusce ipsum felis, cursus at congue vel, consectetur tincidunt purus. Pellentesque et fringilla lorem. In at augue malesuada, sollicitudin ex ut, convallis elit. Curabitur metus nibh, consequat vel libero sit amet, iaculis congue nisl. Maecenas eleifend sodales sapien, fringilla sagittis nisi ornare volutpat. Integer tellus enim, volutpat vitae nisl et, dignissim pharetra leo. Sed sit amet efficitur sapien, at tristique sapien. Aenean dignissim semper sagittis. Nullam sit amet volutpat mi.
Curabitur auctor orci et justo molestie iaculis. Integer elementum tortor ac ipsum egestas pharetra. Etiam ultrices elementum pharetra. Maecenas lobortis ultrices risus dignissim luctus. Nunc malesuada cursus posuere. Vestibulum tristique lectus pretium pellentesque pellentesque. Nunc ac nisi lacus. Duis ultrices dui ac viverra ullamcorper. Morbi placerat laoreet lacus sit amet ullamcorper.
Nulla convallis pulvinar hendrerit. Nulla mattis sem et aliquam ultrices. Nam egestas magna leo, nec luctus turpis sollicitudin ac. Sed id leo luctus, lobortis tortor ut, rhoncus ex. Aliquam gravida enim ac dapibus ultricies. Vestibulum at interdum est, et vehicula nibh. Phasellus dignissim iaculis rhoncus. Vestibulum tempus leo lectus, quis euismod metus ullamcorper quis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut id ipsum at enim eleifend porttitor id quis metus. Proin bibendum ornare iaculis. Duis elementum lacus vel cursus efficitur. Nunc eu tortor sed risus lacinia scelerisque.
Praesent lobortis elit sit amet mauris pulvinar, viverra condimentum massa pellentesque. Curabitur massa ex, dignissim eget neque at, fringilla consectetur justo. Cras sollicitudin vel ligula sed cursus. Aliquam porta sem hendrerit diam porta ultricies. Sed eu mi erat. Curabitur id justo vel tortor hendrerit vestibulum id eget est. Morbi eros magna, placerat id diam ut, varius sollicitudin mi. Curabitur pretium finibus accumsan.`;
const MyNavScreen = ({ navigation, banner }) => (
<SafeAreaView forceInset={{ horizontal: 'always', top: 'always' }}>
<SampleText>{banner}</SampleText>
<Button
onPress={() => navigation.navigate('Home')}
title="Go to home tab"
/>
<Button
onPress={() => navigation.navigate('Settings')}
title="Go to settings tab"
/>
<Button onPress={() => navigation.goBack(null)} title="Go back" />
<StatusBar barStyle="default" />
</SafeAreaView>
<ScrollView navigation={navigation} style={{ flex: 1 }}>
<SafeAreaView forceInset={{ horizontal: 'always', top: 'always' }}>
<SampleText>{banner}</SampleText>
<Button
onPress={() => navigation.navigate('Home')}
title="Go to home tab"
/>
<Button
onPress={() => navigation.navigate('Settings')}
title="Go to settings tab"
/>
<Button onPress={() => navigation.goBack(null)} title="Go back" />
{TEXT.split('\n').map((p, n) => (
<Text key={n} style={{ marginVertical: 10, marginHorizontal: 8 }}>
{p}
</Text>
))}
<StatusBar barStyle="default" />
</SafeAreaView>
</ScrollView>
);
const MyListScreen = ({ navigation, data }) => (
<FlatList
navigation={navigation}
data={TEXT.split('\n')}
style={{ paddingTop: 10 }}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => (
<Text style={{ fontSize: 16, marginVertical: 10, marginHorizontal: 8 }}>
{item}
</Text>
)}
/>
);
const MyHomeScreen = ({ navigation }) => (
@@ -40,14 +71,15 @@ MyHomeScreen.navigationOptions = {
accessibilityLabel: 'TEST_ID_HOME_ACLBL',
},
tabBarLabel: 'Home',
tabBarIcon: ({ tintColor, focused }) => (
tabBarIcon: ({ tintColor, focused, horizontal }) => (
<Ionicons
name={focused ? 'ios-home' : 'ios-home-outline'}
size={26}
size={horizontal ? 20 : 26}
style={{ color: tintColor }}
/>
),
};
MyListScreen.navigationOptions = MyHomeScreen.navigationOptions;
type MyPeopleScreenProps = {
navigation: NavigationScreenProp<*>,
@@ -60,25 +92,25 @@ class MyPeopleScreen extends React.Component<MyPeopleScreenProps> {
static navigationOptions = {
tabBarLabel: 'People',
tabBarIcon: ({ tintColor, focused }) => (
tabBarIcon: ({ tintColor, focused, horizontal }) => (
<Ionicons
name={focused ? 'ios-people' : 'ios-people-outline'}
size={26}
size={horizontal ? 20 : 26}
style={{ color: tintColor }}
/>
),
};
componentDidMount() {
this._s0 = this.props.navigation.addListener('willFocus', this._onEvent);
this._s1 = this.props.navigation.addListener('didFocus', this._onEvent);
this._s2 = this.props.navigation.addListener('willBlur', this._onEvent);
this._s3 = this.props.navigation.addListener('didBlur', this._onEvent);
// this._s0 = this.props.navigation.addListener('willFocus', this._onEvent);
// this._s1 = this.props.navigation.addListener('didFocus', this._onEvent);
// this._s2 = this.props.navigation.addListener('willBlur', this._onEvent);
// this._s3 = this.props.navigation.addListener('didBlur', this._onEvent);
}
componentWillUnmount() {
this._s0.remove();
this._s1.remove();
this._s2.remove();
this._s3.remove();
// this._s0.remove();
// this._s1.remove();
// this._s2.remove();
// this._s3.remove();
}
_onEvent = a => {
console.log('EVENT ON PEOPLE TAB', a.type, a);
@@ -100,25 +132,25 @@ class MyChatScreen extends React.Component<MyChatScreenProps> {
static navigationOptions = {
tabBarLabel: 'Chat',
tabBarIcon: ({ tintColor, focused }) => (
tabBarIcon: ({ tintColor, focused, horizontal }) => (
<Ionicons
name={focused ? 'ios-chatboxes' : 'ios-chatboxes-outline'}
size={26}
size={horizontal ? 20 : 26}
style={{ color: tintColor }}
/>
),
};
componentDidMount() {
this._s0 = this.props.navigation.addListener('willFocus', this._onEvent);
this._s1 = this.props.navigation.addListener('didFocus', this._onEvent);
this._s2 = this.props.navigation.addListener('willBlur', this._onEvent);
this._s3 = this.props.navigation.addListener('didBlur', this._onEvent);
// this._s0 = this.props.navigation.addListener('willFocus', this._onEvent);
// this._s1 = this.props.navigation.addListener('didFocus', this._onEvent);
// this._s2 = this.props.navigation.addListener('willBlur', this._onEvent);
// this._s3 = this.props.navigation.addListener('didBlur', this._onEvent);
}
componentWillUnmount() {
this._s0.remove();
this._s1.remove();
this._s2.remove();
this._s3.remove();
// this._s0.remove();
// this._s1.remove();
// this._s2.remove();
// this._s3.remove();
}
_onEvent = a => {
console.log('EVENT ON CHAT TAB', a.type, a);
@@ -135,10 +167,10 @@ const MySettingsScreen = ({ navigation }) => (
MySettingsScreen.navigationOptions = {
tabBarLabel: 'Settings',
tabBarIcon: ({ tintColor, focused }) => (
tabBarIcon: ({ tintColor, focused, horizontal }) => (
<Ionicons
name={focused ? 'ios-settings' : 'ios-settings-outline'}
size={26}
size={horizontal ? 20 : 26}
style={{ color: tintColor }}
/>
),
@@ -147,7 +179,7 @@ MySettingsScreen.navigationOptions = {
const SimpleTabs = createBottomTabNavigator(
{
Home: {
screen: MyHomeScreen,
screen: MyListScreen,
path: '',
},
People: {
@@ -165,7 +197,7 @@ const SimpleTabs = createBottomTabNavigator(
},
{
tabBarOptions: {
activeTintColor: Platform.OS === 'ios' ? '#e91e63' : '#fff',
activeTintColor: '#e91e63',
},
}
);
@@ -182,16 +214,16 @@ class SimpleTabsContainer extends React.Component<SimpleTabsContainerProps> {
_s3: NavigationEventSubscription;
componentDidMount() {
this._s0 = this.props.navigation.addListener('willFocus', this._onAction);
this._s1 = this.props.navigation.addListener('didFocus', this._onAction);
this._s2 = this.props.navigation.addListener('willBlur', this._onAction);
this._s3 = this.props.navigation.addListener('didBlur', this._onAction);
// this._s0 = this.props.navigation.addListener('willFocus', this._onAction);
// this._s1 = this.props.navigation.addListener('didFocus', this._onAction);
// this._s2 = this.props.navigation.addListener('willBlur', this._onAction);
// this._s3 = this.props.navigation.addListener('didBlur', this._onAction);
}
componentWillUnmount() {
this._s0.remove();
this._s1.remove();
this._s2.remove();
this._s3.remove();
// this._s0.remove();
// this._s1.remove();
// this._s2.remove();
// this._s3.remove();
}
_onAction = a => {
console.log('TABS EVENT', a.type, a);

View File

@@ -122,7 +122,7 @@ const StackWithCustomHeaderBackImage = createStackNavigator(
},
},
{
navigationOptions: {
defaultNavigationOptions: {
headerBackImage: MyCustomHeaderBackImage,
},
}

View File

@@ -6,6 +6,8 @@ import type { NavigationScreenProp } from 'react-navigation';
import * as React from 'react';
import { ScrollView, StatusBar } from 'react-native';
import { createStackNavigator, SafeAreaView } from 'react-navigation';
import invariant from 'invariant';
import { Button } from './commonComponents/ButtonWithMargin';
type NavScreenProps = {
@@ -19,15 +21,14 @@ class HomeScreen extends React.Component<NavScreenProps> {
render() {
const { navigation } = this.props;
const { push } = navigation;
invariant(push, 'missing `push` action creator for StackNavigator');
return (
<SafeAreaView style={{ paddingTop: 30 }}>
<Button onPress={() => push('Other')} title="Push another screen" />
<Button
onPress={() => navigation.push('Other')}
title="Push another screen"
/>
<Button
onPress={() => navigation.push('ScreenWithNoHeader')}
onPress={() => push('ScreenWithNoHeader')}
title="Push screen with no header"
/>
<Button onPress={() => navigation.goBack(null)} title="Go Home" />
@@ -44,18 +45,20 @@ class OtherScreen extends React.Component<NavScreenProps> {
render() {
const { navigation } = this.props;
const { push, pop } = navigation;
invariant(push && pop, 'missing action creators for StackNavigator');
return (
<SafeAreaView style={{ paddingTop: 30 }}>
<Button
onPress={() => navigation.push('ScreenWithLongTitle')}
onPress={() => push('ScreenWithLongTitle')}
title="Push another screen"
/>
<Button
onPress={() => navigation.push('ScreenWithNoHeader')}
onPress={() => push('ScreenWithNoHeader')}
title="Push screen with no header"
/>
<Button onPress={() => navigation.pop()} title="Pop" />
<Button onPress={() => pop()} title="Pop" />
<Button onPress={() => navigation.goBack(null)} title="Go back" />
<StatusBar barStyle="default" />
</SafeAreaView>
@@ -70,10 +73,12 @@ class ScreenWithLongTitle extends React.Component<NavScreenProps> {
render() {
const { navigation } = this.props;
const { pop } = navigation;
invariant(pop, 'missing `pop` action creator for StackNavigator');
return (
<SafeAreaView style={{ paddingTop: 30 }}>
<Button onPress={() => navigation.pop()} title="Pop" />
<Button onPress={() => pop()} title="Pop" />
<Button onPress={() => navigation.goBack(null)} title="Go back" />
<StatusBar barStyle="default" />
</SafeAreaView>
@@ -89,14 +94,13 @@ class ScreenWithNoHeader extends React.Component<NavScreenProps> {
render() {
const { navigation } = this.props;
const { push, pop } = navigation;
invariant(push && pop, 'missing action creators for StackNavigator');
return (
<SafeAreaView style={{ paddingTop: 30 }}>
<Button
onPress={() => navigation.push('Other')}
title="Push another screen"
/>
<Button onPress={() => navigation.pop()} title="Pop" />
<Button onPress={() => push('Other')} title="Push another screen" />
<Button onPress={() => pop()} title="Pop" />
<Button onPress={() => navigation.goBack(null)} title="Go back" />
<StatusBar barStyle="default" />
</SafeAreaView>

View File

@@ -16,9 +16,16 @@ import {
Platform,
ScrollView,
StatusBar,
StyleSheet,
View,
} from 'react-native';
import { Header, createStackNavigator } from 'react-navigation';
import {
Header,
HeaderStyleInterpolator,
createStackNavigator,
} from 'react-navigation';
import invariant from 'invariant';
import SampleText from './SampleText';
import { Button } from './commonComponents/ButtonWithMargin';
import { HeaderButtons } from './commonComponents/HeaderButtons';
@@ -31,11 +38,16 @@ type MyNavScreenProps = {
class MyNavScreen extends React.Component<MyNavScreenProps> {
render() {
const { navigation, banner } = this.props;
const { push, replace, popToTop, pop } = navigation;
invariant(
push && replace && popToTop && pop,
'missing action creators for StackNavigator'
);
return (
<ScrollView style={{ flex: 1 }} {...this.getHeaderInset()}>
<SampleText>{banner}</SampleText>
<Button
onPress={() => navigation.push('Profile', { name: 'Jane' })}
onPress={() => push('Profile', { name: 'Jane' })}
title="Push a profile screen"
/>
<Button
@@ -43,11 +55,11 @@ class MyNavScreen extends React.Component<MyNavScreenProps> {
title="Navigate to a photos screen"
/>
<Button
onPress={() => navigation.replace('Profile', { name: 'Lucy' })}
onPress={() => replace('Profile', { name: 'Lucy' })}
title="Replace with profile"
/>
<Button onPress={() => navigation.popToTop()} title="Pop to top" />
<Button onPress={() => navigation.pop()} title="Pop" />
<Button onPress={() => popToTop()} title="Pop to top" />
<Button onPress={() => pop()} title="Pop" />
<Button onPress={() => navigation.goBack(null)} title="Go back" />
<StatusBar barStyle="default" />
</ScrollView>
@@ -222,8 +234,18 @@ const StackWithTranslucentHeader = createStackNavigator(
},
{
headerTransitionPreset: 'uikit',
navigationOptions: {
// You can leave this out if you don't want the card shadow to
// be visible through the header
transitionConfig: () => ({
headerBackgroundInterpolator:
HeaderStyleInterpolator.forBackgroundWithTranslation,
}),
defaultNavigationOptions: {
headerTransparent: true,
headerStyle: {
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#A7A7AA',
},
headerBackground: Platform.select({
ios: <BlurView style={{ flex: 1 }} intensity={98} />,
android: (

View File

@@ -3,43 +3,59 @@
*/
import React from 'react';
import { ScrollView, StatusBar } from 'react-native';
import { StatusBar, Text } from 'react-native';
import {
ScrollView,
SafeAreaView,
createStackNavigator,
createBottomTabNavigator,
withNavigation,
} from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
import SampleText from './SampleText';
import { Button } from './commonComponents/ButtonWithMargin';
const MyNavScreen = ({ navigation, banner }) => (
<ScrollView>
<SafeAreaView forceInset={{ horizontal: 'always' }}>
<SampleText>{banner}</SampleText>
<Button
onPress={() => navigation.navigate('Profile', { name: 'Jordan' })}
title="Open profile screen"
/>
<Button
onPress={() => navigation.navigate('NotifSettings')}
title="Open notifications screen"
/>
<Button
onPress={() => navigation.navigate('SettingsTab')}
title="Go to settings tab"
/>
<Button onPress={() => navigation.goBack(null)} title="Go back" />
</SafeAreaView>
const TEXT = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla a hendrerit dui, id consectetur nulla. Curabitur mattis sapien nunc, quis dignissim eros venenatis sit amet. Praesent rutrum dapibus diam quis eleifend. Donec vulputate quis purus sed vulputate. Fusce ipsum felis, cursus at congue vel, consectetur tincidunt purus. Pellentesque et fringilla lorem. In at augue malesuada, sollicitudin ex ut, convallis elit. Curabitur metus nibh, consequat vel libero sit amet, iaculis congue nisl. Maecenas eleifend sodales sapien, fringilla sagittis nisi ornare volutpat. Integer tellus enim, volutpat vitae nisl et, dignissim pharetra leo. Sed sit amet efficitur sapien, at tristique sapien. Aenean dignissim semper sagittis. Nullam sit amet volutpat mi.
Curabitur auctor orci et justo molestie iaculis. Integer elementum tortor ac ipsum egestas pharetra. Etiam ultrices elementum pharetra. Maecenas lobortis ultrices risus dignissim luctus. Nunc malesuada cursus posuere. Vestibulum tristique lectus pretium pellentesque pellentesque. Nunc ac nisi lacus. Duis ultrices dui ac viverra ullamcorper. Morbi placerat laoreet lacus sit amet ullamcorper.
Nulla convallis pulvinar hendrerit. Nulla mattis sem et aliquam ultrices. Nam egestas magna leo, nec luctus turpis sollicitudin ac. Sed id leo luctus, lobortis tortor ut, rhoncus ex. Aliquam gravida enim ac dapibus ultricies. Vestibulum at interdum est, et vehicula nibh. Phasellus dignissim iaculis rhoncus. Vestibulum tempus leo lectus, quis euismod metus ullamcorper quis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut id ipsum at enim eleifend porttitor id quis metus. Proin bibendum ornare iaculis. Duis elementum lacus vel cursus efficitur. Nunc eu tortor sed risus lacinia scelerisque.
Praesent lobortis elit sit amet mauris pulvinar, viverra condimentum massa pellentesque. Curabitur massa ex, dignissim eget neque at, fringilla consectetur justo. Cras sollicitudin vel ligula sed cursus. Aliquam porta sem hendrerit diam porta ultricies. Sed eu mi erat. Curabitur id justo vel tortor hendrerit vestibulum id eget est. Morbi eros magna, placerat id diam ut, varius sollicitudin mi. Curabitur pretium finibus accumsan.`;
<StatusBar barStyle="default" />
</ScrollView>
);
class MyNavScreen extends React.Component {
render() {
const { navigation } = this.props;
const banner = navigation.getParam('banner');
const MyHomeScreen = ({ navigation }) => (
<MyNavScreen banner="Home Screen" navigation={navigation} />
);
return (
<ScrollView style={{ flex: 1 }}>
<SafeAreaView forceInset={{ horizontal: 'always' }}>
<SampleText>{banner}</SampleText>
<Button
onPress={() => navigation.navigate('Profile', { name: 'Jordan' })}
title="Open profile screen"
/>
<Button
onPress={() => navigation.navigate('NotifSettings')}
title="Open notifications screen"
/>
<Button
onPress={() => navigation.navigate('SettingsTab')}
title="Go to settings tab"
/>
<Button onPress={() => navigation.goBack(null)} title="Go back" />
{TEXT.split('\n').map((p, n) => (
<Text key={n} style={{ marginVertical: 10, marginHorizontal: 8 }}>
{p}
</Text>
))}
</SafeAreaView>
<StatusBar barStyle="default" />
</ScrollView>
);
}
}
const MyProfileScreen = ({ navigation }) => (
<MyNavScreen
@@ -48,18 +64,11 @@ const MyProfileScreen = ({ navigation }) => (
/>
);
const MyNotificationsSettingsScreen = ({ navigation }) => (
<MyNavScreen banner="Notifications Screen" navigation={navigation} />
);
const MySettingsScreen = ({ navigation }) => (
<MyNavScreen banner="Settings Screen" navigation={navigation} />
);
const MainTab = createStackNavigator({
Home: {
screen: MyHomeScreen,
screen: MyNavScreen,
path: '/',
params: { banner: 'Home Screen' },
navigationOptions: {
title: 'Welcome',
},
@@ -75,14 +84,16 @@ const MainTab = createStackNavigator({
const SettingsTab = createStackNavigator({
Settings: {
screen: MySettingsScreen,
screen: MyNavScreen,
path: '/',
params: { banner: 'Settings Screen' },
navigationOptions: () => ({
title: 'Settings',
}),
},
NotifSettings: {
screen: MyNotificationsSettingsScreen,
screen: MyNavScreen,
params: { banner: 'Notifications Screen' },
navigationOptions: {
title: 'Notifications',
},

View File

@@ -8,6 +8,7 @@ import {
SafeAreaView,
createStackNavigator,
createBottomTabNavigator,
getActiveChildNavigationOptions,
} from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
@@ -16,7 +17,7 @@ import { Button } from './commonComponents/ButtonWithMargin';
const MyNavScreen = ({ navigation, banner }) => (
<ScrollView>
<SafeAreaView forceInset={{ horizontal: 'always' }}>
<SafeAreaView forceInset={{ horizontal: 'always', vertical: 'never' }}>
<SampleText>{banner}</SampleText>
<Button
onPress={() => navigation.navigate('Profile', { name: 'Jordan' })}
@@ -94,6 +95,13 @@ const TabNav = createBottomTabNavigator(
}
);
TabNav.navigationOptions = ({ navigation, screenProps }) => {
const childOptions = getActiveChildNavigationOptions(navigation, screenProps);
return {
title: childOptions.title,
};
};
const StacksOverTabs = createStackNavigator({
Root: {
screen: TabNav,
@@ -107,9 +115,9 @@ const StacksOverTabs = createStackNavigator({
Profile: {
screen: MyProfileScreen,
path: '/people/:name',
navigationOptions: ({ navigation }) => {
title: `${navigation.state.params.name}'s Profile!`;
},
navigationOptions: ({ navigation }) => ({
title: `${navigation.state.params.name}'s Profile!`,
}),
},
});

View File

@@ -0,0 +1,142 @@
/**
* @flow
*/
import React from 'react';
import { View, ScrollView, StatusBar, StyleSheet } from 'react-native';
import {
SafeAreaView,
createStackNavigator,
createMaterialTopTabNavigator,
} from 'react-navigation';
import { Constants } from 'expo';
import { MaterialTopTabBar } from 'react-navigation-tabs';
import SampleText from './SampleText';
import { Button } from './commonComponents/ButtonWithMargin';
const HEADER_HEIGHT = 64;
const MyNavScreen = ({ navigation, banner, statusBarStyle }) => (
<ScrollView>
<SafeAreaView forceInset={{ horizontal: 'always' }}>
<SampleText>{banner}</SampleText>
<Button
onPress={() => navigation.navigate('Profile', { name: 'Jordan' })}
title="Open profile screen"
/>
<Button
onPress={() => navigation.navigate('NotifSettings')}
title="Open notifications screen"
/>
<Button
onPress={() => navigation.navigate('SettingsTab')}
title="Go to settings tab"
/>
<Button onPress={() => navigation.goBack(null)} title="Go back" />
</SafeAreaView>
<StatusBar barStyle={statusBarStyle || 'default'} />
</ScrollView>
);
const MyHomeScreen = ({ navigation }) => (
<MyNavScreen
banner="Home Screen"
navigation={navigation}
statusBarStyle="light-content"
/>
);
const MyProfileScreen = ({ navigation }) => (
<MyNavScreen
banner={`${navigation.state.params.name}s Profile`}
navigation={navigation}
/>
);
const MyNotificationsSettingsScreen = ({ navigation }) => (
<MyNavScreen banner="Notifications Screen" navigation={navigation} />
);
const MySettingsScreen = ({ navigation }) => (
<MyNavScreen
banner="Settings Screen"
navigation={navigation}
statusBarStyle="light-content"
/>
);
const styles = StyleSheet.create({
stackHeader: {
height: HEADER_HEIGHT,
},
tab: {
height: HEADER_HEIGHT,
},
});
function MaterialTopTabBarWithStatusBar(props) {
return (
<View
style={{
paddingTop: Constants.statusBarHeight,
backgroundColor: '#2196f3',
}}
>
<MaterialTopTabBar {...props} jumpToIndex={() => {}} />
</View>
);
}
const TabNavigator = createMaterialTopTabNavigator(
{
MainTab: {
screen: MyHomeScreen,
navigationOptions: {
title: 'Welcome',
},
},
SettingsTab: {
screen: MySettingsScreen,
navigationOptions: {
title: 'Settings',
},
},
},
{
tabBarComponent: MaterialTopTabBarWithStatusBar,
tabBarOptions: {
tabStyle: styles.tab,
},
}
);
const StackNavigator = createStackNavigator(
{
Root: {
screen: TabNavigator,
navigationOptions: {
header: null,
},
},
NotifSettings: {
screen: MyNotificationsSettingsScreen,
navigationOptions: {
title: 'Notifications',
},
},
Profile: {
screen: MyProfileScreen,
navigationOptions: ({ navigation }) => ({
title: `${navigation.state.params.name}'s Profile!`,
}),
},
},
{
defaultNavigationOptions: {
headerStyle: styles.stackHeader,
},
}
);
export default StackNavigator;

View File

@@ -13,31 +13,19 @@ const TabsInDrawer = createDrawerNavigator({
SimpleTabs: {
screen: SimpleTabs,
navigationOptions: {
drawer: () => ({
label: 'Simple Tabs',
icon: ({ tintColor }) => (
<MaterialIcons
name="filter-1"
size={24}
style={{ color: tintColor }}
/>
),
}),
drawerLabel: 'Simple tabs',
drawerIcon: ({ tintColor }) => (
<MaterialIcons name="filter-1" size={24} style={{ color: tintColor }} />
),
},
},
StacksOverTabs: {
screen: StacksOverTabs,
navigationOptions: {
drawer: () => ({
label: 'Stacks Over Tabs',
icon: ({ tintColor }) => (
<MaterialIcons
name="filter-2"
size={24}
style={{ color: tintColor }}
/>
),
}),
drawerLabel: 'Stacks Over Tabs',
drawerIcon: ({ tintColor }) => (
<MaterialIcons name="filter-2" size={24} style={{ color: tintColor }} />
),
},
},
});

View File

@@ -0,0 +1,127 @@
/**
* @flow
*/
import React from 'react';
import { FlatList, SafeAreaView, StatusBar, Text, View } from 'react-native';
import { NavigationEvents } from 'react-navigation';
import { createMaterialBottomTabNavigator } from 'react-navigation-material-bottom-tabs';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
const Event = ({ event }) => (
<View
style={{
borderColor: 'grey',
borderWidth: 1,
borderRadius: 3,
padding: 5,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
}}
>
<Text>{event.type}</Text>
<Text>
{event.action.type.replace('Navigation/', '')}
{event.action.routeName ? '=>' + event.action.routeName : ''}
</Text>
</View>
);
const createTabScreen = (name, icon, focusedIcon) => {
class TabScreen extends React.Component<any, any> {
static navigationOptions = {
tabBarLabel: name,
tabBarIcon: ({ tintColor, focused }) => (
<MaterialCommunityIcons
name={focused ? focusedIcon : icon}
size={26}
style={{ color: focused ? tintColor : '#ccc' }}
/>
),
};
state = { eventLog: [] };
append = navigationEvent => {
this.setState(({ eventLog }) => ({
eventLog: eventLog.concat(navigationEvent),
}));
};
render() {
return (
<SafeAreaView
forceInset={{ horizontal: 'always', top: 'always' }}
style={{
flex: 1,
}}
>
<Text
style={{
margin: 10,
marginTop: 30,
fontSize: 30,
fontWeight: 'bold',
}}
>
Events for tab {name}
</Text>
<View style={{ flex: 1, width: '100%', marginTop: 10 }}>
<FlatList
data={this.state.eventLog}
keyExtractor={item => `${this.state.eventLog.indexOf(item)}`}
renderItem={({ item }) => (
<View
style={{
marginVertical: 5,
marginHorizontal: 10,
backgroundColor: '#e4e4e4',
}}
>
<Event event={item} />
</View>
)}
/>
</View>
<NavigationEvents
onWillFocus={this.append}
onDidFocus={this.append}
onWillBlur={this.append}
onDidBlur={this.append}
/>
<StatusBar barStyle="default" />
</SafeAreaView>
);
}
}
return TabScreen;
};
const TabsWithNavigationEvents = createMaterialBottomTabNavigator(
{
One: {
screen: createTabScreen('One', 'numeric-1-box-outline', 'numeric-1-box'),
},
Two: {
screen: createTabScreen('Two', 'numeric-2-box-outline', 'numeric-2-box'),
},
Three: {
screen: createTabScreen(
'Three',
'numeric-3-box-outline',
'numeric-3-box'
),
},
},
{
shifting: false,
activeTintColor: '#F44336',
}
);
export default TabsWithNavigationEvents;

View File

@@ -0,0 +1,165 @@
import React from 'react';
import { Platform, StyleSheet, Text, View } from 'react-native';
import { BorderlessButton, RectButton } from 'react-native-gesture-handler';
const invariant = require('fbjs/lib/invariant');
type ButtonProps = $ReadOnly<{|
/**
* Text to display inside the button
*/
title: string,
/**
* Handler to be called when the user taps the button
*/
onPress: (event?: any) => mixed,
/**
* Color of the text (iOS), or background color of the button (Android)
*/
color?: ?string,
/**
* TV preferred focus (see documentation for the View component).
*/
hasTVPreferredFocus?: ?boolean,
/**
* Text to display for blindness accessibility features
*/
accessibilityLabel?: ?string,
/**
* If true, disable all interactions for this component.
*/
disabled?: ?boolean,
/**
* Used to locate this view in end-to-end tests.
*/
testID?: ?string,
|}>;
/**
* A basic button component that should render nicely on any platform. Supports
* a minimal level of customization.
*
* <center><img src="img/buttonExample.png"></img></center>
*
* If this button doesn't look right for your app, you can build your own
* button using [TouchableOpacity](docs/touchableopacity.html)
* or [TouchableNativeFeedback](docs/touchablenativefeedback.html).
* For inspiration, look at the [source code for this button component](https://github.com/facebook/react-native/blob/master/Libraries/Components/Button.js).
* Or, take a look at the [wide variety of button components built by the community](https://js.coach/react-native?search=button).
*
* Example usage:
*
* ```
* import { Button } from 'react-native';
* ...
*
* <Button
* onPress={onPressLearnMore}
* title="Learn More"
* color="#841584"
* accessibilityLabel="Learn more about this purple button"
* />
* ```
*
*/
export default class Button extends React.Component<ButtonProps> {
render() {
const {
accessibilityLabel,
color,
onPress,
title,
hasTVPreferredFocus,
disabled,
testID,
} = this.props;
const buttonStyles = [styles.button];
const textStyles = [styles.text];
if (color) {
if (Platform.OS === 'ios') {
textStyles.push({ color: color });
} else {
buttonStyles.push({ backgroundColor: color });
}
}
const accessibilityStates = [];
if (disabled) {
buttonStyles.push(styles.buttonDisabled);
textStyles.push(styles.textDisabled);
accessibilityStates.push('disabled');
}
invariant(
typeof title === 'string',
'The title prop of a Button must be a string'
);
const formattedTitle =
Platform.OS === 'android' ? title.toUpperCase() : title;
const Touchable = Platform.OS === 'android' ? RectButton : BorderlessButton;
return (
<Touchable
accessibilityLabel={accessibilityLabel}
accessibilityRole="button"
accessibilityStates={accessibilityStates}
hasTVPreferredFocus={hasTVPreferredFocus}
testID={testID}
disabled={disabled}
onPress={onPress}
>
<View style={buttonStyles}>
<Text style={textStyles} disabled={disabled}>
{formattedTitle}
</Text>
</View>
</Touchable>
);
}
}
const styles = StyleSheet.create({
button: Platform.select({
ios: {},
android: {
elevation: 4,
// Material design blue from https://material.google.com/style/color.html#color-color-palette
backgroundColor: '#2196F3',
borderRadius: 2,
},
}),
text: {
textAlign: 'center',
padding: 8,
...Platform.select({
ios: {
// iOS blue from https://developer.apple.com/ios/human-interface-guidelines/visual-design/color/
color: '#007AFF',
fontSize: 18,
},
android: {
color: 'white',
fontWeight: '500',
},
}),
},
buttonDisabled: Platform.select({
ios: {},
android: {
elevation: 0,
backgroundColor: '#dfdfdf',
},
}),
textDisabled: Platform.select({
ios: {
color: '#cdcdcd',
},
android: {
color: '#a1a1a1',
},
}),
});

View File

@@ -1,9 +1,10 @@
import { Button as RNButton, StyleSheet, View, Platform } from 'react-native';
import { StyleSheet, View, Platform } from 'react-native';
import BaseButton from './Button';
import React from 'react';
export const Button = props => (
<View style={styles.margin}>
<RNButton {...props} />
<BaseButton {...props} />
</View>
);

View File

@@ -2,31 +2,33 @@
"name": "NavigationPlayground",
"version": "0.1.0",
"private": true,
"main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "react-native-scripts start",
"eject": "react-native-scripts eject",
"android": "react-native-scripts android",
"ios": "react-native-scripts ios",
"postinstall": "rm -rf node_modules/react-native-screens && rm -rf node_modules/react-native-gesture-handler",
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"test": "flow"
},
"dependencies": {
"expo": "^26.0.0",
"react": "16.3.0-alpha.1",
"react-native": "^0.54.0",
"expo": "^30.0.0",
"hoist-non-react-statics": "^3.0.1",
"invariant": "^2.2.4",
"react": "16.3.1",
"react-native": "^0.55.0",
"react-native-iphone-x-helper": "^1.0.2",
"react-native-paper": "^2.1.3",
"react-navigation": "link:../..",
"react-navigation-header-buttons": "^0.0.4",
"react-navigation-material-bottom-tabs": "0.1.3"
"react-navigation-material-bottom-tabs": "1.0.0"
},
"devDependencies": {
"babel-jest": "^22.4.1",
"babel-plugin-transform-remove-console": "^6.9.0",
"flow-bin": "^0.61.0",
"flow-bin": "^0.67.0",
"jest": "^22.1.3",
"jest-expo": "^26.0.0",
"react-native-scripts": "^1.5.0",
"react-test-renderer": "16.3.0-alpha.1"
"jest-expo": "^28.0.0",
"react-test-renderer": "16.3.1"
},
"jest": {
"preset": "jest-expo",

File diff suppressed because it is too large Load Diff

View File

@@ -2,4 +2,4 @@
## Usage
Please see the [Contributors Guide](https://reactnavigation.org/docs/contributing.html#run-the-example-app) for instructions on running these example apps.
Please see the [Contributors Guide](https://reactnavigation.org/docs/en/contributing.html#run-the-example-app) for instructions.

View File

@@ -1,8 +0,0 @@
{
"presets": ["babel-preset-expo"],
"env": {
"development": {
"plugins": ["transform-react-jsx-source"]
}
}
}

View File

@@ -1,3 +0,0 @@
node_modules/
.expo/
npm-debug.*

View File

@@ -1 +0,0 @@
{}

View File

@@ -1,27 +0,0 @@
import React from 'react';
import { AppRegistry } from 'react-native';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import AppReducer from './src/reducers';
import AppWithNavigationState from './src/navigators/AppNavigator';
import { middleware } from './src/utils/redux';
const store = createStore(
AppReducer,
applyMiddleware(middleware),
);
class ReduxExampleApp extends React.Component {
render() {
return (
<Provider store={store}>
<AppWithNavigationState />
</Provider>
);
}
}
AppRegistry.registerComponent('ReduxExample', () => ReduxExampleApp);
export default ReduxExampleApp;

View File

@@ -1,9 +0,0 @@
import React from 'react';
import App from './App';
import renderer from 'react-test-renderer';
it('renders without crashing', () => {
const rendered = renderer.create(<App />).toJSON();
expect(rendered).toBeTruthy();
});

View File

@@ -1,5 +0,0 @@
# Redux example
## Usage
Please see the [Contributors Guide](https://reactnavigation.org/docs/contributing.html#run-the-example-app) for instructions on running these example apps.

View File

@@ -1,24 +0,0 @@
{
"expo": {
"name": "ReduxExample",
"description": "Try out react-navigation with this awesome Redux example",
"version": "1.0.0",
"slug": "ReduxExample",
"privacy": "public",
"orientation": "portrait",
"primaryColor": "#cccccc",
"icon": "./assets/icons/react-navigation.png",
"loading": {
"icon": "./assets/icons/react-navigation.png",
"hideExponentText": false
},
"sdkVersion": "25.0.0",
"entryPoint": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
"packagerOpts": {
"assetExts": ["ttf", "mp4"]
},
"ios": {
"supportsTablet": true
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1,40 +0,0 @@
{
"name": "ReduxExample",
"version": "0.0.1",
"private": true,
"main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
"scripts": {
"start": "react-native-scripts start",
"eject": "react-native-scripts eject",
"android": "react-native-scripts android",
"ios": "react-native-scripts ios",
"test": "node node_modules/jest/bin/jest.js"
},
"jest": {
"preset": "jest-expo",
"modulePathIgnorePatterns": [
"/node_modules/.*/react-native/",
"/node_modules/.*/react/"
],
"transformIgnorePatterns": [
"/node_modules/(?!react-native|react-navigation)/"
]
},
"dependencies": {
"expo": "^25.0.0",
"prop-types": "^15.5.10",
"react": "16.2.0",
"react-native": "^0.52.0",
"react-navigation": "link:../..",
"react-navigation-redux-helpers": "^1.0.0",
"react-redux": "^5.0.6",
"redux": "^3.7.2"
},
"devDependencies": {
"babel-jest": "^22.4.1",
"jest": "^22.1.3",
"jest-expo": "^25.1.0",
"react-native-scripts": "^1.3.1",
"react-test-renderer": "16.0.0"
}
}

View File

@@ -1,52 +0,0 @@
/**
* @noflow
*/
const fs = require('fs');
const path = require('path');
const blacklist = require('metro/src/blacklist');
module.exports = {
getBlacklistRE() {
return blacklist([
/react\-navigation\/examples\/(?!ReduxExample).*/,
/react\-navigation\/node_modules\/react-native\/(.*)/,
/react\-navigation\/node_modules\/react\/(.*)/
]);
},
extraNodeModules: getNodeModulesForDirectory(path.resolve('.')),
};
function getNodeModulesForDirectory(rootPath) {
const nodeModulePath = path.join(rootPath, 'node_modules');
const folders = fs.readdirSync(nodeModulePath);
return folders.reduce((modules, folderName) => {
const folderPath = path.join(nodeModulePath, folderName);
if (folderName.startsWith('@')) {
const scopedModuleFolders = fs.readdirSync(folderPath);
const scopedModules = scopedModuleFolders.reduce(
(scopedModules, scopedFolderName) => {
scopedModules[
`${folderName}/${scopedFolderName}`
] = maybeResolveSymlink(path.join(folderPath, scopedFolderName));
return scopedModules;
},
{}
);
return Object.assign({}, modules, scopedModules);
}
modules[folderName] = maybeResolveSymlink(folderPath);
return modules;
}, {});
}
function maybeResolveSymlink(maybeSymlinkPath) {
if (fs.lstatSync(maybeSymlinkPath).isSymbolicLink()) {
const resolved = path.resolve(
path.dirname(maybeSymlinkPath),
fs.readlinkSync(maybeSymlinkPath)
);
return resolved;
}
return maybeSymlinkPath;
}

View File

@@ -1,30 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Button } from 'react-native';
import { NavigationActions } from 'react-navigation';
const AuthButton = ({ logout, loginScreen, isLoggedIn }) => (
<Button
title={isLoggedIn ? 'Log Out' : 'Open Login Screen'}
onPress={isLoggedIn ? logout : loginScreen}
/>
);
AuthButton.propTypes = {
isLoggedIn: PropTypes.bool.isRequired,
logout: PropTypes.func.isRequired,
loginScreen: PropTypes.func.isRequired,
};
const mapStateToProps = state => ({
isLoggedIn: state.auth.isLoggedIn,
});
const mapDispatchToProps = dispatch => ({
logout: () => dispatch({ type: 'Logout' }),
loginScreen: () =>
dispatch(NavigationActions.navigate({ routeName: 'Login' })),
});
export default connect(mapStateToProps, mapDispatchToProps)(AuthButton);

View File

@@ -1,42 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, StyleSheet, Text, View } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
const LoginScreen = ({ navigation }) => (
<View style={styles.container}>
<Text style={styles.welcome}>
Screen A
</Text>
<Text style={styles.instructions}>
This is great
</Text>
<Button
onPress={() => navigation.dispatch({ type: 'Login' })}
title="Log in"
/>
</View>
);
LoginScreen.propTypes = {
navigation: PropTypes.object.isRequired,
};
LoginScreen.navigationOptions = {
title: 'Log In',
};
export default LoginScreen;

View File

@@ -1,42 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Button, StyleSheet, Text, View } from 'react-native';
import { NavigationActions } from 'react-navigation';
const styles = StyleSheet.create({
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
const LoginStatusMessage = ({ isLoggedIn, dispatch }) => {
if (!isLoggedIn) {
return <Text>Please log in</Text>;
}
return (
<View>
<Text style={styles.welcome}>
{'You are "logged in" right now'}
</Text>
<Button
onPress={() =>
dispatch(NavigationActions.navigate({ routeName: 'Profile' }))}
title="Profile"
/>
</View>
);
};
LoginStatusMessage.propTypes = {
isLoggedIn: PropTypes.bool.isRequired,
dispatch: PropTypes.func.isRequired,
};
const mapStateToProps = state => ({
isLoggedIn: state.auth.isLoggedIn,
});
export default connect(mapStateToProps)(LoginStatusMessage);

View File

@@ -1,27 +0,0 @@
import React from 'react';
import { StyleSheet, View } from 'react-native';
import LoginStatusMessage from './LoginStatusMessage';
import AuthButton from './AuthButton';
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
const MainScreen = () => (
<View style={styles.container}>
<LoginStatusMessage />
<AuthButton />
</View>
);
MainScreen.navigationOptions = {
title: 'Home Screen',
};
export default MainScreen;

View File

@@ -1,30 +0,0 @@
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
const ProfileScreen = () => (
<View style={styles.container}>
<Text style={styles.welcome}>
Profile Screen
</Text>
</View>
);
ProfileScreen.navigationOptions = {
title: 'Profile',
};
export default ProfileScreen;

View File

@@ -1,41 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { StackNavigator } from 'react-navigation';
import LoginScreen from '../components/LoginScreen';
import MainScreen from '../components/MainScreen';
import ProfileScreen from '../components/ProfileScreen';
import { addListener } from '../utils/redux';
export const AppNavigator = StackNavigator({
Login: { screen: LoginScreen },
Main: { screen: MainScreen },
Profile: { screen: ProfileScreen },
});
class AppWithNavigationState extends React.Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
nav: PropTypes.object.isRequired,
};
render() {
const { dispatch, nav } = this.props;
return (
<AppNavigator
navigation={{
dispatch,
state: nav,
addListener,
}}
/>
);
}
}
const mapStateToProps = state => ({
nav: state.nav,
});
export default connect(mapStateToProps)(AppWithNavigationState);

View File

@@ -1,57 +0,0 @@
import { combineReducers } from 'redux';
import { NavigationActions } from 'react-navigation';
import { AppNavigator } from '../navigators/AppNavigator';
// Start with two routes: The Main screen, with the Login screen on top.
const firstAction = AppNavigator.router.getActionForPathAndParams('Main');
const tempNavState = AppNavigator.router.getStateForAction(firstAction);
const secondAction = AppNavigator.router.getActionForPathAndParams('Login');
const initialNavState = AppNavigator.router.getStateForAction(
secondAction,
tempNavState
);
function nav(state = initialNavState, action) {
let nextState;
switch (action.type) {
case 'Login':
nextState = AppNavigator.router.getStateForAction(
NavigationActions.back(),
state
);
break;
case 'Logout':
nextState = AppNavigator.router.getStateForAction(
NavigationActions.navigate({ routeName: 'Login' }),
state
);
break;
default:
nextState = AppNavigator.router.getStateForAction(action, state);
break;
}
// Simply return the original `state` if `nextState` is null or undefined.
return nextState || state;
}
const initialAuthState = { isLoggedIn: false };
function auth(state = initialAuthState, action) {
switch (action.type) {
case 'Login':
return { ...state, isLoggedIn: true };
case 'Logout':
return { ...state, isLoggedIn: false };
default:
return state;
}
}
const AppReducer = combineReducers({
nav,
auth,
});
export default AppReducer;

View File

@@ -1,15 +0,0 @@
import {
createReactNavigationReduxMiddleware,
createReduxBoundAddListener,
} from 'react-navigation-redux-helpers';
const middleware = createReactNavigationReduxMiddleware(
"root",
state => state.nav,
);
const addListener = createReduxBoundAddListener("root");
export {
middleware,
addListener,
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +0,0 @@
{
"presets": ["babel-preset-expo"],
"env": {
"development": {
"plugins": ["transform-react-jsx-source"]
}
}
}

View File

@@ -1,3 +0,0 @@
node_modules/**/*
.expo/*
npm-debug.*

View File

@@ -1 +0,0 @@
{}

View File

@@ -1,244 +0,0 @@
import React from 'react';
import { ScrollView, StyleSheet, Text, View } from 'react-native';
import { StackNavigator, withNavigation } from 'react-navigation';
import { Constants } from 'expo';
import Touchable from 'react-native-platform-touchable';
import TabsScreen from './screens/TabsScreen';
import DrawerScreen from './screens/DrawerScreen';
import createDumbStack from './screens/createDumbStack';
import createDumbTabs from './screens/createDumbTabs';
export default class App extends React.Component {
render() {
return <RootStack />;
}
}
@withNavigation
class ExampleItem extends React.Component {
render() {
return (
<View
style={{
borderBottomColor: '#eee',
borderBottomWidth: 1,
}}>
<Touchable
onPress={this._handlePress}
style={{
height: 50,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: this.props.common ? '#fffcd3' : '#fff',
}}>
<Text style={{ fontSize: 15 }}>
{this.props.title} {this.props.common ? '(commonly used)' : null}
</Text>
</Touchable>
</View>
);
}
_handlePress = () => {
this.props.navigation.navigate(this.props.route);
};
}
class ExampleListScreen extends React.Component {
render() {
return (
<View style={{ flex: 1 }}>
<ScrollView
style={{ flex: 1 }}
contentContainerStyle={{ paddingTop: 50, backgroundColor: '#fff' }}>
<Text
style={{
fontSize: 25,
textAlign: 'center',
marginBottom: 20,
paddingBottom: 20,
}}>
SafeAreaView Examples
</Text>
<ExampleItem title="Basic Tabs" route="tabs" common />
{/* <ExampleItem title="Basic Drawer" route="drawer" /> */}
<ExampleItem title="Header height" route="headerHeight" common />
<ExampleItem title="Header padding" route="headerPadding" />
<ExampleItem
title="Header height and padding"
route="headerHeightAndPadding"
/>
<ExampleItem
title="Header padding as percent"
route="headerPaddingPercent"
/>
<ExampleItem title="Header with margin" route="headerMargin" />
<ExampleItem title="Tab bar height" route="tabBarHeight" common />
<ExampleItem title="Tab bar padding" route="tabBarPadding" common />
<ExampleItem
common
title="Tab bar height and padding"
route="tabBarHeightAndPadding"
/>
<ExampleItem title="Tab bar margin" route="tabBarMargin" />
</ScrollView>
<View
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: Constants.statusBarHeight,
backgroundColor: '#fff',
}}
/>
</View>
);
}
}
const StackWithHeaderHeight = createDumbStack({
title: 'height: 150',
headerStyle: { height: 150 },
});
const StackWithHeaderPadding = createDumbStack({
title: 'padding: 100',
headerStyle: { padding: 100 },
});
const StackWithHeaderHeightAndPadding = createDumbStack({
title: 'height: 150, padding: 100',
headerStyle: { height: 150, padding: 100 },
});
const StackWithHeaderPaddingPercent = createDumbStack({
title: 'padding: 60%',
headerStyle: { padding: '60%' },
});
const StackWithHeaderMargin = createDumbStack({
title: 'margin: 20 (but why? :/)',
headerStyle: { margin: 20 },
});
const TabBarWithHeight = createDumbTabs(
{
tabBarLabel: 'label!',
tabBarOptions: {
style: {
height: 100,
},
},
},
createDumbStack({
title: 'tabBar height 100',
})
);
const TabBarWithPadding = createDumbTabs(
{
tabBarLabel: 'label!',
tabBarOptions: {
style: {
padding: 20,
},
},
},
createDumbStack({
title: 'tabBar padding 20',
})
);
const TabBarWithHeightAndPadding = createDumbTabs(
{
tabBarLabel: 'label!',
tabBarOptions: {
style: {
padding: 20,
height: 100,
},
},
},
createDumbStack({
title: 'tabBar height 100 padding 20',
})
);
const TabBarWithMargin = createDumbTabs(
{
tabBarLabel: 'label!',
tabBarOptions: {
style: {
margin: 20,
},
},
},
createDumbStack({
title: 'tabBar margin 20',
})
);
const RootStack = StackNavigator(
{
exampleList: {
screen: ExampleListScreen,
},
tabs: {
screen: TabsScreen,
},
headerHeight: {
screen: StackWithHeaderHeight,
},
headerPadding: {
screen: StackWithHeaderPadding,
},
headerHeightAndPadding: {
screen: StackWithHeaderHeightAndPadding,
},
headerPaddingPercent: {
screen: StackWithHeaderPaddingPercent,
},
headerMargin: {
screen: StackWithHeaderMargin,
},
tabBarHeight: {
screen: TabBarWithHeight,
},
tabBarPadding: {
screen: TabBarWithPadding,
},
tabBarHeightAndPadding: {
screen: TabBarWithHeightAndPadding,
},
tabBarMargin: {
screen: TabBarWithMargin,
},
},
{
headerMode: 'none',
cardStyle: {
backgroundColor: '#fff',
},
}
);
// basic tabs (different navbar color, different tabbar color)
// different header height
// different header padding
// different header height and padding
// different header margin
// different tabbar height
// different tabbar padding
// different tabbar height and padding
// different tabbar margin
// without navbar, without safeareaview in one tab and with safeareaview in another tab
// all should be able to toggle between landscape and portrait
// basic drawer (different navbar color, mess around with drawer options)
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});

View File

@@ -1,23 +0,0 @@
{
"expo": {
"name": "SafeAreaExample",
"description": "An empty new project",
"slug": "SafeAreaExample",
"privacy": "public",
"sdkVersion": "25.0.0",
"version": "1.0.0",
"primaryColor": "#cccccc",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"packagerOpts": {
"assetExts": ["ttf", "mp4"]
},
"ios": {
"supportsTablet": true
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -1,15 +0,0 @@
{
"main": "node_modules/expo/AppEntry.js",
"private": true,
"dependencies": {
"expo": "^25.0.0",
"react": "16.2.0",
"react-native": "^0.52.0",
"react-native-platform-touchable": "^1.1.1",
"react-navigation": "link:../.."
},
"name": "SafeAreaExample",
"version": "0.0.0",
"description": "Hello Expo!",
"author": null
}

View File

@@ -1,52 +0,0 @@
/**
* @noflow
*/
const fs = require('fs');
const path = require('path');
const blacklist = require('metro/src/blacklist');
module.exports = {
getBlacklistRE() {
return blacklist([
/react\-navigation\/examples\/(?!SafeAreaExample).*/,
/react\-navigation\/node_modules\/react-native\/(.*)/,
/react\-navigation\/node_modules\/react\/(.*)/
]);
},
extraNodeModules: getNodeModulesForDirectory(path.resolve('.')),
};
function getNodeModulesForDirectory(rootPath) {
const nodeModulePath = path.join(rootPath, 'node_modules');
const folders = fs.readdirSync(nodeModulePath);
return folders.reduce((modules, folderName) => {
const folderPath = path.join(nodeModulePath, folderName);
if (folderName.startsWith('@')) {
const scopedModuleFolders = fs.readdirSync(folderPath);
const scopedModules = scopedModuleFolders.reduce(
(scopedModules, scopedFolderName) => {
scopedModules[
`${folderName}/${scopedFolderName}`
] = maybeResolveSymlink(path.join(folderPath, scopedFolderName));
return scopedModules;
},
{}
);
return Object.assign({}, modules, scopedModules);
}
modules[folderName] = maybeResolveSymlink(folderPath);
return modules;
}, {});
}
function maybeResolveSymlink(maybeSymlinkPath) {
if (fs.lstatSync(maybeSymlinkPath).isSymbolicLink()) {
const resolved = path.resolve(
path.dirname(maybeSymlinkPath),
fs.readlinkSync(maybeSymlinkPath)
);
return resolved;
}
return maybeSymlinkPath;
}

View File

@@ -1,3 +0,0 @@
import createDumbStack from './createDumbStack';
export default createDumbStack();

View File

@@ -1,3 +0,0 @@
import createDumbTabs from './createDumbTabs';
export default createDumbTabs();

View File

@@ -1,99 +0,0 @@
import React from 'react';
import { StackNavigator } from 'react-navigation';
import {
Dimensions,
Button,
Platform,
ScrollView,
Text,
View,
StatusBar,
} from 'react-native';
import { ScreenOrientation } from 'expo';
const Separator = () => (
<View
style={{
width: Dimensions.get('window').width - 100,
height: 1,
backgroundColor: '#ccc',
marginHorizontal: 50,
marginTop: 15,
marginBottom: 15,
}}
/>
);
const Spacer = () => (
<View
style={{
marginBottom: Platform.OS === 'android' ? 20 : 5,
}}
/>
);
export default (navigationOptions = {}) => {
class DumbScreen extends React.Component {
static navigationOptions = {
title: 'Title!',
...navigationOptions,
headerStyle: {
backgroundColor: '#6b52ae',
...navigationOptions.headerStyle,
},
headerTitleStyle: {
color: '#fff',
...navigationOptions.headerTitleStyle,
},
};
render() {
return (
<ScrollView style={{ flex: 1 }}>
<View
style={{
paddingTop: 30,
alignItems: 'center',
justifyContent: 'center',
}}>
<Button onPress={this._goBack} title="Go back" />
<Separator />
<Button onPress={this._setPortrait} title="Set portrait" />
<Spacer />
<Button onPress={this._setLandscape} title="Set landscape" />
<Separator />
<Button onPress={this._hideStatusBar} title="Hide status bar" />
<Spacer />
<Button onPress={this._showStatusBar} title="Show status bar" />
</View>
</ScrollView>
);
}
_goBack = () => {
this.props.navigation.goBack(null);
};
_setPortrait = () => {
ScreenOrientation.allow(ScreenOrientation.Orientation.PORTRAIT);
};
_setLandscape = () => {
ScreenOrientation.allow(ScreenOrientation.Orientation.LANDSCAPE);
};
_hideStatusBar = () => {
StatusBar.setHidden(true, 'slide');
};
_showStatusBar = () => {
StatusBar.setHidden(false, 'slide');
};
}
return StackNavigator({
dumb: {
screen: DumbScreen,
},
});
};

View File

@@ -1,69 +0,0 @@
import React from 'react';
import { Platform, View } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { TabNavigator, TabBarBottom } from 'react-navigation';
import createDumbStack from './createDumbStack';
export default (navigationOptions = {}, DumbStack = createDumbStack()) => {
return TabNavigator(
{
Home: {
screen: DumbStack,
},
Links: {
screen: DumbStack,
},
Settings: {
screen: DumbStack,
},
},
{
navigationOptions: ({ navigation }) => ({
...navigationOptions,
tabBarIcon: ({ focused }) => {
const { routeName } = navigation.state;
let iconName;
switch (routeName) {
case 'Home':
iconName =
Platform.OS === 'ios'
? `ios-information-circle${focused ? '' : '-outline'}`
: 'md-information-circle';
break;
case 'Links':
iconName =
Platform.OS === 'ios'
? `ios-link${focused ? '' : '-outline'}`
: 'md-link';
break;
case 'Settings':
iconName =
Platform.OS === 'ios'
? `ios-options${focused ? '' : '-outline'}`
: 'md-options';
}
return (
<Ionicons
name={iconName}
size={28}
style={{ marginBottom: -3 }}
color={focused ? '#6b52ae' : '#ccc'}
/>
);
},
}),
tabBarOptions: {
activeTintColor: '#6b52ae',
...navigationOptions.tabBarOptions,
style: {
backgroundColor: '#F5F1FF',
...(navigationOptions.tabBarOptions ? navigationOptions.tabBarOptions.style : {}),
}
},
tabBarComponent: TabBarBottom,
tabBarPosition: 'bottom',
animationEnabled: false,
swipeEnabled: false,
}
);
};

File diff suppressed because it is too large Load Diff

View File

@@ -69,6 +69,14 @@ declare module 'react-navigation' {
[key: string]: mixed,
};
declare export type NavigationBackAction = {|
type: 'Navigation/BACK',
key?: ?string,
|};
declare export type NavigationInitAction = {|
type: 'Navigation/INIT',
params?: NavigationParams,
|};
declare export type NavigationNavigateAction = {|
type: 'Navigation/NAVIGATE',
routeName: string,
@@ -79,12 +87,6 @@ declare module 'react-navigation' {
key?: string,
|};
declare export type NavigationBackAction = {|
type: 'Navigation/BACK',
key?: ?string,
|};
declare export type NavigationSetParamsAction = {|
type: 'Navigation/SET_PARAMS',
@@ -95,30 +97,6 @@ declare module 'react-navigation' {
params: NavigationParams,
|};
declare export type NavigationInitAction = {|
type: 'Navigation/INIT',
params?: NavigationParams,
|};
declare export type NavigationResetAction = {|
type: 'Navigation/RESET',
index: number,
key?: ?string,
actions: Array<NavigationNavigateAction>,
|};
declare export type NavigationUriAction = {|
type: 'Navigation/URI',
uri: string,
|};
declare export type NavigationReplaceAction = {|
+type: 'Navigation/REPLACE',
+key: string,
+routeName: string,
+params?: NavigationParams,
+action?: NavigationNavigateAction,
|};
declare export type NavigationPopAction = {|
+type: 'Navigation/POP',
+n?: number,
@@ -135,17 +113,61 @@ declare module 'react-navigation' {
+action?: NavigationNavigateAction,
+key?: string,
|};
declare export type NavigationResetAction = {|
type: 'Navigation/RESET',
index: number,
key?: ?string,
actions: Array<NavigationNavigateAction>,
|};
declare export type NavigationReplaceAction = {|
+type: 'Navigation/REPLACE',
+key: string,
+routeName: string,
+params?: NavigationParams,
+action?: NavigationNavigateAction,
|};
declare export type NavigationCompleteTransitionAction = {|
+type: 'Navigation/COMPLETE_TRANSITION',
+key?: string,
|};
declare export type NavigationOpenDrawerAction = {|
+type: 'Navigation/OPEN_DRAWER',
+key?: string,
|};
declare export type NavigationCloseDrawerAction = {|
+type: 'Navigation/CLOSE_DRAWER',
+key?: string,
|};
declare export type NavigationToggleDrawerAction = {|
+type: 'Navigation/TOGGLE_DRAWER',
+key?: string,
|};
declare export type NavigationDrawerOpenedAction = {|
+type: 'Navigation/DRAWER_OPENED',
+key?: string,
|};
declare export type NavigationDrawerClosedAction = {|
+type: 'Navigation/DRAWER_CLOSED',
+key?: string,
|};
declare export type NavigationAction =
| NavigationBackAction
| NavigationInitAction
| NavigationNavigateAction
| NavigationReplaceAction
| NavigationSetParamsAction
| NavigationPopAction
| NavigationPopToTopAction
| NavigationPushAction
| NavigationBackAction
| NavigationSetParamsAction
| NavigationResetAction;
| NavigationResetAction
| NavigationReplaceAction
| NavigationCompleteTransitionAction
| NavigationOpenDrawerAction
| NavigationCloseDrawerAction
| NavigationToggleDrawerAction
| NavigationDrawerOpenedAction
| NavigationDrawerClosedAction;
/**
* NavigationState is a tree of routes for a single navigator, where each
@@ -172,7 +194,7 @@ declare module 'react-navigation' {
| NavigationLeafRoute
| NavigationStateRoute;
declare export type NavigationLeafRoute = {
declare export type NavigationLeafRoute = {|
/**
* React's key used by some navigators. No need to specify these manually,
* they will be defined by the router.
@@ -192,10 +214,12 @@ declare module 'react-navigation' {
* e.g. `{ car_id: 123 }` in a route that displays a car.
*/
params?: NavigationParams,
};
|};
declare export type NavigationStateRoute = NavigationLeafRoute &
NavigationState;
declare export type NavigationStateRoute = {|
...NavigationLeafRoute,
...$Exact<NavigationState>,
|};
/**
* Router
@@ -269,24 +293,36 @@ declare module 'react-navigation' {
declare export type NavigationComponent =
| NavigationScreenComponent<NavigationRoute, *, *>
| NavigationContainer<*, *, *>
| any;
| NavigationNavigator<*, *, *>;
declare interface withOptionalNavigationOptions<Options> {
navigationOptions?: NavigationScreenConfig<Options>;
}
declare export type NavigationScreenComponent<
Route: NavigationRoute,
Options: {},
Props: {}
> = React$ComponentType<NavigationNavigatorProps<Options, Route> & Props> &
({} | { navigationOptions: NavigationScreenConfig<Options> });
> = React$ComponentType<{
...Props,
...NavigationNavigatorProps<Options, Route>,
}> &
withOptionalNavigationOptions<Options>;
declare interface withRouter<State, Options> {
router: NavigationRouter<State, Options>;
}
declare export type NavigationNavigator<
State: NavigationState,
Options: {},
Props: {}
> = React$ComponentType<NavigationNavigatorProps<Options, State> & Props> & {
router: NavigationRouter<State, Options>,
navigationOptions?: ?NavigationScreenConfig<Options>,
};
> = React$StatelessFunctionalComponent<{
...Props,
...NavigationNavigatorProps<Options, State>,
}> &
withRouter<State, Options> &
withOptionalNavigationOptions<Options>;
declare export type NavigationRouteConfig =
| NavigationComponent
@@ -360,7 +396,7 @@ declare module 'react-navigation' {
initialRouteName?: string,
initialRouteParams?: NavigationParams,
paths?: NavigationPathsConfig,
navigationOptions?: NavigationScreenConfig<*>,
defaultNavigationOptions?: NavigationScreenConfig<*>,
initialRouteKey?: string,
|};
@@ -368,10 +404,18 @@ declare module 'react-navigation' {
mode?: 'card' | 'modal',
headerMode?: HeaderMode,
headerTransitionPreset?: 'fade-in-place' | 'uikit',
headerLayoutPreset?: 'left' | 'center',
headerBackTitleVisible?: boolean,
cardStyle?: ViewStyleProp,
transitionConfig?: () => TransitionConfig,
transitionConfig?: (
transitionProps: NavigationTransitionProps,
prevTransitionProps: ?NavigationTransitionProps,
isModal: boolean
) => TransitionConfig,
onTransitionStart?: () => void,
onTransitionEnd?: () => void,
transparentCard?: boolean,
disableKeyboardHandling?: boolean,
|};
declare export type StackNavigatorConfig = {|
@@ -387,7 +431,7 @@ declare module 'react-navigation' {
initialRouteName?: string,
initialRouteParams?: NavigationParams,
paths?: NavigationPathsConfig,
navigationOptions?: NavigationScreenConfig<*>,
defaultNavigationOptions?: NavigationScreenConfig<*>,
order?: Array<string>,
backBehavior?: 'none' | 'initialRoute', // defaults to `'none'`
resetOnBlur?: boolean, // defaults to `true`
@@ -401,7 +445,7 @@ declare module 'react-navigation' {
initialRouteName?: string,
initialRouteParams?: NavigationParams,
paths?: NavigationPathsConfig,
navigationOptions?: NavigationScreenConfig<*>,
defaultNavigationOptions?: NavigationScreenConfig<*>,
// todo: type these as the real route names rather than 'string'
order?: Array<string>,
// Does the back button cause the router to switch to the initial tab
@@ -426,10 +470,10 @@ declare module 'react-navigation' {
| ((options: { tintColor: ?string, focused: boolean }) => ?React$Node),
tabBarVisible?: boolean,
tabBarTestIDProps?: { testID?: string, accessibilityLabel?: string },
tabBarOnPress?: (
scene: TabScene,
jumpToIndex: (index: number) => void
) => void,
tabBarOnPress?: ({
navigation: NavigationScreenProp<NavigationRoute>,
defaultHandler: () => void,
}) => void,
|};
/**
@@ -485,8 +529,36 @@ declare module 'react-navigation' {
declare export type NavigationScreenProp<+S> = {
+state: S,
dispatch: NavigationDispatch,
addListener: (
eventName: string,
callback: NavigationEventCallback
) => NavigationEventSubscription,
getParam: <ParamName: string>(
paramName: ParamName,
fallback?: $ElementType<
$PropertyType<
{|
...{| params: {| [ParamName]: void |} |},
...$Exact<S>,
|},
'params'
>,
ParamName
>
) => $ElementType<
$PropertyType<
{|
...{| params: {| [ParamName]: void |} |},
...$Exact<S>,
|},
'params'
>,
ParamName
>,
dangerouslyGetParent: () => NavigationScreenProp<*>,
isFocused: () => boolean,
// Shared action creators that exist for all routers
goBack: (routeKey?: ?string) => boolean,
dismiss: () => boolean,
navigate: (
routeName:
| string
@@ -500,24 +572,25 @@ declare module 'react-navigation' {
action?: NavigationNavigateAction
) => boolean,
setParams: (newParams: NavigationParams) => boolean,
getParam: (paramName: string, fallback?: any) => any,
addListener: (
eventName: string,
callback: NavigationEventCallback
) => NavigationEventSubscription,
push: (
// StackRouter action creators
pop?: (n?: number, params?: { immediate?: boolean }) => boolean,
popToTop?: (params?: { immediate?: boolean }) => boolean,
push?: (
routeName: string,
params?: NavigationParams,
action?: NavigationNavigateAction
) => boolean,
replace: (
replace?: (
routeName: string,
params?: NavigationParams,
action?: NavigationNavigateAction
) => boolean,
pop: (n?: number, params?: { immediate?: boolean }) => boolean,
popToTop: (params?: { immediate?: boolean }) => boolean,
isFocused: () => boolean,
reset?: (actions: NavigationAction[], index: number) => boolean,
dismiss?: () => boolean,
// DrawerRouter action creators
openDrawer?: () => boolean,
closeDrawer?: () => boolean,
toggleDrawer?: () => boolean,
};
declare export type NavigationNavigatorProps<O: {}, S: {}> = $Shape<{
@@ -526,6 +599,21 @@ declare module 'react-navigation' {
navigationOptions?: O,
}>;
/**
* NavigationEvents component
*/
declare type _NavigationEventsProps = {
navigation?: NavigationScreenProp<NavigationState>,
onWillFocus?: NavigationEventCallback,
onDidFocus?: NavigationEventCallback,
onWillBlur?: NavigationEventCallback,
onDidBlur?: NavigationEventCallback,
};
declare export var NavigationEvents: React$ComponentType<
_NavigationEventsProps
>;
/**
* Navigation container
*/
@@ -534,10 +622,12 @@ declare module 'react-navigation' {
State: NavigationState,
Options: {},
Props: {}
> = React$ComponentType<NavigationContainerProps<State, Options> & Props> & {
router: NavigationRouter<State, Options>,
navigationOptions?: ?NavigationScreenConfig<Options>,
};
> = React$ComponentType<{
...Props,
...NavigationContainerProps<State, Options>,
}> &
withRouter<State, Options> &
withOptionalNavigationOptions<Options>;
declare export type NavigationContainerProps<S: {}, O: {}> = $Shape<{
uriPrefix?: string | RegExp,
@@ -671,6 +761,9 @@ declare module 'react-navigation' {
* Now we type the actual exported module
*/
declare export function createAppContainer<S: NavigationState, O: {}>(
Component: NavigationNavigator<S, O, *>
): NavigationContainer<S, O, *>;
declare export function createNavigationContainer<S: NavigationState, O: {}>(
Component: NavigationNavigator<S, O, *>
): NavigationContainer<S, O, *>;
@@ -706,44 +799,75 @@ declare module 'react-navigation' {
BACK: 'Navigation/BACK',
INIT: 'Navigation/INIT',
NAVIGATE: 'Navigation/NAVIGATE',
RESET: 'Navigation/RESET',
SET_PARAMS: 'Navigation/SET_PARAMS',
URI: 'Navigation/URI',
back: {
(payload?: { key?: ?string }): NavigationBackAction,
toString: () => string,
},
init: {
(payload?: { params?: NavigationParams }): NavigationInitAction,
toString: () => string,
},
navigate: {
(payload: {
routeName: string,
params?: ?NavigationParams,
action?: ?NavigationNavigateAction,
}): NavigationNavigateAction,
toString: () => string,
},
reset: {
(payload: {
index: number,
key?: ?string,
actions: Array<NavigationNavigateAction>,
}): NavigationResetAction,
toString: () => string,
},
setParams: {
(payload: {
key: string,
params: NavigationParams,
}): NavigationSetParamsAction,
toString: () => string,
},
uri: {
(payload: { uri: string }): NavigationUriAction,
toString: () => string,
},
back: (payload?: { key?: ?string }) => NavigationBackAction,
init: (payload?: { params?: NavigationParams }) => NavigationInitAction,
navigate: (payload: {
routeName: string,
params?: ?NavigationParams,
action?: ?NavigationNavigateAction,
key?: string,
}) => NavigationNavigateAction,
setParams: (payload: {
key: string,
params: NavigationParams,
}) => NavigationSetParamsAction,
};
declare export var StackActions: {
POP: 'Navigation/POP',
POP_TO_TOP: 'Navigation/POP_TO_TOP',
PUSH: 'Navigation/PUSH',
RESET: 'Navigation/RESET',
REPLACE: 'Navigation/REPLACE',
COMPLETE_TRANSITION: 'Navigation/COMPLETE_TRANSITION',
pop: (payload: {
n?: number,
immediate?: boolean,
}) => NavigationPopAction,
popToTop: (payload: {
immediate?: boolean,
}) => NavigationPopToTopAction,
push: (payload: {
routeName: string,
params?: NavigationParams,
action?: NavigationNavigateAction,
key?: string,
}) => NavigationPushAction,
reset: (payload: {
index: number,
key?: ?string,
actions: Array<NavigationNavigateAction>,
}) => NavigationResetAction,
replace: (payload: {
key?: string,
routeName: string,
params?: NavigationParams,
action?: NavigationNavigateAction,
}) => NavigationReplaceAction,
completeTransition: (payload: {
key?: string,
}) => NavigationCompleteTransitionAction,
};
declare export var DrawerActions: {
OPEN_DRAWER: 'Navigation/OPEN_DRAWER',
CLOSE_DRAWER: 'Navigation/CLOSE_DRAWER',
TOGGLE_DRAWER: 'Navigation/TOGGLE_DRAWER',
DRAWER_OPENED: 'Navigation/DRAWER_OPENED',
DRAWER_CLOSED: 'Navigation/DRAWER_CLOSED',
openDrawer: (payload: {
key?: string,
}) => NavigationOpenDrawerAction,
closeDrawer: (payload: {
key?: string,
}) => NavigationCloseDrawerAction,
toggleDrawer: (payload: {
key?: string,
}) => NavigationToggleDrawerAction,
};
declare type _RouterProp<S: NavigationState, O: {}> = {
@@ -766,16 +890,16 @@ declare module 'react-navigation' {
view: NavigationView<O, S>,
router: NavigationRouter<S, O>,
navigatorConfig?: NavigatorConfig
): any;
): NavigationNavigator<S, O, *>;
declare export function StackNavigator(
routeConfigMap: NavigationRouteConfigMap,
stackConfig?: StackNavigatorConfig
): NavigationContainer<*, *, *>;
): NavigationNavigator<*, *, *>;
declare export function createStackNavigator(
routeConfigMap: NavigationRouteConfigMap,
stackConfig?: StackNavigatorConfig
): NavigationContainer<*, *, *>;
): NavigationNavigator<*, *, *>;
declare type _TabViewConfig = {|
tabBarComponent?: React$ElementType,
@@ -799,31 +923,31 @@ declare module 'react-navigation' {
declare export function TabNavigator(
routeConfigs: NavigationRouteConfigMap,
config?: _TabNavigatorConfig
): NavigationContainer<*, *, *>;
): NavigationNavigator<*, *, *>;
declare export function createTabNavigator(
routeConfigs: NavigationRouteConfigMap,
config?: _TabNavigatorConfig
): NavigationContainer<*, *, *>;
): NavigationNavigator<*, *, *>;
/* TODO: fix the config for each of these tab navigator types */
declare export function createBottomTabNavigator(
routeConfigs: NavigationRouteConfigMap,
config?: _TabNavigatorConfig
): NavigationContainer<*, *, *>;
): NavigationNavigator<*, *, *>;
declare export function createMaterialTopTabNavigator(
routeConfigs: NavigationRouteConfigMap,
config?: _TabNavigatorConfig
): NavigationContainer<*, *, *>;
): NavigationNavigator<*, *, *>;
declare type _SwitchNavigatorConfig = {|
...NavigationSwitchRouterConfig,
|};
declare export function SwitchNavigator(
routeConfigs: NavigationRouteConfigMap,
config?: _SwitchNavigatorConfig
): NavigationContainer<*, *, *>;
): NavigationNavigator<*, *, *>;
declare export function createSwitchNavigator(
routeConfigs: NavigationRouteConfigMap,
config?: _SwitchNavigatorConfig
): NavigationContainer<*, *, *>;
): NavigationNavigator<*, *, *>;
declare type _DrawerViewConfig = {|
drawerLockMode?: 'unlocked' | 'locked-closed' | 'locked-open',
@@ -844,11 +968,11 @@ declare module 'react-navigation' {
declare export function DrawerNavigator(
routeConfigs: NavigationRouteConfigMap,
config?: _DrawerNavigatorConfig
): NavigationContainer<*, *, *>;
): NavigationNavigator<*, *, *>;
declare export function createDrawerNavigator(
routeConfigs: NavigationRouteConfigMap,
config?: _DrawerNavigatorConfig
): NavigationContainer<*, *, *>;
): NavigationNavigator<*, *, *>;
declare export function StackRouter(
routeConfigs: NavigationRouteConfigMap,
@@ -1097,4 +1221,13 @@ declare module 'react-navigation' {
declare export function withNavigationFocus<Props: {}>(
Component: React$ComponentType<Props>
): React$ComponentType<$Diff<Props, { isFocused: boolean | void }>>;
declare export function getNavigation<State: NavigationState, Options: {}>(
router: NavigationRouter<State, Options>,
state: State,
dispatch: NavigationDispatch,
actionSubscribers: Set<NavigationEventCallback>,
getScreenProps: () => {},
getCurrentNavigation: () => ?NavigationScreenProp<State>
): NavigationScreenProp<State>;
}

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation",
"version": "2.0.2",
"version": "3.0.7",
"description": "Routing and navigation for your React Native apps",
"main": "src/react-navigation.js",
"repository": {
@@ -19,26 +19,26 @@
"test-update-snapshot": "jest --updateSnapshot",
"lint": "eslint .",
"format": "eslint --fix .",
"precommit": "lint-staged"
"precommit": "lint-staged",
"release": "release-it"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/"
},
"files": [
"src"
"src",
"NavigationTestUtils.js"
],
"peerDependencies": {
"react": "*",
"react-native": "*"
},
"dependencies": {
"clamp": "^1.0.1",
"create-react-context": "^0.2.1",
"hoist-non-react-statics": "^2.2.0",
"path-to-regexp": "^1.7.0",
"prop-types": "^15.5.10",
"react-lifecycles-compat": "^3",
"react-native-drawer-layout-polyfill": "^1.3.2",
"react-native-safe-area-view": "^0.7.0",
"react-navigation-deprecated-tab-navigator": "1.2.0",
"react-navigation-tabs": "0.2.0"
"@react-navigation/core": "3.0.1",
"@react-navigation/native": "3.0.3",
"react-navigation-drawer": "1.0.5",
"react-navigation-stack": "1.0.5",
"react-navigation-tabs": "1.0.1"
},
"devDependencies": {
"babel-cli": "^6.24.1",
@@ -47,6 +47,7 @@
"babel-jest": "^22.4.1",
"babel-preset-react-native": "^2.1.0",
"codecov": "^2.2.0",
"conventional-changelog-cli": "^2.0.5",
"eslint": "^4.2.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.7.0",
@@ -62,7 +63,8 @@
"react": "16.2.0",
"react-native": "^0.52.0",
"react-native-vector-icons": "^4.2.0",
"react-test-renderer": "^16.0.0"
"react-test-renderer": "^16.0.0",
"release-it": "^7.6.1"
},
"jest": {
"notify": true,
@@ -89,7 +91,7 @@
"<rootDir>/examples/"
],
"transformIgnorePatterns": [
"node_modules/(?!(jest-)?react-native|react-clone-referenced-element|react-navigation-deprecated-tab-navigator)"
"node_modules/(?!(jest-)?react-native|react-clone-referenced-element|react-navigation-deprecated-tab-navigator|react-navigation-stack|@react-navigation/core|@react-navigation/native)"
]
},
"lint-staged": {

View File

@@ -4,6 +4,6 @@ set -eo pipefail
case $CIRCLE_NODE_INDEX in
0) yarn test && yarn codecov ;;
1) cd examples/NavigationPlayground && yarn && yarn test ;;
#1) cd examples/NavigationPlayground && yarn && yarn test ;;
#2) cd examples/ReduxExample && yarn && yarn test ;;
esac

View File

@@ -1,57 +0,0 @@
const BACK = 'Navigation/BACK';
const INIT = 'Navigation/INIT';
const NAVIGATE = 'Navigation/NAVIGATE';
const SET_PARAMS = 'Navigation/SET_PARAMS';
const back = (payload = {}) => ({
type: BACK,
key: payload.key,
immediate: payload.immediate,
});
const init = (payload = {}) => {
const action = {
type: INIT,
};
if (payload.params) {
action.params = payload.params;
}
return action;
};
const navigate = payload => {
const action = {
type: NAVIGATE,
routeName: payload.routeName,
};
if (payload.params) {
action.params = payload.params;
}
if (payload.action) {
action.action = payload.action;
}
if (payload.key) {
action.key = payload.key;
}
return action;
};
const setParams = payload => ({
type: SET_PARAMS,
key: payload.key,
params: payload.params,
});
export default {
// Action constants
BACK,
INIT,
NAVIGATE,
SET_PARAMS,
// Action creators
back,
init,
navigate,
setParams,
};

View File

@@ -1,9 +0,0 @@
import {
BackAndroid as DeprecatedBackAndroid,
BackHandler as ModernBackHandler,
MaskedViewIOS,
} from 'react-native';
const BackHandler = ModernBackHandler || DeprecatedBackAndroid;
export { BackHandler, MaskedViewIOS };

View File

@@ -1,6 +0,0 @@
import React from 'react';
import { BackHandler, View } from 'react-native';
const MaskedViewIOS = () => <View>{this.props.children}</View>;
export { BackHandler, MaskedViewIOS };

View File

@@ -1,199 +0,0 @@
import invariant from './utils/invariant';
/**
* Utilities to perform atomic operation with navigate state and routes.
*
* ```javascript
* const state1 = {key: 'screen 1'};
* const state2 = NavigationStateUtils.push(state1, {key: 'screen 2'});
* ```
*/
const StateUtils = {
/**
* Gets a route by key. If the route isn't found, returns `null`.
*/
get(state, key) {
return state.routes.find(route => route.key === key) || null;
},
/**
* Returns the first index at which a given route's key can be found in the
* routes of the navigation state, or -1 if it is not present.
*/
indexOf(state, key) {
return state.routes.findIndex(route => route.key === key);
},
/**
* Returns `true` at which a given route's key can be found in the
* routes of the navigation state.
*/
has(state, key) {
return !!state.routes.some(route => route.key === key);
},
/**
* Pushes a new route into the navigation state.
* Note that this moves the index to the positon to where the last route in the
* stack is at.
*/
push(state, route) {
invariant(
StateUtils.indexOf(state, route.key) === -1,
'should not push route with duplicated key %s',
route.key
);
const routes = state.routes.slice();
routes.push(route);
return {
...state,
index: routes.length - 1,
routes,
};
},
/**
* Pops out a route from the navigation state.
* Note that this moves the index to the positon to where the last route in the
* stack is at.
*/
pop(state) {
if (state.index <= 0) {
// [Note]: Over-popping does not throw error. Instead, it will be no-op.
return state;
}
const routes = state.routes.slice(0, -1);
return {
...state,
index: routes.length - 1,
routes,
};
},
/**
* Sets the focused route of the navigation state by index.
*/
jumpToIndex(state, index) {
if (index === state.index) {
return state;
}
invariant(!!state.routes[index], 'invalid index %s to jump to', index);
return {
...state,
index,
};
},
/**
* Sets the focused route of the navigation state by key.
*/
jumpTo(state, key) {
const index = StateUtils.indexOf(state, key);
return StateUtils.jumpToIndex(state, index);
},
/**
* Sets the focused route to the previous route.
*/
back(state) {
const index = state.index - 1;
const route = state.routes[index];
return route ? StateUtils.jumpToIndex(state, index) : state;
},
/**
* Sets the focused route to the next route.
*/
forward(state) {
const index = state.index + 1;
const route = state.routes[index];
return route ? StateUtils.jumpToIndex(state, index) : state;
},
/**
* Replace a route by a key.
* Note that this moves the index to the position to where the new route in the
* stack is at and updates the routes array accordingly.
*/
replaceAndPrune(state, key, route) {
const index = StateUtils.indexOf(state, key);
const replaced = StateUtils.replaceAtIndex(state, index, route);
return {
...replaced,
routes: replaced.routes.slice(0, index + 1),
};
},
/**
* Replace a route by a key.
* Note that this moves the index to the position to where the new route in the
* stack is at. Does not prune the routes.
*/
replaceAt(state, key, route) {
const index = StateUtils.indexOf(state, key);
return StateUtils.replaceAtIndex(state, index, route);
},
/**
* Replace a route by a index.
* Note that this moves the index to the positon to where the new route in the
* stack is at.
*/
replaceAtIndex(state, index, route) {
invariant(
!!state.routes[index],
'invalid index %s for replacing route %s',
index,
route.key
);
if (state.routes[index] === route && index === state.index) {
return state;
}
const routes = state.routes.slice();
routes[index] = route;
return {
...state,
index,
routes,
};
},
/**
* Resets all routes.
* Note that this moves the index to the position to where the last route in the
* stack is at if the param `index` isn't provided.
*/
reset(state, routes, index) {
invariant(
routes.length && Array.isArray(routes),
'invalid routes to replace'
);
const nextIndex = index === undefined ? routes.length - 1 : index;
if (state.routes.length === routes.length && state.index === nextIndex) {
const compare = (route, ii) => routes[ii] === route;
if (state.routes.every(compare)) {
return state;
}
}
invariant(!!routes[nextIndex], 'invalid index %s to reset', nextIndex);
return {
...state,
index: nextIndex,
routes,
};
},
};
export default StateUtils;

View File

@@ -1,57 +0,0 @@
import NavigationActions from '../NavigationActions';
describe('generic navigation actions', () => {
const params = { foo: 'bar' };
const navigateAction = NavigationActions.navigate({ routeName: 'another' });
it('exports back action and type', () => {
expect(NavigationActions.back()).toEqual({ type: NavigationActions.BACK });
expect(NavigationActions.back({ key: 'test' })).toEqual({
type: NavigationActions.BACK,
key: 'test',
});
});
it('exports init action and type', () => {
expect(NavigationActions.init()).toEqual({ type: NavigationActions.INIT });
expect(NavigationActions.init({ params })).toEqual({
type: NavigationActions.INIT,
params,
});
});
it('exports navigate action and type', () => {
expect(NavigationActions.navigate({ routeName: 'test' })).toEqual({
type: NavigationActions.NAVIGATE,
routeName: 'test',
});
expect(
NavigationActions.navigate({
routeName: 'test',
params,
action: navigateAction,
})
).toEqual({
type: NavigationActions.NAVIGATE,
routeName: 'test',
params,
action: {
type: NavigationActions.NAVIGATE,
routeName: 'another',
},
});
});
it('exports setParams action and type', () => {
expect(
NavigationActions.setParams({
key: 'test',
params,
})
).toEqual({
type: NavigationActions.SET_PARAMS,
key: 'test',
params,
});
});
});

View File

@@ -1,258 +0,0 @@
import React from 'react';
import { StyleSheet, View } from 'react-native';
import renderer from 'react-test-renderer';
import NavigationActions from '../NavigationActions';
import createStackNavigator from '../navigators/createStackNavigator';
import { _TESTING_ONLY_reset_container_count } from '../createNavigationContainer';
describe('NavigationContainer', () => {
jest.useFakeTimers();
beforeEach(() => {
_TESTING_ONLY_reset_container_count();
});
const FooScreen = () => <div />;
const BarScreen = () => <div />;
const BazScreen = () => <div />;
const CarScreen = () => <div />;
const DogScreen = () => <div />;
const ElkScreen = () => <div />;
const NavigationContainer = createStackNavigator(
{
foo: {
screen: FooScreen,
},
bar: {
screen: BarScreen,
},
baz: {
screen: BazScreen,
},
car: {
screen: CarScreen,
},
dog: {
screen: DogScreen,
},
elk: {
screen: ElkScreen,
},
},
{
initialRouteName: 'foo',
}
);
describe('state.nav', () => {
it("should be preloaded with the router's initial state", () => {
const navigationContainer = renderer
.create(<NavigationContainer />)
.getInstance();
expect(navigationContainer.state.nav).toMatchObject({ index: 0 });
expect(navigationContainer.state.nav.routes).toBeInstanceOf(Array);
expect(navigationContainer.state.nav.routes.length).toBe(1);
expect(navigationContainer.state.nav.routes[0]).toMatchObject({
routeName: 'foo',
});
});
});
describe('dispatch', () => {
it('returns true when given a valid action', () => {
const navigationContainer = renderer
.create(<NavigationContainer />)
.getInstance();
jest.runOnlyPendingTimers();
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'bar' })
)
).toEqual(true);
});
it('returns false when given an invalid action', () => {
const navigationContainer = renderer
.create(<NavigationContainer />)
.getInstance();
jest.runOnlyPendingTimers();
expect(navigationContainer.dispatch(NavigationActions.back())).toEqual(
false
);
});
it('updates state.nav with an action by the next tick', () => {
const navigationContainer = renderer
.create(<NavigationContainer />)
.getInstance();
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'bar' })
)
).toEqual(true);
// Fake the passing of a tick
jest.runOnlyPendingTimers();
expect(navigationContainer.state.nav).toMatchObject({
index: 1,
routes: [{ routeName: 'foo' }, { routeName: 'bar' }],
});
});
it('does not discard actions when called twice in one tick', () => {
const navigationContainer = renderer
.create(<NavigationContainer />)
.getInstance();
const initialState = JSON.parse(
JSON.stringify(navigationContainer.state.nav)
);
// First dispatch
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'bar' })
)
).toEqual(true);
// Make sure that the test runner has NOT synchronously applied setState before the tick
expect(navigationContainer.state.nav).toMatchObject(initialState);
// Second dispatch
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'baz' })
)
).toEqual(true);
// Fake the passing of a tick
jest.runOnlyPendingTimers();
expect(navigationContainer.state.nav).toMatchObject({
index: 2,
routes: [
{ routeName: 'foo' },
{ routeName: 'bar' },
{ routeName: 'baz' },
],
});
});
it('does not discard actions when called more than 2 times in one tick', () => {
const navigationContainer = renderer
.create(<NavigationContainer />)
.getInstance();
const initialState = JSON.parse(
JSON.stringify(navigationContainer.state.nav)
);
// First dispatch
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'bar' })
)
).toEqual(true);
// Make sure that the test runner has NOT synchronously applied setState before the tick
expect(navigationContainer.state.nav).toMatchObject(initialState);
// Second dispatch
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'baz' })
)
).toEqual(true);
// Third dispatch
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'car' })
)
).toEqual(true);
// Fourth dispatch
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'dog' })
)
).toEqual(true);
// Fifth dispatch
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'elk' })
)
).toEqual(true);
// Fake the passing of a tick
jest.runOnlyPendingTimers();
expect(navigationContainer.state.nav).toMatchObject({
index: 5,
routes: [
{ routeName: 'foo' },
{ routeName: 'bar' },
{ routeName: 'baz' },
{ routeName: 'car' },
{ routeName: 'dog' },
{ routeName: 'elk' },
],
});
});
});
describe('warnings', () => {
function spyConsole() {
let spy = {};
beforeEach(() => {
spy.console = jest.spyOn(console, 'error').mockImplementation(() => {});
});
afterEach(() => {
spy.console.mockRestore();
});
return spy;
}
describe('detached navigators', () => {
beforeEach(() => {
_TESTING_ONLY_reset_container_count();
});
let spy = spyConsole();
it('warns when you render more than one navigator explicitly', () => {
class BlankScreen extends React.Component {
render() {
return <View />;
}
}
class RootScreen extends React.Component {
render() {
return (
<View>
<ChildNavigator />
</View>
);
}
}
const ChildNavigator = createStackNavigator({
Child: BlankScreen,
});
const RootStack = createStackNavigator({
Root: RootScreen,
});
renderer.create(<RootStack />).toJSON();
expect(spy).toMatchSnapshot();
});
});
});
});

View File

@@ -1,267 +0,0 @@
import NavigationStateUtils from '../StateUtils';
const routeName = 'Anything';
describe('StateUtils', () => {
// Getters
it('gets route', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.get(state, 'a')).toEqual({
key: 'a',
routeName,
});
expect(NavigationStateUtils.get(state, 'b')).toBe(null);
});
it('gets route index', () => {
const state = {
index: 1,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.indexOf(state, 'a')).toBe(0);
expect(NavigationStateUtils.indexOf(state, 'b')).toBe(1);
expect(NavigationStateUtils.indexOf(state, 'c')).toBe(-1);
});
it('has a route', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.has(state, 'b')).toBe(true);
expect(NavigationStateUtils.has(state, 'c')).toBe(false);
});
// Push
it('pushes a route', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
const newState = {
index: 1,
isTransitioning: false,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
};
expect(NavigationStateUtils.push(state, { key: 'b', routeName })).toEqual(
newState
);
});
it('does not push duplicated route', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
expect(() =>
NavigationStateUtils.push(state, { key: 'a', routeName })
).toThrow();
});
// Pop
it('pops route', () => {
const state = {
index: 1,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
const newState = {
index: 0,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.pop(state)).toEqual(newState);
});
it('does not pop route if not applicable', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.pop(state)).toBe(state);
});
// Jump
it('jumps to new index', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.jumpToIndex(state, 0)).toBe(state);
expect(NavigationStateUtils.jumpToIndex(state, 1)).toEqual(newState);
});
it('throws if jumps to invalid index', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
expect(() => NavigationStateUtils.jumpToIndex(state, 2)).toThrow();
});
it('jumps to new key', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.jumpTo(state, 'a')).toBe(state);
expect(NavigationStateUtils.jumpTo(state, 'b')).toEqual(newState);
});
it('throws if jumps to invalid key', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
expect(() => NavigationStateUtils.jumpTo(state, 'c')).toThrow();
});
it('move backwards', () => {
const state = {
index: 1,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
const newState = {
index: 0,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.back(state)).toEqual(newState);
expect(NavigationStateUtils.back(newState)).toBe(newState);
});
it('move forwards', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.forward(state)).toEqual(newState);
expect(NavigationStateUtils.forward(newState)).toBe(newState);
});
// Replace
it('Replaces by key', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [{ key: 'a', routeName }, { key: 'c', routeName }],
isTransitioning: false,
};
expect(
NavigationStateUtils.replaceAt(state, 'b', { key: 'c', routeName })
).toEqual(newState);
});
it('Replaces by index', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [{ key: 'a', routeName }, { key: 'c', routeName }],
isTransitioning: false,
};
expect(
NavigationStateUtils.replaceAtIndex(state, 1, { key: 'c', routeName })
).toEqual(newState);
});
it('Returns the state with updated index if route is unchanged but index changes', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
expect(
NavigationStateUtils.replaceAtIndex(state, 1, state.routes[1])
).toEqual({ ...state, index: 1 });
});
// Reset
it('Resets routes', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [{ key: 'x', routeName }, { key: 'y', routeName }],
isTransitioning: false,
};
expect(
NavigationStateUtils.reset(state, [
{ key: 'x', routeName },
{ key: 'y', routeName },
])
).toEqual(newState);
expect(() => {
NavigationStateUtils.reset(state, []);
}).toThrow();
});
it('Resets routes with index', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
isTransitioning: false,
};
const newState = {
index: 0,
routes: [{ key: 'x', routeName }, { key: 'y', routeName }],
isTransitioning: false,
};
expect(
NavigationStateUtils.reset(
state,
[{ key: 'x', routeName }, { key: 'y', routeName }],
0
)
).toEqual(newState);
expect(() => {
NavigationStateUtils.reset(
state,
[{ key: 'x', routeName }, { key: 'y', routeName }],
100
);
}).toThrow();
});
});

View File

@@ -1,13 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`NavigationContainer warnings detached navigators warns when you render more than one navigator explicitly 1`] = `
Object {
"console": [MockFunction] {
"calls": Array [
Array [
"You should only render one navigator explicitly in your app, and other navigators should by rendered by including them in that navigator. Full details at: https://v2.reactnavigation.org/docs/common-mistakes.html#explicitly-rendering-more-than-one-navigator",
],
],
},
}
`;

View File

@@ -1,460 +0,0 @@
import getChildEventSubscriber from '../getChildEventSubscriber';
test('child action events only flow when focused', () => {
const parentSubscriber = jest.fn();
const emitParentAction = payload => {
parentSubscriber.mock.calls.forEach(subs => {
if (subs[0] === payload.type) {
subs[1](payload);
}
});
};
const subscriptionRemove = () => {};
parentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
.addListener;
const testState = {
key: 'foo',
routeName: 'FooRoute',
routes: [{ key: 'key0' }, { key: 'key1' }],
index: 0,
isTransitioning: false,
};
const focusedTestState = {
...testState,
index: 1,
};
const childActionHandler = jest.fn();
const childWillFocusHandler = jest.fn();
const childDidFocusHandler = jest.fn();
childEventSubscriber('action', childActionHandler);
childEventSubscriber('willFocus', childWillFocusHandler);
childEventSubscriber('didFocus', childDidFocusHandler);
emitParentAction({
type: 'action',
state: focusedTestState,
lastState: testState,
action: { type: 'FooAction' },
});
expect(childActionHandler.mock.calls.length).toBe(0);
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(1);
emitParentAction({
type: 'action',
state: focusedTestState,
lastState: focusedTestState,
action: { type: 'FooAction' },
});
expect(childActionHandler.mock.calls.length).toBe(1);
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(1);
});
test('grandchildren subscription', () => {
const grandParentSubscriber = jest.fn();
const emitGrandParentAction = payload => {
grandParentSubscriber.mock.calls.forEach(subs => {
if (subs[0] === payload.type) {
subs[1](payload);
}
});
};
const subscriptionRemove = () => {};
grandParentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
const parentSubscriber = getChildEventSubscriber(
grandParentSubscriber,
'parent'
).addListener;
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
.addListener;
const parentBlurState = {
key: 'foo',
routeName: 'FooRoute',
routes: [
{ key: 'aunt' },
{
key: 'parent',
routes: [{ key: 'key0' }, { key: 'key1' }],
index: 1,
isTransitioning: false,
},
],
index: 0,
isTransitioning: false,
};
const parentTransitionState = {
...parentBlurState,
index: 1,
isTransitioning: true,
};
const parentFocusState = {
...parentTransitionState,
isTransitioning: false,
};
const childActionHandler = jest.fn();
const childWillFocusHandler = jest.fn();
const childDidFocusHandler = jest.fn();
childEventSubscriber('action', childActionHandler);
childEventSubscriber('willFocus', childWillFocusHandler);
childEventSubscriber('didFocus', childDidFocusHandler);
emitGrandParentAction({
type: 'action',
state: parentTransitionState,
lastState: parentBlurState,
action: { type: 'FooAction' },
});
expect(childActionHandler.mock.calls.length).toBe(0);
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(0);
emitGrandParentAction({
type: 'action',
state: parentFocusState,
lastState: parentTransitionState,
action: { type: 'FooAction' },
});
expect(childActionHandler.mock.calls.length).toBe(0);
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(1);
});
test('grandchildren transitions', () => {
const grandParentSubscriber = jest.fn();
const emitGrandParentAction = payload => {
grandParentSubscriber.mock.calls.forEach(subs => {
if (subs[0] === payload.type) {
subs[1](payload);
}
});
};
const subscriptionRemove = () => {};
grandParentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
const parentSubscriber = getChildEventSubscriber(
grandParentSubscriber,
'parent'
).addListener;
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
.addListener;
const makeFakeState = (childIndex, childIsTransitioning) => ({
index: 1,
isTransitioning: false,
routes: [
{ key: 'nothing' },
{
key: 'parent',
index: childIndex,
isTransitioning: childIsTransitioning,
routes: [{ key: 'key0' }, { key: 'key1' }, { key: 'key2' }],
},
],
});
const blurredState = makeFakeState(0, false);
const transitionState = makeFakeState(1, true);
const focusState = makeFakeState(1, false);
const transition2State = makeFakeState(2, true);
const blurred2State = makeFakeState(2, false);
const childActionHandler = jest.fn();
const childWillFocusHandler = jest.fn();
const childDidFocusHandler = jest.fn();
const childWillBlurHandler = jest.fn();
const childDidBlurHandler = jest.fn();
childEventSubscriber('action', childActionHandler);
childEventSubscriber('willFocus', childWillFocusHandler);
childEventSubscriber('didFocus', childDidFocusHandler);
childEventSubscriber('willBlur', childWillBlurHandler);
childEventSubscriber('didBlur', childDidBlurHandler);
emitGrandParentAction({
type: 'action',
state: transitionState,
lastState: blurredState,
action: { type: 'FooAction' },
});
expect(childActionHandler.mock.calls.length).toBe(0);
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(0);
emitGrandParentAction({
type: 'action',
state: focusState,
lastState: transitionState,
action: { type: 'FooAction' },
});
expect(childActionHandler.mock.calls.length).toBe(0);
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(1);
emitGrandParentAction({
type: 'action',
state: focusState,
lastState: focusState,
action: { type: 'TestAction' },
});
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(1);
expect(childActionHandler.mock.calls.length).toBe(1);
emitGrandParentAction({
type: 'action',
state: transition2State,
lastState: focusState,
action: { type: 'CauseWillBlurAction' },
});
expect(childWillBlurHandler.mock.calls.length).toBe(1);
expect(childDidBlurHandler.mock.calls.length).toBe(0);
expect(childActionHandler.mock.calls.length).toBe(1);
emitGrandParentAction({
type: 'action',
state: blurred2State,
lastState: transition2State,
action: { type: 'CauseDidBlurAction' },
});
expect(childWillBlurHandler.mock.calls.length).toBe(1);
expect(childDidBlurHandler.mock.calls.length).toBe(1);
expect(childActionHandler.mock.calls.length).toBe(1);
});
test('grandchildren pass through transitions', () => {
const grandParentSubscriber = jest.fn();
const emitGrandParentAction = payload => {
grandParentSubscriber.mock.calls.forEach(subs => {
if (subs[0] === payload.type) {
subs[1](payload);
}
});
};
const subscriptionRemove = () => {};
grandParentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
const parentSubscriber = getChildEventSubscriber(
grandParentSubscriber,
'parent'
).addListener;
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
.addListener;
const makeFakeState = (childIndex, childIsTransitioning) => ({
index: childIndex,
isTransitioning: childIsTransitioning,
routes: [
{ key: 'nothing' },
{
key: 'parent',
index: 1,
isTransitioning: false,
routes: [{ key: 'key0' }, { key: 'key1' }, { key: 'key2' }],
},
].slice(0, childIndex + 1),
});
const blurredState = makeFakeState(0, false);
const transitionState = makeFakeState(1, true);
const focusState = makeFakeState(1, false);
const transition2State = makeFakeState(0, true);
const blurred2State = makeFakeState(0, false);
const childActionHandler = jest.fn();
const childWillFocusHandler = jest.fn();
const childDidFocusHandler = jest.fn();
const childWillBlurHandler = jest.fn();
const childDidBlurHandler = jest.fn();
childEventSubscriber('action', childActionHandler);
childEventSubscriber('willFocus', childWillFocusHandler);
childEventSubscriber('didFocus', childDidFocusHandler);
childEventSubscriber('willBlur', childWillBlurHandler);
childEventSubscriber('didBlur', childDidBlurHandler);
emitGrandParentAction({
type: 'action',
state: transitionState,
lastState: blurredState,
action: { type: 'FooAction' },
});
expect(childActionHandler.mock.calls.length).toBe(0);
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(0);
emitGrandParentAction({
type: 'action',
state: focusState,
lastState: transitionState,
action: { type: 'FooAction' },
});
expect(childActionHandler.mock.calls.length).toBe(0);
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(1);
emitGrandParentAction({
type: 'action',
state: focusState,
lastState: focusState,
action: { type: 'TestAction' },
});
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(1);
expect(childActionHandler.mock.calls.length).toBe(1);
emitGrandParentAction({
type: 'action',
state: transition2State,
lastState: focusState,
action: { type: 'CauseWillBlurAction' },
});
expect(childWillBlurHandler.mock.calls.length).toBe(1);
expect(childDidBlurHandler.mock.calls.length).toBe(0);
expect(childActionHandler.mock.calls.length).toBe(1);
emitGrandParentAction({
type: 'action',
state: blurred2State,
lastState: transition2State,
action: { type: 'CauseDidBlurAction' },
});
expect(childWillBlurHandler.mock.calls.length).toBe(1);
expect(childDidBlurHandler.mock.calls.length).toBe(1);
expect(childActionHandler.mock.calls.length).toBe(1);
});
test('child focus with transition', () => {
const parentSubscriber = jest.fn();
const emitParentAction = payload => {
parentSubscriber.mock.calls.forEach(subs => {
if (subs[0] === payload.type) {
subs[1](payload);
}
});
};
const subscriptionRemove = () => {};
parentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
.addListener;
const randomAction = { type: 'FooAction' };
const testState = {
key: 'foo',
routeName: 'FooRoute',
routes: [{ key: 'key0' }, { key: 'key1' }],
index: 0,
isTransitioning: false,
};
const childWillFocusHandler = jest.fn();
const childDidFocusHandler = jest.fn();
const childWillBlurHandler = jest.fn();
const childDidBlurHandler = jest.fn();
childEventSubscriber('willFocus', childWillFocusHandler);
childEventSubscriber('didFocus', childDidFocusHandler);
childEventSubscriber('willBlur', childWillBlurHandler);
childEventSubscriber('didBlur', childDidBlurHandler);
emitParentAction({
type: 'didFocus',
action: randomAction,
lastState: testState,
state: testState,
});
emitParentAction({
type: 'action',
action: randomAction,
lastState: testState,
state: {
...testState,
index: 1,
isTransitioning: true,
},
});
expect(childWillFocusHandler.mock.calls.length).toBe(1);
emitParentAction({
type: 'action',
action: randomAction,
lastState: {
...testState,
index: 1,
isTransitioning: true,
},
state: {
...testState,
index: 1,
isTransitioning: false,
},
});
expect(childDidFocusHandler.mock.calls.length).toBe(1);
emitParentAction({
type: 'action',
action: randomAction,
lastState: {
...testState,
index: 1,
isTransitioning: false,
},
state: {
...testState,
index: 0,
isTransitioning: true,
},
});
expect(childWillBlurHandler.mock.calls.length).toBe(1);
emitParentAction({
type: 'action',
action: randomAction,
lastState: {
...testState,
index: 0,
isTransitioning: true,
},
state: {
...testState,
index: 0,
isTransitioning: false,
},
});
expect(childDidBlurHandler.mock.calls.length).toBe(1);
});
test('child focus with immediate transition', () => {
const parentSubscriber = jest.fn();
const emitParentAction = payload => {
parentSubscriber.mock.calls.forEach(subs => {
if (subs[0] === payload.type) {
subs[1](payload);
}
});
};
const subscriptionRemove = () => {};
parentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
.addListener;
const randomAction = { type: 'FooAction' };
const testState = {
key: 'foo',
routeName: 'FooRoute',
routes: [{ key: 'key0' }, { key: 'key1' }],
index: 0,
isTransitioning: false,
};
const childWillFocusHandler = jest.fn();
const childDidFocusHandler = jest.fn();
const childWillBlurHandler = jest.fn();
const childDidBlurHandler = jest.fn();
childEventSubscriber('willFocus', childWillFocusHandler);
childEventSubscriber('didFocus', childDidFocusHandler);
childEventSubscriber('willBlur', childWillBlurHandler);
childEventSubscriber('didBlur', childDidBlurHandler);
emitParentAction({
type: 'didFocus',
action: randomAction,
lastState: testState,
state: testState,
});
emitParentAction({
type: 'action',
action: randomAction,
lastState: testState,
state: {
...testState,
index: 1,
},
});
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(1);
emitParentAction({
type: 'action',
action: randomAction,
lastState: {
...testState,
index: 1,
},
state: {
...testState,
index: 0,
},
});
expect(childWillBlurHandler.mock.calls.length).toBe(1);
expect(childDidBlurHandler.mock.calls.length).toBe(1);
});

View File

@@ -1,396 +0,0 @@
import React from 'react';
import { AsyncStorage, Linking, Platform } from 'react-native';
import { polyfill } from 'react-lifecycles-compat';
import { BackHandler } from './PlatformHelpers';
import NavigationActions from './NavigationActions';
import invariant from './utils/invariant';
import getNavigationActionCreators from './routers/getNavigationActionCreators';
import docsUrl from './utils/docsUrl';
function isStateful(props) {
return !props.navigation;
}
function validateProps(props) {
if (isStateful(props)) {
return;
}
const { navigation, screenProps, ...containerProps } = props;
const keys = Object.keys(containerProps);
if (keys.length !== 0) {
throw new Error(
'This navigator has both navigation and container props, so it is ' +
`unclear if it should own its own state. Remove props: "${keys.join(
', '
)}" ` +
'if the navigator should get its state from the navigation prop. If the ' +
'navigator should maintain its own state, do not pass a navigation prop.'
);
}
}
// Track the number of stateful container instances. Warn if >0 and not using the
// detached prop to explicitly acknowledge the behavior. We should deprecated implicit
// stateful navigation containers in a future release and require a provider style pattern
// instead in order to eliminate confusion entirely.
let _statefulContainerCount = 0;
export function _TESTING_ONLY_reset_container_count() {
_statefulContainerCount = 0;
}
// We keep a global flag to catch errors during the state persistence hydrating scenario.
// The innermost navigator who catches the error will dispatch a new init action.
let _reactNavigationIsHydratingState = false;
// Unfortunate to use global state here, but it seems necessesary for the time
// being. There seems to be some problems with cascading componentDidCatch
// handlers. Ideally the inner non-stateful navigator catches the error and
// re-throws it, to be caught by the top-level stateful navigator.
/**
* Create an HOC that injects the navigation and manages the navigation state
* in case it's not passed from above.
* This allows to use e.g. the StackNavigator and TabNavigator as root-level
* components.
*/
export default function createNavigationContainer(Component) {
class NavigationContainer extends React.Component {
subs = null;
static router = Component.router;
static navigationOptions = null;
static getDerivedStateFromProps(nextProps, prevState) {
validateProps(nextProps);
return null;
}
_actionEventSubscribers = new Set();
constructor(props) {
super(props);
validateProps(props);
this._initialAction = NavigationActions.init();
if (this._isStateful()) {
this.subs = BackHandler.addEventListener('hardwareBackPress', () => {
if (!this._isMounted) {
this.subs && this.subs.remove();
} else {
// dispatch returns true if the action results in a state change,
// and false otherwise. This maps well to what BackHandler expects
// from a callback -- true if handled, false if not handled
return this.dispatch(NavigationActions.back());
}
});
}
this.state = {
nav:
this._isStateful() && !props.persistenceKey
? Component.router.getStateForAction(this._initialAction)
: null,
};
}
_renderLoading() {
return this.props.renderLoadingExperimental
? this.props.renderLoadingExperimental()
: null;
}
_isStateful() {
return isStateful(this.props);
}
_validateProps(props) {
if (this._isStateful()) {
return;
}
const { navigation, screenProps, ...containerProps } = props;
const keys = Object.keys(containerProps);
if (keys.length !== 0) {
throw new Error(
'This navigator has both navigation and container props, so it is ' +
`unclear if it should own its own state. Remove props: "${keys.join(
', '
)}" ` +
'if the navigator should get its state from the navigation prop. If the ' +
'navigator should maintain its own state, do not pass a navigation prop.'
);
}
}
_urlToPathAndParams(url) {
const params = {};
const delimiter = this.props.uriPrefix || '://';
let path = url.split(delimiter)[1];
if (typeof path === 'undefined') {
path = url;
} else if (path === '') {
path = '/';
}
return {
path,
params,
};
}
_handleOpenURL = ({ url }) => {
const parsedUrl = this._urlToPathAndParams(url);
if (parsedUrl) {
const { path, params } = parsedUrl;
const action = Component.router.getActionForPathAndParams(path, params);
if (action) {
this.dispatch(action);
}
}
};
_onNavigationStateChange(prevNav, nav, action) {
if (
typeof this.props.onNavigationStateChange === 'undefined' &&
this._isStateful() &&
!!process.env.REACT_NAV_LOGGING
) {
/* eslint-disable no-console */
if (console.group) {
console.group('Navigation Dispatch: ');
console.log('Action: ', action);
console.log('New State: ', nav);
console.log('Last State: ', prevNav);
console.groupEnd();
} else {
console.log('Navigation Dispatch: ', {
action,
newState: nav,
lastState: prevNav,
});
}
/* eslint-enable no-console */
return;
}
if (typeof this.props.onNavigationStateChange === 'function') {
this.props.onNavigationStateChange(prevNav, nav, action);
}
}
componentDidUpdate() {
// Clear cached _navState every tick
if (this._navState === this.state.nav) {
this._navState = null;
}
}
async componentDidMount() {
this._isMounted = true;
if (!this._isStateful()) {
return;
}
if (__DEV__ && !this.props.detached) {
if (_statefulContainerCount > 0) {
// Temporarily only show this on iOS due to this issue:
// https://github.com/react-navigation/react-navigation/issues/4196#issuecomment-390827829
if (Platform.OS === 'ios') {
console.warn(
`You should only render one navigator explicitly in your app, and other navigators should by rendered by including them in that navigator. Full details at: ${docsUrl(
'common-mistakes.html#explicitly-rendering-more-than-one-navigator'
)}`
);
}
}
}
_statefulContainerCount++;
Linking.addEventListener('url', this._handleOpenURL);
// Pull out anything that can impact state
const { persistenceKey } = this.props;
const startupStateJSON =
persistenceKey && (await AsyncStorage.getItem(persistenceKey));
const url = await Linking.getInitialURL();
const parsedUrl = url && this._urlToPathAndParams(url);
// Initialize state. This must be done *after* any async code
// so we don't end up with a different value for this.state.nav
// due to changes while async function was resolving
let action = this._initialAction;
let startupState = this.state.nav;
if (!startupState) {
!!process.env.REACT_NAV_LOGGING &&
console.log('Init new Navigation State');
startupState = Component.router.getStateForAction(action);
}
// Pull persisted state from AsyncStorage
if (startupStateJSON) {
try {
startupState = JSON.parse(startupStateJSON);
_reactNavigationIsHydratingState = true;
} catch (e) {}
}
// Pull state out of URL
if (parsedUrl) {
const { path, params } = parsedUrl;
const urlAction = Component.router.getActionForPathAndParams(
path,
params
);
if (urlAction) {
!!process.env.REACT_NAV_LOGGING &&
console.log('Applying Navigation Action for Initial URL:', url);
action = urlAction;
startupState = Component.router.getStateForAction(
urlAction,
startupState
);
}
}
const dispatchActions = () =>
this._actionEventSubscribers.forEach(subscriber =>
subscriber({
type: 'action',
action,
state: this.state.nav,
lastState: null,
})
);
if (startupState === this.state.nav) {
dispatchActions();
return;
}
this.setState({ nav: startupState }, () => {
_reactNavigationIsHydratingState = false;
dispatchActions();
});
}
componentDidCatch(e, errorInfo) {
if (_reactNavigationIsHydratingState) {
_reactNavigationIsHydratingState = false;
console.warn(
'Uncaught exception while starting app from persisted navigation state! Trying to render again with a fresh navigation state..'
);
this.dispatch(NavigationActions.init());
}
}
_persistNavigationState = async nav => {
const { persistenceKey } = this.props;
if (!persistenceKey) {
return;
}
await AsyncStorage.setItem(persistenceKey, JSON.stringify(nav));
};
componentWillUnmount() {
this._isMounted = false;
Linking.removeEventListener('url', this._handleOpenURL);
this.subs && this.subs.remove();
if (this._isStateful()) {
_statefulContainerCount--;
}
}
// Per-tick temporary storage for state.nav
dispatch = action => {
if (this.props.navigation) {
return this.props.navigation.dispatch(action);
}
// navState will have the most up-to-date value, because setState sometimes behaves asyncronously
this._navState = this._navState || this.state.nav;
const lastNavState = this._navState;
invariant(lastNavState, 'should be set in constructor if stateful');
const reducedState = Component.router.getStateForAction(
action,
lastNavState
);
const navState = reducedState === null ? lastNavState : reducedState;
const dispatchActionEvents = () => {
this._actionEventSubscribers.forEach(subscriber =>
subscriber({
type: 'action',
action,
state: navState,
lastState: lastNavState,
})
);
};
if (reducedState === null) {
// The router will return null when action has been handled and the state hasn't changed.
// dispatch returns true when something has been handled.
dispatchActionEvents();
return true;
}
if (navState !== lastNavState) {
// Cache updates to state.nav during the tick to ensure that subsequent calls will not discard this change
this._navState = navState;
this.setState({ nav: navState }, () => {
this._onNavigationStateChange(lastNavState, navState, action);
dispatchActionEvents();
this._persistNavigationState(navState);
});
return true;
}
dispatchActionEvents();
return false;
};
render() {
let navigation = this.props.navigation;
if (this._isStateful()) {
const nav = this.state.nav;
if (!nav) {
return this._renderLoading();
}
if (!this._navigation || this._navigation.state !== nav) {
this._navigation = {
dispatch: this.dispatch,
state: nav,
addListener: (eventName, handler) => {
if (eventName !== 'action') {
return { remove: () => {} };
}
this._actionEventSubscribers.add(handler);
return {
remove: () => {
this._actionEventSubscribers.delete(handler);
},
};
},
};
const actionCreators = getNavigationActionCreators(nav);
Object.keys(actionCreators).forEach(actionName => {
this._navigation[actionName] = (...args) =>
this.dispatch(actionCreators[actionName](...args));
});
}
navigation = this._navigation;
}
invariant(navigation, 'failed to get navigation');
return <Component {...this.props} navigation={navigation} />;
}
}
return polyfill(NavigationContainer);
}

View File

@@ -1,161 +0,0 @@
/*
* This is used to extract one children's worth of events from a stream of navigation action events
*
* Based on the 'action' events that get fired for this navigation state, this utility will fire
* focus and blur events for this child
*/
export default function getChildEventSubscriber(addListener, key) {
const actionSubscribers = new Set();
const willFocusSubscribers = new Set();
const didFocusSubscribers = new Set();
const willBlurSubscribers = new Set();
const didBlurSubscribers = new Set();
const removeAll = () => {
[
actionSubscribers,
willFocusSubscribers,
didFocusSubscribers,
willBlurSubscribers,
didBlurSubscribers,
].forEach(set => set.clear());
upstreamSubscribers.forEach(subs => subs && subs.remove());
};
const getChildSubscribers = evtName => {
switch (evtName) {
case 'action':
return actionSubscribers;
case 'willFocus':
return willFocusSubscribers;
case 'didFocus':
return didFocusSubscribers;
case 'willBlur':
return willBlurSubscribers;
case 'didBlur':
return didBlurSubscribers;
default:
return null;
}
};
const emit = (type, payload) => {
const payloadWithType = { ...payload, type };
const subscribers = getChildSubscribers(type);
subscribers &&
subscribers.forEach(subs => {
subs(payloadWithType);
});
};
// lastEmittedEvent keeps track of focus state for one route. First we assume
// we are blurred. If we are focused on initialization, the first 'action'
// event will cause onFocus+willFocus events because we had previously been
// considered blurred
let lastEmittedEvent = 'didBlur';
const upstreamEvents = [
'willFocus',
'didFocus',
'willBlur',
'didBlur',
'action',
];
const upstreamSubscribers = upstreamEvents.map(eventName =>
addListener(eventName, payload => {
const { state, lastState, action } = payload;
const lastRoutes = lastState && lastState.routes;
const routes = state && state.routes;
const lastFocusKey =
lastState && lastState.routes && lastState.routes[lastState.index].key;
const focusKey = routes && routes[state.index].key;
const isChildFocused = focusKey === key;
const lastRoute =
lastRoutes && lastRoutes.find(route => route.key === key);
const newRoute = routes && routes.find(route => route.key === key);
const childPayload = {
context: `${key}:${action.type}_${payload.context || 'Root'}`,
state: newRoute,
lastState: lastRoute,
action,
type: eventName,
};
const isTransitioning = !!state && state.isTransitioning;
const previouslyLastEmittedEvent = lastEmittedEvent;
if (lastEmittedEvent === 'didBlur') {
// The child is currently blurred. Look for willFocus conditions
if (eventName === 'willFocus' && isChildFocused) {
emit((lastEmittedEvent = 'willFocus'), childPayload);
} else if (eventName === 'action' && isChildFocused) {
emit((lastEmittedEvent = 'willFocus'), childPayload);
}
}
if (lastEmittedEvent === 'willFocus') {
// We are currently mid-focus. Look for didFocus conditions.
// If state.isTransitioning is false, this child event happens immediately after willFocus
if (eventName === 'didFocus' && isChildFocused && !isTransitioning) {
emit((lastEmittedEvent = 'didFocus'), childPayload);
} else if (
eventName === 'action' &&
isChildFocused &&
!isTransitioning
) {
emit((lastEmittedEvent = 'didFocus'), childPayload);
}
}
if (lastEmittedEvent === 'didFocus') {
// The child is currently focused. Look for blurring events
if (!isChildFocused) {
// The child is no longer focused within this navigation state
emit((lastEmittedEvent = 'willBlur'), childPayload);
} else if (eventName === 'willBlur') {
// The parent is getting a willBlur event
emit((lastEmittedEvent = 'willBlur'), childPayload);
} else if (
eventName === 'action' &&
previouslyLastEmittedEvent === 'didFocus'
) {
// While focused, pass action events to children for grandchildren focus
emit('action', childPayload);
}
}
if (lastEmittedEvent === 'willBlur') {
// The child is mid-blur. Wait for transition to end
if (eventName === 'action' && !isChildFocused && !isTransitioning) {
// The child is done blurring because transitioning is over, or isTransitioning
// never began and didBlur fires immediately after willBlur
emit((lastEmittedEvent = 'didBlur'), childPayload);
} else if (eventName === 'didBlur') {
// Pass through the parent didBlur event if it happens
emit((lastEmittedEvent = 'didBlur'), childPayload);
}
}
if (lastEmittedEvent === 'didBlur' && !newRoute) {
removeAll();
}
})
);
return {
addListener(eventName, eventHandler) {
const subscribers = getChildSubscribers(eventName);
if (!subscribers) {
throw new Error(`Invalid event name "${eventName}"`);
}
subscribers.add(eventHandler);
const remove = () => {
subscribers.delete(eventHandler);
};
return { remove };
},
};
}

View File

@@ -1,33 +0,0 @@
import React, { Component } from 'react';
import { View } from 'react-native';
import renderer from 'react-test-renderer';
import DrawerNavigator from '../createDrawerNavigator';
class HomeScreen extends Component {
static navigationOptions = ({ navigation }) => ({
title: `Welcome ${
navigation.state.params ? navigation.state.params.user : 'anonymous'
}`,
gesturesEnabled: true,
});
render() {
return null;
}
}
const routeConfig = {
Home: {
screen: HomeScreen,
},
};
describe('DrawerNavigator', () => {
it('renders successfully', () => {
const MyDrawerNavigator = DrawerNavigator(routeConfig);
const rendered = renderer.create(<MyDrawerNavigator />).toJSON();
expect(rendered).toMatchSnapshot();
});
});

View File

@@ -1,93 +0,0 @@
import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
import renderer from 'react-test-renderer';
import StackNavigator from '../createStackNavigator';
import withNavigation from '../../views/withNavigation';
import { _TESTING_ONLY_reset_container_count } from '../../createNavigationContainer';
const styles = StyleSheet.create({
header: {
opacity: 0.5,
},
});
class HomeScreen extends Component {
static navigationOptions = ({ navigation }) => ({
title: `Welcome ${
navigation.state.params ? navigation.state.params.user : 'anonymous'
}`,
gesturesEnabled: true,
headerStyle: [{ backgroundColor: 'red' }, styles.header],
});
render() {
return null;
}
}
const routeConfig = {
Home: {
screen: HomeScreen,
},
};
describe('StackNavigator', () => {
beforeEach(() => {
_TESTING_ONLY_reset_container_count();
});
it('renders successfully', () => {
const MyStackNavigator = StackNavigator(routeConfig);
const rendered = renderer.create(<MyStackNavigator />).toJSON();
expect(rendered).toMatchSnapshot();
});
it('applies correct values when headerRight is present', () => {
const MyStackNavigator = StackNavigator({
Home: {
screen: HomeScreen,
navigationOptions: {
headerRight: <View />,
},
},
});
const rendered = renderer.create(<MyStackNavigator />).toJSON();
expect(rendered).toMatchSnapshot();
});
it('passes navigation to headerRight when wrapped in withNavigation', () => {
const spy = jest.fn();
class TestComponent extends React.Component {
render() {
return <View>{this.props.onPress(this.props.navigation)}</View>;
}
}
const TestComponentWithNavigation = withNavigation(TestComponent);
class A extends React.Component {
static navigationOptions = {
headerRight: <TestComponentWithNavigation onPress={spy} />,
};
render() {
return <View />;
}
}
const Nav = StackNavigator({ A: { screen: A } });
renderer.create(<Nav />);
expect(spy).toBeCalledWith(
expect.objectContaining({
navigate: expect.any(Function),
addListener: expect.any(Function),
})
);
});
});

View File

@@ -1,8 +1,9 @@
import React, { Component } from 'react';
import React from 'react';
import { View } from 'react-native';
import renderer from 'react-test-renderer';
import SwitchNavigator from '../createSwitchNavigator';
import { createSwitchNavigator } from '@react-navigation/core';
import { createAppContainer } from '@react-navigation/native';
const A = () => <View />;
const B = () => <View />;
@@ -10,8 +11,9 @@ const routeConfig = { A, B };
describe('SwitchNavigator', () => {
it('renders successfully', () => {
const MySwitchNavigator = SwitchNavigator(routeConfig);
const rendered = renderer.create(<MySwitchNavigator />).toJSON();
const MySwitchNavigator = createSwitchNavigator(routeConfig);
const App = createAppContainer(MySwitchNavigator);
const rendered = renderer.create(<App />).toJSON();
expect(rendered).toMatchSnapshot();
});

View File

@@ -1,35 +0,0 @@
import React, { Component } from 'react';
import { View } from 'react-native';
import renderer from 'react-test-renderer';
const {
createTabNavigator,
} = require('react-navigation-deprecated-tab-navigator');
class HomeScreen extends Component {
static navigationOptions = ({ navigation }) => ({
title: `Welcome ${
navigation.state.params ? navigation.state.params.user : 'anonymous'
}`,
gesturesEnabled: true,
});
render() {
return null;
}
}
const routeConfig = {
Home: {
screen: HomeScreen,
},
};
describe('TabNavigator', () => {
it('renders successfully', () => {
const MyTabNavigator = createTabNavigator(routeConfig);
const rendered = renderer.create(<MyTabNavigator />).toJSON();
expect(rendered).toMatchSnapshot();
});
});

View File

@@ -1,243 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DrawerNavigator renders successfully 1`] = `
<View
onMoveShouldSetResponder={[Function]}
onMoveShouldSetResponderCapture={[Function]}
onResponderEnd={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderReject={[Function]}
onResponderRelease={[Function]}
onResponderStart={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
onStartShouldSetResponderCapture={[Function]}
style={
Object {
"backgroundColor": "transparent",
"flex": 1,
}
}
>
<View
collapsable={undefined}
style={
Object {
"flex": 1,
"zIndex": 0,
}
}
/>
<View
accessibilityComponentType={undefined}
accessibilityLabel={undefined}
accessibilityTraits={undefined}
accessible={true}
collapsable={undefined}
hitSlop={undefined}
nativeID={undefined}
onLayout={undefined}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
pointerEvents="none"
style={
Object {
"backgroundColor": "#000",
"bottom": 0,
"left": 0,
"opacity": 0,
"position": "absolute",
"right": 0,
"top": 0,
"zIndex": 1000,
}
}
testID={undefined}
/>
<View
accessibilityViewIsModal={false}
collapsable={undefined}
style={
Object {
"backgroundColor": "white",
"bottom": 0,
"left": 0,
"position": "absolute",
"right": null,
"top": 0,
"transform": Array [
Object {
"translateX": -320,
},
],
"width": 320,
"zIndex": 1001,
}
}
>
<View
style={
Array [
Object {
"flex": 1,
},
undefined,
]
}
>
<RCTScrollView
DEPRECATED_sendUpdatedChildFrames={false}
alwaysBounceHorizontal={undefined}
alwaysBounceVertical={false}
onContentSizeChange={null}
onMomentumScrollBegin={[Function]}
onMomentumScrollEnd={[Function]}
onResponderGrant={[Function]}
onResponderReject={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={undefined}
onResponderTerminationRequest={[Function]}
onScroll={[Function]}
onScrollBeginDrag={[Function]}
onScrollEndDrag={[Function]}
onScrollShouldSetResponder={[Function]}
onStartShouldSetResponder={[Function]}
onStartShouldSetResponderCapture={[Function]}
onTouchCancel={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
onTouchStart={[Function]}
scrollEventThrottle={undefined}
sendMomentumEvents={false}
style={
Array [
Object {
"flexDirection": "column",
"flexGrow": 1,
"flexShrink": 1,
"overflow": "scroll",
},
undefined,
]
}
>
<RCTScrollContentView
collapsable={false}
removeClippedSubviews={undefined}
style={
Array [
undefined,
undefined,
]
}
>
<View
collapsable={undefined}
onLayout={[Function]}
pointerEvents="box-none"
style={
Object {
"paddingBottom": 0,
"paddingLeft": 0,
"paddingRight": 0,
"paddingTop": 20,
}
}
>
<View
style={
Array [
Object {
"paddingVertical": 4,
},
undefined,
]
}
>
<View
accessibilityComponentType={undefined}
accessibilityLabel={undefined}
accessibilityTraits={undefined}
accessible={true}
collapsable={undefined}
hasTVPreferredFocus={undefined}
hitSlop={undefined}
isTVSelectable={true}
nativeID={undefined}
onLayout={undefined}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Object {
"opacity": 1,
}
}
testID={undefined}
tvParallaxProperties={undefined}
>
<View
collapsable={undefined}
onLayout={[Function]}
pointerEvents="box-none"
style={
Object {
"backgroundColor": "rgba(0, 0, 0, .04)",
"paddingBottom": 0,
"paddingLeft": 0,
"paddingRight": 0,
"paddingTop": 0,
}
}
>
<View
style={
Array [
Object {
"alignItems": "center",
"flexDirection": "row",
},
undefined,
]
}
>
<Text
accessible={true}
allowFontScaling={true}
ellipsizeMode="tail"
style={
Array [
Object {
"fontWeight": "bold",
"margin": 16,
},
Object {
"color": "#2196f3",
},
undefined,
undefined,
]
}
>
Welcome anonymous
</Text>
</View>
</View>
</View>
</View>
</View>
</RCTScrollContentView>
</RCTScrollView>
</View>
</View>
</View>
`;

View File

@@ -1,383 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`StackNavigator applies correct values when headerRight is present 1`] = `
<View
onLayout={[Function]}
style={
Array [
Object {
"flex": 1,
},
]
}
>
<View
onMoveShouldSetResponder={[Function]}
onMoveShouldSetResponderCapture={[Function]}
onResponderEnd={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderReject={[Function]}
onResponderRelease={[Function]}
onResponderStart={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
onStartShouldSetResponderCapture={[Function]}
style={
Array [
Object {
"flex": 1,
"flexDirection": "column-reverse",
},
Object {
"backgroundColor": "#000",
},
]
}
>
<View
style={
Object {
"flex": 1,
}
}
>
<View
collapsable={undefined}
pointerEvents="auto"
style={
Object {
"backgroundColor": "#E9E9EF",
"bottom": 0,
"left": 0,
"marginTop": 0,
"opacity": 1,
"position": "absolute",
"right": 0,
"shadowColor": "black",
"shadowOffset": Object {
"height": 0,
"width": 0,
},
"shadowOpacity": 0.2,
"shadowRadius": 5,
"top": 0,
"transform": Array [
Object {
"translateX": 0,
},
Object {
"translateY": 0,
},
],
}
}
/>
</View>
<View
collapsable={undefined}
style={
Object {
"transform": Array [
Object {
"translateX": 0,
},
],
}
}
>
<View
collapsable={undefined}
onLayout={[Function]}
pointerEvents="box-none"
style={
Object {
"backgroundColor": "red",
"borderBottomColor": "#A7A7AA",
"borderBottomWidth": 0.5,
"height": 64,
"opacity": 0.5,
"paddingBottom": 0,
"paddingLeft": 0,
"paddingRight": 0,
"paddingTop": 20,
}
}
>
<View
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
/>
<View
style={
Object {
"flex": 1,
}
}
>
<View
style={
Object {
"bottom": 0,
"flexDirection": "row",
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
>
<View
collapsable={undefined}
pointerEvents="box-none"
style={
Object {
"alignItems": "center",
"backgroundColor": "transparent",
"bottom": 0,
"flexDirection": "row",
"justifyContent": "center",
"left": 70,
"opacity": 1,
"position": "absolute",
"right": 70,
"top": 0,
}
}
>
<Text
accessibilityTraits="header"
accessible={true}
allowFontScaling={true}
collapsable={undefined}
ellipsizeMode="tail"
numberOfLines={1}
onLayout={[Function]}
style={
Object {
"color": "rgba(0, 0, 0, .9)",
"fontSize": 17,
"fontWeight": "700",
"marginHorizontal": 16,
"textAlign": "center",
}
}
>
Welcome anonymous
</Text>
</View>
<View
collapsable={undefined}
pointerEvents="box-none"
style={
Object {
"alignItems": "center",
"backgroundColor": "transparent",
"bottom": 0,
"flexDirection": "row",
"opacity": 1,
"position": "absolute",
"right": 0,
"top": 0,
}
}
>
<View />
</View>
</View>
</View>
</View>
</View>
</View>
</View>
`;
exports[`StackNavigator renders successfully 1`] = `
<View
onLayout={[Function]}
style={
Array [
Object {
"flex": 1,
},
]
}
>
<View
onMoveShouldSetResponder={[Function]}
onMoveShouldSetResponderCapture={[Function]}
onResponderEnd={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderReject={[Function]}
onResponderRelease={[Function]}
onResponderStart={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
onStartShouldSetResponderCapture={[Function]}
style={
Array [
Object {
"flex": 1,
"flexDirection": "column-reverse",
},
Object {
"backgroundColor": "#000",
},
]
}
>
<View
style={
Object {
"flex": 1,
}
}
>
<View
collapsable={undefined}
pointerEvents="auto"
style={
Object {
"backgroundColor": "#E9E9EF",
"bottom": 0,
"left": 0,
"marginTop": 0,
"opacity": 1,
"position": "absolute",
"right": 0,
"shadowColor": "black",
"shadowOffset": Object {
"height": 0,
"width": 0,
},
"shadowOpacity": 0.2,
"shadowRadius": 5,
"top": 0,
"transform": Array [
Object {
"translateX": 0,
},
Object {
"translateY": 0,
},
],
}
}
/>
</View>
<View
collapsable={undefined}
style={
Object {
"transform": Array [
Object {
"translateX": 0,
},
],
}
}
>
<View
collapsable={undefined}
onLayout={[Function]}
pointerEvents="box-none"
style={
Object {
"backgroundColor": "red",
"borderBottomColor": "#A7A7AA",
"borderBottomWidth": 0.5,
"height": 64,
"opacity": 0.5,
"paddingBottom": 0,
"paddingLeft": 0,
"paddingRight": 0,
"paddingTop": 20,
}
}
>
<View
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
/>
<View
style={
Object {
"flex": 1,
}
}
>
<View
style={
Object {
"bottom": 0,
"flexDirection": "row",
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
>
<View
collapsable={undefined}
pointerEvents="box-none"
style={
Object {
"alignItems": "center",
"backgroundColor": "transparent",
"bottom": 0,
"flexDirection": "row",
"justifyContent": "center",
"left": 0,
"opacity": 1,
"position": "absolute",
"right": 0,
"top": 0,
}
}
>
<Text
accessibilityTraits="header"
accessible={true}
allowFontScaling={true}
collapsable={undefined}
ellipsizeMode="tail"
numberOfLines={1}
onLayout={[Function]}
style={
Object {
"color": "rgba(0, 0, 0, .9)",
"fontSize": 17,
"fontWeight": "700",
"marginHorizontal": 16,
"textAlign": "center",
}
}
>
Welcome anonymous
</Text>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
`;

View File

@@ -1,205 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`TabNavigator renders successfully 1`] = `
<View
loaded={
Array [
0,
]
}
onLayout={[Function]}
style={
Array [
Object {
"flex": 1,
"overflow": "hidden",
},
Object {
"flex": 1,
},
]
}
>
<View
collapsable={undefined}
onMoveShouldSetResponder={[Function]}
onMoveShouldSetResponderCapture={[Function]}
onResponderEnd={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderReject={[Function]}
onResponderRelease={[Function]}
onResponderStart={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
onStartShouldSetResponderCapture={[Function]}
style={
Object {
"alignItems": "stretch",
"flex": 1,
"flexDirection": "row",
}
}
>
<View
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
testID={undefined}
>
<View
collapsable={false}
removeClippedSubviews={false}
style={
Object {
"flex": 1,
"overflow": "hidden",
}
}
>
<View
style={
Object {
"flex": 1,
}
}
/>
</View>
</View>
</View>
<View
collapsable={undefined}
style={undefined}
>
<View
collapsable={undefined}
onLayout={[Function]}
pointerEvents="box-none"
style={
Object {
"backgroundColor": "#F7F7F7",
"borderTopColor": "rgba(0, 0, 0, .3)",
"borderTopWidth": 0.5,
"flexDirection": "row",
"height": 49,
"paddingBottom": 0,
"paddingLeft": 0,
"paddingRight": 0,
"paddingTop": 0,
}
}
>
<View
accessibilityComponentType={undefined}
accessibilityLabel={undefined}
accessibilityTraits={undefined}
accessible={true}
collapsable={undefined}
hitSlop={undefined}
nativeID={undefined}
onLayout={undefined}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Object {
"alignItems": "center",
"backgroundColor": "rgba(0, 0, 0, 0)",
"flex": 1,
}
}
testID={undefined}
>
<View
style={
Array [
Object {
"alignItems": "center",
"flex": 1,
},
Object {
"flexDirection": "column",
"justifyContent": "flex-end",
},
undefined,
]
}
>
<View
style={
Array [
Object {
"height": 29,
},
false,
Object {
"flex": 1,
},
]
}
>
<View
collapsable={undefined}
style={
Object {
"alignItems": "center",
"alignSelf": "center",
"height": "100%",
"justifyContent": "center",
"minWidth": 30,
"opacity": 1,
"position": "absolute",
"width": "100%",
}
}
/>
<View
collapsable={undefined}
style={
Object {
"alignItems": "center",
"alignSelf": "center",
"height": "100%",
"justifyContent": "center",
"minWidth": 30,
"opacity": 0,
"position": "absolute",
"width": "100%",
}
}
/>
</View>
<Text
accessible={true}
allowFontScaling={true}
collapsable={undefined}
ellipsizeMode="tail"
numberOfLines={1}
style={
Object {
"backgroundColor": "transparent",
"color": "rgba(52, 120, 246, 1)",
"fontSize": 10,
"marginBottom": 1.5,
"textAlign": "center",
}
}
>
Welcome anonymous
</Text>
</View>
</View>
</View>
</View>
</View>
`;

View File

@@ -1,71 +0,0 @@
import React from 'react';
import { Dimensions, Platform, ScrollView } from 'react-native';
import SafeAreaView from 'react-native-safe-area-view';
import createNavigator from './createNavigator';
import createNavigationContainer from '../createNavigationContainer';
import DrawerRouter from '../routers/DrawerRouter';
import DrawerScreen from '../views/Drawer/DrawerScreen';
import DrawerView from '../views/Drawer/DrawerView';
import DrawerItems from '../views/Drawer/DrawerNavigatorItems';
// A stack navigators props are the intersection between
// the base navigator props (navgiation, screenProps, etc)
// and the view's props
const defaultContentComponent = props => (
<ScrollView alwaysBounceVertical={false}>
<SafeAreaView forceInset={{ top: 'always', horizontal: 'never' }}>
<DrawerItems {...props} />
</SafeAreaView>
</ScrollView>
);
const DefaultDrawerConfig = {
drawerWidth: () => {
/*
* Default drawer width is screen width - header height
* with a max width of 280 on mobile and 320 on tablet
* https://material.io/guidelines/patterns/navigation-drawer.html
*/
const { height, width } = Dimensions.get('window');
const smallerAxisSize = Math.min(height, width);
const isLandscape = width > height;
const isTablet = smallerAxisSize >= 600;
const appBarHeight = Platform.OS === 'ios' ? (isLandscape ? 32 : 44) : 56;
const maxWidth = isTablet ? 320 : 280;
return Math.min(smallerAxisSize - appBarHeight, maxWidth);
},
contentComponent: defaultContentComponent,
drawerPosition: 'left',
drawerBackgroundColor: 'white',
useNativeAnimations: true,
};
const DrawerNavigator = (routeConfigs, config = {}) => {
const mergedConfig = { ...DefaultDrawerConfig, ...config };
const {
order,
paths,
initialRouteName,
backBehavior,
...drawerConfig
} = mergedConfig;
const routerConfig = {
order,
paths,
initialRouteName,
backBehavior,
};
const drawerRouter = DrawerRouter(routeConfigs, routerConfig);
const navigator = createNavigator(DrawerView, drawerRouter, drawerConfig);
return createNavigationContainer(navigator);
};
export default DrawerNavigator;

View File

@@ -1,55 +0,0 @@
import React from 'react';
import { TextInput } from 'react-native';
export default Navigator =>
class KeyboardAwareNavigator extends React.Component {
static router = Navigator.router;
_previouslyFocusedTextInput = null;
render() {
return (
<Navigator
{...this.props}
onGestureBegin={this._handleGestureBegin}
onGestureCanceled={this._handleGestureCanceled}
onGestureFinish={this._handleGestureFinish}
onTransitionStart={this._handleTransitionStart}
/>
);
}
_handleGestureBegin = () => {
this._previouslyFocusedTextInput = TextInput.State.currentlyFocusedField();
if (this._previouslyFocusedTextInput) {
TextInput.State.blurTextInput(this._previouslyFocusedTextInput);
}
this.props.onGestureBegin && this.props.onGestureBegin();
};
_handleGestureCanceled = () => {
if (this._previouslyFocusedTextInput) {
TextInput.State.focusTextInput(this._previouslyFocusedTextInput);
}
this.props.onGestureFinish && this.props.onGestureFinish();
};
_handleGestureFinish = () => {
this._previouslyFocusedTextInput = null;
this.props.onGestureCanceled && this.props.onGestureCanceled();
};
_handleTransitionStart = (transitionProps, prevTransitionProps) => {
// TODO: We should not even have received the transition start event
// in the case where the index did not change, I believe. We
// should revisit this after 2.0 release.
if (transitionProps.index !== prevTransitionProps.index) {
const currentField = TextInput.State.currentlyFocusedField();
if (currentField) {
TextInput.State.blurTextInput(currentField);
}
}
this.props.onTransitionStart &&
this.props.onTransitionStart(transitionProps, prevTransitionProps);
};
};

View File

@@ -1,109 +0,0 @@
import React from 'react';
import getChildEventSubscriber from '../getChildEventSubscriber';
function createNavigator(NavigatorView, router, navigationConfig) {
class Navigator extends React.Component {
static router = router;
static navigationOptions = null;
childEventSubscribers = {};
// Cleanup subscriptions for routes that no longer exist
componentDidUpdate() {
const activeKeys = this.props.navigation.state.routes.map(r => r.key);
Object.keys(this.childEventSubscribers).forEach(key => {
if (!activeKeys.includes(key)) {
delete this.childEventSubscribers[key];
}
});
}
// Remove all subscription references
componentWillUnmount() {
this.childEventSubscribers = {};
}
_isRouteFocused = route => {
const { state } = this.props.navigation;
const focusedRoute = state.routes[state.index];
return route === focusedRoute;
};
_dangerouslyGetParent = () => {
return this.props.navigation;
};
render() {
const { navigation, screenProps } = this.props;
const { dispatch, state, addListener } = navigation;
const { routes } = state;
const descriptors = {};
routes.forEach(route => {
const getComponent = () =>
router.getComponentForRouteName(route.routeName);
if (!this.childEventSubscribers[route.key]) {
this.childEventSubscribers[route.key] = getChildEventSubscriber(
addListener,
route.key
);
}
const actionCreators = {
...navigation.actions,
...router.getActionCreators(route, state.key),
};
const actionHelpers = {};
Object.keys(actionCreators).forEach(actionName => {
actionHelpers[actionName] = (...args) => {
const actionCreator = actionCreators[actionName];
const action = actionCreator(...args);
dispatch(action);
};
});
const childNavigation = {
...actionHelpers,
actions: actionCreators,
dispatch,
state: route,
isFocused: () => this._isRouteFocused(route),
dangerouslyGetParent: this._dangerouslyGetParent,
addListener: this.childEventSubscribers[route.key].addListener,
getParam: (paramName, defaultValue) => {
const params = route.params;
if (params && paramName in params) {
return params[paramName];
}
return defaultValue;
},
};
const options = router.getScreenOptions(childNavigation, screenProps);
descriptors[route.key] = {
key: route.key,
getComponent,
options,
state: route,
navigation: childNavigation,
};
});
return (
<NavigatorView
{...this.props}
screenProps={screenProps}
navigation={navigation}
navigationConfig={navigationConfig}
descriptors={descriptors}
/>
);
}
}
return Navigator;
}
export default createNavigator;

View File

@@ -1,38 +0,0 @@
import * as React from 'react';
import createNavigationContainer from '../createNavigationContainer';
import createKeyboardAwareNavigator from './createKeyboardAwareNavigator';
import createNavigator from './createNavigator';
import StackView from '../views/StackView/StackView';
import StackRouter from '../routers/StackRouter';
function createStackNavigator(routeConfigMap, stackConfig = {}) {
const {
initialRouteKey,
initialRouteName,
initialRouteParams,
paths,
navigationOptions,
disableKeyboardHandling,
} = stackConfig;
const stackRouterConfig = {
initialRouteKey,
initialRouteName,
initialRouteParams,
paths,
navigationOptions,
};
const router = StackRouter(routeConfigMap, stackRouterConfig);
// Create a navigator with StackView as the view
let Navigator = createNavigator(StackView, router, stackConfig);
if (!disableKeyboardHandling) {
Navigator = createKeyboardAwareNavigator(Navigator);
}
// HOC to provide the navigation prop for the top-level navigator (when the prop is missing)
return createNavigationContainer(Navigator);
}
export default createStackNavigator;

View File

@@ -1,13 +0,0 @@
import React from 'react';
import createNavigationContainer from '../createNavigationContainer';
import createNavigator from '../navigators/createNavigator';
import SwitchRouter from '../routers/SwitchRouter';
import SwitchView from '../views/SwitchView/SwitchView';
function createSwitchNavigator(routeConfigMap, switchConfig = {}) {
const router = SwitchRouter(routeConfigMap, switchConfig);
const Navigator = createNavigator(SwitchView, router, switchConfig);
return createNavigationContainer(Navigator);
}
export default createSwitchNavigator;

View File

@@ -1,59 +1,115 @@
/* eslint global-require: 0 */
module.exports = {
// Core
// Native
get createAppContainer() {
return require('@react-navigation/native').createAppContainer;
},
get createNavigationContainer() {
return require('./createNavigationContainer').default;
console.warn(
'`createNavigationContainer()` has been deprecated, please use `createAppContainer()` instead. You can also import createAppContainer directly from @react-navigation/native'
);
return require('@react-navigation/native').createAppContainer;
},
get createKeyboardAwareNavigator() {
return require('@react-navigation/native').createKeyboardAwareNavigator;
},
get createNavigationAwareScrollable() {
return require('@react-navigation/native').createNavigationAwareScrollable;
},
get ScrollView() {
return require('@react-navigation/native').ScrollView;
},
get FlatList() {
return require('@react-navigation/native').FlatList;
},
get SectionList() {
return require('@react-navigation/native').SectionList;
},
get ResourceSavingSceneView() {
return require('@react-navigation/native').ResourceSavingSceneView;
},
get SafeAreaView() {
return require('@react-navigation/native').SafeAreaView;
},
get withOrientation() {
return require('@react-navigation/native').withOrientation;
},
// Core
get createNavigator() {
return require('@react-navigation/core').createNavigator;
},
get StateUtils() {
return require('./StateUtils').default;
return require('@react-navigation/core').StateUtils;
},
get getNavigation() {
return require('@react-navigation/core').getNavigation;
},
get NavigationContext() {
return require('@react-navigation/core').NavigationContext;
},
get NavigationProvider() {
return require('@react-navigation/core').NavigationProvider;
},
get NavigationConsumer() {
return require('@react-navigation/core').NavigationConsumer;
},
get NavigationActions() {
return require('@react-navigation/core').NavigationActions;
},
get StackActions() {
return require('@react-navigation/core').StackActions;
},
get StackRouter() {
return require('@react-navigation/core').StackRouter;
},
get TabRouter() {
return require('@react-navigation/core').TabRouter;
},
get SwitchRouter() {
return require('@react-navigation/core').SwitchRouter;
},
get createConfigGetter() {
return require('@react-navigation/core').StackAcreateConfigGetterctions;
},
get getScreenForRouteName() {
return require('@react-navigation/core').getScreenForRouteName;
},
get validateRouteConfigMap() {
return require('@react-navigation/core').validateRouteConfigMap;
},
get getActiveChildNavigationOptions() {
return require('@react-navigation/core').getActiveChildNavigationOptions;
},
get pathUtils() {
return require('@react-navigation/core').pathUtils;
},
get SceneView() {
return require('@react-navigation/core').SceneView;
},
get SwitchView() {
return require('@react-navigation/core').SwitchView;
},
get NavigationEvents() {
return require('@react-navigation/core').NavigationEvents;
},
get withNavigation() {
return require('@react-navigation/core').withNavigation;
},
get withNavigationFocus() {
return require('@react-navigation/core').withNavigationFocus;
},
// Navigators
get createNavigator() {
return require('./navigators/createNavigator').default;
},
get createStackNavigator() {
return require('./navigators/createStackNavigator').default;
},
get StackNavigator() {
console.warn(
'The StackNavigator function name is deprecated, please use createStackNavigator instead'
);
return require('./navigators/createStackNavigator').default;
return require('react-navigation-stack').createStackNavigator;
},
get createSwitchNavigator() {
return require('./navigators/createSwitchNavigator').default;
},
get SwitchNavigator() {
console.warn(
'The SwitchNavigator function name is deprecated, please use createSwitchNavigator instead'
);
return require('./navigators/createSwitchNavigator').default;
},
get createDrawerNavigator() {
return require('./navigators/createDrawerNavigator').default;
},
get DrawerNavigator() {
console.warn(
'The DrawerNavigator function name is deprecated, please use createDrawerNavigator instead'
);
return require('./navigators/createDrawerNavigator').default;
},
get createTabNavigator() {
console.warn(
'createTabNavigator is deprecated. Please use the createBottomTabNavigator or createMaterialTopTabNavigator instead.'
);
return require('react-navigation-deprecated-tab-navigator')
.createTabNavigator;
},
get TabNavigator() {
console.warn(
'TabNavigator is deprecated. Please use the createBottomTabNavigator or createMaterialTopTabNavigator instead.'
);
return require('react-navigation-deprecated-tab-navigator')
.createTabNavigator;
return require('@react-navigation/core').createSwitchNavigator;
},
get createBottomTabNavigator() {
return require('react-navigation-tabs').createBottomTabNavigator;
},
@@ -61,100 +117,66 @@ module.exports = {
return require('react-navigation-tabs').createMaterialTopTabNavigator;
},
// Actions
get NavigationActions() {
return require('./NavigationActions').default;
},
get StackActions() {
return require('./routers/StackActions').default;
},
get DrawerActions() {
return require('./routers/DrawerActions').default;
get createDrawerNavigator() {
return require('react-navigation-drawer').createDrawerNavigator;
},
// Routers
get StackRouter() {
return require('./routers/StackRouter').default;
// Routers and Actions
get DrawerRouter() {
return require('react-navigation-drawer').DrawerRouter;
},
get TabRouter() {
return require('./routers/TabRouter').default;
},
get SwitchRouter() {
return require('./routers/SwitchRouter').default;
get DrawerActions() {
return require('react-navigation-drawer').DrawerActions;
},
// Views
get Transitioner() {
return require('./views/Transitioner').default;
console.warn(
'Importing the stack Transitioner directly from react-navigation is now deprecated. Instead, import { Transitioner } from "react-navigation-stack";'
);
return require('react-navigation-stack').Transitioner;
},
get StackView() {
return require('./views/StackView/StackView').default;
return require('react-navigation-stack').StackView;
},
get StackViewCard() {
return require('./views/StackView/StackViewCard').default;
return require('react-navigation-stack').StackViewCard;
},
get SafeAreaView() {
return require('react-native-safe-area-view').default;
},
get SceneView() {
return require('./views/SceneView').default;
},
get ResourceSavingSceneView() {
return require('./views/ResourceSavingSceneView').default;
get StackViewTransitionConfigs() {
return require('react-navigation-stack').StackViewTransitionConfigs;
},
// Header
get Header() {
return require('./views/Header/Header').default;
return require('react-navigation-stack').Header;
},
get HeaderTitle() {
return require('./views/Header/HeaderTitle').default;
return require('react-navigation-stack').HeaderTitle;
},
get HeaderBackButton() {
return require('./views/Header/HeaderBackButton').default;
return require('react-navigation-stack').HeaderBackButton;
},
get HeaderStyleInterpolator() {
return require('react-navigation-stack').HeaderStyleInterpolator;
},
// DrawerView
get DrawerView() {
return require('./views/Drawer/DrawerView').default;
return require('react-navigation-drawer').DrawerView;
},
get DrawerItems() {
return require('./views/Drawer/DrawerNavigatorItems').default;
return require('react-navigation-drawer').DrawerNavigatorItems;
},
get DrawerSidebar() {
return require('react-navigation-drawer').DrawerSidebar;
},
// TabView
get TabView() {
console.warn(
'TabView is deprecated. Please use the react-navigation-tabs package instead: https://github.com/react-navigation/react-navigation-tabs'
);
return require('react-navigation-deprecated-tab-navigator').TabView;
// Tabs
get BottomTabBar() {
return require('react-navigation-tabs').BottomTabBar;
},
get TabBarTop() {
console.warn(
'TabBarTop is deprecated. Please use the react-navigation-tabs package instead: https://github.com/react-navigation/react-navigation-tabs'
);
return require('react-navigation-deprecated-tab-navigator').TabBarTop;
},
get TabBarBottom() {
console.warn(
'TabBarBottom is deprecated. Please use the react-navigation-tabs package instead: https://github.com/react-navigation/react-navigation-tabs'
);
return require('react-navigation-deprecated-tab-navigator').TabBarBottom;
},
// SwitchView
get SwitchView() {
return require('./views/SwitchView/SwitchView').default;
},
// HOCs
get withNavigation() {
return require('./views/withNavigation').default;
},
get withNavigationFocus() {
return require('./views/withNavigationFocus').default;
},
get withOrientation() {
return require('./views/withOrientation').default;
get MaterialTopTabBar() {
return require('react-navigation-tabs').MaterialTopTabBar;
},
};

View File

@@ -1,46 +0,0 @@
/* eslint global-require: 0 */
module.exports = {
// Core
get createNavigationContainer() {
return require('./createNavigationContainer').default;
},
get StateUtils() {
return require('./StateUtils').default;
},
// Navigators
get createNavigator() {
return require('./navigators/createNavigator').default;
},
// Actions
get NavigationActions() {
return require('./NavigationActions').default;
},
get StackActions() {
return require('./routers/StackActions').default;
},
get DrawerActions() {
return require('./routers/DrawerActions').default;
},
// Routers
get StackRouter() {
return require('./routers/StackRouter').default;
},
get TabRouter() {
return require('./routers/TabRouter').default;
},
get SwitchRouter() {
return require('./routers/SwitchRouter').default;
},
// HOCs
get withNavigation() {
return require('./views/withNavigation').default;
},
get withNavigationFocus() {
return require('./views/withNavigationFocus').default;
},
};

View File

@@ -1,28 +0,0 @@
const OPEN_DRAWER = 'Navigation/OPEN_DRAWER';
const CLOSE_DRAWER = 'Navigation/CLOSE_DRAWER';
const TOGGLE_DRAWER = 'Navigation/TOGGLE_DRAWER';
const openDrawer = payload => ({
type: OPEN_DRAWER,
...payload,
});
const closeDrawer = payload => ({
type: CLOSE_DRAWER,
...payload,
});
const toggleDrawer = payload => ({
type: TOGGLE_DRAWER,
...payload,
});
export default {
OPEN_DRAWER,
CLOSE_DRAWER,
TOGGLE_DRAWER,
openDrawer,
closeDrawer,
toggleDrawer,
};

View File

@@ -1,85 +0,0 @@
import SwitchRouter from './SwitchRouter';
import NavigationActions from '../NavigationActions';
import invariant from '../utils/invariant';
import withDefaultValue from '../utils/withDefaultValue';
import DrawerActions from './DrawerActions';
export default (routeConfigs, config = {}) => {
config = { ...config };
config = withDefaultValue(config, 'resetOnBlur', false);
config = withDefaultValue(config, 'backBehavior', 'initialRoute');
const switchRouter = SwitchRouter(routeConfigs, config);
return {
...switchRouter,
getActionCreators(route, navStateKey) {
return {
openDrawer: () => DrawerActions.openDrawer({ key: navStateKey }),
closeDrawer: () => DrawerActions.closeDrawer({ key: navStateKey }),
toggleDrawer: () => DrawerActions.toggleDrawer({ key: navStateKey }),
...switchRouter.getActionCreators(route, navStateKey),
};
},
getStateForAction(action, lastState) {
const state = lastState || {
...switchRouter.getStateForAction(action, undefined),
isDrawerOpen: false,
};
const isRouterTargeted = action.key == null || action.key === state.key;
if (isRouterTargeted) {
// Only handle actions that are meant for this drawer, as specified by action.key.
if (action.type === DrawerActions.CLOSE_DRAWER && state.isDrawerOpen) {
return {
...state,
isDrawerOpen: false,
};
}
if (action.type === DrawerActions.OPEN_DRAWER && !state.isDrawerOpen) {
return {
...state,
isDrawerOpen: true,
};
}
if (action.type === DrawerActions.TOGGLE_DRAWER) {
return {
...state,
isDrawerOpen: !state.isDrawerOpen,
};
}
}
// Fall back on switch router for screen switching logic, and handling of child routers
const switchedState = switchRouter.getStateForAction(action, state);
if (switchedState === null) {
// The switch router or a child router is attempting to swallow this action. We return null to allow this.
return null;
}
if (switchedState !== state) {
if (switchedState.index !== state.index) {
// If the tabs have changed, make sure to close the drawer
return {
...switchedState,
isDrawerOpen: false,
};
}
// Return the state new state, as returned by the switch router.
// The index hasn't changed, so this most likely means that a child router has returned a new state
return switchedState;
}
return state;
},
};
};

View File

@@ -1,11 +0,0 @@
let uniqueBaseId = `id-${Date.now()}`;
let uuidCount = 0;
export function _TESTING_ONLY_normalize_keys() {
uniqueBaseId = 'id';
uuidCount = 0;
}
export function generateKey() {
return `${uniqueBaseId}-${uuidCount++}`;
}

View File

@@ -1,52 +0,0 @@
const POP = 'Navigation/POP';
const POP_TO_TOP = 'Navigation/POP_TO_TOP';
const PUSH = 'Navigation/PUSH';
const RESET = 'Navigation/RESET';
const REPLACE = 'Navigation/REPLACE';
const COMPLETE_TRANSITION = 'Navigation/COMPLETE_TRANSITION';
const pop = payload => ({
type: POP,
...payload,
});
const popToTop = payload => ({
type: POP_TO_TOP,
...payload,
});
const push = payload => ({
type: PUSH,
...payload,
});
const reset = payload => ({
type: RESET,
...payload,
});
const replace = payload => ({
type: REPLACE,
...payload,
});
const completeTransition = payload => ({
type: COMPLETE_TRANSITION,
...payload,
});
export default {
POP,
POP_TO_TOP,
PUSH,
RESET,
REPLACE,
COMPLETE_TRANSITION,
pop,
popToTop,
push,
reset,
replace,
completeTransition,
};

View File

@@ -1,666 +0,0 @@
import pathToRegexp from 'path-to-regexp';
import NavigationActions from '../NavigationActions';
import StackActions from './StackActions';
import createConfigGetter from './createConfigGetter';
import getScreenForRouteName from './getScreenForRouteName';
import StateUtils from '../StateUtils';
import validateRouteConfigMap from './validateRouteConfigMap';
import invariant from '../utils/invariant';
import { generateKey } from './KeyGenerator';
import getNavigationActionCreators from './getNavigationActionCreators';
function isEmpty(obj) {
if (!obj) return true;
for (let key in obj) {
return false;
}
return true;
}
function behavesLikePushAction(action) {
return (
action.type === NavigationActions.NAVIGATE ||
action.type === StackActions.PUSH
);
}
const defaultActionCreators = (route, navStateKey) => ({});
function isResetToRootStack(action) {
return action.type === StackActions.RESET && action.key === null;
}
export default (routeConfigs, stackConfig = {}) => {
// Fail fast on invalid route definitions
validateRouteConfigMap(routeConfigs);
const childRouters = {};
const routeNames = Object.keys(routeConfigs);
// Loop through routes and find child routers
routeNames.forEach(routeName => {
const screen = getScreenForRouteName(routeConfigs, routeName);
if (screen && screen.router) {
// If it has a router it's a navigator.
childRouters[routeName] = screen.router;
} else {
// If it doesn't have router it's an ordinary React component.
childRouters[routeName] = null;
}
});
const { initialRouteParams } = stackConfig;
const getCustomActionCreators =
stackConfig.getCustomActionCreators || defaultActionCreators;
const initialRouteName = stackConfig.initialRouteName || routeNames[0];
const initialChildRouter = childRouters[initialRouteName];
const pathsByRouteNames = { ...stackConfig.paths } || {};
let paths = [];
function getInitialState(action) {
let route = {};
const childRouter = childRouters[action.routeName];
// This is a push-like action, and childRouter will be a router or null if we are responsible for this routeName
if (behavesLikePushAction(action) && childRouter !== undefined) {
let childState = {};
// The router is null for normal leaf routes
if (childRouter !== null) {
const childAction =
action.action || NavigationActions.init({ params: action.params });
childState = childRouter.getStateForAction(childAction);
}
return {
key: 'StackRouterRoot',
isTransitioning: false,
index: 0,
routes: [
{
params: action.params,
...childState,
key: action.key || generateKey(),
routeName: action.routeName,
},
],
};
}
if (initialChildRouter) {
route = initialChildRouter.getStateForAction(
NavigationActions.navigate({
routeName: initialRouteName,
params: initialRouteParams,
})
);
}
const params = (route.params || action.params || initialRouteParams) && {
...(route.params || {}),
...(action.params || {}),
...(initialRouteParams || {}),
};
const { initialRouteKey } = stackConfig;
route = {
...route,
...(params ? { params } : {}),
routeName: initialRouteName,
key: action.key || (initialRouteKey || generateKey()),
};
return {
key: 'StackRouterRoot',
isTransitioning: false,
index: 0,
routes: [route],
};
}
// Build paths for each route
routeNames.forEach(routeName => {
let pathPattern =
pathsByRouteNames[routeName] || routeConfigs[routeName].path;
let matchExact = !!pathPattern && !childRouters[routeName];
if (pathPattern === undefined) {
pathPattern = routeName;
}
const keys = [];
let re, toPath, priority;
if (typeof pathPattern === 'string') {
// pathPattern may be either a string or a regexp object according to path-to-regexp docs.
re = pathToRegexp(pathPattern, keys);
toPath = pathToRegexp.compile(pathPattern);
priority = 0;
} else {
// for wildcard match
re = pathToRegexp('*', keys);
toPath = () => '';
matchExact = true;
priority = -1;
}
if (!matchExact) {
const wildcardRe = pathToRegexp(`${pathPattern}/*`, keys);
re = new RegExp(`(?:${re.source})|(?:${wildcardRe.source})`);
}
pathsByRouteNames[routeName] = { re, keys, toPath, priority };
});
paths = Object.entries(pathsByRouteNames);
paths.sort((a, b) => b[1].priority - a[1].priority);
return {
getComponentForState(state) {
const activeChildRoute = state.routes[state.index];
const { routeName } = activeChildRoute;
if (childRouters[routeName]) {
return childRouters[routeName].getComponentForState(activeChildRoute);
}
return getScreenForRouteName(routeConfigs, routeName);
},
getComponentForRouteName(routeName) {
return getScreenForRouteName(routeConfigs, routeName);
},
getActionCreators(route, navStateKey) {
return {
...getNavigationActionCreators(route),
...getCustomActionCreators(route, navStateKey),
pop: (n, params) =>
StackActions.pop({
n,
...params,
}),
popToTop: params => StackActions.popToTop(params),
push: (routeName, params, action) =>
StackActions.push({
routeName,
params,
action,
}),
replace: (replaceWith, params, action, newKey) => {
if (typeof replaceWith === 'string') {
return StackActions.replace({
routeName: replaceWith,
params,
action,
key: route.key,
newKey,
});
}
invariant(
typeof replaceWith === 'object',
'Must replaceWith an object or a string'
);
invariant(
params == null,
'Params must not be provided to .replace() when specifying an object'
);
invariant(
action == null,
'Child action must not be provided to .replace() when specifying an object'
);
invariant(
newKey == null,
'Child action must not be provided to .replace() when specifying an object'
);
return StackActions.replace(replaceWith);
},
reset: (actions, index) =>
StackActions.reset({
actions,
index: index == null ? actions.length - 1 : index,
key: navStateKey,
}),
dismiss: () =>
NavigationActions.back({
key: navStateKey,
}),
};
},
getStateForAction(action, state) {
// Set up the initial state if needed
if (!state) {
return getInitialState(action);
}
// Check if the focused child scene wants to handle the action, as long as
// it is not a reset to the root stack
if (
!isResetToRootStack(action) &&
action.type !== NavigationActions.NAVIGATE
) {
const keyIndex = action.key
? StateUtils.indexOf(state, action.key)
: -1;
const childIndex = keyIndex >= 0 ? keyIndex : state.index;
const childRoute = state.routes[childIndex];
invariant(
childRoute,
`StateUtils erroneously thought index ${childIndex} exists`
);
const childRouter = childRouters[childRoute.routeName];
if (childRouter) {
const route = childRouter.getStateForAction(action, childRoute);
if (route === null) {
return state;
}
if (route && route !== childRoute) {
return StateUtils.replaceAt(state, childRoute.key, route);
}
}
} else if (action.type === NavigationActions.NAVIGATE) {
// Traverse routes from the top of the stack to the bottom, so the
// active route has the first opportunity, then the one before it, etc.
for (let childRoute of state.routes.slice().reverse()) {
let childRouter = childRouters[childRoute.routeName];
let childAction =
action.routeName === childRoute.routeName && action.action
? action.action
: action;
if (childRouter) {
const nextRouteState = childRouter.getStateForAction(
childAction,
childRoute
);
if (nextRouteState === null || nextRouteState !== childRoute) {
return StateUtils.replaceAndPrune(
state,
nextRouteState ? nextRouteState.key : childRoute.key,
nextRouteState ? nextRouteState : childRoute
);
}
}
}
}
// Handle explicit push navigation action. This must happen after the
// focused child router has had a chance to handle the action.
if (
behavesLikePushAction(action) &&
childRouters[action.routeName] !== undefined
) {
const childRouter = childRouters[action.routeName];
let route;
invariant(
action.type !== StackActions.PUSH || action.key == null,
'StackRouter does not support key on the push action'
);
// Before pushing a new route we first try to find one in the existing route stack
// More information on this: https://github.com/react-navigation/rfcs/blob/master/text/0004-less-pushy-navigate.md
const lastRouteIndex = state.routes.findIndex(r => {
if (action.key) {
return r.key === action.key;
} else {
return r.routeName === action.routeName;
}
});
if (action.type !== StackActions.PUSH && lastRouteIndex !== -1) {
// If index is unchanged and params are not being set, leave state identity intact
if (state.index === lastRouteIndex && !action.params) {
return null;
}
// Remove the now unused routes at the tail of the routes array
const routes = state.routes.slice(0, lastRouteIndex + 1);
// Apply params if provided, otherwise leave route identity intact
if (action.params) {
const route = state.routes[lastRouteIndex];
routes[lastRouteIndex] = {
...route,
params: {
...route.params,
...action.params,
},
};
}
// Return state with new index. Change isTransitioning only if index has changed
return {
...state,
isTransitioning:
state.index !== lastRouteIndex
? action.immediate !== true
: state.isTransitioning,
index: lastRouteIndex,
routes,
};
}
if (childRouter) {
const childAction =
action.action || NavigationActions.init({ params: action.params });
route = {
params: action.params,
// merge the child state in this order to allow params override
...childRouter.getStateForAction(childAction),
routeName: action.routeName,
key: action.key || generateKey(),
};
} else {
route = {
params: action.params,
routeName: action.routeName,
key: action.key || generateKey(),
};
}
return {
...StateUtils.push(state, route),
isTransitioning: action.immediate !== true,
};
} else if (
action.type === StackActions.PUSH &&
childRouters[action.routeName] === undefined
) {
// Return the state identity to bubble the action up
return state;
}
// Handle navigation to other child routers that are not yet pushed
if (behavesLikePushAction(action)) {
const childRouterNames = Object.keys(childRouters);
for (let i = 0; i < childRouterNames.length; i++) {
const childRouterName = childRouterNames[i];
const childRouter = childRouters[childRouterName];
if (childRouter) {
// For each child router, start with a blank state
const initChildRoute = childRouter.getStateForAction(
NavigationActions.init()
);
// Then check to see if the router handles our navigate action
const navigatedChildRoute = childRouter.getStateForAction(
action,
initChildRoute
);
let routeToPush = null;
if (navigatedChildRoute === null) {
// Push the route if the router has 'handled' the action and returned null
routeToPush = initChildRoute;
} else if (navigatedChildRoute !== initChildRoute) {
// Push the route if the state has changed in response to this navigation
routeToPush = navigatedChildRoute;
}
if (routeToPush) {
const route = {
...routeToPush,
routeName: childRouterName,
key: action.key || generateKey(),
};
return StateUtils.push(state, route);
}
}
}
}
// Handle pop-to-top behavior. Make sure this happens after children have had a chance to handle the action, so that the inner stack pops to top first.
if (action.type === StackActions.POP_TO_TOP) {
// Refuse to handle pop to top if a key is given that doesn't correspond
// to this router
if (action.key && state.key !== action.key) {
return state;
}
// If we're already at the top, then we return the state with a new
// identity so that the action is handled by this router.
if (state.index > 0) {
return {
...state,
isTransitioning: action.immediate !== true,
index: 0,
routes: [state.routes[0]],
};
}
return state;
}
// Handle replace action
if (action.type === StackActions.REPLACE) {
const routeIndex = state.routes.findIndex(r => r.key === action.key);
// Only replace if the key matches one of our routes
if (routeIndex !== -1) {
const childRouter = childRouters[action.routeName];
let childState = {};
if (childRouter) {
const childAction =
action.action ||
NavigationActions.init({ params: action.params });
childState = childRouter.getStateForAction(childAction);
}
const routes = [...state.routes];
routes[routeIndex] = {
params: action.params,
// merge the child state in this order to allow params override
...childState,
routeName: action.routeName,
key: action.newKey || generateKey(),
};
return { ...state, routes };
}
}
// Update transitioning state
if (
action.type === StackActions.COMPLETE_TRANSITION &&
(action.key == null || action.key === state.key) &&
state.isTransitioning
) {
return {
...state,
isTransitioning: false,
};
}
if (action.type === NavigationActions.SET_PARAMS) {
const key = action.key;
const lastRoute = state.routes.find(route => route.key === key);
if (lastRoute) {
const params = {
...lastRoute.params,
...action.params,
};
const routes = [...state.routes];
routes[state.routes.indexOf(lastRoute)] = {
...lastRoute,
params,
};
return {
...state,
routes,
};
}
}
if (action.type === StackActions.RESET) {
// Only handle reset actions that are unspecified or match this state key
if (action.key != null && action.key != state.key) {
// Deliberately use != instead of !== so we can match null with
// undefined on either the state or the action
return state;
}
const newStackActions = action.actions;
return {
...state,
routes: newStackActions.map(newStackAction => {
const router = childRouters[newStackAction.routeName];
let childState = {};
if (router) {
const childAction =
newStackAction.action ||
NavigationActions.init({ params: newStackAction.params });
childState = router.getStateForAction(childAction);
}
return {
params: newStackAction.params,
...childState,
routeName: newStackAction.routeName,
key: newStackAction.key || generateKey(),
};
}),
index: action.index,
};
}
if (
action.type === NavigationActions.BACK ||
action.type === StackActions.POP
) {
const { key, n, immediate } = action;
let backRouteIndex = state.index;
if (action.type === StackActions.POP && n != null) {
// determine the index to go back *from*. In this case, n=1 means to go
// back from state.index, as if it were a normal "BACK" action
backRouteIndex = Math.max(1, state.index - n + 1);
} else if (key) {
const backRoute = state.routes.find(route => route.key === key);
backRouteIndex = state.routes.indexOf(backRoute);
}
if (backRouteIndex > 0) {
return {
...state,
routes: state.routes.slice(0, backRouteIndex),
index: backRouteIndex - 1,
isTransitioning: immediate !== true,
};
}
}
return state;
},
getPathAndParamsForState(state) {
const route = state.routes[state.index];
const routeName = route.routeName;
const screen = getScreenForRouteName(routeConfigs, routeName);
const subPath = pathsByRouteNames[routeName].toPath(route.params);
let path = subPath;
let params = route.params;
if (screen && screen.router) {
const stateRoute = route;
// If it has a router it's a navigator.
// If it doesn't have router it's an ordinary React component.
const child = screen.router.getPathAndParamsForState(stateRoute);
path = subPath ? `${subPath}/${child.path}` : child.path;
params = child.params ? { ...params, ...child.params } : params;
}
return {
path,
params,
};
},
getActionForPathAndParams(pathToResolve, inputParams) {
// If the path is empty (null or empty string)
// just return the initial route action
if (!pathToResolve) {
return NavigationActions.navigate({
routeName: initialRouteName,
params: inputParams,
});
}
const [pathNameToResolve, queryString] = pathToResolve.split('?');
// Attempt to match `pathNameToResolve` with a route in this router's
// routeConfigs
let matchedRouteName;
let pathMatch;
let pathMatchKeys;
// eslint-disable-next-line no-restricted-syntax
for (const [routeName, path] of paths) {
const { re, keys } = path;
pathMatch = re.exec(pathNameToResolve);
if (pathMatch && pathMatch.length) {
pathMatchKeys = keys;
matchedRouteName = routeName;
break;
}
}
// We didn't match -- return null
if (!matchedRouteName) {
// If the path is empty (null or empty string)
// just return the initial route action
if (!pathToResolve) {
return NavigationActions.navigate({
routeName: initialRouteName,
});
}
return null;
}
// Determine nested actions:
// If our matched route for this router is a child router,
// get the action for the path AFTER the matched path for this
// router
let nestedAction;
let nestedQueryString = queryString ? '?' + queryString : '';
if (childRouters[matchedRouteName]) {
nestedAction = childRouters[matchedRouteName].getActionForPathAndParams(
pathMatch.slice(pathMatchKeys.length).join('/') + nestedQueryString
);
if (!nestedAction) {
return null;
}
}
// reduce the items of the query string. any query params may
// be overridden by path params
const queryParams = !isEmpty(inputParams)
? inputParams
: (queryString || '').split('&').reduce((result, item) => {
if (item !== '') {
const nextResult = result || {};
const [key, value] = item.split('=');
nextResult[key] = value;
return nextResult;
}
return result;
}, null);
// reduce the matched pieces of the path into the params
// of the route. `params` is null if there are no params.
const params = pathMatch.slice(1).reduce((result, matchResult, i) => {
const key = pathMatchKeys[i];
if (key.asterisk || !key) {
return result;
}
const nextResult = result || inputParams || {};
const paramName = key.name;
let decodedMatchResult;
try {
decodedMatchResult = decodeURIComponent(matchResult);
} catch (e) {
// ignore `URIError: malformed URI`
}
nextResult[paramName] = decodedMatchResult || matchResult;
return nextResult;
}, queryParams);
return NavigationActions.navigate({
routeName: matchedRouteName,
...(params ? { params } : {}),
...(nestedAction ? { action: nestedAction } : {}),
});
},
getScreenOptions: createConfigGetter(
routeConfigs,
stackConfig.navigationOptions
),
};
};

View File

@@ -1,388 +0,0 @@
import invariant from '../utils/invariant';
import getScreenForRouteName from './getScreenForRouteName';
import createConfigGetter from './createConfigGetter';
import NavigationActions from '../NavigationActions';
import StackActions from './StackActions';
import validateRouteConfigMap from './validateRouteConfigMap';
import getNavigationActionCreators from './getNavigationActionCreators';
const defaultActionCreators = (route, navStateKey) => ({});
function childrenUpdateWithoutSwitchingIndex(actionType) {
return [
NavigationActions.SET_PARAMS,
// Todo: make SwitchRouter not depend on StackActions..
StackActions.COMPLETE_TRANSITION,
].includes(actionType);
}
export default (routeConfigs, config = {}) => {
// Fail fast on invalid route definitions
validateRouteConfigMap(routeConfigs);
const order = config.order || Object.keys(routeConfigs);
const paths = config.paths || {};
const getCustomActionCreators =
config.getCustomActionCreators || defaultActionCreators;
const initialRouteParams = config.initialRouteParams;
const initialRouteName = config.initialRouteName || order[0];
const backBehavior = config.backBehavior || 'none';
const shouldBackNavigateToInitialRoute = backBehavior === 'initialRoute';
const resetOnBlur = config.hasOwnProperty('resetOnBlur')
? config.resetOnBlur
: true;
const initialRouteIndex = order.indexOf(initialRouteName);
const childRouters = {};
order.forEach(routeName => {
const routeConfig = routeConfigs[routeName];
if (!paths[routeName]) {
paths[routeName] =
typeof routeConfig.path === 'string' ? routeConfig.path : routeName;
}
childRouters[routeName] = null;
const screen = getScreenForRouteName(routeConfigs, routeName);
if (screen.router) {
childRouters[routeName] = screen.router;
}
});
if (initialRouteIndex === -1) {
throw new Error(
`Invalid initialRouteName '${initialRouteName}'.` +
`Should be one of ${order.map(n => `"${n}"`).join(', ')}`
);
}
function resetChildRoute(routeName) {
const params =
routeName === initialRouteName ? initialRouteParams : undefined;
const childRouter = childRouters[routeName];
if (childRouter) {
const childAction = NavigationActions.init();
return {
...childRouter.getStateForAction(childAction),
key: routeName,
routeName,
params,
};
}
return {
key: routeName,
routeName,
params,
};
}
return {
getInitialState() {
const routes = order.map(resetChildRoute);
return {
routes,
index: initialRouteIndex,
isTransitioning: false,
};
},
getNextState(prevState, possibleNextState) {
if (!prevState) {
return possibleNextState;
}
let nextState;
if (prevState.index !== possibleNextState.index && resetOnBlur) {
const prevRouteName = prevState.routes[prevState.index].routeName;
const nextRoutes = [...possibleNextState.routes];
nextRoutes[prevState.index] = resetChildRoute(prevRouteName);
return {
...possibleNextState,
routes: nextRoutes,
};
} else {
nextState = possibleNextState;
}
return nextState;
},
getActionCreators(route, stateKey) {
return {
...getNavigationActionCreators(route),
...getCustomActionCreators(route, stateKey),
};
},
getStateForAction(action, inputState) {
let prevState = inputState ? { ...inputState } : inputState;
let state = inputState || this.getInitialState();
let activeChildIndex = state.index;
if (action.type === NavigationActions.INIT) {
// NOTE(brentvatne): this seems weird... why are we merging these
// params into child routes?
// ---------------------------------------------------------------
// Merge any params from the action into all the child routes
const { params } = action;
if (params) {
state.routes = state.routes.map(route => ({
...route,
params: {
...route.params,
...params,
...(route.routeName === initialRouteName
? initialRouteParams
: null),
},
}));
}
}
// Let the current child handle it
const activeChildLastState = state.routes[state.index];
const activeChildRouter = childRouters[order[state.index]];
if (activeChildRouter) {
const activeChildState = activeChildRouter.getStateForAction(
action,
activeChildLastState
);
if (!activeChildState && inputState) {
return null;
}
if (activeChildState && activeChildState !== activeChildLastState) {
const routes = [...state.routes];
routes[state.index] = activeChildState;
return this.getNextState(prevState, {
...state,
routes,
});
}
}
// Handle tab changing. Do this after letting the current tab try to
// handle the action, to allow inner children to change first
const isBackEligible =
action.key == null || action.key === activeChildLastState.key;
if (action.type === NavigationActions.BACK) {
if (isBackEligible && shouldBackNavigateToInitialRoute) {
activeChildIndex = initialRouteIndex;
} else {
return state;
}
}
let didNavigate = false;
if (action.type === NavigationActions.NAVIGATE) {
didNavigate = !!order.find((childId, i) => {
if (childId === action.routeName) {
activeChildIndex = i;
return true;
}
return false;
});
if (didNavigate) {
const childState = state.routes[activeChildIndex];
const childRouter = childRouters[action.routeName];
let newChildState;
if (action.action) {
newChildState = childRouter
? childRouter.getStateForAction(action.action, childState)
: null;
} else if (!action.action && !childRouter && action.params) {
newChildState = {
...childState,
params: {
...(childState.params || {}),
...action.params,
},
};
}
if (newChildState && newChildState !== childState) {
const routes = [...state.routes];
routes[activeChildIndex] = newChildState;
return this.getNextState(prevState, {
...state,
routes,
index: activeChildIndex,
});
} else if (
!newChildState &&
state.index === activeChildIndex &&
prevState
) {
return null;
}
}
}
if (action.type === NavigationActions.SET_PARAMS) {
const key = action.key;
const lastRoute = state.routes.find(route => route.key === key);
if (lastRoute) {
const params = {
...lastRoute.params,
...action.params,
};
const routes = [...state.routes];
routes[state.routes.indexOf(lastRoute)] = {
...lastRoute,
params,
};
return this.getNextState(prevState, {
...state,
routes,
});
}
}
if (activeChildIndex !== state.index) {
return this.getNextState(prevState, {
...state,
index: activeChildIndex,
});
} else if (didNavigate && !inputState) {
return state;
} else if (didNavigate) {
return { ...state };
}
// Let other children handle it and switch to the first child that returns a new state
let index = state.index;
let routes = state.routes;
order.find((childId, i) => {
const childRouter = childRouters[childId];
if (i === index) {
return false;
}
let childState = routes[i];
if (childRouter) {
childState = childRouter.getStateForAction(action, childState);
}
if (!childState) {
index = i;
return true;
}
if (childState !== routes[i]) {
routes = [...routes];
routes[i] = childState;
index = i;
return true;
}
return false;
});
// Nested routers can be updated after switching children with actions such as SET_PARAMS
// and COMPLETE_TRANSITION.
// NOTE: This may be problematic with custom routers because we whitelist the actions
// that can be handled by child routers without automatically changing index.
if (childrenUpdateWithoutSwitchingIndex(action.type)) {
index = state.index;
}
if (index !== state.index || routes !== state.routes) {
return this.getNextState(prevState, {
...state,
index,
routes,
});
}
return state;
},
getComponentForState(state) {
const routeName = state.routes[state.index].routeName;
invariant(
routeName,
`There is no route defined for index ${state.index}. Check that
that you passed in a navigation state with a valid tab/screen index.`
);
const childRouter = childRouters[routeName];
if (childRouter) {
return childRouter.getComponentForState(state.routes[state.index]);
}
return getScreenForRouteName(routeConfigs, routeName);
},
getComponentForRouteName(routeName) {
return getScreenForRouteName(routeConfigs, routeName);
},
getPathAndParamsForState(state) {
const route = state.routes[state.index];
const routeName = order[state.index];
const subPath = paths[routeName];
const screen = getScreenForRouteName(routeConfigs, routeName);
let path = subPath;
let params = route.params;
if (screen && screen.router) {
const stateRoute = route;
// If it has a router it's a navigator.
// If it doesn't have router it's an ordinary React component.
const child = screen.router.getPathAndParamsForState(stateRoute);
path = subPath ? `${subPath}/${child.path}` : child.path;
params = child.params ? { ...params, ...child.params } : params;
}
return {
path,
params,
};
},
/**
* Gets an optional action, based on a relative path and query params.
*
* This will return null if there is no action matched
*/
getActionForPathAndParams(path, params) {
if (!path) {
return NavigationActions.navigate({
routeName: initialRouteName,
params,
});
}
return (
order
.map(childId => {
const parts = path.split('/');
const pathToTest = paths[childId];
const partsInTestPath = pathToTest.split('/').length;
const pathPartsToTest = parts.slice(0, partsInTestPath).join('/');
if (pathPartsToTest === pathToTest) {
const childRouter = childRouters[childId];
const action = NavigationActions.navigate({
routeName: childId,
});
if (childRouter && childRouter.getActionForPathAndParams) {
action.action = childRouter.getActionForPathAndParams(
parts.slice(partsInTestPath).join('/'),
params
);
}
if (params) {
action.params = params;
}
return action;
}
return null;
})
.find(action => !!action) ||
order
.map(childId => {
const childRouter = childRouters[childId];
return (
childRouter && childRouter.getActionForPathAndParams(path, params)
);
})
.find(action => !!action) ||
null
);
},
getScreenOptions: createConfigGetter(
routeConfigs,
config.navigationOptions
),
};
};

View File

@@ -1,11 +0,0 @@
import SwitchRouter from './SwitchRouter';
import withDefaultValue from '../utils/withDefaultValue';
export default (routeConfigs, config = {}) => {
config = { ...config };
config = withDefaultValue(config, 'resetOnBlur', false);
config = withDefaultValue(config, 'backBehavior', 'initialRoute');
const switchRouter = SwitchRouter(routeConfigs, config);
return switchRouter;
};

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