Compare commits
56 Commits
@react-nav
...
@react-nav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
264537bdb4 | ||
|
|
ca4a36070a | ||
|
|
4ca5cc6329 | ||
|
|
25c3fc440f | ||
|
|
89fa363883 | ||
|
|
bec2f754d4 | ||
|
|
b277927925 | ||
|
|
72993c6463 | ||
|
|
3fbfb70699 | ||
|
|
abdf9d12b5 | ||
|
|
ba85db28d4 | ||
|
|
e5063b9339 | ||
|
|
ec35bd5821 | ||
|
|
22e306004a | ||
|
|
8a835b3556 | ||
|
|
d9237e9a0d | ||
|
|
0644ac03aa | ||
|
|
41a5e1a385 | ||
|
|
3d937d1e65 | ||
|
|
343320783f | ||
|
|
5411816188 | ||
|
|
d8bda604ee | ||
|
|
c70635b7d7 | ||
|
|
94d7b28c0b | ||
|
|
b19b3b2725 | ||
|
|
3dcec142f7 | ||
|
|
0c159db4c9 | ||
|
|
2479da98ed | ||
|
|
5197ee2a9c | ||
|
|
0ead2662ec | ||
|
|
5af5c29f07 | ||
|
|
d448cdc11f | ||
|
|
0e8fda3196 | ||
|
|
9198597b7f | ||
|
|
9be904d9c4 | ||
|
|
fa4a959549 | ||
|
|
d0510d0220 | ||
|
|
0b4bf1dcc8 | ||
|
|
5a3f8356b0 | ||
|
|
eeae11033a | ||
|
|
b931ae62df | ||
|
|
ea66b1a3b8 | ||
|
|
4bc0c8f66f | ||
|
|
68016de385 | ||
|
|
e55e866af2 | ||
|
|
50b366e734 | ||
|
|
edf96d839f | ||
|
|
141d397bdf | ||
|
|
0f18b91690 | ||
|
|
6262f7298b | ||
|
|
a6f58677dc | ||
|
|
9bfb295620 | ||
|
|
ecd68afb46 | ||
|
|
5fe140e61b | ||
|
|
944fa35ed4 | ||
|
|
2243b45cc1 |
54
.github/workflows/expo-preview.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
name: Expo Preview
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Install and publish
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.head.repo.owner.login == 'react-navigation'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 10.x
|
||||
|
||||
- name: Setup Expo
|
||||
uses: expo/expo-github-action@v5
|
||||
with:
|
||||
expo-version: 3.x
|
||||
expo-username: ${{ secrets.EXPO_CLI_USERNAME }}
|
||||
expo-password: ${{ secrets.EXPO_CLI_PASSWORD }}
|
||||
expo-cache: true
|
||||
|
||||
- name: Get yarn cache
|
||||
id: yarn-cache
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- name: Check yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Publish Expo app
|
||||
working-directory: ./example
|
||||
run: expo publish --release-channel=pr-${{ github.event.number }}
|
||||
|
||||
- name: Get expo link
|
||||
id: expo
|
||||
run: echo "::set-output name=path::@react-navigation/react-navigation-example?release-channel=pr-${{ github.event.number }}"
|
||||
|
||||
- name: Comment on PR
|
||||
uses: unsplash/comment-on-pr@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
msg: The Expo app for the example from this branch is ready!<br><br>[expo.io/${{ steps.expo.outputs.path }}](https://expo.io/${{ steps.expo.outputs.path }})<br><br><a href="https://exp.host/${{ steps.expo.outputs.path }}"><img src="https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=exp://exp.host/${{ steps.expo.outputs.path }}" height="200px" width="200px"></a>.
|
||||
44
.github/workflows/expo.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: Expo Publish
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Install and publish
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 10.x
|
||||
|
||||
- name: Setup Expo
|
||||
uses: expo/expo-github-action@v5
|
||||
with:
|
||||
expo-version: 3.x
|
||||
expo-username: ${{ secrets.EXPO_CLI_USERNAME }}
|
||||
expo-password: ${{ secrets.EXPO_CLI_PASSWORD }}
|
||||
expo-cache: true
|
||||
|
||||
- name: Get yarn cache
|
||||
id: yarn-cache
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Publish Expo app
|
||||
working-directory: ./example
|
||||
run: expo publish
|
||||
27
.github/workflows/rebase.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Automatic Rebase
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
rebase:
|
||||
name: Rebase
|
||||
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Automatic Rebase
|
||||
uses: cirrus-actions/rebase@1.2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# https://github.community/t5/GitHub-Actions/Workflow-is-failing-if-no-job-can-be-ran-due-to-condition/m-p/38186#M3250
|
||||
always_job:
|
||||
name: Always run job
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Always run
|
||||
run: echo "This job is used to prevent the workflow to fail when all other jobs are skipped."
|
||||
@@ -16,5 +16,7 @@ module.exports = {
|
||||
plugins: [
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
'@babel/transform-flow-strip-types',
|
||||
'@babel/plugin-proposal-nullish-coalescing-operator',
|
||||
],
|
||||
};
|
||||
|
||||
@@ -5,3 +5,5 @@ If you want to run the example from the repo,
|
||||
- Clone the repository and run `yarn` in the project root
|
||||
- Run `yarn example start` to start the packager
|
||||
- Follow the instructions to open it with the [Expo app](https://expo.io/)
|
||||
|
||||
You can also run the currently published [app on Expo](https://expo.io/@react-navigation/react-navigation-example) on your Android device or iOS simulator or the [web app](https://react-navigation-example.netlify.com/) in your browser.
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
<facet type="android-gradle" name="Android-Gradle">
|
||||
<configuration>
|
||||
<option name="GRADLE_PROJECT_PATH" value=":app" />
|
||||
<option name="LAST_SUCCESSFUL_SYNC_AGP_VERSION" value="3.3.0" />
|
||||
<option name="LAST_KNOWN_AGP_VERSION" value="3.3.0" />
|
||||
<option name="LAST_SUCCESSFUL_SYNC_AGP_VERSION" value="3.4.2" />
|
||||
<option name="LAST_KNOWN_AGP_VERSION" value="3.4.2" />
|
||||
</configuration>
|
||||
</facet>
|
||||
<facet type="android" name="Android">
|
||||
@@ -19,8 +19,8 @@
|
||||
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
||||
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
|
||||
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
|
||||
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res;file://$MODULE_DIR$/build/generated/res/rs/debug;file://$MODULE_DIR$/build/generated/res/resValues/debug" />
|
||||
<option name="TEST_RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" />
|
||||
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res;file://$MODULE_DIR$/build/generated/res/resValues/debug" />
|
||||
<option name="TEST_RES_FOLDERS_RELATIVE_PATH" value="" />
|
||||
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
|
||||
</configuration>
|
||||
</facet>
|
||||
@@ -70,6 +70,7 @@
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/rncli/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
|
||||
@@ -90,91 +91,103 @@
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android API 28 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:collections:28.0.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.1.1@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.core:common:1.1.1@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-annotations:28.0.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.infer.annotation:infer-annotation:0.11.2@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: javax.inject:javax.inject:1@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: javax.inject:javax.inject:1@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.collection:collection:1.1.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-common:2.1.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.arch.core:core-common:2.1.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.annotation:annotation:1.1.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp-urlconnection:3.12.1@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.12.1@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.15.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.github.bumptech.glide:disklrucache:4.9.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.github.bumptech.glide:annotations:4.9.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.41@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.41@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.device.yearclass:yearclass:2.1.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: commons-codec:commons-codec:1.10@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: commons-io:commons-io:1.4@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.github.bumptech.glide:disklrucache:4.9.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.github.bumptech.glide:annotations:4.9.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.parse.bolts:bolts-tasks:1.4.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.react:react-native:0.61.5@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:appcompat-v7:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.appcompat:appcompat:1.1.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.browser:browser:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.github.bumptech.glide:glide:4.9.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.github.bumptech.glide:gifdecoder:4.9.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-location:16.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-base:16.0.1@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-places-placereport:16.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-tasks:16.0.1@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-basement:16.0.1@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-v4:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-fragment:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:customtabs:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-core-ui:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-core-utils:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:loader:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-media-compat:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:viewpager:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:coordinatorlayout:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:drawerlayout:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:slidingpanelayout:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:customview:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:swiperefreshlayout:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:asynclayoutinflater:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-compat:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:versionedparcelable:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:cursoradapter:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime:1.1.1@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:documentfile:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:localbroadcastmanager:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:print:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel:1.1.1@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.github.bumptech.glide:gifdecoder:4.9.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:interpolator:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata:1.1.1@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core:1.1.1@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.core:runtime:1.1.1@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.fresco:fresco:1.10.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-okhttp3:1.10.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.fresco:drawee:1.10.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline:1.10.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-base:1.10.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.legacy:legacy-support-v4:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.fragment:fragment:1.1.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.media:media:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.legacy:legacy-support-core-ui:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.legacy:legacy-support-core-utils:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.appcompat:appcompat-resources:1.1.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.drawerlayout:drawerlayout:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.viewpager:viewpager:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.loader:loader:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.activity:activity:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.vectordrawable:vectordrawable-animated:1.1.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.vectordrawable:vectordrawable:1.1.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.coordinatorlayout:coordinatorlayout:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.slidingpanelayout:slidingpanelayout:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.customview:customview:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.swiperefreshlayout:swiperefreshlayout:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.asynclayoutinflater:asynclayoutinflater:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.core:core:1.1.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.cursoradapter:cursoradapter:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.versionedparcelable:versionedparcelable:1.1.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.interpolator:interpolator:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-viewmodel:2.1.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-runtime:2.1.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.documentfile:documentfile:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.localbroadcastmanager:localbroadcastmanager:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.print:print:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.savedstate:savedstate:1.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-livedata:2.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-livedata-core:2.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: androidx.arch.core:core-runtime:2.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.fresco:fresco:2.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.fresco:fbcore:2.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.fresco:drawee:2.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline:2.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-base:2.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.soloader:soloader:0.6.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.fresco:nativeimagefilters:2.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.fresco:nativeimagetranscoder:2.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-okhttp3:2.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: io.nlopez.smartlocation:library:3.2.11@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.facebook.fresco:fbcore:1.10.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.webkit:android-jsc:r245459@aar" level="project" />
|
||||
<orderEntry type="module" module-name="android-expo-permissions" />
|
||||
<orderEntry type="module" module-name="android-expo-constants" />
|
||||
<orderEntry type="module" module-name="android-unimodules-image-loader-interface" />
|
||||
<orderEntry type="module" module-name="android-expo-web-browser" />
|
||||
<orderEntry type="module" module-name="android-unimodules-react-native-adapter" />
|
||||
<orderEntry type="module" module-name="android-expo-file-system" />
|
||||
<orderEntry type="module" module-name="android-expo-location" />
|
||||
<orderEntry type="module" module-name="android-expo-error-recovery" />
|
||||
<orderEntry type="module" module-name="android-unimodules-permissions-interface" />
|
||||
<orderEntry type="module" module-name="android-unimodules-core" />
|
||||
<orderEntry type="module" module-name="android-expo-app-loader-provider" />
|
||||
<orderEntry type="module" module-name="android-expo-font" />
|
||||
<orderEntry type="module" module-name="android-expo-keep-awake" />
|
||||
<orderEntry type="module" module-name="android-expo-linear-gradient" />
|
||||
<orderEntry type="module" module-name="android-expo-sqlite" />
|
||||
<orderEntry type="module" module-name="android-unimodules-barcode-scanner-interface" />
|
||||
<orderEntry type="module" module-name="android-unimodules-camera-interface" />
|
||||
<orderEntry type="module" module-name="android-unimodules-constants-interface" />
|
||||
<orderEntry type="module" module-name="android-unimodules-face-detector-interface" />
|
||||
<orderEntry type="module" module-name="android-unimodules-file-system-interface" />
|
||||
<orderEntry type="module" module-name="android-unimodules-font-interface" />
|
||||
<orderEntry type="module" module-name="android-unimodules-sensors-interface" />
|
||||
<orderEntry type="module" module-name="android-unimodules-task-manager-interface" />
|
||||
<orderEntry type="module" module-name="android-@react-native-community_masked-view" />
|
||||
<orderEntry type="module" module-name="android-react-native-gesture-handler" />
|
||||
<orderEntry type="module" module-name="android-react-native-reanimated" />
|
||||
<orderEntry type="module" module-name="react-native-safe-area-context" />
|
||||
<orderEntry type="module" module-name="react-native-screens" />
|
||||
<orderEntry type="module" module-name="react-native-reanimated" />
|
||||
<orderEntry type="module" module-name="react-native-gesture-handler" />
|
||||
<orderEntry type="module" module-name="expo-permissions" />
|
||||
<orderEntry type="module" module-name="unimodules-core" />
|
||||
<orderEntry type="module" module-name="unimodules-react-native-adapter" />
|
||||
<orderEntry type="module" module-name="expo-app-loader-provider" />
|
||||
<orderEntry type="module" module-name="expo-constants" />
|
||||
<orderEntry type="module" module-name="expo-file-system" />
|
||||
<orderEntry type="module" module-name="expo-font" />
|
||||
<orderEntry type="module" module-name="expo-keep-awake" />
|
||||
<orderEntry type="module" module-name="expo-linear-gradient" />
|
||||
<orderEntry type="module" module-name="expo-location" />
|
||||
<orderEntry type="module" module-name="expo-sqlite" />
|
||||
<orderEntry type="module" module-name="expo-web-browser" />
|
||||
<orderEntry type="module" module-name="unimodules-barcode-scanner-interface" />
|
||||
<orderEntry type="module" module-name="unimodules-camera-interface" />
|
||||
<orderEntry type="module" module-name="unimodules-constants-interface" />
|
||||
<orderEntry type="module" module-name="unimodules-face-detector-interface" />
|
||||
<orderEntry type="module" module-name="unimodules-file-system-interface" />
|
||||
<orderEntry type="module" module-name="unimodules-font-interface" />
|
||||
<orderEntry type="module" module-name="unimodules-image-loader-interface" />
|
||||
<orderEntry type="module" module-name="unimodules-permissions-interface" />
|
||||
<orderEntry type="module" module-name="unimodules-sensors-interface" />
|
||||
<orderEntry type="module" module-name="unimodules-task-manager-interface" />
|
||||
</component>
|
||||
</module>
|
||||
</module>
|
||||
@@ -36,12 +36,19 @@
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="rne" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
</application>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"displayName": "React Navigation Example",
|
||||
"expo": {
|
||||
"name": "@react-navigation/example",
|
||||
"owner": "react-navigation",
|
||||
"slug": "react-navigation-example",
|
||||
"description": "Demo app to showcase various functionality of React Navigation",
|
||||
"privacy": "public",
|
||||
|
||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
BIN
example/assets/album-art-04.jpg
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
example/assets/album-art-05.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
example/assets/album-art-06.jpg
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
example/assets/album-art-07.jpg
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
example/assets/album-art-08.jpg
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
example/assets/album-art-09.jpg
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
example/assets/album-art-10.jpg
Normal file
|
After Width: | Height: | Size: 229 KiB |
BIN
example/assets/album-art-11.jpg
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
example/assets/album-art-12.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
example/assets/album-art-13.jpg
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
example/assets/album-art-14.jpg
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
example/assets/album-art-15.jpg
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
example/assets/album-art-16.jpg
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
example/assets/album-art-17.jpg
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
example/assets/album-art-18.jpg
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
example/assets/album-art-19.jpg
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
example/assets/album-art-20.jpg
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
example/assets/album-art-21.jpg
Normal file
|
After Width: | Height: | Size: 186 KiB |
BIN
example/assets/album-art-22.jpg
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
example/assets/album-art-23.jpg
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
example/assets/album-art-24.jpg
Normal file
|
After Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 900 B After Width: | Height: | Size: 683 B |
@@ -2,6 +2,8 @@ PODS:
|
||||
- boost-for-react-native (1.63.0)
|
||||
- DoubleConversion (1.1.6)
|
||||
- EXAppLoaderProvider (8.0.0)
|
||||
- EXBlur (8.0.0):
|
||||
- UMCore
|
||||
- EXConstants (8.0.0):
|
||||
- UMConstantsInterface
|
||||
- UMCore
|
||||
@@ -48,6 +50,8 @@ PODS:
|
||||
- glog
|
||||
- glog (0.3.5)
|
||||
- RCTRequired (0.61.5)
|
||||
- RCTRestart (0.0.13):
|
||||
- React
|
||||
- RCTTypeSafety (0.61.5):
|
||||
- FBLazyVector (= 0.61.5)
|
||||
- Folly (= 2018.10.22.00)
|
||||
@@ -249,11 +253,11 @@ PODS:
|
||||
- ReactCommon/jscallinvoker (= 0.61.5)
|
||||
- RNCMaskedView (0.1.5):
|
||||
- React
|
||||
- RNGestureHandler (1.5.3):
|
||||
- RNGestureHandler (1.5.5):
|
||||
- React
|
||||
- RNReanimated (1.4.0):
|
||||
- React
|
||||
- RNScreens (2.0.0-alpha.22):
|
||||
- RNScreens (2.0.0-alpha.33):
|
||||
- React
|
||||
- UMBarCodeScannerInterface (5.0.0)
|
||||
- UMCameraInterface (5.0.0)
|
||||
@@ -275,6 +279,7 @@ PODS:
|
||||
DEPENDENCIES:
|
||||
- DoubleConversion (from `../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
||||
- EXAppLoaderProvider (from `../../node_modules/expo-app-loader-provider/ios`)
|
||||
- EXBlur (from `../../node_modules/expo-blur/ios`)
|
||||
- EXConstants (from `../../node_modules/expo-constants/ios`)
|
||||
- EXErrorRecovery (from `../../node_modules/expo-error-recovery/ios`)
|
||||
- EXFileSystem (from `../../node_modules/expo-file-system/ios`)
|
||||
@@ -290,6 +295,7 @@ DEPENDENCIES:
|
||||
- Folly (from `../../node_modules/react-native/third-party-podspecs/Folly.podspec`)
|
||||
- glog (from `../../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||
- RCTRequired (from `../../node_modules/react-native/Libraries/RCTRequired`)
|
||||
- RCTRestart (from `../../node_modules/react-native-restart/ios`)
|
||||
- RCTTypeSafety (from `../../node_modules/react-native/Libraries/TypeSafety`)
|
||||
- React (from `../../node_modules/react-native/`)
|
||||
- React-Core (from `../../node_modules/react-native/`)
|
||||
@@ -340,6 +346,9 @@ EXTERNAL SOURCES:
|
||||
EXAppLoaderProvider:
|
||||
:path: !ruby/object:Pathname
|
||||
path: "../../node_modules/expo-app-loader-provider/ios"
|
||||
EXBlur:
|
||||
:path: !ruby/object:Pathname
|
||||
path: "../../node_modules/expo-blur/ios"
|
||||
EXConstants:
|
||||
:path: !ruby/object:Pathname
|
||||
path: "../../node_modules/expo-constants/ios"
|
||||
@@ -380,6 +389,8 @@ EXTERNAL SOURCES:
|
||||
:podspec: "../../node_modules/react-native/third-party-podspecs/glog.podspec"
|
||||
RCTRequired:
|
||||
:path: "../../node_modules/react-native/Libraries/RCTRequired"
|
||||
RCTRestart:
|
||||
:path: "../../node_modules/react-native-restart/ios"
|
||||
RCTTypeSafety:
|
||||
:path: "../../node_modules/react-native/Libraries/TypeSafety"
|
||||
React:
|
||||
@@ -469,6 +480,7 @@ SPEC CHECKSUMS:
|
||||
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
||||
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
|
||||
EXAppLoaderProvider: ebdb6bc2632c1ccadbe49f5e4104d8d690969c49
|
||||
EXBlur: d1604f66f89a9414f5ee65dfb23874437c1bb147
|
||||
EXConstants: 4051b16c17ef3defa03c541d42811dc92b249146
|
||||
EXErrorRecovery: d36db99ec6a3808f313f01b0890eb443796dd1c2
|
||||
EXFileSystem: 6e0d9bb6cc4ea404dbb8f583c1a8a2dcdf4b83b6
|
||||
@@ -484,6 +496,7 @@ SPEC CHECKSUMS:
|
||||
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
|
||||
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
|
||||
RCTRequired: b153add4da6e7dbc44aebf93f3cf4fcae392ddf1
|
||||
RCTRestart: dd19aab87fc1118e05b6b5b91b959105647f56b4
|
||||
RCTTypeSafety: 9aa1b91d7f9310fc6eadc3cf95126ffe818af320
|
||||
React: b6a59ef847b2b40bb6e0180a97d0ca716969ac78
|
||||
React-Core: 688b451f7d616cc1134ac95295b593d1b5158a04
|
||||
@@ -504,9 +517,9 @@ SPEC CHECKSUMS:
|
||||
React-RCTVibration: a49a1f42bf8f5acf1c3e297097517c6b3af377ad
|
||||
ReactCommon: 198c7c8d3591f975e5431bec1b0b3b581aa1c5dd
|
||||
RNCMaskedView: dd13f9f7b146a9ad82f9b7eb6c9b5548fcf6e990
|
||||
RNGestureHandler: 02905abe54e1f6e59c081a10b4bd689721e17aa6
|
||||
RNGestureHandler: d2270608171c868581b840cfc692f2962c05cd17
|
||||
RNReanimated: b2ab0b693dddd2339bd2f300e770f6302d2e960c
|
||||
RNScreens: 6adf01eb4080f44af6cca551207c6b0505066857
|
||||
RNScreens: 1c7fd499b915c77c21e8e6c327890c5af9b4cf7e
|
||||
UMBarCodeScannerInterface: 3802c8574ef119c150701d679ab386e2266d6a54
|
||||
UMCameraInterface: 985d301f688ed392f815728f0dd906ca34b7ccb1
|
||||
UMConstantsInterface: bda5f8bd3403ad99e663eb3c4da685d063c5653c
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#import <UMCore/UMModuleRegistry.h>
|
||||
#import <UMReactNativeAdapter/UMNativeModulesProxy.h>
|
||||
#import <UMReactNativeAdapter/UMModuleRegistryAdapter.h>
|
||||
#import <React/RCTLinkingManager.h>
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
@@ -52,4 +53,10 @@
|
||||
#endif
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
|
||||
{
|
||||
return [RCTLinkingManager application:app openURL:url options:options];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -20,33 +20,27 @@
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>React Navigation Example</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>rne</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/ -->
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
@@ -78,5 +72,19 @@
|
||||
<string>Give React Navigation Example periences permission to access your photos</string>
|
||||
<key>NSRemindersUsageDescription</key>
|
||||
<string>Allow React Navigation Example to access your reminders</string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -6,7 +6,7 @@ const escape = require('escape-string-regexp');
|
||||
const blacklist = require('metro-config/src/defaults/blacklist');
|
||||
|
||||
const root = path.resolve(__dirname, '..');
|
||||
const packages = path.resolve(__dirname, '..', 'packages');
|
||||
const packages = path.resolve(root, 'packages');
|
||||
|
||||
// Get the list of dependencies for all packages in the monorepo
|
||||
const modules = ['@expo/vector-icons']
|
||||
@@ -22,7 +22,7 @@ const modules = ['@expo/vector-icons']
|
||||
);
|
||||
|
||||
// We need to collect list of deps that this package imports
|
||||
// Collecting both dependencies are peerDependencies sould do it
|
||||
// Collecting both dependencies are peerDependencies should do it
|
||||
return Object.keys({
|
||||
...pak.dependencies,
|
||||
...pak.peerDependencies,
|
||||
@@ -59,7 +59,7 @@ module.exports = {
|
||||
// When we import a package from the monorepo, metro won't be able to find their deps
|
||||
// We need to specify them in `extraNodeModules` to tell metro where to find them
|
||||
extraNodeModules: modules.reduce((acc, name) => {
|
||||
acc[name] = path.join(__dirname, '..', 'node_modules', name);
|
||||
acc[name] = path.join(root, 'node_modules', name);
|
||||
return acc;
|
||||
}, {}),
|
||||
},
|
||||
@@ -67,11 +67,11 @@ module.exports = {
|
||||
server: {
|
||||
enhanceMiddleware: middleware => {
|
||||
return (req, res, next) => {
|
||||
// When an asset is imported outside the project root, it has wrong path on Android
|
||||
// This happens for the back button in stack, so we fix the path to correct one
|
||||
const assets = '/packages/stack/src/views/assets';
|
||||
|
||||
if (req.url.startsWith(assets)) {
|
||||
// When an asset is imported outside the project root, it has wrong path on Android
|
||||
// This happens for the back button in stack, so we fix the path to correct one
|
||||
req.url = req.url.replace(
|
||||
assets,
|
||||
'/assets/../packages/stack/src/views/assets'
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "expo start",
|
||||
"web": "expo start --web",
|
||||
"web": "expo start:web",
|
||||
"native": "react-native start",
|
||||
"android": "react-native run-android",
|
||||
"ios": "react-native run-ios"
|
||||
@@ -13,6 +13,7 @@
|
||||
"dependencies": {
|
||||
"@expo/vector-icons": "^10.0.0",
|
||||
"@react-native-community/masked-view": "0.1.5",
|
||||
"@types/react-native-restart": "^0.0.0",
|
||||
"color": "^3.1.2",
|
||||
"expo": "^36.0.2",
|
||||
"expo-asset": "~8.0.0",
|
||||
@@ -20,12 +21,13 @@
|
||||
"react": "~16.9.0",
|
||||
"react-dom": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-gesture-handler": "~1.5.3",
|
||||
"react-native-gesture-handler": "^1.5.5",
|
||||
"react-native-paper": "^3.5.0",
|
||||
"react-native-reanimated": "^1.4.0",
|
||||
"react-native-restart": "^0.0.13",
|
||||
"react-native-safe-area-context": "^0.6.2",
|
||||
"react-native-screens": "^2.0.0-alpha.25",
|
||||
"react-native-tab-view": "2.11.0",
|
||||
"react-native-screens": "^2.0.0-alpha.33",
|
||||
"react-native-tab-view": "2.13.0",
|
||||
"react-native-unimodules": "^0.7.0",
|
||||
"react-native-web": "^0.11.7"
|
||||
},
|
||||
|
||||
@@ -1 +1 @@
|
||||
export default [];
|
||||
export default ['rne://127.0.0.1:19000/--/'];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { View, TextInput, ActivityIndicator, StyleSheet } from 'react-native';
|
||||
import { Title, Button } from 'react-native-paper';
|
||||
import { ParamListBase } from '@react-navigation/native';
|
||||
import { useTheme, ParamListBase } from '@react-navigation/native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
HeaderBackButton,
|
||||
@@ -40,11 +40,25 @@ const SplashScreen = () => {
|
||||
|
||||
const SignInScreen = () => {
|
||||
const { signIn } = React.useContext(AuthContext);
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<View style={styles.content}>
|
||||
<TextInput placeholder="Username" style={styles.input} />
|
||||
<TextInput placeholder="Password" secureTextEntry style={styles.input} />
|
||||
<TextInput
|
||||
placeholder="Username"
|
||||
style={[
|
||||
styles.input,
|
||||
{ backgroundColor: colors.card, color: colors.text },
|
||||
]}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="Password"
|
||||
secureTextEntry
|
||||
style={[
|
||||
styles.input,
|
||||
{ backgroundColor: colors.card, color: colors.text },
|
||||
]}
|
||||
/>
|
||||
<Button mode="contained" onPress={signIn} style={styles.button}>
|
||||
Sign in
|
||||
</Button>
|
||||
@@ -73,6 +87,7 @@ type Props = {
|
||||
|
||||
type State = {
|
||||
isLoading: boolean;
|
||||
isSignout: boolean;
|
||||
userToken: undefined | string;
|
||||
};
|
||||
|
||||
@@ -94,17 +109,20 @@ export default function SimpleStackScreen({ navigation }: Props) {
|
||||
case 'SIGN_IN':
|
||||
return {
|
||||
...prevState,
|
||||
isSignout: false,
|
||||
userToken: action.token,
|
||||
};
|
||||
case 'SIGN_OUT':
|
||||
return {
|
||||
...prevState,
|
||||
isSignout: true,
|
||||
userToken: undefined,
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
isLoading: true,
|
||||
isSignout: false,
|
||||
userToken: undefined,
|
||||
}
|
||||
);
|
||||
@@ -147,7 +165,10 @@ export default function SimpleStackScreen({ navigation }: Props) {
|
||||
) : state.userToken === undefined ? (
|
||||
<SimpleStack.Screen
|
||||
name="SignIn"
|
||||
options={{ title: 'Sign in' }}
|
||||
options={{
|
||||
title: 'Sign in',
|
||||
animationTypeForReplace: state.isSignout ? 'pop' : 'push',
|
||||
}}
|
||||
component={SignInScreen}
|
||||
/>
|
||||
) : (
|
||||
@@ -171,7 +192,6 @@ const styles = StyleSheet.create({
|
||||
input: {
|
||||
margin: 8,
|
||||
padding: 10,
|
||||
backgroundColor: 'white',
|
||||
borderRadius: 3,
|
||||
borderWidth: StyleSheet.hairlineWidth,
|
||||
borderColor: 'rgba(0, 0, 0, 0.08)',
|
||||
|
||||
154
example/src/Screens/StackTransparent.tsx
Normal file
@@ -0,0 +1,154 @@
|
||||
import * as React from 'react';
|
||||
import { View, StyleSheet, ScrollView } from 'react-native';
|
||||
import { Button, Paragraph } from 'react-native-paper';
|
||||
import { RouteProp, ParamListBase, useTheme } from '@react-navigation/native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
StackNavigationProp,
|
||||
} from '@react-navigation/stack';
|
||||
import Article from '../Shared/Article';
|
||||
|
||||
type SimpleStackParams = {
|
||||
Article: { author: string };
|
||||
Dialog: undefined;
|
||||
};
|
||||
|
||||
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
|
||||
|
||||
const ArticleScreen = ({
|
||||
navigation,
|
||||
route,
|
||||
}: {
|
||||
navigation: SimpleStackNavigation;
|
||||
route: RouteProp<SimpleStackParams, 'Article'>;
|
||||
}) => {
|
||||
return (
|
||||
<ScrollView>
|
||||
<View style={styles.buttons}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => navigation.push('Dialog')}
|
||||
style={styles.button}
|
||||
>
|
||||
Show Dialog
|
||||
</Button>
|
||||
<Button
|
||||
mode="outlined"
|
||||
onPress={() => navigation.goBack()}
|
||||
style={styles.button}
|
||||
>
|
||||
Go back
|
||||
</Button>
|
||||
</View>
|
||||
<Article author={{ name: route.params.author }} scrollEnabled={false} />
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
const DialogScreen = ({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: SimpleStackNavigation;
|
||||
}) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={[styles.dialog, { backgroundColor: colors.card }]}>
|
||||
<Paragraph>
|
||||
Mise en place is a French term that literally means “put in place.” It
|
||||
also refers to a way cooks in professional kitchens and restaurants
|
||||
set up their work stations—first by gathering all ingredients for a
|
||||
recipes, partially preparing them (like measuring out and chopping),
|
||||
and setting them all near each other. Setting up mise en place before
|
||||
cooking is another top tip for home cooks, as it seriously helps with
|
||||
organization. It’ll pretty much guarantee you never forget to add an
|
||||
ingredient and save you time from running back and forth from the
|
||||
pantry ten times.
|
||||
</Paragraph>
|
||||
<Button style={styles.close} compact onPress={navigation.goBack}>
|
||||
Okay
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const SimpleStack = createStackNavigator<SimpleStackParams>();
|
||||
|
||||
type Props = Partial<React.ComponentProps<typeof SimpleStack.Navigator>> & {
|
||||
navigation: StackNavigationProp<ParamListBase>;
|
||||
};
|
||||
|
||||
export default function SimpleStackScreen({ navigation, ...rest }: Props) {
|
||||
navigation.setOptions({
|
||||
headerShown: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<SimpleStack.Navigator mode="modal" {...rest}>
|
||||
<SimpleStack.Screen
|
||||
name="Article"
|
||||
component={ArticleScreen}
|
||||
initialParams={{ author: 'Gandalf' }}
|
||||
/>
|
||||
<SimpleStack.Screen
|
||||
name="Dialog"
|
||||
component={DialogScreen}
|
||||
options={{
|
||||
headerShown: false,
|
||||
cardStyle: { backgroundColor: 'transparent' },
|
||||
cardOverlayEnabled: true,
|
||||
cardStyleInterpolator: ({ current: { progress } }) => ({
|
||||
cardStyle: {
|
||||
opacity: progress.interpolate({
|
||||
inputRange: [0, 0.5, 0.9, 1],
|
||||
outputRange: [0, 0.25, 0.7, 1],
|
||||
}),
|
||||
transform: [
|
||||
{
|
||||
scale: progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0.9, 1],
|
||||
extrapolate: 'clamp',
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
overlayStyle: {
|
||||
opacity: progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 0.5],
|
||||
extrapolate: 'clamp',
|
||||
}),
|
||||
},
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</SimpleStack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
buttons: {
|
||||
flexDirection: 'row',
|
||||
padding: 8,
|
||||
},
|
||||
button: {
|
||||
margin: 8,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
dialog: {
|
||||
padding: 16,
|
||||
width: '90%',
|
||||
maxWidth: 400,
|
||||
borderRadius: 3,
|
||||
},
|
||||
close: {
|
||||
alignSelf: 'flex-end',
|
||||
},
|
||||
});
|
||||
@@ -2,23 +2,41 @@
|
||||
|
||||
import * as React from 'react';
|
||||
import {
|
||||
View,
|
||||
Image,
|
||||
Dimensions,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
ScrollViewProps,
|
||||
Dimensions,
|
||||
Platform,
|
||||
} from 'react-native';
|
||||
import { useScrollToTop } from '@react-navigation/native';
|
||||
|
||||
const COVERS = [
|
||||
require('../../assets/album-art-1.jpg'),
|
||||
require('../../assets/album-art-2.jpg'),
|
||||
require('../../assets/album-art-3.jpg'),
|
||||
require('../../assets/album-art-4.jpg'),
|
||||
require('../../assets/album-art-5.jpg'),
|
||||
require('../../assets/album-art-6.jpg'),
|
||||
require('../../assets/album-art-7.jpg'),
|
||||
require('../../assets/album-art-8.jpg'),
|
||||
require('../../assets/album-art-01.jpg'),
|
||||
require('../../assets/album-art-02.jpg'),
|
||||
require('../../assets/album-art-03.jpg'),
|
||||
require('../../assets/album-art-04.jpg'),
|
||||
require('../../assets/album-art-05.jpg'),
|
||||
require('../../assets/album-art-06.jpg'),
|
||||
require('../../assets/album-art-07.jpg'),
|
||||
require('../../assets/album-art-08.jpg'),
|
||||
require('../../assets/album-art-09.jpg'),
|
||||
require('../../assets/album-art-10.jpg'),
|
||||
require('../../assets/album-art-11.jpg'),
|
||||
require('../../assets/album-art-12.jpg'),
|
||||
require('../../assets/album-art-13.jpg'),
|
||||
require('../../assets/album-art-14.jpg'),
|
||||
require('../../assets/album-art-15.jpg'),
|
||||
require('../../assets/album-art-16.jpg'),
|
||||
require('../../assets/album-art-17.jpg'),
|
||||
require('../../assets/album-art-18.jpg'),
|
||||
require('../../assets/album-art-19.jpg'),
|
||||
require('../../assets/album-art-20.jpg'),
|
||||
require('../../assets/album-art-21.jpg'),
|
||||
require('../../assets/album-art-22.jpg'),
|
||||
require('../../assets/album-art-23.jpg'),
|
||||
require('../../assets/album-art-24.jpg'),
|
||||
];
|
||||
|
||||
export default function Albums(props: Partial<ScrollViewProps>) {
|
||||
@@ -27,34 +45,46 @@ export default function Albums(props: Partial<ScrollViewProps>) {
|
||||
useScrollToTop(ref);
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
ref={ref}
|
||||
style={styles.container}
|
||||
contentContainerStyle={styles.content}
|
||||
{...props}
|
||||
>
|
||||
<ScrollView ref={ref} contentContainerStyle={styles.content} {...props}>
|
||||
{COVERS.map((source, i) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<Image key={i} source={source} style={styles.cover} />
|
||||
))}
|
||||
{COVERS.map((source, i) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<Image key={i + 'F'} source={source} style={styles.cover} />
|
||||
<View key={i} style={styles.item}>
|
||||
<Image source={source} style={styles.photo} />
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: '#000',
|
||||
},
|
||||
content: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
cover: {
|
||||
width: '50%',
|
||||
height: Dimensions.get('window').width / 2,
|
||||
...Platform.select({
|
||||
web: {
|
||||
content: {
|
||||
display: 'grid' as 'none',
|
||||
gridTemplateColumns: 'repeat(auto-fill, minmax(150px, 1fr))',
|
||||
},
|
||||
item: {
|
||||
width: '100%',
|
||||
},
|
||||
},
|
||||
default: {
|
||||
content: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
item: {
|
||||
height: Dimensions.get('window').width / 2,
|
||||
width: '50%',
|
||||
},
|
||||
},
|
||||
}),
|
||||
photo: {
|
||||
flex: 1,
|
||||
resizeMode: 'cover',
|
||||
paddingTop: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
StatusBar,
|
||||
I18nManager,
|
||||
} from 'react-native';
|
||||
import RNRestart from 'react-native-restart';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import {
|
||||
Provider as PaperProvider,
|
||||
@@ -21,7 +22,7 @@ import {
|
||||
InitialState,
|
||||
useLinking,
|
||||
NavigationContainerRef,
|
||||
NavigationNativeContainer,
|
||||
NavigationContainer,
|
||||
DefaultTheme,
|
||||
DarkTheme,
|
||||
} from '@react-navigation/native';
|
||||
@@ -40,6 +41,7 @@ import LinkingPrefixes from './LinkingPrefixes';
|
||||
import SimpleStack from './Screens/SimpleStack';
|
||||
import NativeStack from './Screens/NativeStack';
|
||||
import ModalPresentationStack from './Screens/ModalPresentationStack';
|
||||
import StackTransparent from './Screens/StackTransparent';
|
||||
import StackHeaderCustomization from './Screens/StackHeaderCustomization';
|
||||
import BottomTabs from './Screens/BottomTabs';
|
||||
import MaterialTopTabsScreen from './Screens/MaterialTopTabs';
|
||||
@@ -70,6 +72,10 @@ const SCREENS = {
|
||||
title: 'Modal Presentation Stack',
|
||||
component: ModalPresentationStack,
|
||||
},
|
||||
StackTransparent: {
|
||||
title: 'Transparent Stack',
|
||||
component: StackTransparent,
|
||||
},
|
||||
StackHeaderCustomization: {
|
||||
title: 'Header Customization in Stack',
|
||||
component: StackHeaderCustomization,
|
||||
@@ -111,22 +117,27 @@ export default function App() {
|
||||
// To test deep linking on, run the following in the Terminal:
|
||||
// Android: adb shell am start -a android.intent.action.VIEW -d "exp://127.0.0.1:19000/--/simple-stack"
|
||||
// iOS: xcrun simctl openurl booted exp://127.0.0.1:19000/--/simple-stack
|
||||
// Android (bare): adb shell am start -a android.intent.action.VIEW -d "rne://127.0.0.1:19000/--/simple-stack"
|
||||
// iOS (bare): xcrun simctl openurl booted rne://127.0.0.1:19000/--/simple-stack
|
||||
// The first segment of the link is the the scheme + host (returned by `Linking.makeUrl`)
|
||||
const { getInitialState } = useLinking(containerRef, {
|
||||
prefixes: LinkingPrefixes,
|
||||
config: {
|
||||
Root: Object.keys(SCREENS).reduce<{ [key: string]: string }>(
|
||||
(acc, name) => {
|
||||
// Convert screen names such as SimpleStack to kebab case (simple-stack)
|
||||
acc[name] = name
|
||||
.replace(/([A-Z]+)/g, '-$1')
|
||||
.replace(/^-/, '')
|
||||
.toLowerCase();
|
||||
Root: {
|
||||
path: 'root',
|
||||
screens: Object.keys(SCREENS).reduce<{ [key: string]: string }>(
|
||||
(acc, name) => {
|
||||
// Convert screen names such as SimpleStack to kebab case (simple-stack)
|
||||
acc[name] = name
|
||||
.replace(/([A-Z]+)/g, '-$1')
|
||||
.replace(/^-/, '')
|
||||
.toLowerCase();
|
||||
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
),
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -142,7 +153,7 @@ export default function App() {
|
||||
try {
|
||||
let state = await getInitialState();
|
||||
|
||||
if (state === undefined) {
|
||||
if (Platform.OS !== 'web' && state === undefined) {
|
||||
const savedState = await AsyncStorage.getItem(
|
||||
NAVIGATION_PERSISTENCE_KEY
|
||||
);
|
||||
@@ -191,7 +202,7 @@ export default function App() {
|
||||
{Platform.OS === 'ios' && (
|
||||
<StatusBar barStyle={theme.dark ? 'light-content' : 'dark-content'} />
|
||||
)}
|
||||
<NavigationNativeContainer
|
||||
<NavigationContainer
|
||||
ref={containerRef}
|
||||
initialState={initialState}
|
||||
onStateChange={state =>
|
||||
@@ -248,7 +259,12 @@ export default function App() {
|
||||
value={I18nManager.isRTL}
|
||||
onValueChange={() => {
|
||||
I18nManager.forceRTL(!I18nManager.isRTL);
|
||||
Updates.reloadFromCache();
|
||||
// @ts-ignore
|
||||
if (global.Expo) {
|
||||
Updates.reloadFromCache();
|
||||
} else {
|
||||
RNRestart.Restart();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Divider />
|
||||
@@ -270,7 +286,7 @@ export default function App() {
|
||||
<List.Item
|
||||
key={name}
|
||||
title={SCREENS[name].title}
|
||||
onPress={() => navigation.push(name)}
|
||||
onPress={() => navigation.navigate(name)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
@@ -291,7 +307,7 @@ export default function App() {
|
||||
)}
|
||||
</Drawer.Screen>
|
||||
</Drawer.Navigator>
|
||||
</NavigationNativeContainer>
|
||||
</NavigationContainer>
|
||||
</PaperProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ const packages = path.resolve(__dirname, '..', 'packages');
|
||||
module.exports = async function(env, argv) {
|
||||
const config = await createExpoWebpackConfigAsync(env, argv);
|
||||
|
||||
config.context = path.resolve(__dirname, '..');
|
||||
|
||||
config.module.rules.push({
|
||||
test: /\.(js|ts|tsx)$/,
|
||||
include: /(packages|example)\/.+/,
|
||||
|
||||
@@ -64,7 +64,8 @@
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"@react-navigation/([^/]+)": "<rootDir>/packages/$1/src"
|
||||
}
|
||||
},
|
||||
"preset": "react-native"
|
||||
},
|
||||
"prettier": {
|
||||
"tabWidth": 2,
|
||||
|
||||
@@ -3,6 +3,52 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.0.0-alpha.45](https://github.com/react-navigation/navigation-ex/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.0-alpha.44...@react-navigation/bottom-tabs@5.0.0-alpha.45) (2020-02-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.44](https://github.com/react-navigation/navigation-ex/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.0-alpha.43...@react-navigation/bottom-tabs@5.0.0-alpha.44) (2020-02-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.43](https://github.com/react-navigation/navigation-ex/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.0-alpha.42...@react-navigation/bottom-tabs@5.0.0-alpha.43) (2020-02-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.42](https://github.com/react-navigation/navigation-ex/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.0-alpha.39...@react-navigation/bottom-tabs@5.0.0-alpha.42) (2020-02-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add licenses ([0c159db](https://github.com/react-navigation/navigation-ex/tree/master/packages/bottom-tabs/commit/0c159db4c9bc85e83b5cfe6819ab2562669a4d8f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.40](https://github.com/react-navigation/navigation-ex/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.0-alpha.39...@react-navigation/bottom-tabs@5.0.0-alpha.40) (2020-02-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add licenses ([0c159db](https://github.com/react-navigation/navigation-ex/tree/master/packages/bottom-tabs/commit/0c159db4c9bc85e83b5cfe6819ab2562669a4d8f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.39](https://github.com/react-navigation/navigation-ex/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.0-alpha.38...@react-navigation/bottom-tabs@5.0.0-alpha.39) (2020-01-24)
|
||||
|
||||
|
||||
|
||||
21
packages/bottom-tabs/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 React Navigation Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -10,7 +10,7 @@
|
||||
"android",
|
||||
"tab"
|
||||
],
|
||||
"version": "5.0.0-alpha.39",
|
||||
"version": "5.0.0-alpha.45",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/react-navigation/navigation-ex/tree/master/packages/bottom-tabs",
|
||||
"main": "lib/commonjs/index.js",
|
||||
@@ -30,7 +30,7 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.0.0-alpha.27",
|
||||
"@react-navigation/routers": "^5.0.0-alpha.33",
|
||||
"color": "^3.1.2",
|
||||
"react-native-iphone-x-helper": "^1.2.1"
|
||||
},
|
||||
|
||||
@@ -99,6 +99,12 @@ export type BottomTabNavigationOptions = {
|
||||
* Renders `TouchableWithoutFeedback` by default.
|
||||
*/
|
||||
tabBarButton?: (props: BottomTabBarButtonProps) => React.ReactNode;
|
||||
|
||||
/**
|
||||
* Whether this screen should be unmounted when navigating away from it.
|
||||
* Defaults to `false`.
|
||||
*/
|
||||
unmountOnBlur?: boolean;
|
||||
};
|
||||
|
||||
export type BottomTabDescriptor = Descriptor<
|
||||
@@ -118,11 +124,6 @@ export type BottomTabNavigationConfig = {
|
||||
* Set it to `false` if you want to render all screens on initial render.
|
||||
*/
|
||||
lazy?: boolean;
|
||||
/**
|
||||
* Whether a screen should be unmounted when navigating away from it.
|
||||
* Defaults to `false`.
|
||||
*/
|
||||
unmountInactiveScreens?: boolean;
|
||||
/**
|
||||
* Function that returns a React element to display as the tab bar.
|
||||
*/
|
||||
|
||||
@@ -92,7 +92,7 @@ export default class BottomTabView extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { state, descriptors, lazy, unmountInactiveScreens } = this.props;
|
||||
const { state, descriptors, lazy } = this.props;
|
||||
const { routes } = state;
|
||||
const { loaded } = this.state;
|
||||
|
||||
@@ -101,17 +101,19 @@ export default class BottomTabView extends React.Component<Props, State> {
|
||||
<View style={styles.container}>
|
||||
<ScreenContainer style={styles.pages}>
|
||||
{routes.map((route, index) => {
|
||||
if (unmountInactiveScreens && index !== state.index) {
|
||||
const descriptor = descriptors[route.key];
|
||||
const { unmountOnBlur } = descriptor.options;
|
||||
const isFocused = state.index === index;
|
||||
|
||||
if (unmountOnBlur && !isFocused) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (lazy && !loaded.includes(index)) {
|
||||
if (lazy && !loaded.includes(index) && !isFocused) {
|
||||
// Don't render a screen if we've never navigated to it
|
||||
return null;
|
||||
}
|
||||
|
||||
const isFocused = state.index === index;
|
||||
|
||||
return (
|
||||
<ResourceSavingScene
|
||||
key={route.key}
|
||||
@@ -119,7 +121,7 @@ export default class BottomTabView extends React.Component<Props, State> {
|
||||
isVisible={isFocused}
|
||||
>
|
||||
<SceneContent isFocused={isFocused}>
|
||||
{descriptors[route.key].render()}
|
||||
{descriptor.render()}
|
||||
</SceneContent>
|
||||
</ResourceSavingScene>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,54 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.0.0-alpha.34](https://github.com/react-navigation/navigation-ex/tree/master/packages/compat/compare/@react-navigation/compat@5.0.0-alpha.33...@react-navigation/compat@5.0.0-alpha.34) (2020-02-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.33](https://github.com/react-navigation/navigation-ex/tree/master/packages/compat/compare/@react-navigation/compat@5.0.0-alpha.32...@react-navigation/compat@5.0.0-alpha.33) (2020-02-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.32](https://github.com/react-navigation/navigation-ex/tree/master/packages/compat/compare/@react-navigation/compat@5.0.0-alpha.31...@react-navigation/compat@5.0.0-alpha.32) (2020-02-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.31](https://github.com/react-navigation/navigation-ex/tree/master/packages/compat/compare/@react-navigation/compat@5.0.0-alpha.28...@react-navigation/compat@5.0.0-alpha.31) (2020-02-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add licenses ([0c159db](https://github.com/react-navigation/navigation-ex/tree/master/packages/compat/commit/0c159db4c9bc85e83b5cfe6819ab2562669a4d8f))
|
||||
* throw when assigning or accessing the router property in compat ([944fa35](https://github.com/react-navigation/navigation-ex/tree/master/packages/compat/commit/944fa35ed4778ebc7fa7cd50092719cbd5bf3caf))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.29](https://github.com/react-navigation/navigation-ex/tree/master/packages/compat/compare/@react-navigation/compat@5.0.0-alpha.28...@react-navigation/compat@5.0.0-alpha.29) (2020-02-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add licenses ([0c159db](https://github.com/react-navigation/navigation-ex/tree/master/packages/compat/commit/0c159db4c9bc85e83b5cfe6819ab2562669a4d8f))
|
||||
* throw when assigning or accessing the router property in compat ([944fa35](https://github.com/react-navigation/navigation-ex/tree/master/packages/compat/commit/944fa35ed4778ebc7fa7cd50092719cbd5bf3caf))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.28](https://github.com/react-navigation/navigation-ex/tree/master/packages/compat/compare/@react-navigation/compat@5.0.0-alpha.27...@react-navigation/compat@5.0.0-alpha.28) (2020-01-24)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
21
packages/compat/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 React Navigation Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/compat",
|
||||
"description": "Compatibility layer to write navigator definitions in static configuration format",
|
||||
"version": "5.0.0-alpha.28",
|
||||
"version": "5.0.0-alpha.34",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/react-navigation/navigation-ex/tree/master/packages/compat",
|
||||
"main": "lib/commonjs/index.js",
|
||||
@@ -21,7 +21,7 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.0.0-alpha.27"
|
||||
"@react-navigation/routers": "^5.0.0-alpha.33"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^16.9.17",
|
||||
|
||||
@@ -165,7 +165,25 @@ export default function createCompatNavigatorFactory<
|
||||
return Navigator;
|
||||
};
|
||||
|
||||
createCompatNavigator.isCompat = true;
|
||||
Object.defineProperties(createCompatNavigator, {
|
||||
isCompat: {
|
||||
get() {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
router: {
|
||||
get() {
|
||||
throw new Error(
|
||||
"It's no longer possible to access the router with the 'router' property."
|
||||
);
|
||||
},
|
||||
set() {
|
||||
throw new Error(
|
||||
"It's no longer possible to override the router by assigning the 'router' property."
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return createCompatNavigator;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,81 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.0.0-alpha.43](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/compare/@react-navigation/core@5.0.0-alpha.42...@react-navigation/core@5.0.0-alpha.43) (2020-02-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve error message for unhandled action ([ca4a360](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/ca4a36070a21c4fe86cb1cc55a4452dca293f215))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add initialRouteName property to config ([#322](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/issues/322)) ([4ca5cc6](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/4ca5cc632992187f12870281e4cf4c7d1f799967))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.42](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/compare/@react-navigation/core@5.0.0-alpha.41...@react-navigation/core@5.0.0-alpha.42) (2020-02-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.41](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/compare/@react-navigation/core@5.0.0-alpha.40...@react-navigation/core@5.0.0-alpha.41) (2020-02-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ignore circular references when checking serializable ([e5063b9](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/e5063b93398350511f3fd2ef48425559f871781f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.40](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/compare/@react-navigation/core@5.0.0-alpha.37...@react-navigation/core@5.0.0-alpha.40) (2020-02-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add licenses ([0c159db](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/0c159db4c9bc85e83b5cfe6819ab2562669a4d8f))
|
||||
* add warning when passing inline function to component prop ([fa4a959](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/fa4a959549ccd9dc2f9bd2ea495e99abdedc9f94))
|
||||
* tweak error messages for validation ([2243b45](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/2243b45cc1addf83727166d82736d214f181b1fb))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add `screens` prop for nested configs ([#308](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/issues/308)) ([b931ae6](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/b931ae62dfb2c5253c94ea5ace73e9070ec17c4a))
|
||||
* add useIsDrawerOpen hook ([#299](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/issues/299)) ([ecd68af](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/ecd68afb46a4c56200748da5e5fb284fa5a839db))
|
||||
* integrate with history API on web ([5a3f835](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/5a3f8356b05bff7ed20893a5db6804612da3e568))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.38](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/compare/@react-navigation/core@5.0.0-alpha.37...@react-navigation/core@5.0.0-alpha.38) (2020-02-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add licenses ([0c159db](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/0c159db4c9bc85e83b5cfe6819ab2562669a4d8f))
|
||||
* add warning when passing inline function to component prop ([fa4a959](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/fa4a959549ccd9dc2f9bd2ea495e99abdedc9f94))
|
||||
* tweak error messages for validation ([2243b45](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/2243b45cc1addf83727166d82736d214f181b1fb))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add `screens` prop for nested configs ([#308](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/issues/308)) ([b931ae6](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/b931ae62dfb2c5253c94ea5ace73e9070ec17c4a))
|
||||
* add useIsDrawerOpen hook ([#299](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/issues/299)) ([ecd68af](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/ecd68afb46a4c56200748da5e5fb284fa5a839db))
|
||||
* integrate with history API on web ([5a3f835](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/commit/5a3f8356b05bff7ed20893a5db6804612da3e568))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.37](https://github.com/react-navigation/navigation-ex/tree/master/packages/core/compare/@react-navigation/core@5.0.0-alpha.36...@react-navigation/core@5.0.0-alpha.37) (2020-01-24)
|
||||
|
||||
|
||||
|
||||
21
packages/core/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 React Navigation Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -6,7 +6,7 @@
|
||||
"react-native",
|
||||
"react-navigation"
|
||||
],
|
||||
"version": "5.0.0-alpha.37",
|
||||
"version": "5.0.0-alpha.43",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/react-navigation/navigation-ex/tree/master/packages/core",
|
||||
"main": "lib/commonjs/index.js",
|
||||
|
||||
300
packages/core/src/BaseNavigationContainer.tsx
Normal file
@@ -0,0 +1,300 @@
|
||||
import * as React from 'react';
|
||||
import * as CommonActions from './CommonActions';
|
||||
import EnsureSingleNavigator from './EnsureSingleNavigator';
|
||||
import NavigationBuilderContext from './NavigationBuilderContext';
|
||||
import useFocusedListeners from './useFocusedListeners';
|
||||
import useDevTools from './useDevTools';
|
||||
import useStateGetters from './useStateGetters';
|
||||
import isSerializable from './isSerializable';
|
||||
|
||||
import {
|
||||
Route,
|
||||
NavigationState,
|
||||
InitialState,
|
||||
PartialState,
|
||||
NavigationAction,
|
||||
NavigationContainerRef,
|
||||
NavigationContainerProps,
|
||||
} from './types';
|
||||
import useEventEmitter from './useEventEmitter';
|
||||
|
||||
type State = NavigationState | PartialState<NavigationState> | undefined;
|
||||
|
||||
const MISSING_CONTEXT_ERROR =
|
||||
"We couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'?";
|
||||
|
||||
export const NavigationStateContext = React.createContext<{
|
||||
isDefault?: true;
|
||||
state?: NavigationState | PartialState<NavigationState>;
|
||||
getState: () => NavigationState | PartialState<NavigationState> | undefined;
|
||||
setState: (
|
||||
state: NavigationState | PartialState<NavigationState> | undefined
|
||||
) => void;
|
||||
key?: string;
|
||||
performTransaction: (action: () => void) => void;
|
||||
}>({
|
||||
isDefault: true,
|
||||
|
||||
get getState(): any {
|
||||
throw new Error(MISSING_CONTEXT_ERROR);
|
||||
},
|
||||
get setState(): any {
|
||||
throw new Error(MISSING_CONTEXT_ERROR);
|
||||
},
|
||||
get performTransaction(): any {
|
||||
throw new Error(MISSING_CONTEXT_ERROR);
|
||||
},
|
||||
});
|
||||
|
||||
let hasWarnedForSerialization = false;
|
||||
|
||||
/**
|
||||
* Remove `key` and `routeNames` from the state objects recursively to get partial state.
|
||||
*
|
||||
* @param state Initial state object.
|
||||
*/
|
||||
const getPartialState = (
|
||||
state: InitialState | undefined
|
||||
): PartialState<NavigationState> | undefined => {
|
||||
if (state === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { key, routeNames, ...partialState } = state;
|
||||
|
||||
// @ts-ignore
|
||||
return {
|
||||
...partialState,
|
||||
stale: true,
|
||||
routes: state.routes.map(route => {
|
||||
if (route.state === undefined) {
|
||||
return route as Route<string> & {
|
||||
state?: PartialState<NavigationState>;
|
||||
};
|
||||
}
|
||||
|
||||
return { ...route, state: getPartialState(route.state) };
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Container component which holds the navigation state.
|
||||
* This should be rendered at the root wrapping the whole app.
|
||||
*
|
||||
* @param props.initialState Initial state object for the navigation tree.
|
||||
* @param props.onStateChange Callback which is called with the latest navigation state when it changes.
|
||||
* @param props.children Child elements to render the content.
|
||||
* @param props.ref Ref object which refers to the navigation object containing helper methods.
|
||||
*/
|
||||
const BaseNavigationContainer = React.forwardRef(
|
||||
function BaseNavigationContainer(
|
||||
{
|
||||
initialState,
|
||||
onStateChange,
|
||||
independent,
|
||||
children,
|
||||
}: NavigationContainerProps,
|
||||
ref: React.Ref<NavigationContainerRef>
|
||||
) {
|
||||
const parent = React.useContext(NavigationStateContext);
|
||||
|
||||
if (!parent.isDefault && !independent) {
|
||||
throw new Error(
|
||||
"Looks like you have nested a 'NavigationContainer' inside another. Normally you need only one container at the root of the app, so this was probably an error. If this was intentional, pass 'independent={true}' explicitely."
|
||||
);
|
||||
}
|
||||
|
||||
const [state, setNavigationState] = React.useState<State>(() =>
|
||||
getPartialState(initialState == null ? undefined : initialState)
|
||||
);
|
||||
|
||||
const navigationStateRef = React.useRef<State>();
|
||||
const transactionStateRef = React.useRef<State | null>(null);
|
||||
const isTransactionActiveRef = React.useRef<boolean>(false);
|
||||
const isFirstMountRef = React.useRef<boolean>(true);
|
||||
const skipTrackingRef = React.useRef<boolean>(false);
|
||||
|
||||
const performTransaction = React.useCallback((callback: () => void) => {
|
||||
if (isTransactionActiveRef.current) {
|
||||
throw new Error(
|
||||
"Only one transaction can be active at a time. Did you accidentally nest 'performTransaction'?"
|
||||
);
|
||||
}
|
||||
|
||||
setNavigationState((navigationState: State) => {
|
||||
isTransactionActiveRef.current = true;
|
||||
transactionStateRef.current = navigationState;
|
||||
|
||||
try {
|
||||
callback();
|
||||
} finally {
|
||||
isTransactionActiveRef.current = false;
|
||||
}
|
||||
|
||||
return transactionStateRef.current;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const getState = React.useCallback(
|
||||
() =>
|
||||
transactionStateRef.current !== null
|
||||
? transactionStateRef.current
|
||||
: navigationStateRef.current,
|
||||
[]
|
||||
);
|
||||
|
||||
const setState = React.useCallback((navigationState: State) => {
|
||||
if (transactionStateRef.current === null) {
|
||||
throw new Error(
|
||||
"Any 'setState' calls need to be done inside 'performTransaction'"
|
||||
);
|
||||
}
|
||||
|
||||
transactionStateRef.current = navigationState;
|
||||
}, []);
|
||||
|
||||
const reset = React.useCallback(
|
||||
(state: NavigationState) => {
|
||||
performTransaction(() => {
|
||||
skipTrackingRef.current = true;
|
||||
setState(state);
|
||||
});
|
||||
},
|
||||
[performTransaction, setState]
|
||||
);
|
||||
|
||||
const { trackState, trackAction } = useDevTools({
|
||||
name: '@react-navigation',
|
||||
reset,
|
||||
state,
|
||||
});
|
||||
|
||||
const {
|
||||
listeners,
|
||||
addListener: addFocusedListener,
|
||||
} = useFocusedListeners();
|
||||
|
||||
const { getStateForRoute, addStateGetter } = useStateGetters();
|
||||
|
||||
const dispatch = (
|
||||
action: NavigationAction | ((state: NavigationState) => NavigationAction)
|
||||
) => {
|
||||
listeners[0](navigation => navigation.dispatch(action));
|
||||
};
|
||||
|
||||
const canGoBack = () => {
|
||||
const { result, handled } = listeners[0](navigation =>
|
||||
navigation.canGoBack()
|
||||
);
|
||||
|
||||
if (handled) {
|
||||
return result;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const resetRoot = React.useCallback(
|
||||
(state?: PartialState<NavigationState> | NavigationState) => {
|
||||
performTransaction(() => {
|
||||
trackAction('@@RESET_ROOT');
|
||||
setState(state);
|
||||
});
|
||||
},
|
||||
[performTransaction, setState, trackAction]
|
||||
);
|
||||
|
||||
const getRootState = React.useCallback(() => {
|
||||
return getStateForRoute('root');
|
||||
}, [getStateForRoute]);
|
||||
|
||||
const emitter = useEventEmitter();
|
||||
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
...(Object.keys(CommonActions) as (keyof typeof CommonActions)[]).reduce<
|
||||
any
|
||||
>((acc, name) => {
|
||||
acc[name] = (...args: any[]) =>
|
||||
dispatch(
|
||||
CommonActions[name](
|
||||
// @ts-ignore
|
||||
...args
|
||||
)
|
||||
);
|
||||
return acc;
|
||||
}, {}),
|
||||
...emitter.create('root'),
|
||||
resetRoot,
|
||||
dispatch,
|
||||
canGoBack,
|
||||
getRootState,
|
||||
}));
|
||||
|
||||
const builderContext = React.useMemo(
|
||||
() => ({
|
||||
addFocusedListener,
|
||||
addStateGetter,
|
||||
trackAction,
|
||||
}),
|
||||
[addFocusedListener, trackAction, addStateGetter]
|
||||
);
|
||||
|
||||
const context = React.useMemo(
|
||||
() => ({
|
||||
state,
|
||||
performTransaction,
|
||||
getState,
|
||||
setState,
|
||||
}),
|
||||
[getState, performTransaction, setState, state]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (
|
||||
state !== undefined &&
|
||||
!isSerializable(state) &&
|
||||
!hasWarnedForSerialization
|
||||
) {
|
||||
hasWarnedForSerialization = true;
|
||||
|
||||
console.warn(
|
||||
"We found non-serializable values in the navigation state, which can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params. If you need to use functions in your options, you can use 'navigation.setOptions' instead."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
emitter.emit({
|
||||
type: 'state',
|
||||
data: { state },
|
||||
});
|
||||
|
||||
if (skipTrackingRef.current) {
|
||||
skipTrackingRef.current = false;
|
||||
} else {
|
||||
trackState(getRootState);
|
||||
}
|
||||
|
||||
navigationStateRef.current = state;
|
||||
transactionStateRef.current = null;
|
||||
|
||||
if (!isFirstMountRef.current && onStateChange) {
|
||||
onStateChange(getRootState());
|
||||
}
|
||||
|
||||
isFirstMountRef.current = false;
|
||||
}, [state, onStateChange, trackState, getRootState, emitter]);
|
||||
|
||||
return (
|
||||
<NavigationBuilderContext.Provider value={builderContext}>
|
||||
<NavigationStateContext.Provider value={context}>
|
||||
<EnsureSingleNavigator>{children}</EnsureSingleNavigator>
|
||||
</NavigationStateContext.Provider>
|
||||
</NavigationBuilderContext.Provider>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default BaseNavigationContainer;
|
||||
@@ -1,286 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import * as CommonActions from './CommonActions';
|
||||
import EnsureSingleNavigator from './EnsureSingleNavigator';
|
||||
import NavigationBuilderContext from './NavigationBuilderContext';
|
||||
import useFocusedListeners from './useFocusedListeners';
|
||||
import useDevTools from './useDevTools';
|
||||
import useStateGetters from './useStateGetters';
|
||||
import isSerializable from './isSerializable';
|
||||
|
||||
import {
|
||||
Route,
|
||||
NavigationState,
|
||||
InitialState,
|
||||
PartialState,
|
||||
NavigationAction,
|
||||
NavigationContainerRef,
|
||||
NavigationContainerProps,
|
||||
} from './types';
|
||||
|
||||
type State = NavigationState | PartialState<NavigationState> | undefined;
|
||||
|
||||
const MISSING_CONTEXT_ERROR =
|
||||
"We couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'?";
|
||||
|
||||
export const NavigationStateContext = React.createContext<{
|
||||
isDefault?: true;
|
||||
state?: NavigationState | PartialState<NavigationState>;
|
||||
getState: () => NavigationState | PartialState<NavigationState> | undefined;
|
||||
setState: (
|
||||
state: NavigationState | PartialState<NavigationState> | undefined
|
||||
) => void;
|
||||
key?: string;
|
||||
performTransaction: (action: () => void) => void;
|
||||
}>({
|
||||
isDefault: true,
|
||||
|
||||
get getState(): any {
|
||||
throw new Error(MISSING_CONTEXT_ERROR);
|
||||
},
|
||||
get setState(): any {
|
||||
throw new Error(MISSING_CONTEXT_ERROR);
|
||||
},
|
||||
get performTransaction(): any {
|
||||
throw new Error(MISSING_CONTEXT_ERROR);
|
||||
},
|
||||
});
|
||||
|
||||
let hasWarnedForSerialization = false;
|
||||
|
||||
/**
|
||||
* Remove `key` and `routeNames` from the state objects recursively to get partial state.
|
||||
*
|
||||
* @param state Initial state object.
|
||||
*/
|
||||
const getPartialState = (
|
||||
state: InitialState | undefined
|
||||
): PartialState<NavigationState> | undefined => {
|
||||
if (state === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { key, routeNames, ...partialState } = state;
|
||||
|
||||
// @ts-ignore
|
||||
return {
|
||||
...partialState,
|
||||
stale: true,
|
||||
routes: state.routes.map(route => {
|
||||
if (route.state === undefined) {
|
||||
return route as Route<string> & {
|
||||
state?: PartialState<NavigationState>;
|
||||
};
|
||||
}
|
||||
|
||||
return { ...route, state: getPartialState(route.state) };
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Container component which holds the navigation state.
|
||||
* This should be rendered at the root wrapping the whole app.
|
||||
*
|
||||
* @param props.initialState Initial state object for the navigation tree.
|
||||
* @param props.onStateChange Callback which is called with the latest navigation state when it changes.
|
||||
* @param props.children Child elements to render the content.
|
||||
* @param props.ref Ref object which refers to the navigation object containing helper methods.
|
||||
*/
|
||||
const Container = React.forwardRef(function NavigationContainer(
|
||||
{
|
||||
initialState,
|
||||
onStateChange,
|
||||
independent,
|
||||
children,
|
||||
}: NavigationContainerProps,
|
||||
ref: React.Ref<NavigationContainerRef>
|
||||
) {
|
||||
const parent = React.useContext(NavigationStateContext);
|
||||
|
||||
if (!parent.isDefault && !independent) {
|
||||
throw new Error(
|
||||
"Looks like you have nested a 'NavigationContainer' inside another. Normally you need only one container at the root of the app, so this was probably an error. If this was intentional, pass 'independent={true}' explicitely."
|
||||
);
|
||||
}
|
||||
|
||||
const [state, setNavigationState] = React.useState<State>(() =>
|
||||
getPartialState(initialState == null ? undefined : initialState)
|
||||
);
|
||||
|
||||
const navigationStateRef = React.useRef<State>();
|
||||
const transactionStateRef = React.useRef<State | null>(null);
|
||||
const isTransactionActiveRef = React.useRef<boolean>(false);
|
||||
const isFirstMountRef = React.useRef<boolean>(true);
|
||||
const skipTrackingRef = React.useRef<boolean>(false);
|
||||
|
||||
const performTransaction = React.useCallback((callback: () => void) => {
|
||||
if (isTransactionActiveRef.current) {
|
||||
throw new Error(
|
||||
"Only one transaction can be active at a time. Did you accidentally nest 'performTransaction'?"
|
||||
);
|
||||
}
|
||||
|
||||
setNavigationState((navigationState: State) => {
|
||||
isTransactionActiveRef.current = true;
|
||||
transactionStateRef.current = navigationState;
|
||||
|
||||
try {
|
||||
callback();
|
||||
} finally {
|
||||
isTransactionActiveRef.current = false;
|
||||
}
|
||||
|
||||
return transactionStateRef.current;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const getState = React.useCallback(
|
||||
() =>
|
||||
transactionStateRef.current !== null
|
||||
? transactionStateRef.current
|
||||
: navigationStateRef.current,
|
||||
[]
|
||||
);
|
||||
|
||||
const setState = React.useCallback((navigationState: State) => {
|
||||
if (transactionStateRef.current === null) {
|
||||
throw new Error(
|
||||
"Any 'setState' calls need to be done inside 'performTransaction'"
|
||||
);
|
||||
}
|
||||
|
||||
transactionStateRef.current = navigationState;
|
||||
}, []);
|
||||
|
||||
const reset = React.useCallback(
|
||||
(state: NavigationState) => {
|
||||
performTransaction(() => {
|
||||
skipTrackingRef.current = true;
|
||||
setState(state);
|
||||
});
|
||||
},
|
||||
[performTransaction, setState]
|
||||
);
|
||||
|
||||
const { trackState, trackAction } = useDevTools({
|
||||
name: '@react-navigation',
|
||||
reset,
|
||||
state,
|
||||
});
|
||||
|
||||
const { listeners, addListener: addFocusedListener } = useFocusedListeners();
|
||||
|
||||
const { getStateForRoute, addStateGetter } = useStateGetters();
|
||||
|
||||
const dispatch = (
|
||||
action: NavigationAction | ((state: NavigationState) => NavigationAction)
|
||||
) => {
|
||||
listeners[0](navigation => navigation.dispatch(action));
|
||||
};
|
||||
|
||||
const canGoBack = () => {
|
||||
const { result, handled } = listeners[0](navigation =>
|
||||
navigation.canGoBack()
|
||||
);
|
||||
|
||||
if (handled) {
|
||||
return result;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const resetRoot = React.useCallback(
|
||||
(state?: PartialState<NavigationState> | NavigationState) => {
|
||||
performTransaction(() => {
|
||||
trackAction('@@RESET_ROOT');
|
||||
setState(state);
|
||||
});
|
||||
},
|
||||
[performTransaction, setState, trackAction]
|
||||
);
|
||||
|
||||
const getRootState = React.useCallback(() => {
|
||||
return getStateForRoute('root');
|
||||
}, [getStateForRoute]);
|
||||
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
...(Object.keys(CommonActions) as (keyof typeof CommonActions)[]).reduce<
|
||||
any
|
||||
>((acc, name) => {
|
||||
acc[name] = (...args: any[]) =>
|
||||
dispatch(
|
||||
CommonActions[name](
|
||||
// @ts-ignore
|
||||
...args
|
||||
)
|
||||
);
|
||||
return acc;
|
||||
}, {}),
|
||||
resetRoot,
|
||||
dispatch,
|
||||
canGoBack,
|
||||
getRootState,
|
||||
}));
|
||||
|
||||
const builderContext = React.useMemo(
|
||||
() => ({
|
||||
addFocusedListener,
|
||||
addStateGetter,
|
||||
trackAction,
|
||||
}),
|
||||
[addFocusedListener, trackAction, addStateGetter]
|
||||
);
|
||||
|
||||
const context = React.useMemo(
|
||||
() => ({
|
||||
state,
|
||||
performTransaction,
|
||||
getState,
|
||||
setState,
|
||||
}),
|
||||
[getState, performTransaction, setState, state]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (
|
||||
state !== undefined &&
|
||||
!isSerializable(state) &&
|
||||
!hasWarnedForSerialization
|
||||
) {
|
||||
hasWarnedForSerialization = true;
|
||||
|
||||
console.warn(
|
||||
"We found non-serializable values in the navigation state, which can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params. If you need to use functions in your options, you can use 'navigation.setOptions' instead."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (skipTrackingRef.current) {
|
||||
skipTrackingRef.current = false;
|
||||
} else {
|
||||
trackState(getRootState);
|
||||
}
|
||||
|
||||
navigationStateRef.current = state;
|
||||
transactionStateRef.current = null;
|
||||
|
||||
if (!isFirstMountRef.current && onStateChange) {
|
||||
onStateChange(getRootState());
|
||||
}
|
||||
|
||||
isFirstMountRef.current = false;
|
||||
}, [state, onStateChange, trackState, getRootState]);
|
||||
|
||||
return (
|
||||
<NavigationBuilderContext.Provider value={builderContext}>
|
||||
<NavigationStateContext.Provider value={context}>
|
||||
<EnsureSingleNavigator>{children}</EnsureSingleNavigator>
|
||||
</NavigationStateContext.Provider>
|
||||
</NavigationBuilderContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
export default Container;
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { NavigationStateContext } from './NavigationContainer';
|
||||
import { NavigationStateContext } from './BaseNavigationContainer';
|
||||
import NavigationContext from './NavigationContext';
|
||||
import NavigationRouteContext from './NavigationRouteContext';
|
||||
import StaticContainer from './StaticContainer';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { act, render } from 'react-native-testing-library';
|
||||
import NavigationContainer, {
|
||||
import BaseNavigationContainer, {
|
||||
NavigationStateContext,
|
||||
} from '../NavigationContainer';
|
||||
} from '../BaseNavigationContainer';
|
||||
import MockRouter, { MockActions } from './__fixtures__/MockRouter';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
import Screen from '../Screen';
|
||||
@@ -85,9 +85,9 @@ it('throws when setState is called outside performTransaction', () => {
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<Test />
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
@@ -112,9 +112,9 @@ it('throws when nesting performTransaction', () => {
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<Test />
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
@@ -125,11 +125,11 @@ it('throws when nesting performTransaction', () => {
|
||||
it('throws when nesting containers', () => {
|
||||
expect(() =>
|
||||
render(
|
||||
<NavigationContainer>
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<React.Fragment />
|
||||
</NavigationContainer>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
)
|
||||
).toThrowError(
|
||||
"Looks like you have nested a 'NavigationContainer' inside another."
|
||||
@@ -137,11 +137,11 @@ it('throws when nesting containers', () => {
|
||||
|
||||
expect(() =>
|
||||
render(
|
||||
<NavigationContainer>
|
||||
<NavigationContainer independent>
|
||||
<BaseNavigationContainer>
|
||||
<BaseNavigationContainer independent>
|
||||
<React.Fragment />
|
||||
</NavigationContainer>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
)
|
||||
).not.toThrowError(
|
||||
"Looks like you have nested a 'NavigationContainer' inside another."
|
||||
@@ -223,7 +223,7 @@ it('handle dispatching with ref', () => {
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer
|
||||
<BaseNavigationContainer
|
||||
ref={ref}
|
||||
initialState={initialState}
|
||||
onStateChange={onStateChange}
|
||||
@@ -233,8 +233,8 @@ it('handle dispatching with ref', () => {
|
||||
<Screen name="foo2">
|
||||
{() => (
|
||||
<ChildNavigator>
|
||||
<Screen name="qux" component={() => null} />
|
||||
<Screen name="lex" component={() => null} />
|
||||
<Screen name="qux">{() => null}</Screen>
|
||||
<Screen name="lex">{() => null}</Screen>
|
||||
</ChildNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
@@ -242,21 +242,19 @@ it('handle dispatching with ref', () => {
|
||||
<Screen name="baz">
|
||||
{() => (
|
||||
<ChildNavigator>
|
||||
<Screen name="qux" component={() => null} />
|
||||
<Screen name="lex" component={() => null} />
|
||||
<Screen name="qux">{() => null}</Screen>
|
||||
<Screen name="lex">{() => null}</Screen>
|
||||
</ChildNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</ParentNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
|
||||
act(() => {
|
||||
if (ref.current != null) {
|
||||
ref.current.dispatch({ type: 'REVERSE' });
|
||||
}
|
||||
ref.current?.dispatch({ type: 'REVERSE' });
|
||||
});
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
@@ -303,14 +301,14 @@ it('handle resetting state with ref', () => {
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer ref={ref} onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer ref={ref} onStateChange={onStateChange}>
|
||||
<TestNavigator>
|
||||
<Screen name="foo">{() => null}</Screen>
|
||||
<Screen name="foo2">
|
||||
{() => (
|
||||
<TestNavigator>
|
||||
<Screen name="qux" component={() => null} />
|
||||
<Screen name="lex" component={() => null} />
|
||||
<Screen name="qux">{() => null}</Screen>
|
||||
<Screen name="lex">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
@@ -318,13 +316,13 @@ it('handle resetting state with ref', () => {
|
||||
<Screen name="baz">
|
||||
{() => (
|
||||
<TestNavigator>
|
||||
<Screen name="qux" component={() => null} />
|
||||
<Screen name="lex" component={() => null} />
|
||||
<Screen name="qux">{() => null}</Screen>
|
||||
<Screen name="lex">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
@@ -350,9 +348,7 @@ it('handle resetting state with ref', () => {
|
||||
};
|
||||
|
||||
act(() => {
|
||||
if (ref.current != null) {
|
||||
ref.current.resetRoot(state);
|
||||
}
|
||||
ref.current?.resetRoot(state);
|
||||
});
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
@@ -383,7 +379,7 @@ it('handle resetting state with ref', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('handle getRootState', () => {
|
||||
it('handles getRootState', () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
@@ -393,19 +389,19 @@ it('handle getRootState', () => {
|
||||
const ref = React.createRef<NavigationContainerRef>();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer ref={ref}>
|
||||
<BaseNavigationContainer ref={ref}>
|
||||
<TestNavigator initialRouteName="foo">
|
||||
<Screen name="foo">
|
||||
{() => (
|
||||
<TestNavigator>
|
||||
<Screen name="qux" component={() => null} />
|
||||
<Screen name="lex" component={() => null} />
|
||||
<Screen name="qux">{() => null}</Screen>
|
||||
<Screen name="lex">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
<Screen name="bar" component={() => null} />
|
||||
<Screen name="bar">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element);
|
||||
@@ -440,3 +436,68 @@ it('handle getRootState', () => {
|
||||
type: 'test',
|
||||
});
|
||||
});
|
||||
it('emits state events when the state changes', () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{state.routes.map(route => descriptors[route.key].render())}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const ref = React.createRef<NavigationContainerRef>();
|
||||
|
||||
const element = (
|
||||
<BaseNavigationContainer ref={ref}>
|
||||
<TestNavigator>
|
||||
<Screen name="foo">{() => null}</Screen>
|
||||
<Screen name="bar">{() => null}</Screen>
|
||||
<Screen name="baz">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
|
||||
const listener = jest.fn();
|
||||
|
||||
ref.current?.addListener('state', listener);
|
||||
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
|
||||
act(() => {
|
||||
ref.current?.navigate('bar');
|
||||
});
|
||||
|
||||
expect(listener.mock.calls[0][0].data.state).toEqual({
|
||||
type: 'test',
|
||||
stale: false,
|
||||
index: 1,
|
||||
key: '9',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'foo', name: 'foo' },
|
||||
{ key: 'bar', name: 'bar' },
|
||||
{ key: 'baz', name: 'baz' },
|
||||
],
|
||||
});
|
||||
|
||||
act(() => {
|
||||
ref.current?.navigate('baz', { answer: 42 });
|
||||
});
|
||||
|
||||
expect(listener.mock.calls[1][0].data.state).toEqual({
|
||||
type: 'test',
|
||||
stale: false,
|
||||
index: 2,
|
||||
key: '9',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'foo', name: 'foo' },
|
||||
{ key: 'bar', name: 'bar' },
|
||||
{ key: 'baz', name: 'baz', params: { answer: 42 } },
|
||||
],
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { render } from 'react-native-testing-library';
|
||||
import Screen from '../Screen';
|
||||
import NavigationContainer from '../NavigationContainer';
|
||||
import BaseNavigationContainer from '../BaseNavigationContainer';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
import MockRouter, { MockRouterKey } from './__fixtures__/MockRouter';
|
||||
|
||||
@@ -26,7 +26,7 @@ it('throws if NAVIGATE dispatched neither key nor name', () => {
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator initialRouteName="foo">
|
||||
<Screen
|
||||
name="foo"
|
||||
@@ -34,7 +34,7 @@ it('throws if NAVIGATE dispatched neither key nor name', () => {
|
||||
initialParams={{ count: 10 }}
|
||||
/>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
|
||||
@@ -116,10 +116,13 @@ it("doesn't add query param for empty params", () => {
|
||||
});
|
||||
|
||||
it('handles state with config with nested screens', () => {
|
||||
const path = '/few/bar/sweet/apple/baz/jane?answer=42&count=10&valid=true';
|
||||
const path = '/foe/bar/sweet/apple/baz/jane?answer=42&count=10&valid=true';
|
||||
const config = {
|
||||
Foo: {
|
||||
Foe: 'few',
|
||||
path: 'foo',
|
||||
screens: {
|
||||
Foe: 'foe',
|
||||
},
|
||||
},
|
||||
Bar: 'bar/:type/:fruit',
|
||||
Baz: {
|
||||
@@ -178,10 +181,13 @@ it('handles state with config with nested screens', () => {
|
||||
});
|
||||
|
||||
it('handles state with config with nested screens and unused configs', () => {
|
||||
const path = '/few/baz/jane?answer=42&count=10&valid=true';
|
||||
const path = '/foe/baz/jane?answer=42&count=10&valid=true';
|
||||
const config = {
|
||||
Foo: {
|
||||
Foe: 'few',
|
||||
path: 'foo',
|
||||
screens: {
|
||||
Foe: 'foe',
|
||||
},
|
||||
},
|
||||
Baz: {
|
||||
path: 'baz/:author',
|
||||
@@ -230,19 +236,31 @@ it('handles state with config with nested screens and unused configs', () => {
|
||||
});
|
||||
|
||||
it('handles nested object with stringify in it', () => {
|
||||
const path = '/bar/sweet/apple/few/bis/jane?answer=42&count=10&valid=true';
|
||||
const path = '/bar/sweet/apple/foe/bis/jane?answer=42&count=10&valid=true';
|
||||
const config = {
|
||||
Foo: {
|
||||
Foe: 'few',
|
||||
path: 'foo',
|
||||
screens: {
|
||||
Foe: 'foe',
|
||||
},
|
||||
},
|
||||
Bar: 'bar/:type/:fruit',
|
||||
Baz: {
|
||||
Bos: 'bos',
|
||||
Bis: {
|
||||
path: 'bis/:author',
|
||||
stringify: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toLowerCase()),
|
||||
path: 'baz',
|
||||
screens: {
|
||||
Bos: 'bos',
|
||||
Bis: {
|
||||
path: 'bis/:author',
|
||||
stringify: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toLowerCase()),
|
||||
},
|
||||
parse: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -292,7 +310,7 @@ it('handles nested object with stringify in it', () => {
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState(getStateFromPath(path))).toBe(path);
|
||||
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||
});
|
||||
|
||||
it('handles nested object for second route depth', () => {
|
||||
@@ -300,9 +318,14 @@ it('handles nested object for second route depth', () => {
|
||||
const config = {
|
||||
Foo: {
|
||||
path: 'foo',
|
||||
Foe: 'foe',
|
||||
Bar: {
|
||||
Baz: 'baz',
|
||||
screens: {
|
||||
Foe: 'foe',
|
||||
Bar: {
|
||||
path: 'bar',
|
||||
screens: {
|
||||
Baz: 'baz',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -326,7 +349,7 @@ it('handles nested object for second route depth', () => {
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState(getStateFromPath(path))).toBe(path);
|
||||
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||
});
|
||||
|
||||
it('handles nested object for second route depth and and path and stringify in roots', () => {
|
||||
@@ -337,16 +360,20 @@ it('handles nested object for second route depth and and path and stringify in r
|
||||
stringify: {
|
||||
id: (id: number) => `id=${id}`,
|
||||
},
|
||||
Foe: 'foe',
|
||||
Bar: {
|
||||
path: 'bar/:id',
|
||||
stringify: {
|
||||
id: (id: number) => `id=${id}`,
|
||||
screens: {
|
||||
Foe: 'foe',
|
||||
Bar: {
|
||||
path: 'bar/:id',
|
||||
stringify: {
|
||||
id: (id: number) => `id=${id}`,
|
||||
},
|
||||
parse: {
|
||||
id: Number,
|
||||
},
|
||||
screens: {
|
||||
Baz: 'baz',
|
||||
},
|
||||
},
|
||||
parse: {
|
||||
id: Number,
|
||||
},
|
||||
Baz: 'baz',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -370,5 +397,5 @@ it('handles nested object for second route depth and and path and stringify in r
|
||||
};
|
||||
|
||||
expect(getPathFromState(state, config)).toBe(path);
|
||||
expect(getPathFromState(getStateFromPath(path))).toBe(path);
|
||||
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||
});
|
||||
|
||||
@@ -35,9 +35,9 @@ it('converts path string to initial state', () => {
|
||||
});
|
||||
|
||||
it('converts path string to initial state with config', () => {
|
||||
const path = '/few/bar/sweet/apple/baz/jane?count=10&answer=42&valid=true';
|
||||
const path = '/foo/bar/sweet/apple/baz/jane?count=10&answer=42&valid=true';
|
||||
const config = {
|
||||
Foo: 'few',
|
||||
Foo: 'foo',
|
||||
Bar: 'bar/:type/:fruit',
|
||||
Baz: {
|
||||
path: 'baz/:author',
|
||||
@@ -141,10 +141,13 @@ it('handles route without param', () => {
|
||||
});
|
||||
|
||||
it('converts path string to initial state with config with nested screens', () => {
|
||||
const path = '/few/bar/sweet/apple/baz/jane?count=10&answer=42&valid=true';
|
||||
const path = '/foe/bar/sweet/apple/baz/jane?count=10&answer=42&valid=true';
|
||||
const config = {
|
||||
Foo: {
|
||||
Foe: 'few',
|
||||
path: 'foo',
|
||||
screens: {
|
||||
Foe: 'foe',
|
||||
},
|
||||
},
|
||||
Bar: 'bar/:type/:fruit',
|
||||
Baz: {
|
||||
@@ -203,10 +206,13 @@ it('converts path string to initial state with config with nested screens', () =
|
||||
});
|
||||
|
||||
it('converts path string to initial state with config with nested screens and unused parse functions', () => {
|
||||
const path = '/few/baz/jane?count=10&answer=42&valid=true';
|
||||
const path = '/foe/baz/jane?count=10&answer=42&valid=true';
|
||||
const config = {
|
||||
Foo: {
|
||||
Foe: 'few',
|
||||
path: 'foo',
|
||||
screens: {
|
||||
Foe: 'foe',
|
||||
},
|
||||
},
|
||||
Baz: {
|
||||
path: 'baz/:author',
|
||||
@@ -254,21 +260,31 @@ it('converts path string to initial state with config with nested screens and un
|
||||
});
|
||||
|
||||
it('handles nested object with unused configs and with parse in it', () => {
|
||||
const path = '/bar/sweet/apple/few/bis/jane?count=10&answer=42&valid=true';
|
||||
const path = '/bar/sweet/apple/foe/bis/jane?count=10&answer=42&valid=true';
|
||||
const config = {
|
||||
Foo: {
|
||||
Foe: 'few',
|
||||
path: 'foo',
|
||||
screens: {
|
||||
Foe: 'foe',
|
||||
},
|
||||
},
|
||||
Bar: 'bar/:type/:fruit',
|
||||
Baz: {
|
||||
Bos: 'bos',
|
||||
Bis: {
|
||||
path: 'bis/:author',
|
||||
parse: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
path: 'baz',
|
||||
screens: {
|
||||
Bos: 'bos',
|
||||
Bis: {
|
||||
path: 'bis/:author',
|
||||
stringify: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toLowerCase()),
|
||||
},
|
||||
parse: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -328,9 +344,14 @@ it('handles parse in nested object for second route depth', () => {
|
||||
const config = {
|
||||
Foo: {
|
||||
path: 'foo',
|
||||
Foe: 'foe',
|
||||
Bar: {
|
||||
Baz: 'baz',
|
||||
screens: {
|
||||
Foe: 'foe',
|
||||
Bar: {
|
||||
path: 'bar',
|
||||
screens: {
|
||||
Baz: 'baz',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -370,16 +391,13 @@ it('handles parse in nested object for second route depth and and path and parse
|
||||
stringify: {
|
||||
id: (id: number) => `id=${id}`,
|
||||
},
|
||||
Foe: 'foe',
|
||||
Bar: {
|
||||
path: 'bar/:id',
|
||||
parse: {
|
||||
id: Number,
|
||||
screens: {
|
||||
Foe: 'foe',
|
||||
Bar: {
|
||||
screens: {
|
||||
Baz: 'baz',
|
||||
},
|
||||
},
|
||||
stringify: {
|
||||
id: (id: number) => `id=${id}`,
|
||||
},
|
||||
Baz: 'baz',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -407,3 +425,270 @@ it('handles parse in nested object for second route depth and and path and parse
|
||||
state
|
||||
);
|
||||
});
|
||||
|
||||
it('handles initialRouteName', () => {
|
||||
const path = '/baz';
|
||||
const config = {
|
||||
Foo: {
|
||||
initialRouteName: 'Foe',
|
||||
screens: {
|
||||
Foe: 'foe',
|
||||
Bar: {
|
||||
screens: {
|
||||
Baz: 'baz',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'Foo',
|
||||
state: {
|
||||
index: 1,
|
||||
routes: [
|
||||
{
|
||||
name: 'Foe',
|
||||
},
|
||||
{
|
||||
name: 'Bar',
|
||||
state: {
|
||||
routes: [{ name: 'Baz' }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
});
|
||||
|
||||
it('handles initialRouteName included in path', () => {
|
||||
const path = '/baz';
|
||||
const config = {
|
||||
Foo: {
|
||||
initialRouteName: 'Foe',
|
||||
screens: {
|
||||
Foe: {
|
||||
screens: {
|
||||
Baz: 'baz',
|
||||
},
|
||||
},
|
||||
Bar: 'bar',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'Foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Foe',
|
||||
state: {
|
||||
routes: [{ name: 'Baz' }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
});
|
||||
|
||||
it('handles two initialRouteNames', () => {
|
||||
const path = '/bar/sweet/apple/foe/bis/jane?count=10&answer=42&valid=true';
|
||||
const config = {
|
||||
Foo: {
|
||||
path: 'foo',
|
||||
screens: {
|
||||
Foe: 'foe',
|
||||
},
|
||||
},
|
||||
Bar: 'bar/:type/:fruit',
|
||||
Baz: {
|
||||
initialRouteName: 'Bos',
|
||||
screens: {
|
||||
Bos: 'bos',
|
||||
Bis: {
|
||||
path: 'bis/:author',
|
||||
stringify: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toLowerCase()),
|
||||
},
|
||||
parse: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'Bar',
|
||||
params: { fruit: 'apple', type: 'sweet' },
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Foe',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Baz',
|
||||
state: {
|
||||
index: 1,
|
||||
routes: [
|
||||
{ name: 'Bos' },
|
||||
{
|
||||
name: 'Bis',
|
||||
params: {
|
||||
author: 'Jane',
|
||||
count: 10,
|
||||
answer: '42',
|
||||
valid: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts initialRouteName without config for it', () => {
|
||||
const path = '/bar/sweet/apple/foe/bis/jane?count=10&answer=42&valid=true';
|
||||
const config = {
|
||||
Foo: {
|
||||
path: 'foo',
|
||||
screens: {
|
||||
Foe: 'foe',
|
||||
},
|
||||
},
|
||||
Bar: 'bar/:type/:fruit',
|
||||
Baz: {
|
||||
initialRouteName: 'Bas',
|
||||
screens: {
|
||||
Bos: 'bos',
|
||||
Bis: {
|
||||
path: 'bis/:author',
|
||||
stringify: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toLowerCase()),
|
||||
},
|
||||
parse: {
|
||||
author: (author: string) =>
|
||||
author.replace(/^\w/, c => c.toUpperCase()),
|
||||
count: Number,
|
||||
valid: Boolean,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'Bar',
|
||||
params: { fruit: 'apple', type: 'sweet' },
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Foe',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'Baz',
|
||||
state: {
|
||||
index: 1,
|
||||
routes: [
|
||||
{ name: 'Bas' },
|
||||
{
|
||||
name: 'Bis',
|
||||
params: {
|
||||
author: 'Jane',
|
||||
count: 10,
|
||||
answer: '42',
|
||||
valid: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(state);
|
||||
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||
state
|
||||
);
|
||||
});
|
||||
|
||||
it('returns undefined if path is empty', () => {
|
||||
const config = {
|
||||
Foo: {
|
||||
screens: {
|
||||
Foe: 'foe',
|
||||
Bar: {
|
||||
screens: {
|
||||
Baz: 'baz',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const path = '';
|
||||
|
||||
expect(getStateFromPath(path, config)).toEqual(undefined);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { render, act } from 'react-native-testing-library';
|
||||
import Screen from '../Screen';
|
||||
import NavigationContainer from '../NavigationContainer';
|
||||
import BaseNavigationContainer from '../BaseNavigationContainer';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
import useNavigation from '../useNavigation';
|
||||
import MockRouter, { MockRouterKey } from './__fixtures__/MockRouter';
|
||||
@@ -28,7 +28,7 @@ it('initializes state for a navigator on navigation', () => {
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator initialRouteName="foo">
|
||||
<Screen
|
||||
name="foo"
|
||||
@@ -44,7 +44,7 @@ it('initializes state for a navigator on navigation', () => {
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
@@ -75,11 +75,11 @@ it("doesn't crash when initialState is null", () => {
|
||||
|
||||
const element = (
|
||||
// @ts-ignore
|
||||
<NavigationContainer initialState={null}>
|
||||
<BaseNavigationContainer initialState={null}>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={TestScreen} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() => render(element)).not.toThrowError();
|
||||
@@ -112,7 +112,7 @@ it('rehydrates state for a navigator on navigation', () => {
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer
|
||||
<BaseNavigationContainer
|
||||
initialState={initialState}
|
||||
onStateChange={onStateChange}
|
||||
>
|
||||
@@ -120,7 +120,7 @@ it('rehydrates state for a navigator on navigation', () => {
|
||||
<Screen name="foo" component={jest.fn()} />
|
||||
<Screen name="bar" component={BarScreen} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
@@ -166,7 +166,7 @@ it("doesn't rehydrate state if the type of state didn't match router", () => {
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer
|
||||
<BaseNavigationContainer
|
||||
initialState={initialState}
|
||||
onStateChange={onStateChange}
|
||||
>
|
||||
@@ -178,7 +178,7 @@ it("doesn't rehydrate state if the type of state didn't match router", () => {
|
||||
/>
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
@@ -219,7 +219,7 @@ it('initializes state for nested screens in React.Fragment', () => {
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={TestScreen} />
|
||||
<React.Fragment>
|
||||
@@ -227,7 +227,7 @@ it('initializes state for nested screens in React.Fragment', () => {
|
||||
<Screen name="baz" component={jest.fn()} />
|
||||
</React.Fragment>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
@@ -266,7 +266,7 @@ it('initializes state for nested navigator on navigation', () => {
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator initialRouteName="baz">
|
||||
<Screen name="foo" component={jest.fn()} />
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
@@ -278,7 +278,7 @@ it('initializes state for nested navigator on navigation', () => {
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
@@ -328,12 +328,12 @@ it("doesn't update state if nothing changed", () => {
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
render(
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator initialRouteName="foo">
|
||||
<Screen name="foo" component={FooScreen} />
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(0);
|
||||
@@ -360,12 +360,12 @@ it("doesn't update state if action wasn't handled", () => {
|
||||
const spy = jest.spyOn(console, 'error').mockImplementation();
|
||||
|
||||
render(
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator initialRouteName="foo">
|
||||
<Screen name="foo" component={FooScreen} />
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(0);
|
||||
@@ -396,12 +396,12 @@ it('cleans up state when the navigator unmounts', () => {
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={FooScreen} />
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
const root = render(element);
|
||||
@@ -422,7 +422,7 @@ it('cleans up state when the navigator unmounts', () => {
|
||||
});
|
||||
|
||||
root.update(
|
||||
<NavigationContainer onStateChange={onStateChange} children={null} />
|
||||
<BaseNavigationContainer onStateChange={onStateChange} children={null} />
|
||||
);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(2);
|
||||
@@ -454,12 +454,12 @@ it('allows state updates by dispatching a function returning an action', () => {
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator initialRouteName="foo">
|
||||
<Screen name="foo" component={FooScreen} />
|
||||
<Screen name="bar" component={BarScreen} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
@@ -496,12 +496,12 @@ it('updates route params with setParams', () => {
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
render(
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator initialRouteName="foo">
|
||||
<Screen name="foo" component={FooScreen} />
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
act(() => setParams({ username: 'alice' }));
|
||||
@@ -556,7 +556,7 @@ it('updates route params with setParams applied to parent', () => {
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
render(
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator initialRouteName="foo">
|
||||
<Screen name="foo">
|
||||
{() => (
|
||||
@@ -567,7 +567,7 @@ it('updates route params with setParams applied to parent', () => {
|
||||
</Screen>
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
act(() => setParams({ username: 'alice' }));
|
||||
@@ -634,22 +634,22 @@ it('handles change in route names', () => {
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const root = render(
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator initialRouteName="bar">
|
||||
<Screen name="foo" component={jest.fn()} />
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
root.update(
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={jest.fn()} />
|
||||
<Screen name="baz" component={jest.fn()} />
|
||||
<Screen name="qux" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(onStateChange).toBeCalledWith({
|
||||
@@ -677,7 +677,7 @@ it('navigates to nested child in a navigator', () => {
|
||||
const navigation = React.createRef<NavigationContainerRef>();
|
||||
|
||||
const element = render(
|
||||
<NavigationContainer ref={navigation} onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer ref={navigation} onStateChange={onStateChange}>
|
||||
<TestNavigator>
|
||||
<Screen name="foo">
|
||||
{() => (
|
||||
@@ -704,7 +704,7 @@ it('navigates to nested child in a navigator', () => {
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(element).toMatchInlineSnapshot(`"[foo-a, undefined]"`);
|
||||
@@ -752,11 +752,11 @@ it('gives access to internal state', () => {
|
||||
};
|
||||
|
||||
const root = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator initialRouteName="bar">
|
||||
<Screen name="bar" component={Test} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(root).update(root);
|
||||
@@ -778,9 +778,9 @@ it("throws if navigator doesn't have any screens", () => {
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator />
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
@@ -812,14 +812,14 @@ it('throws if multiple navigators rendered under one container', () => {
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
@@ -836,12 +836,12 @@ it('throws when Screen is not the direct children', () => {
|
||||
const Bar = () => null;
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={jest.fn()} />
|
||||
<Bar />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
@@ -856,12 +856,12 @@ it('throws when a React Element is not the direct children', () => {
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={jest.fn()} />
|
||||
Hello world
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
@@ -877,7 +877,7 @@ it("doesn't throw when direct children is Screen or empty element", () => {
|
||||
};
|
||||
|
||||
render(
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={jest.fn()} />
|
||||
{null}
|
||||
@@ -885,7 +885,7 @@ it("doesn't throw when direct children is Screen or empty element", () => {
|
||||
{false}
|
||||
{true}
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -896,13 +896,13 @@ it('throws when multiple screens with same name are defined', () => {
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={jest.fn()} />
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
<Screen name="foo" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
@@ -917,26 +917,64 @@ it('switches rendered navigators', () => {
|
||||
};
|
||||
|
||||
const root = render(
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator key="a">
|
||||
<Screen name="foo" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() =>
|
||||
root.update(
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator key="b">
|
||||
<Screen name="foo" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
)
|
||||
).not.toThrowError(
|
||||
'Another navigator is already registered for this container.'
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if no name is passed to Screen', () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
useNavigationBuilder(MockRouter, props);
|
||||
return null;
|
||||
};
|
||||
|
||||
const element = (
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name={undefined as any} component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
'Got an invalid name (undefined) for the screen. It must be a non-empty string.'
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if invalid name is passed to Screen', () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
useNavigationBuilder(MockRouter, props);
|
||||
return null;
|
||||
};
|
||||
|
||||
const element = (
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name={[] as any} component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
'Got an invalid name ([]) for the screen. It must be a non-empty string.'
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if both children and component are passed', () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
useNavigationBuilder(MockRouter, props);
|
||||
@@ -944,17 +982,17 @@ it('throws if both children and component are passed', () => {
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={jest.fn()}>
|
||||
{jest.fn()}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
"We got both 'component' and 'children' props for 'Screen'. You must pass only one of them."
|
||||
"Got both 'component' and 'children' props for the screen 'foo'. You must pass only one of them."
|
||||
);
|
||||
});
|
||||
|
||||
@@ -965,15 +1003,15 @@ it('throws descriptive error for undefined screen component', () => {
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={undefined as any} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
"We couldn't find a 'component' or 'children' prop for 'Screen'"
|
||||
"Couldn't find a 'component' or 'children' prop for the screen 'foo'"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -984,15 +1022,15 @@ it('throws descriptive error for invalid screen component', () => {
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={{} as any} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
"We got an invalid value for 'component' prop for 'Screen'. It must be a a valid React Component."
|
||||
"Got an invalid value for 'component' prop for the screen 'foo'. It must be a a valid React Component."
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1003,15 +1041,15 @@ it('throws descriptive error for invalid children', () => {
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo">{[] as any}</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).toThrowError(
|
||||
"We got an invalid value for 'children' prop for 'Screen'. It must be a function returning a React Element."
|
||||
"Got an invalid value for 'children' prop for the screen 'foo'. It must be a function returning a React Element."
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1022,13 +1060,13 @@ it("doesn't throw if children is null", () => {
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={jest.fn()}>
|
||||
{null as any}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(() => render(element).update(element)).not.toThrowError();
|
||||
|
||||
70
packages/core/src/__tests__/isSerializable.test.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import isSerializable from '../isSerializable';
|
||||
|
||||
it('returns true for serializable object', () => {
|
||||
expect(
|
||||
isSerializable({
|
||||
index: 0,
|
||||
key: '7',
|
||||
routeNames: ['foo', 'bar'],
|
||||
routes: [
|
||||
{
|
||||
key: 'foo',
|
||||
name: 'foo',
|
||||
state: {
|
||||
index: 0,
|
||||
key: '8',
|
||||
routeNames: ['qux', 'lex'],
|
||||
routes: [
|
||||
{ key: 'qux', name: 'qux' },
|
||||
{ key: 'lex', name: 'lex' },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for non-serializable object', () => {
|
||||
expect(
|
||||
isSerializable({
|
||||
index: 0,
|
||||
key: '7',
|
||||
routeNames: ['foo', 'bar'],
|
||||
routes: [
|
||||
{
|
||||
key: 'foo',
|
||||
name: 'foo',
|
||||
state: {
|
||||
index: 0,
|
||||
key: '8',
|
||||
routeNames: ['qux', 'lex'],
|
||||
routes: [
|
||||
{ key: 'qux', name: 'qux', params: () => 42 },
|
||||
{ key: 'lex', name: 'lex' },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for circular references', () => {
|
||||
const o = {
|
||||
index: 0,
|
||||
key: '7',
|
||||
routeNames: ['foo', 'bar'],
|
||||
routes: [
|
||||
{
|
||||
key: 'foo',
|
||||
name: 'foo',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
o.routes[0].state = o;
|
||||
|
||||
expect(isSerializable(o)).toBe(false);
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { render, act } from 'react-native-testing-library';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
import NavigationContainer from '../NavigationContainer';
|
||||
import BaseNavigationContainer from '../BaseNavigationContainer';
|
||||
import Screen from '../Screen';
|
||||
import MockRouter, {
|
||||
MockActions,
|
||||
@@ -34,7 +34,7 @@ it('sets options with options prop as an object', () => {
|
||||
const TestScreen = (): any => 'Test screen';
|
||||
|
||||
const root = render(
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen
|
||||
name="foo"
|
||||
@@ -43,7 +43,7 @@ it('sets options with options prop as an object', () => {
|
||||
/>
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(root).toMatchInlineSnapshot(`
|
||||
@@ -79,7 +79,7 @@ it('sets options with options prop as a fuction', () => {
|
||||
const TestScreen = (): any => 'Test screen';
|
||||
|
||||
const root = render(
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen
|
||||
name="foo"
|
||||
@@ -89,7 +89,7 @@ it('sets options with options prop as a fuction', () => {
|
||||
/>
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(root).toMatchInlineSnapshot(`
|
||||
@@ -134,12 +134,12 @@ it('sets options with screenOptions prop as an object', () => {
|
||||
const TestScreenB = (): any => 'Test screen B';
|
||||
|
||||
const root = render(
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator screenOptions={{ title: 'Hello world' }}>
|
||||
<Screen name="foo" component={TestScreenA} />
|
||||
<Screen name="bar" component={TestScreenB} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(root).toMatchInlineSnapshot(`
|
||||
@@ -194,7 +194,7 @@ it('sets options with screenOptions prop as a fuction', () => {
|
||||
const TestScreenB = (): any => 'Test screen B';
|
||||
|
||||
const root = render(
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator
|
||||
screenOptions={({ route }: any) => ({
|
||||
title: `${route.name}: ${route.params.author || route.params.fruit}`,
|
||||
@@ -211,7 +211,7 @@ it('sets options with screenOptions prop as a fuction', () => {
|
||||
initialParams={{ fruit: 'Apple' }}
|
||||
/>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(root).toMatchInlineSnapshot(`
|
||||
@@ -266,14 +266,14 @@ it('sets initial options with setOptions', () => {
|
||||
};
|
||||
|
||||
const root = render(
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" options={{ color: 'blue' }}>
|
||||
{props => <TestScreen {...props} />}
|
||||
</Screen>
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(root).toMatchInlineSnapshot(`
|
||||
@@ -331,14 +331,14 @@ it('updates options with setOptions', () => {
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" options={{ color: 'blue' }}>
|
||||
{props => <TestScreen {...props} />}
|
||||
</Screen>
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
const root = render(element);
|
||||
@@ -396,7 +396,7 @@ it("returns correct value for canGoBack when it's not overridden", () => {
|
||||
};
|
||||
|
||||
const root = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen
|
||||
name="foo"
|
||||
@@ -405,7 +405,7 @@ it("returns correct value for canGoBack when it's not overridden", () => {
|
||||
/>
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(root).update(root);
|
||||
@@ -452,11 +452,11 @@ it(`returns false for canGoBack when current router doesn't handle GO_BACK`, ()
|
||||
};
|
||||
|
||||
const root = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="baz" component={TestScreen} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(root).update(root);
|
||||
@@ -513,7 +513,7 @@ it('returns true for canGoBack when current router handles GO_BACK', () => {
|
||||
};
|
||||
|
||||
const root = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<ParentNavigator>
|
||||
<Screen name="baz">
|
||||
{() => (
|
||||
@@ -523,7 +523,7 @@ it('returns true for canGoBack when current router handles GO_BACK', () => {
|
||||
)}
|
||||
</Screen>
|
||||
</ParentNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(root).update(root);
|
||||
@@ -580,7 +580,7 @@ it('returns true for canGoBack when parent router handles GO_BACK', () => {
|
||||
};
|
||||
|
||||
const root = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="baz">
|
||||
{() => (
|
||||
@@ -592,12 +592,12 @@ it('returns true for canGoBack when parent router handles GO_BACK', () => {
|
||||
<Screen name="qux">
|
||||
{() => (
|
||||
<OverrodeNavigator>
|
||||
<Screen name="qux" component={() => null} />
|
||||
<Screen name="qux">{() => null}</Screen>
|
||||
</OverrodeNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(root).update(root);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { render, act } from 'react-native-testing-library';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
import NavigationContainer from '../NavigationContainer';
|
||||
import BaseNavigationContainer from '../BaseNavigationContainer';
|
||||
import Screen from '../Screen';
|
||||
import MockRouter from './__fixtures__/MockRouter';
|
||||
import { Router, NavigationState } from '../types';
|
||||
@@ -47,7 +47,7 @@ it('fires focus and blur events in root navigator', () => {
|
||||
const navigation = React.createRef<any>();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator ref={navigation}>
|
||||
<Screen
|
||||
name="first"
|
||||
@@ -66,7 +66,7 @@ it('fires focus and blur events in root navigator', () => {
|
||||
component={createComponent(fourthFocusCallback, fourthBlurCallback)}
|
||||
/>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element);
|
||||
@@ -139,7 +139,7 @@ it('fires focus and blur events in nested navigator', () => {
|
||||
const child = React.createRef<any>();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator ref={parent}>
|
||||
<Screen
|
||||
name="first"
|
||||
@@ -170,7 +170,7 @@ it('fires focus and blur events in nested navigator', () => {
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element);
|
||||
@@ -307,12 +307,12 @@ it('fires blur event when a route is removed with a delay', async () => {
|
||||
const navigation = React.createRef<any>();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator ref={navigation}>
|
||||
<Screen name="first" component={First} />
|
||||
<Screen name="second" component={Second} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element);
|
||||
@@ -363,13 +363,13 @@ it('fires custom events', () => {
|
||||
const ref = React.createRef<any>();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator ref={ref}>
|
||||
<Screen name="first" component={createComponent(firstCallback)} />
|
||||
<Screen name="second" component={createComponent(secondCallback)} />
|
||||
<Screen name="third" component={createComponent(thirdCallback)} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element);
|
||||
@@ -444,11 +444,11 @@ it('has option to prevent default', () => {
|
||||
const ref = React.createRef<any>();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator ref={ref}>
|
||||
<Screen name="first" component={Test} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element);
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as React from 'react';
|
||||
import { render, act } from 'react-native-testing-library';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
import useFocusEffect from '../useFocusEffect';
|
||||
import NavigationContainer from '../NavigationContainer';
|
||||
import BaseNavigationContainer from '../BaseNavigationContainer';
|
||||
import Screen from '../Screen';
|
||||
import MockRouter from './__fixtures__/MockRouter';
|
||||
|
||||
@@ -31,13 +31,13 @@ it('runs focus effect on focus change', () => {
|
||||
const navigation = React.createRef<any>();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer ref={navigation}>
|
||||
<BaseNavigationContainer ref={navigation}>
|
||||
<TestNavigator>
|
||||
<Screen name="first">{() => null}</Screen>
|
||||
<Screen name="second" component={Test} />
|
||||
<Screen name="third">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element);
|
||||
@@ -84,12 +84,12 @@ it('runs focus effect on deps change', () => {
|
||||
};
|
||||
|
||||
const App = ({ count }: { count: number }) => (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="first">{() => <Test count={count} />}</Screen>
|
||||
<Screen name="second">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
const root = render(<App count={1} />);
|
||||
@@ -135,13 +135,13 @@ it('runs focus effect when initial state is given', () => {
|
||||
const navigation = React.createRef<any>();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer ref={navigation} initialState={initialState}>
|
||||
<BaseNavigationContainer ref={navigation} initialState={initialState}>
|
||||
<TestNavigator>
|
||||
<Screen name="first">{() => null}</Screen>
|
||||
<Screen name="second">{() => null}</Screen>
|
||||
<Screen name="third" component={Test} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element);
|
||||
@@ -178,13 +178,13 @@ it('runs focus effect when only focused route is rendered', () => {
|
||||
const navigation = React.createRef<any>();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer ref={navigation}>
|
||||
<BaseNavigationContainer ref={navigation}>
|
||||
<TestNavigator>
|
||||
<Screen name="first" component={Test} />
|
||||
<Screen name="second">{() => null}</Screen>
|
||||
<Screen name="third">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element);
|
||||
@@ -221,12 +221,12 @@ it('runs cleanup when component is unmounted', () => {
|
||||
const TestB = () => null;
|
||||
|
||||
const App = ({ mounted }: { mounted: boolean }) => (
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="first" component={mounted ? TestA : TestB} />
|
||||
<Screen name="second">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
const root = render(<App mounted />);
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as React from 'react';
|
||||
import { render, act } from 'react-native-testing-library';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
import useIsFocused from '../useIsFocused';
|
||||
import NavigationContainer from '../NavigationContainer';
|
||||
import BaseNavigationContainer from '../BaseNavigationContainer';
|
||||
import Screen from '../Screen';
|
||||
import MockRouter from './__fixtures__/MockRouter';
|
||||
|
||||
@@ -24,13 +24,13 @@ it('renders correct focus state', () => {
|
||||
const navigation = React.createRef<any>();
|
||||
|
||||
const root = render(
|
||||
<NavigationContainer ref={navigation}>
|
||||
<BaseNavigationContainer ref={navigation}>
|
||||
<TestNavigator>
|
||||
<Screen name="first">{() => null}</Screen>
|
||||
<Screen name="second" component={Test} />
|
||||
<Screen name="third">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(root).toMatchInlineSnapshot(`"unfocused"`);
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as React from 'react';
|
||||
import { render } from 'react-native-testing-library';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
import useNavigation from '../useNavigation';
|
||||
import NavigationContainer from '../NavigationContainer';
|
||||
import BaseNavigationContainer from '../BaseNavigationContainer';
|
||||
import Screen from '../Screen';
|
||||
import MockRouter from './__fixtures__/MockRouter';
|
||||
|
||||
@@ -24,11 +24,11 @@ it('gets navigation prop from context', () => {
|
||||
};
|
||||
|
||||
render(
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={Test} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -50,7 +50,7 @@ it("gets navigation's parent from context", () => {
|
||||
};
|
||||
|
||||
render(
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo">
|
||||
{() => (
|
||||
@@ -60,7 +60,7 @@ it("gets navigation's parent from context", () => {
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -86,7 +86,7 @@ it("gets navigation's parent's parent from context", () => {
|
||||
};
|
||||
|
||||
render(
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo">
|
||||
{() => (
|
||||
@@ -102,7 +102,7 @@ it("gets navigation's parent's parent from context", () => {
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as React from 'react';
|
||||
import { render, act } from 'react-native-testing-library';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
import useNavigationState from '../useNavigationState';
|
||||
import NavigationContainer from '../NavigationContainer';
|
||||
import BaseNavigationContainer from '../BaseNavigationContainer';
|
||||
import Screen from '../Screen';
|
||||
import MockRouter from './__fixtures__/MockRouter';
|
||||
import { NavigationState } from '../types';
|
||||
@@ -27,13 +27,13 @@ it('gets the current navigation state', () => {
|
||||
const navigation = React.createRef<any>();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer ref={navigation}>
|
||||
<BaseNavigationContainer ref={navigation}>
|
||||
<TestNavigator>
|
||||
<Screen name="first" component={Test} />
|
||||
<Screen name="second">{() => null}</Screen>
|
||||
<Screen name="third">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element);
|
||||
@@ -78,13 +78,13 @@ it('gets the current navigation state with selector', () => {
|
||||
const navigation = React.createRef<any>();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer ref={navigation}>
|
||||
<BaseNavigationContainer ref={navigation}>
|
||||
<TestNavigator>
|
||||
<Screen name="first" component={Test} />
|
||||
<Screen name="second">{() => null}</Screen>
|
||||
<Screen name="third">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element);
|
||||
@@ -133,13 +133,13 @@ it('gets the correct value if selector changes', () => {
|
||||
const App = ({ selector }: { selector: (state: NavigationState) => any }) => {
|
||||
return (
|
||||
<SelectorContext.Provider value={selector}>
|
||||
<NavigationContainer ref={navigation}>
|
||||
<BaseNavigationContainer ref={navigation}>
|
||||
<TestNavigator>
|
||||
<Screen name="first" component={Test} />
|
||||
<Screen name="second">{() => null}</Screen>
|
||||
<Screen name="third">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
</SelectorContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { render } from 'react-native-testing-library';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
import NavigationContainer from '../NavigationContainer';
|
||||
import BaseNavigationContainer from '../BaseNavigationContainer';
|
||||
import Screen from '../Screen';
|
||||
import MockRouter, {
|
||||
MockActions,
|
||||
@@ -58,7 +58,7 @@ it("lets parent handle the action if child didn't", () => {
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
render(
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer onStateChange={onStateChange}>
|
||||
<ParentNavigator initialRouteName="baz">
|
||||
<Screen name="foo">{() => null}</Screen>
|
||||
<Screen name="bar">{() => null}</Screen>
|
||||
@@ -70,7 +70,7 @@ it("lets parent handle the action if child didn't", () => {
|
||||
)}
|
||||
</Screen>
|
||||
</ParentNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
@@ -171,7 +171,7 @@ it("lets children handle the action if parent didn't", () => {
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer
|
||||
<BaseNavigationContainer
|
||||
initialState={initialState}
|
||||
onStateChange={onStateChange}
|
||||
>
|
||||
@@ -181,13 +181,13 @@ it("lets children handle the action if parent didn't", () => {
|
||||
<Screen name="baz">
|
||||
{() => (
|
||||
<ChildNavigator>
|
||||
<Screen name="qux" component={() => null} />
|
||||
<Screen name="lex" component={() => null} />
|
||||
<Screen name="qux">{() => null}</Screen>
|
||||
<Screen name="lex">{() => null}</Screen>
|
||||
</ChildNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</ParentNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
@@ -284,20 +284,20 @@ it("action doesn't bubble if target is specified", () => {
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<BaseNavigationContainer onStateChange={onStateChange}>
|
||||
<ParentNavigator>
|
||||
<Screen name="foo">{() => null}</Screen>
|
||||
<Screen name="bar" component={TestScreen} />
|
||||
<Screen name="baz">
|
||||
{() => (
|
||||
<ChildNavigator>
|
||||
<Screen name="qux" component={() => null} />
|
||||
<Screen name="lex" component={() => null} />
|
||||
<Screen name="qux">{() => null}</Screen>
|
||||
<Screen name="lex">{() => null}</Screen>
|
||||
</ChildNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</ParentNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
@@ -349,20 +349,20 @@ it('logs error if no navigator handled the action', () => {
|
||||
};
|
||||
|
||||
const element = (
|
||||
<NavigationContainer initialState={initialState}>
|
||||
<BaseNavigationContainer initialState={initialState}>
|
||||
<TestNavigator>
|
||||
<Screen name="foo">{() => null}</Screen>
|
||||
<Screen name="bar" component={TestScreen} />
|
||||
<Screen name="baz">
|
||||
{() => (
|
||||
<TestNavigator>
|
||||
<Screen name="qux" component={() => null} />
|
||||
<Screen name="lex" component={() => null} />
|
||||
<Screen name="qux">{() => null}</Screen>
|
||||
<Screen name="lex">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
const spy = jest.spyOn(console, 'error').mockImplementation();
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as React from 'react';
|
||||
import { render } from 'react-native-testing-library';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
import useRoute from '../useRoute';
|
||||
import NavigationContainer from '../NavigationContainer';
|
||||
import BaseNavigationContainer from '../BaseNavigationContainer';
|
||||
import Screen from '../Screen';
|
||||
import MockRouter from './__fixtures__/MockRouter';
|
||||
import { RouteProp } from '../types';
|
||||
@@ -25,10 +25,10 @@ it('gets route prop from context', () => {
|
||||
};
|
||||
|
||||
render(
|
||||
<NavigationContainer>
|
||||
<BaseNavigationContainer>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={Test} initialParams={{ x: 1 }} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -8,8 +8,11 @@ type StringifyConfig = Record<string, (value: any) => string>;
|
||||
type Options = {
|
||||
[routeName: string]:
|
||||
| string
|
||||
| { path: string; stringify?: StringifyConfig }
|
||||
| Options;
|
||||
| {
|
||||
path?: string;
|
||||
stringify?: StringifyConfig;
|
||||
screens?: Options;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -43,6 +46,9 @@ export default function getPathFromState(
|
||||
state?: State,
|
||||
options: Options = {}
|
||||
): string {
|
||||
if (state === undefined) {
|
||||
throw Error('NavigationState not passed');
|
||||
}
|
||||
let path = '/';
|
||||
|
||||
let current: State | undefined = state;
|
||||
@@ -50,7 +56,7 @@ export default function getPathFromState(
|
||||
while (current) {
|
||||
let index = typeof current.index === 'number' ? current.index : 0;
|
||||
let route = current.routes[index] as Route<string> & {
|
||||
state?: State | undefined;
|
||||
state?: State;
|
||||
};
|
||||
let currentOptions = options;
|
||||
let pattern = route.name;
|
||||
@@ -64,10 +70,14 @@ export default function getPathFromState(
|
||||
pattern = (currentOptions[route.name] as { path: string }).path;
|
||||
break;
|
||||
} else {
|
||||
currentOptions = currentOptions[route.name] as Options;
|
||||
if (!(currentOptions[route.name] as { screens?: Options }).screens) {
|
||||
throw Error('Wrong Options object passed');
|
||||
}
|
||||
currentOptions = (currentOptions[route.name] as { screens: Options })
|
||||
.screens;
|
||||
index = typeof route.state.index === 'number' ? route.state.index : 0;
|
||||
route = route.state.routes[index] as Route<string> & {
|
||||
state?: State | undefined;
|
||||
state?: State;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,14 @@ import { NavigationState, PartialState, InitialState } from './types';
|
||||
type ParseConfig = Record<string, (value: string) => any>;
|
||||
|
||||
type Options = {
|
||||
[routeName: string]: string | { path: string; parse?: ParseConfig } | Options;
|
||||
[routeName: string]:
|
||||
| string
|
||||
| {
|
||||
path?: string;
|
||||
parse?: ParseConfig;
|
||||
screens?: Options;
|
||||
initialRouteName?: string;
|
||||
};
|
||||
};
|
||||
|
||||
type RouteConfig = {
|
||||
@@ -15,6 +22,11 @@ type RouteConfig = {
|
||||
parse: ParseConfig | undefined;
|
||||
};
|
||||
|
||||
type InitialRouteConfig = {
|
||||
initialRouteName: string;
|
||||
connectedRoutes: string[];
|
||||
};
|
||||
|
||||
type ResultState = PartialState<NavigationState> & {
|
||||
state?: ResultState;
|
||||
};
|
||||
@@ -42,9 +54,15 @@ export default function getStateFromPath(
|
||||
path: string,
|
||||
options: Options = {}
|
||||
): ResultState | undefined {
|
||||
if (path === '') {
|
||||
return undefined;
|
||||
}
|
||||
let initialRoutes: InitialRouteConfig[] = [];
|
||||
// Create a normalized configs array which will be easier to use
|
||||
const configs = ([] as RouteConfig[]).concat(
|
||||
...Object.keys(options).map(key => createNormalizedConfigs(key, options))
|
||||
...Object.keys(options).map(key =>
|
||||
createNormalizedConfigs(key, options, [], initialRoutes)
|
||||
)
|
||||
);
|
||||
|
||||
let result: PartialState<NavigationState> | undefined;
|
||||
@@ -56,8 +74,8 @@ export default function getStateFromPath(
|
||||
.replace(/\?.*/, ''); // Remove query params which we will handle later
|
||||
|
||||
while (remaining) {
|
||||
let routeNames;
|
||||
let params;
|
||||
let routeNames: string[] | undefined;
|
||||
let params: Record<string, any> | undefined;
|
||||
|
||||
// Go through all configs, and see if the next path segment matches our regex
|
||||
for (const config of configs) {
|
||||
@@ -65,7 +83,7 @@ export default function getStateFromPath(
|
||||
|
||||
// If our regex matches, we need to extract params from the path
|
||||
if (match) {
|
||||
routeNames = config.routeNames;
|
||||
routeNames = [...config.routeNames];
|
||||
|
||||
const paramPatterns = config.pattern
|
||||
.split('/')
|
||||
@@ -102,46 +120,43 @@ export default function getStateFromPath(
|
||||
}
|
||||
|
||||
let state: InitialState;
|
||||
let routeName = routeNames.shift() as string;
|
||||
let initialRoute = findInitialRoute(routeName, initialRoutes);
|
||||
|
||||
if (routeNames.length === 1) {
|
||||
state = {
|
||||
routes: [
|
||||
{ name: routeNames.shift() as string, ...(params && { params }) },
|
||||
],
|
||||
};
|
||||
} else {
|
||||
state = {
|
||||
routes: [{ name: routeNames.shift() as string, state: { routes: [] } }],
|
||||
};
|
||||
state = createNestedState(
|
||||
initialRoute,
|
||||
routeName,
|
||||
routeNames.length === 0,
|
||||
params
|
||||
);
|
||||
|
||||
let helper = state.routes[0].state as InitialState;
|
||||
let routeName;
|
||||
if (routeNames.length > 0) {
|
||||
let nestedState = state;
|
||||
|
||||
while ((routeName = routeNames.shift())) {
|
||||
if (routeNames.length === 0) {
|
||||
helper.routes.push({
|
||||
name: routeName,
|
||||
...(params && { params }),
|
||||
});
|
||||
} else {
|
||||
helper.routes[0] = {
|
||||
name: routeName,
|
||||
state: {
|
||||
routes: [],
|
||||
},
|
||||
};
|
||||
helper = helper.routes[0].state as InitialState;
|
||||
while ((routeName = routeNames.shift() as string)) {
|
||||
initialRoute = findInitialRoute(routeName, initialRoutes);
|
||||
nestedState.routes[nestedState.index || 0].state = createNestedState(
|
||||
initialRoute,
|
||||
routeName,
|
||||
routeNames.length === 0,
|
||||
params
|
||||
);
|
||||
if (routeNames.length > 0) {
|
||||
nestedState = nestedState.routes[nestedState.index || 0]
|
||||
.state as InitialState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current) {
|
||||
// The state should be nested inside the deepest route we parsed before
|
||||
while (current.routes[0].state) {
|
||||
current = current.routes[0].state;
|
||||
while (current?.routes[current.index || 0].state) {
|
||||
current = current.routes[current.index || 0].state;
|
||||
}
|
||||
|
||||
current.routes[0].state = state;
|
||||
(current as PartialState<NavigationState>).routes[
|
||||
current?.index || 0
|
||||
].state = state;
|
||||
} else {
|
||||
result = state;
|
||||
}
|
||||
@@ -156,15 +171,17 @@ export default function getStateFromPath(
|
||||
const query = path.split('?')[1];
|
||||
|
||||
if (query) {
|
||||
while (current.routes[0].state) {
|
||||
while (current?.routes[current.index || 0].state) {
|
||||
// The query params apply to the deepest route
|
||||
current = current.routes[0].state;
|
||||
current = current.routes[current.index || 0].state;
|
||||
}
|
||||
|
||||
const route = current.routes[0];
|
||||
const route = (current as PartialState<NavigationState>).routes[
|
||||
current?.index || 0
|
||||
];
|
||||
|
||||
const params = queryString.parse(query);
|
||||
const parseFunction = findParseConfigForRoute(route.name, options);
|
||||
const parseFunction = findParseConfigForRoute(route.name, configs);
|
||||
|
||||
if (parseFunction) {
|
||||
Object.keys(params).forEach(name => {
|
||||
@@ -183,9 +200,10 @@ export default function getStateFromPath(
|
||||
function createNormalizedConfigs(
|
||||
key: string,
|
||||
routeConfig: Options,
|
||||
routeNames: string[] = []
|
||||
routeNames: string[] = [],
|
||||
initials: InitialRouteConfig[]
|
||||
): RouteConfig[] {
|
||||
const configs = [];
|
||||
const configs: RouteConfig[] = [];
|
||||
|
||||
routeNames.push(key);
|
||||
|
||||
@@ -196,30 +214,29 @@ function createNormalizedConfigs(
|
||||
configs.push(createConfigItem(routeNames, value));
|
||||
} else if (typeof value === 'object') {
|
||||
// if an object is specified as the value (e.g. Foo: { ... }),
|
||||
// it could have config object and optionally nested config
|
||||
Object.keys(value).forEach(nestedKey => {
|
||||
if (nestedKey === 'path') {
|
||||
configs.push(
|
||||
createConfigItem(
|
||||
routeNames,
|
||||
value[nestedKey] as string,
|
||||
value.parse ? (value.parse as ParseConfig) : undefined
|
||||
)
|
||||
);
|
||||
} else if (nestedKey === 'parse') {
|
||||
// We handle custom parse function when a `path` is specified (in nestedKey === path)
|
||||
} else {
|
||||
// If the name of the key is not `path` or `parse`, it's a nested config for route
|
||||
// So we need to traverse into it and collect the configs
|
||||
const result = createNormalizedConfigs(
|
||||
nestedKey,
|
||||
routeConfig[key] as Options,
|
||||
routeNames
|
||||
);
|
||||
|
||||
configs.push(...result);
|
||||
// it can have `path` property and
|
||||
// it could have `screens` prop which has nested configs
|
||||
if (value.path) {
|
||||
configs.push(createConfigItem(routeNames, value.path, value.parse));
|
||||
}
|
||||
if (value.screens) {
|
||||
// property `initialRouteName` without `screens` has no purpose
|
||||
if (value.initialRouteName) {
|
||||
initials.push({
|
||||
initialRouteName: value.initialRouteName,
|
||||
connectedRoutes: Object.keys(value.screens),
|
||||
});
|
||||
}
|
||||
});
|
||||
Object.keys(value.screens).forEach(nestedConfig => {
|
||||
const result = createNormalizedConfigs(
|
||||
nestedConfig,
|
||||
value.screens as Options,
|
||||
routeNames,
|
||||
initials
|
||||
);
|
||||
configs.push(...result);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
routeNames.pop();
|
||||
@@ -247,21 +264,64 @@ function createConfigItem(
|
||||
|
||||
function findParseConfigForRoute(
|
||||
routeName: string,
|
||||
config: Options
|
||||
flatConfig: RouteConfig[]
|
||||
): ParseConfig | undefined {
|
||||
if (config[routeName]) {
|
||||
return (config[routeName] as { parse?: ParseConfig }).parse;
|
||||
}
|
||||
|
||||
for (const name in config) {
|
||||
if (typeof config[name] === 'object') {
|
||||
const parse = findParseConfigForRoute(routeName, config[name] as Options);
|
||||
|
||||
if (parse) {
|
||||
return parse;
|
||||
}
|
||||
for (const config of flatConfig) {
|
||||
if (routeName === config.routeNames[config.routeNames.length - 1]) {
|
||||
return config.parse;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// tries to find an initial route connected with the one passed
|
||||
function findInitialRoute(
|
||||
routeName: string,
|
||||
initialRoutes: InitialRouteConfig[]
|
||||
): string | undefined {
|
||||
for (const config of initialRoutes) {
|
||||
if (config.connectedRoutes.includes(routeName)) {
|
||||
return config.initialRouteName === routeName
|
||||
? undefined
|
||||
: config.initialRouteName;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// returns nested state object with values depending on whether
|
||||
// it is the end of state and if there is initialRoute for this level
|
||||
function createNestedState(
|
||||
initialRoute: string | undefined,
|
||||
routeName: string,
|
||||
isEmpty: boolean,
|
||||
params?: Record<string, any> | undefined
|
||||
): InitialState {
|
||||
if (isEmpty) {
|
||||
if (initialRoute) {
|
||||
return {
|
||||
index: 1,
|
||||
routes: [
|
||||
{ name: initialRoute },
|
||||
{ name: routeName as string, ...(params && { params }) },
|
||||
],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
routes: [{ name: routeName as string, ...(params && { params }) }],
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (initialRoute) {
|
||||
return {
|
||||
index: 1,
|
||||
routes: [
|
||||
{ name: initialRoute },
|
||||
{ name: routeName as string, state: { routes: [] } },
|
||||
],
|
||||
};
|
||||
} else {
|
||||
return { routes: [{ name: routeName as string, state: { routes: [] } }] };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as CommonActions from './CommonActions';
|
||||
export { CommonActions };
|
||||
|
||||
export { default as BaseRouter } from './BaseRouter';
|
||||
export { default as NavigationContainer } from './NavigationContainer';
|
||||
export { default as BaseNavigationContainer } from './BaseNavigationContainer';
|
||||
export { default as createNavigatorFactory } from './createNavigatorFactory';
|
||||
|
||||
export { default as NavigationContext } from './NavigationContext';
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
export default function isSerializable(o: { [key: string]: any }): boolean {
|
||||
const isSerializableWithoutCircularReference = (
|
||||
o: { [key: string]: any },
|
||||
seen = new Set<any>()
|
||||
): boolean => {
|
||||
if (
|
||||
o === undefined ||
|
||||
o === null ||
|
||||
@@ -16,19 +19,29 @@ export default function isSerializable(o: { [key: string]: any }): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (seen.has(o)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
seen.add(o);
|
||||
|
||||
if (Array.isArray(o)) {
|
||||
for (const it of o) {
|
||||
if (!isSerializable(it)) {
|
||||
if (!isSerializableWithoutCircularReference(it, seen)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const key in o) {
|
||||
if (!isSerializable(o[key])) {
|
||||
if (!isSerializableWithoutCircularReference(o[key], seen)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
export default function isSerializable(o: { [key: string]: any }) {
|
||||
return isSerializableWithoutCircularReference(o);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@ export type NavigationState = {
|
||||
* List of valid route names as defined in the screen components.
|
||||
*/
|
||||
routeNames: string[];
|
||||
/**
|
||||
* Alternative entries for history.
|
||||
*/
|
||||
history?: unknown[];
|
||||
/**
|
||||
* List of rendered routes.
|
||||
*/
|
||||
@@ -212,10 +216,10 @@ export type EventMapBase = Record<
|
||||
{ data?: any; canPreventDefault?: boolean }
|
||||
>;
|
||||
|
||||
export type EventMapCore = {
|
||||
export type EventMapCore<State extends NavigationState> = {
|
||||
focus: { data: undefined };
|
||||
blur: { data: undefined };
|
||||
state: { data: { state: NavigationState } };
|
||||
state: { data: { state: State } };
|
||||
};
|
||||
|
||||
export type EventArg<
|
||||
@@ -458,7 +462,7 @@ export type NavigationProp<
|
||||
* Note that this method doesn't re-render screen when the result changes. So don't use it in `render`.
|
||||
*/
|
||||
dangerouslyGetState(): State;
|
||||
} & EventConsumer<EventMap & EventMapCore> &
|
||||
} & EventConsumer<EventMap & EventMapCore<State>> &
|
||||
PrivateValueStore<ParamList, RouteName, EventMap>;
|
||||
|
||||
export type RouteProp<
|
||||
@@ -579,18 +583,21 @@ export type RouteConfig<
|
||||
);
|
||||
|
||||
export type NavigationContainerRef =
|
||||
| (NavigationHelpers<ParamListBase> & {
|
||||
/**
|
||||
* Reset the navigation state of the root navigator to the provided state.
|
||||
*
|
||||
* @param state Navigation state object.
|
||||
*/
|
||||
resetRoot(state?: PartialState<NavigationState> | NavigationState): void;
|
||||
/**
|
||||
* Get the rehydrated navigation state of the navigation tree.
|
||||
*/
|
||||
getRootState(): NavigationState;
|
||||
})
|
||||
| (NavigationHelpers<ParamListBase> &
|
||||
EventConsumer<{ state: { data: { state: NavigationState } } }> & {
|
||||
/**
|
||||
* Reset the navigation state of the root navigator to the provided state.
|
||||
*
|
||||
* @param state Navigation state object.
|
||||
*/
|
||||
resetRoot(
|
||||
state?: PartialState<NavigationState> | NavigationState
|
||||
): void;
|
||||
/**
|
||||
* Get the rehydrated navigation state of the navigation tree.
|
||||
*/
|
||||
getRootState(): NavigationState;
|
||||
})
|
||||
| undefined
|
||||
| null;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { isValidElementType } from 'react-is';
|
||||
import { NavigationStateContext } from './NavigationContainer';
|
||||
import { NavigationStateContext } from './BaseNavigationContainer';
|
||||
import NavigationRouteContext from './NavigationRouteContext';
|
||||
import Screen from './Screen';
|
||||
import { navigate } from './CommonActions';
|
||||
@@ -88,29 +88,46 @@ const getRouteConfigsFromChildren = <ScreenOptions extends object>(
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
configs.forEach(config => {
|
||||
const { children, component } = config as any;
|
||||
const { name, children, component } = config as any;
|
||||
|
||||
if (typeof name !== 'string' || !name) {
|
||||
throw new Error(
|
||||
`Got an invalid name (${JSON.stringify(
|
||||
name
|
||||
)}) for the screen. It must be a non-empty string.`
|
||||
);
|
||||
}
|
||||
|
||||
if (children != null || component !== undefined) {
|
||||
if (children != null && component !== undefined) {
|
||||
throw new Error(
|
||||
"We got both 'component' and 'children' props for 'Screen'. You must pass only one of them."
|
||||
`Got both 'component' and 'children' props for the screen '${name}'. You must pass only one of them.`
|
||||
);
|
||||
}
|
||||
|
||||
if (children != null && typeof children !== 'function') {
|
||||
throw new Error(
|
||||
`We got an invalid value for 'children' prop for 'Screen'. It must be a function returning a React Element.`
|
||||
`Got an invalid value for 'children' prop for the screen '${name}'. It must be a function returning a React Element.`
|
||||
);
|
||||
}
|
||||
|
||||
if (component !== undefined && !isValidElementType(component)) {
|
||||
throw new Error(
|
||||
`We got an invalid value for 'component' prop for 'Screen'. It must be a a valid React Component.`
|
||||
`Got an invalid value for 'component' prop for the screen '${name}'. It must be a a valid React Component.`
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof component === 'function' && component.name === 'component') {
|
||||
// Inline anonymous functions passed in the `component` prop will have the name of the prop
|
||||
// It's relatively safe to assume that it's not a component since it should also have PascalCase name
|
||||
// We won't catch all scenarios here, but this should catch a good chunk of incorrect use.
|
||||
console.warn(
|
||||
`Looks like you're passing an inline function for 'component' prop for the screen '${name}' (e.g. component={() => <SomeComponent />}). Passing an inline function will cause the component state to be lost on re-render and cause perf issues since it's re-created every render. You can pass the function as children to 'Screen' instead to achieve the desired behaviour.`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
"We couldn't find a 'component' or 'children' prop for 'Screen'. This can happen if you passed 'undefined'. You likely forgot to export your component from the file it's defined in, or mixed up default import and named import when importing."
|
||||
`Couldn't find a 'component' or 'children' prop for the screen '${name}'. This can happen if you passed 'undefined'. You likely forgot to export your component from the file it's defined in, or mixed up default import and named import when importing.`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import * as CommonActions from './CommonActions';
|
||||
import NavigationContext from './NavigationContext';
|
||||
import { NavigationStateContext } from './NavigationContainer';
|
||||
import { NavigationStateContext } from './BaseNavigationContainer';
|
||||
import { NavigationEventEmitter } from './useEventEmitter';
|
||||
import {
|
||||
NavigationHelpers,
|
||||
@@ -51,7 +51,7 @@ export default function useNavigationHelpers<
|
||||
console.error(
|
||||
`The action '${payload.type}' with payload '${JSON.stringify(
|
||||
payload.payload
|
||||
)}' was not handled by any navigator.`
|
||||
)}' was not handled by any navigator. If you are trying to navigate to a screen, check if the screen exists in your navigator.`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,6 +3,76 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.0.0-alpha.47](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.0-alpha.46...@react-navigation/drawer@5.0.0-alpha.47) (2020-02-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.46](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.0-alpha.45...@react-navigation/drawer@5.0.0-alpha.46) (2020-02-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* disable pan gesture by default in the browser for Apple devices ([b277927](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/b2779279251b1f157ba825cc34e39046b44f00d8)), closes [#287](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/issues/287)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.45](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.0-alpha.44...@react-navigation/drawer@5.0.0-alpha.45) (2020-02-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.44](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.0-alpha.41...@react-navigation/drawer@5.0.0-alpha.44) (2020-02-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add licenses ([0c159db](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/0c159db4c9bc85e83b5cfe6819ab2562669a4d8f))
|
||||
* fix drawerType=back when drawer is on right ([9198597](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/9198597b7f0a34fbe3844ec86a8b82171036f8ed)), closes [#316](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/issues/316)
|
||||
* handle back button in drawer itself ([0e8fda3](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/0e8fda319685a34090cfe82da08084c156eb5783))
|
||||
* screens integration on Android ([#294](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/issues/294)) ([9bfb295](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/9bfb29562020c61b4d5c9bee278bcb1c7bdb8b67))
|
||||
* update screens for native stack ([5411816](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/54118161885738a6d20b062c7e6679f3bace8424))
|
||||
* wrap navigators in gesture handler root ([41a5e1a](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/41a5e1a385aa5180abc3992a4c67077c37b998b9))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add useIsDrawerOpen hook ([#299](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/issues/299)) ([ecd68af](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/ecd68afb46a4c56200748da5e5fb284fa5a839db))
|
||||
* integrate with history API on web ([5a3f835](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/5a3f8356b05bff7ed20893a5db6804612da3e568))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.42](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.0-alpha.41...@react-navigation/drawer@5.0.0-alpha.42) (2020-02-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add licenses ([0c159db](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/0c159db4c9bc85e83b5cfe6819ab2562669a4d8f))
|
||||
* fix drawerType=back when drawer is on right ([9198597](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/9198597b7f0a34fbe3844ec86a8b82171036f8ed)), closes [#316](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/issues/316)
|
||||
* handle back button in drawer itself ([0e8fda3](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/0e8fda319685a34090cfe82da08084c156eb5783))
|
||||
* screens integration on Android ([#294](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/issues/294)) ([9bfb295](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/9bfb29562020c61b4d5c9bee278bcb1c7bdb8b67))
|
||||
* update screens for native stack ([5411816](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/54118161885738a6d20b062c7e6679f3bace8424))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add useIsDrawerOpen hook ([#299](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/issues/299)) ([ecd68af](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/ecd68afb46a4c56200748da5e5fb284fa5a839db))
|
||||
* integrate with history API on web ([5a3f835](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/commit/5a3f8356b05bff7ed20893a5db6804612da3e568))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.41](https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.0-alpha.40...@react-navigation/drawer@5.0.0-alpha.41) (2020-01-24)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
21
packages/drawer/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 React Navigation Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -11,7 +11,7 @@
|
||||
"material",
|
||||
"drawer"
|
||||
],
|
||||
"version": "5.0.0-alpha.41",
|
||||
"version": "5.0.0-alpha.47",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/react-navigation/navigation-ex/tree/master/packages/drawer",
|
||||
"main": "lib/commonjs/index.js",
|
||||
@@ -31,7 +31,7 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.0.0-alpha.27",
|
||||
"@react-navigation/routers": "^5.0.0-alpha.33",
|
||||
"color": "^3.1.2",
|
||||
"react-native-iphone-x-helper": "^1.2.1"
|
||||
},
|
||||
@@ -42,10 +42,10 @@
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-gesture-handler": "^1.5.3",
|
||||
"react-native-gesture-handler": "^1.5.5",
|
||||
"react-native-reanimated": "^1.4.0",
|
||||
"react-native-safe-area-context": "^0.6.2",
|
||||
"react-native-screens": "^2.0.0-alpha.25",
|
||||
"react-native-screens": "^2.0.0-alpha.33",
|
||||
"typescript": "^3.7.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -55,7 +55,7 @@
|
||||
"react-native-gesture-handler": "^1.0.0",
|
||||
"react-native-reanimated": "^1.0.0",
|
||||
"react-native-safe-area-context": "^0.6.0",
|
||||
"react-native-screens": "^1.0.0-alpha.0 || ^2.0.0-alpha.0"
|
||||
"react-native-screens": "^2.0.0-alpha.33"
|
||||
},
|
||||
"@react-native-community/bob": {
|
||||
"source": "src",
|
||||
|
||||
@@ -17,6 +17,8 @@ export { default as DrawerContentScrollView } from './views/DrawerContentScrollV
|
||||
*/
|
||||
export { default as DrawerGestureContext } from './utils/DrawerGestureContext';
|
||||
|
||||
export { default as useIsDrawerOpen } from './utils/useIsDrawerOpen';
|
||||
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
|
||||
@@ -63,11 +63,6 @@ export type DrawerNavigationConfig<T = DrawerContentOptions> = {
|
||||
* Set it to `false` if you want to render all screens on initial render.
|
||||
*/
|
||||
lazy?: boolean;
|
||||
/**
|
||||
* Whether a screen should be unmounted when navigating away from it.
|
||||
* Defaults to `false`.
|
||||
*/
|
||||
unmountInactiveScreens?: boolean;
|
||||
/**
|
||||
* Function that returns React element to render as the content of the drawer, for example, navigation items.
|
||||
* Defaults to `DrawerContent`.
|
||||
@@ -117,6 +112,11 @@ export type DrawerNavigationOptions = {
|
||||
* Defaults to `true`
|
||||
*/
|
||||
gestureEnabled?: boolean;
|
||||
/**
|
||||
* Whether this screen should be unmounted when navigating away from it.
|
||||
* Defaults to `false`.
|
||||
*/
|
||||
unmountOnBlur?: boolean;
|
||||
};
|
||||
|
||||
export type DrawerContentComponentProps<T = DrawerContentOptions> = T & {
|
||||
@@ -128,10 +128,6 @@ export type DrawerContentComponentProps<T = DrawerContentOptions> = T & {
|
||||
* `0` is closed, `1` is open.
|
||||
*/
|
||||
progress: Animated.Node<number>;
|
||||
/**
|
||||
* Position of the drawer on the screen.
|
||||
*/
|
||||
drawerPosition: 'left' | 'right';
|
||||
};
|
||||
|
||||
export type DrawerContentOptions = {
|
||||
|
||||
3
packages/drawer/src/utils/DrawerPositionContext.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export default React.createContext<'left' | 'right' | undefined>(undefined);
|
||||
38
packages/drawer/src/utils/useIsDrawerOpen.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import * as React from 'react';
|
||||
import { useNavigation, ParamListBase } from '@react-navigation/native';
|
||||
import { DrawerNavigationProp } from '../types';
|
||||
|
||||
/**
|
||||
* Hook to detect if the drawer is open in a parent navigator.
|
||||
*/
|
||||
export default function useIsDrawerOpen() {
|
||||
const navigation = useNavigation();
|
||||
|
||||
let drawer = navigation as DrawerNavigationProp<ParamListBase>;
|
||||
|
||||
// The screen might be inside another navigator such as stack nested in drawer
|
||||
// We need to find the closest drawer navigator and add the listener there
|
||||
while (drawer && drawer.dangerouslyGetState().type !== 'drawer') {
|
||||
drawer = drawer.dangerouslyGetParent();
|
||||
}
|
||||
|
||||
const [isDrawerOpen, setIsDrawerOpen] = React.useState(() =>
|
||||
drawer
|
||||
? Boolean(
|
||||
drawer.dangerouslyGetState().history.find(it => it.type === 'drawer')
|
||||
)
|
||||
: false
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const unsubscribe = drawer.addListener('state', e => {
|
||||
setIsDrawerOpen(
|
||||
Boolean(e.data.state.history.find(it => it.type === 'drawer'))
|
||||
);
|
||||
});
|
||||
|
||||
return unsubscribe;
|
||||
}, [drawer, isDrawerOpen]);
|
||||
|
||||
return isDrawerOpen;
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
State,
|
||||
} from 'react-native-gesture-handler';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import Overlay from './Overlay';
|
||||
|
||||
const {
|
||||
Clock,
|
||||
@@ -25,7 +26,6 @@ const {
|
||||
clockRunning,
|
||||
startClock,
|
||||
stopClock,
|
||||
interpolate,
|
||||
spring,
|
||||
abs,
|
||||
add,
|
||||
@@ -52,8 +52,6 @@ const FALSE = 0;
|
||||
const NOOP = 0;
|
||||
const UNSET = -1;
|
||||
|
||||
const PROGRESS_EPSILON = 0.05;
|
||||
|
||||
const DIRECTION_LEFT = 1;
|
||||
const DIRECTION_RIGHT = -1;
|
||||
|
||||
@@ -96,11 +94,30 @@ type Props = {
|
||||
gestureHandlerProps?: React.ComponentProps<typeof PanGestureHandler>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Disables the pan gesture by default on Apple devices in the browser.
|
||||
* https://stackoverflow.com/a/9039885
|
||||
*/
|
||||
function shouldEnableGesture(): boolean {
|
||||
if (
|
||||
Platform.OS === 'web' &&
|
||||
typeof navigator !== 'undefined' &&
|
||||
typeof window !== 'undefined'
|
||||
) {
|
||||
const isWebAppleDevice =
|
||||
/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
||||
|
||||
return !isWebAppleDevice;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export default class DrawerView extends React.PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
drawerPostion: I18nManager.isRTL ? 'left' : 'right',
|
||||
drawerType: 'front',
|
||||
gestureEnabled: true,
|
||||
gestureEnabled: shouldEnableGesture(),
|
||||
swipeEdgeWidth: 32,
|
||||
swipeVelocityThreshold: 500,
|
||||
keyboardDismissMode: 'on-drag',
|
||||
@@ -525,21 +542,29 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
gestureHandlerProps,
|
||||
} = this.props;
|
||||
|
||||
const right = drawerPosition === 'right';
|
||||
const isRight = drawerPosition === 'right';
|
||||
|
||||
const contentTranslateX = drawerType === 'front' ? 0 : this.translateX;
|
||||
const drawerTranslateX =
|
||||
drawerType === 'back'
|
||||
? I18nManager.isRTL
|
||||
? multiply(this.drawerWidth, DIRECTION_RIGHT)
|
||||
: this.drawerWidth
|
||||
? multiply(
|
||||
sub(this.containerWidth, this.drawerWidth),
|
||||
isRight ? 1 : -1
|
||||
)
|
||||
: 0
|
||||
: this.translateX;
|
||||
|
||||
const offset = I18nManager.isRTL ? '100%' : multiply(this.drawerWidth, -1);
|
||||
const offset =
|
||||
drawerType === 'back'
|
||||
? 0
|
||||
: I18nManager.isRTL
|
||||
? '100%'
|
||||
: multiply(this.drawerWidth, -1);
|
||||
|
||||
// FIXME: Currently hitSlop is broken when on Android when drawer is on right
|
||||
// https://github.com/kmagiera/react-native-gesture-handler/issues/569
|
||||
const hitSlop = right
|
||||
const hitSlop = isRight
|
||||
? // Extend hitSlop to the side of the screen when drawer is closed
|
||||
// This lets the user drag the drawer from the side of the screen
|
||||
{ right: 0, width: open ? undefined : swipeEdgeWidth }
|
||||
@@ -577,26 +602,7 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
{renderSceneContent({ progress: this.progress })}
|
||||
</View>
|
||||
<TapGestureHandler onHandlerStateChange={this.handleTapStateChange}>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.overlay,
|
||||
{
|
||||
opacity: interpolate(this.progress, {
|
||||
inputRange: [PROGRESS_EPSILON, 1],
|
||||
outputRange: [0, 1],
|
||||
}),
|
||||
// We don't want the user to be able to press through the overlay when drawer is open
|
||||
// One approach is to adjust the pointerEvents based on the progress
|
||||
// But we can also send the overlay behind the screen, which works, and is much less code
|
||||
zIndex: cond(
|
||||
greaterThan(this.progress, PROGRESS_EPSILON),
|
||||
0,
|
||||
-1
|
||||
),
|
||||
},
|
||||
overlayStyle,
|
||||
]}
|
||||
/>
|
||||
<Overlay progress={this.progress} style={overlayStyle} />
|
||||
</TapGestureHandler>
|
||||
</Animated.View>
|
||||
<Animated.Code
|
||||
@@ -615,7 +621,7 @@ export default class DrawerView extends React.PureComponent<Props> {
|
||||
onLayout={this.handleDrawerLayout}
|
||||
style={[
|
||||
styles.container,
|
||||
right ? { right: offset } : { left: offset },
|
||||
isRight ? { right: offset } : { left: offset },
|
||||
{
|
||||
transform: [{ translateX: drawerTranslateX }],
|
||||
opacity: this.drawerOpacity,
|
||||
@@ -641,10 +647,6 @@ const styles = StyleSheet.create({
|
||||
width: '80%',
|
||||
maxWidth: '100%',
|
||||
},
|
||||
overlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
},
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import * as React from 'react';
|
||||
import { ScrollView, StyleSheet, ScrollViewProps } from 'react-native';
|
||||
import { useSafeArea } from 'react-native-safe-area-context';
|
||||
import DrawerPositionContext from '../utils/DrawerPositionContext';
|
||||
|
||||
type Props = ScrollViewProps & {
|
||||
drawerPosition: 'left' | 'right';
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export default function DrawerContentScrollView({
|
||||
contentContainerStyle,
|
||||
style,
|
||||
drawerPosition,
|
||||
children,
|
||||
...rest
|
||||
}: Props) {
|
||||
const drawerPosition = React.useContext(DrawerPositionContext);
|
||||
const insets = useSafeArea();
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
View,
|
||||
Dimensions,
|
||||
StyleSheet,
|
||||
I18nManager,
|
||||
Platform,
|
||||
ScaledSize,
|
||||
BackHandler,
|
||||
NativeEventSubscription,
|
||||
} from 'react-native';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { ScreenContainer } from 'react-native-screens';
|
||||
import { PanGestureHandler } from 'react-native-gesture-handler';
|
||||
import {
|
||||
PanGestureHandler,
|
||||
GestureHandlerRootView,
|
||||
} from 'react-native-gesture-handler';
|
||||
import {
|
||||
DrawerNavigationState,
|
||||
DrawerActions,
|
||||
@@ -26,6 +32,7 @@ import {
|
||||
DrawerNavigationHelpers,
|
||||
DrawerContentComponentProps,
|
||||
} from '../types';
|
||||
import DrawerPositionContext from '../utils/DrawerPositionContext';
|
||||
|
||||
type Props = DrawerNavigationConfig & {
|
||||
state: DrawerNavigationState;
|
||||
@@ -54,6 +61,8 @@ const getDefaultDrawerWidth = ({
|
||||
return Math.min(smallerAxisSize - appBarHeight, maxWidth);
|
||||
};
|
||||
|
||||
const GestureHandlerWrapper = GestureHandlerRootView ?? View;
|
||||
|
||||
/**
|
||||
* Component that renders the drawer.
|
||||
*/
|
||||
@@ -77,7 +86,6 @@ export default function DrawerView({
|
||||
gestureHandlerProps,
|
||||
minSwipeDistance,
|
||||
sceneContainerStyle,
|
||||
unmountInactiveScreens,
|
||||
}: Props) {
|
||||
const [loaded, setLoaded] = React.useState([state.index]);
|
||||
const [drawerWidth, setDrawerWidth] = React.useState(() =>
|
||||
@@ -88,6 +96,47 @@ export default function DrawerView({
|
||||
|
||||
const { colors } = useTheme();
|
||||
|
||||
const isDrawerOpen = Boolean(state.history.find(it => it.type === 'drawer'));
|
||||
|
||||
const handleDrawerOpen = React.useCallback(() => {
|
||||
navigation.dispatch({
|
||||
...DrawerActions.openDrawer(),
|
||||
target: state.key,
|
||||
});
|
||||
}, [navigation, state.key]);
|
||||
|
||||
const handleDrawerClose = React.useCallback(() => {
|
||||
navigation.dispatch({
|
||||
...DrawerActions.closeDrawer(),
|
||||
target: state.key,
|
||||
});
|
||||
}, [navigation, state.key]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isDrawerOpen) {
|
||||
navigation.emit({ type: 'drawerOpen' });
|
||||
} else {
|
||||
navigation.emit({ type: 'drawerClose' });
|
||||
}
|
||||
}, [isDrawerOpen, navigation]);
|
||||
|
||||
React.useEffect(() => {
|
||||
let subscription: NativeEventSubscription | undefined;
|
||||
|
||||
if (isDrawerOpen) {
|
||||
// We only add the subscription when drawer opens
|
||||
// This way we can make sure that the subscription is added as late as possible
|
||||
// This will make sure that our handler will run first when back button is pressed
|
||||
subscription = BackHandler.addEventListener('hardwareBackPress', () => {
|
||||
handleDrawerClose();
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
return () => subscription?.remove();
|
||||
}, [handleDrawerClose, isDrawerOpen, navigation, state.key]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const updateWidth = ({ window }: { window: ScaledSize }) => {
|
||||
setDrawerWidth(getDefaultDrawerWidth(window));
|
||||
@@ -102,51 +151,37 @@ export default function DrawerView({
|
||||
setLoaded([...loaded, state.index]);
|
||||
}
|
||||
|
||||
const handleDrawerOpen = () => {
|
||||
navigation.dispatch({
|
||||
...DrawerActions.openDrawer(),
|
||||
target: state.key,
|
||||
});
|
||||
|
||||
navigation.emit({ type: 'drawerOpen' });
|
||||
};
|
||||
|
||||
const handleDrawerClose = () => {
|
||||
navigation.dispatch({
|
||||
...DrawerActions.closeDrawer(),
|
||||
target: state.key,
|
||||
});
|
||||
|
||||
navigation.emit({ type: 'drawerClose' });
|
||||
};
|
||||
|
||||
const renderNavigationView = ({ progress }: any) => {
|
||||
return drawerContent({
|
||||
...drawerContentOptions,
|
||||
progress: progress,
|
||||
state: state,
|
||||
navigation: navigation,
|
||||
descriptors: descriptors,
|
||||
drawerPosition: drawerPosition,
|
||||
});
|
||||
return (
|
||||
<DrawerPositionContext.Provider value={drawerPosition}>
|
||||
{drawerContent({
|
||||
...drawerContentOptions,
|
||||
progress: progress,
|
||||
state: state,
|
||||
navigation: navigation,
|
||||
descriptors: descriptors,
|
||||
})}
|
||||
</DrawerPositionContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const renderContent = () => {
|
||||
return (
|
||||
<ScreenContainer style={styles.content}>
|
||||
{state.routes.map((route, index) => {
|
||||
if (unmountInactiveScreens && index !== state.index) {
|
||||
const descriptor = descriptors[route.key];
|
||||
const { unmountOnBlur } = descriptor.options;
|
||||
const isFocused = state.index === index;
|
||||
|
||||
if (unmountOnBlur && !isFocused) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (lazy && !loaded.includes(index) && index !== state.index) {
|
||||
if (lazy && !loaded.includes(index) && !isFocused) {
|
||||
// Don't render a screen if we've never navigated to it
|
||||
return null;
|
||||
}
|
||||
|
||||
const isFocused = state.index === index;
|
||||
const descriptor = descriptors[route.key];
|
||||
|
||||
return (
|
||||
<ResourceSavingScene
|
||||
key={route.key}
|
||||
@@ -165,40 +200,42 @@ export default function DrawerView({
|
||||
const { gestureEnabled } = descriptors[activeKey].options;
|
||||
|
||||
return (
|
||||
<SafeAreaProviderCompat>
|
||||
<DrawerGestureContext.Provider value={drawerGestureRef}>
|
||||
<Drawer
|
||||
open={state.isDrawerOpen}
|
||||
gestureEnabled={gestureEnabled !== false}
|
||||
onOpen={handleDrawerOpen}
|
||||
onClose={handleDrawerClose}
|
||||
onGestureRef={ref => {
|
||||
// @ts-ignore
|
||||
drawerGestureRef.current = ref;
|
||||
}}
|
||||
gestureHandlerProps={gestureHandlerProps}
|
||||
drawerType={drawerType}
|
||||
drawerPosition={drawerPosition}
|
||||
sceneContainerStyle={[
|
||||
{ backgroundColor: colors.background },
|
||||
sceneContainerStyle,
|
||||
]}
|
||||
drawerStyle={[
|
||||
{ width: drawerWidth, backgroundColor: colors.card },
|
||||
drawerStyle,
|
||||
]}
|
||||
overlayStyle={{ backgroundColor: overlayColor }}
|
||||
swipeEdgeWidth={edgeWidth}
|
||||
swipeDistanceThreshold={minSwipeDistance}
|
||||
hideStatusBar={hideStatusBar}
|
||||
statusBarAnimation={statusBarAnimation}
|
||||
renderDrawerContent={renderNavigationView}
|
||||
renderSceneContent={renderContent}
|
||||
keyboardDismissMode={keyboardDismissMode}
|
||||
drawerPostion={drawerPosition}
|
||||
/>
|
||||
</DrawerGestureContext.Provider>
|
||||
</SafeAreaProviderCompat>
|
||||
<GestureHandlerWrapper style={styles.content}>
|
||||
<SafeAreaProviderCompat>
|
||||
<DrawerGestureContext.Provider value={drawerGestureRef}>
|
||||
<Drawer
|
||||
open={isDrawerOpen}
|
||||
gestureEnabled={gestureEnabled}
|
||||
onOpen={handleDrawerOpen}
|
||||
onClose={handleDrawerClose}
|
||||
onGestureRef={ref => {
|
||||
// @ts-ignore
|
||||
drawerGestureRef.current = ref;
|
||||
}}
|
||||
gestureHandlerProps={gestureHandlerProps}
|
||||
drawerType={drawerType}
|
||||
drawerPosition={drawerPosition}
|
||||
sceneContainerStyle={[
|
||||
{ backgroundColor: colors.background },
|
||||
sceneContainerStyle,
|
||||
]}
|
||||
drawerStyle={[
|
||||
{ width: drawerWidth, backgroundColor: colors.card },
|
||||
drawerStyle,
|
||||
]}
|
||||
overlayStyle={{ backgroundColor: overlayColor }}
|
||||
swipeEdgeWidth={edgeWidth}
|
||||
swipeDistanceThreshold={minSwipeDistance}
|
||||
hideStatusBar={hideStatusBar}
|
||||
statusBarAnimation={statusBarAnimation}
|
||||
renderDrawerContent={renderNavigationView}
|
||||
renderSceneContent={renderContent}
|
||||
keyboardDismissMode={keyboardDismissMode}
|
||||
drawerPostion={drawerPosition}
|
||||
/>
|
||||
</DrawerGestureContext.Provider>
|
||||
</SafeAreaProviderCompat>
|
||||
</GestureHandlerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
51
packages/drawer/src/views/Overlay.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import * as React from 'react';
|
||||
import { Platform, StyleSheet } from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
|
||||
const { interpolate, cond, greaterThan } = Animated;
|
||||
|
||||
const PROGRESS_EPSILON = 0.05;
|
||||
|
||||
type Props = React.ComponentProps<typeof Animated.View> & {
|
||||
progress: Animated.Node<number>;
|
||||
};
|
||||
|
||||
const Overlay = React.forwardRef(function Overlay(
|
||||
{ progress, style, ...props }: Props,
|
||||
ref: React.Ref<Animated.View>
|
||||
) {
|
||||
const animatedStyle = {
|
||||
opacity: interpolate(progress, {
|
||||
inputRange: [PROGRESS_EPSILON, 1],
|
||||
outputRange: [0, 1],
|
||||
}),
|
||||
// We don't want the user to be able to press through the overlay when drawer is open
|
||||
// One approach is to adjust the pointerEvents based on the progress
|
||||
// But we can also send the overlay behind the screen, which works, and is much less code
|
||||
zIndex: cond(greaterThan(progress, PROGRESS_EPSILON), 0, -1),
|
||||
};
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
{...props}
|
||||
ref={ref}
|
||||
style={[styles.overlay, animatedStyle, style]}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
...Platform.select({
|
||||
web: {
|
||||
// Disable touch highlight on mobile Safari.
|
||||
WebkitTapHighlightColor: 'transparent',
|
||||
},
|
||||
default: {},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
export default Overlay;
|
||||
@@ -3,6 +3,52 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [5.0.0-alpha.42](https://github.com/react-navigation/navigation-ex/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.41...@react-navigation/material-bottom-tabs@5.0.0-alpha.42) (2020-02-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.41](https://github.com/react-navigation/navigation-ex/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.40...@react-navigation/material-bottom-tabs@5.0.0-alpha.41) (2020-02-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.40](https://github.com/react-navigation/navigation-ex/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.39...@react-navigation/material-bottom-tabs@5.0.0-alpha.40) (2020-02-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.39](https://github.com/react-navigation/navigation-ex/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.36...@react-navigation/material-bottom-tabs@5.0.0-alpha.39) (2020-02-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add licenses ([0c159db](https://github.com/react-navigation/navigation-ex/tree/master/packages/material-bottom-tabs/commit/0c159db4c9bc85e83b5cfe6819ab2562669a4d8f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.37](https://github.com/react-navigation/navigation-ex/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.36...@react-navigation/material-bottom-tabs@5.0.0-alpha.37) (2020-02-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add licenses ([0c159db](https://github.com/react-navigation/navigation-ex/tree/master/packages/material-bottom-tabs/commit/0c159db4c9bc85e83b5cfe6819ab2562669a4d8f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.0.0-alpha.36](https://github.com/react-navigation/navigation-ex/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.35...@react-navigation/material-bottom-tabs@5.0.0-alpha.36) (2020-01-24)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
21
packages/material-bottom-tabs/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 React Navigation Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||