mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-14 17:42:29 +08:00
Compare commits
109 Commits
@react-nav
...
@react-nav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ac4c13d44 | ||
|
|
a0b9f94120 | ||
|
|
717dffdb81 | ||
|
|
9016ba00e3 | ||
|
|
9d822b95a6 | ||
|
|
52d5cb4179 | ||
|
|
af1722d1e9 | ||
|
|
0b1a718756 | ||
|
|
9ab29558d0 | ||
|
|
00c23f2c9e | ||
|
|
68e750d5a6 | ||
|
|
ced2a24aa6 | ||
|
|
ebf1345b39 | ||
|
|
df3544d9b4 | ||
|
|
c1e46f8e33 | ||
|
|
021a9111d7 | ||
|
|
d3ace96981 | ||
|
|
edbc6b1e84 | ||
|
|
c52d19bec8 | ||
|
|
6dd45fcff9 | ||
|
|
d62fbfe255 | ||
|
|
b14094619f | ||
|
|
4c4d864af2 | ||
|
|
e1969f4e17 | ||
|
|
175c07a28c | ||
|
|
2980627cbf | ||
|
|
d024ec6d74 | ||
|
|
4d1b85c751 | ||
|
|
4a818fdfb3 | ||
|
|
0194de1061 | ||
|
|
7b25c8eb2e | ||
|
|
9304a8a16c | ||
|
|
51b40879bd | ||
|
|
51f4d11fdf | ||
|
|
c2aa4bb2eb | ||
|
|
199a892a6d | ||
|
|
60cb3c9ba7 | ||
|
|
6ccceaea8b | ||
|
|
d14f38b80a | ||
|
|
c481748f00 | ||
|
|
d45dbe97dc | ||
|
|
7623945f6e | ||
|
|
1dddaff45c | ||
|
|
21b397f0d6 | ||
|
|
2ff0531695 | ||
|
|
0149e85a95 | ||
|
|
3c47716826 | ||
|
|
acc9646426 | ||
|
|
6dce0780ed | ||
|
|
dd7cff2016 | ||
|
|
740c6b6706 | ||
|
|
039017bc2a | ||
|
|
b85a1c3055 | ||
|
|
18f8188dc8 | ||
|
|
47a1229837 | ||
|
|
00b11e303e | ||
|
|
f384706741 | ||
|
|
d1a6f3e30e | ||
|
|
fd6636a8cd | ||
|
|
eb24fea8b9 | ||
|
|
85ae378d8c | ||
|
|
bea14aa26f | ||
|
|
4d1e102f8c | ||
|
|
f07cd13561 | ||
|
|
f6d06768d3 | ||
|
|
3381d680d7 | ||
|
|
fcd1cc64c1 | ||
|
|
3999fc2836 | ||
|
|
9fd2635756 | ||
|
|
6bec620a3f | ||
|
|
c7b8e2e966 | ||
|
|
719e1a7b46 | ||
|
|
10eca8b92e | ||
|
|
b66e3436a7 | ||
|
|
1c075ffb16 | ||
|
|
1ee3038a4d | ||
|
|
961b519fb1 | ||
|
|
0a19e94b23 | ||
|
|
1e73fed6de | ||
|
|
3193a30da6 | ||
|
|
499c50cd43 | ||
|
|
420f6926e1 | ||
|
|
70be3f6d86 | ||
|
|
bd35b4fc20 | ||
|
|
c511bc0b2b | ||
|
|
b4834ce703 | ||
|
|
ae5442ebe8 | ||
|
|
6dd52d35cf | ||
|
|
d6fa279d93 | ||
|
|
c3fa83efe0 | ||
|
|
f2291d110f | ||
|
|
942d2be2c7 | ||
|
|
b747e527a4 | ||
|
|
38020de80b | ||
|
|
67404f4999 | ||
|
|
2792f438fe | ||
|
|
2573b5beaa | ||
|
|
2697355ab2 | ||
|
|
a695cf9c05 | ||
|
|
c9c825bee6 | ||
|
|
b172b51f17 | ||
|
|
9c05af50b4 | ||
|
|
24febf6ea9 | ||
|
|
8cbb201f1a | ||
|
|
2467ce4ff7 | ||
|
|
5683bebfd6 | ||
|
|
78485cea69 | ||
|
|
1613915669 | ||
|
|
335a04edc1 |
@@ -1,58 +1,97 @@
|
|||||||
version: 2
|
version: 2.1
|
||||||
|
|
||||||
defaults: &defaults
|
executors:
|
||||||
docker:
|
default:
|
||||||
- image: circleci/node:10
|
docker:
|
||||||
working_directory: ~/project
|
- image: circleci/node:10
|
||||||
|
working_directory: ~/project
|
||||||
|
environment:
|
||||||
|
YARN_CACHE_FOLDER: "~/.cache/yarn"
|
||||||
|
|
||||||
|
commands:
|
||||||
|
attach_project:
|
||||||
|
steps:
|
||||||
|
- attach_workspace:
|
||||||
|
at: ~/project
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
install-dependencies:
|
install-dependencies:
|
||||||
<<: *defaults
|
executor: default
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- attach_workspace:
|
- attach_project
|
||||||
at: ~/project
|
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- v1-dependencies-{{ checksum "yarn.lock" }}
|
- yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||||
- v1-dependencies-
|
- yarn-packages-v1-{{ .Branch }}-
|
||||||
- run: yarn install --frozen-lockfile
|
- yarn-packages-v1-
|
||||||
|
- run:
|
||||||
|
name: Install project dependencies
|
||||||
|
command: yarn install --frozen-lockfile
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: v1-dependencies-{{ checksum "yarn.lock" }}
|
key: yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||||
paths: node_modules
|
paths: ~/.cache/yarn
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
root: .
|
root: .
|
||||||
paths: .
|
paths: .
|
||||||
|
|
||||||
lint-and-typecheck:
|
lint-and-typecheck:
|
||||||
<<: *defaults
|
executor: default
|
||||||
steps:
|
steps:
|
||||||
- attach_workspace:
|
- attach_project
|
||||||
at: ~/project
|
- run:
|
||||||
- run: |
|
name: Lint files
|
||||||
yarn lint
|
command: yarn lint
|
||||||
yarn typescript
|
- run:
|
||||||
|
name: Typecheck files
|
||||||
|
command: yarn typescript
|
||||||
|
|
||||||
unit-tests:
|
unit-tests:
|
||||||
<<: *defaults
|
executor: default
|
||||||
steps:
|
steps:
|
||||||
- attach_workspace:
|
- attach_project
|
||||||
at: ~/project
|
- run:
|
||||||
- run: |
|
name: Run unit tests
|
||||||
yarn test --coverage
|
command: yarn test --coverage
|
||||||
cat ./coverage/lcov.info | ./node_modules/.bin/codecov
|
- run:
|
||||||
- store_artifacts:
|
name: Upload test coverage
|
||||||
path: coverage
|
command: cat ./coverage/lcov.info | ./node_modules/.bin/codecov
|
||||||
destination: coverage
|
- store_artifacts:
|
||||||
|
path: coverage
|
||||||
|
destination: coverage
|
||||||
|
|
||||||
|
integration-tests:
|
||||||
|
executor: default
|
||||||
|
steps:
|
||||||
|
- attach_project
|
||||||
|
- run:
|
||||||
|
name: Install Headless Chrome dependencies
|
||||||
|
command: |
|
||||||
|
sudo apt-get install -yq \
|
||||||
|
gconf-service libasound2 libatk1.0-0 libatk-bridge2.0-0 libc6 libcairo2 libcups2 libdbus-1-3 \
|
||||||
|
libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 \
|
||||||
|
libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 \
|
||||||
|
libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates \
|
||||||
|
fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
|
||||||
|
- run:
|
||||||
|
name: Build example for web
|
||||||
|
command: yarn example expo build:web --no-pwa
|
||||||
|
- run:
|
||||||
|
name: Run integration tests
|
||||||
|
command: yarn example test --maxWorkers=2
|
||||||
|
|
||||||
build-packages:
|
build-packages:
|
||||||
<<: *defaults
|
executor: default
|
||||||
steps:
|
steps:
|
||||||
- attach_workspace:
|
- attach_project
|
||||||
at: ~/project
|
- run:
|
||||||
- run: |
|
name: Build packages in the monorepo
|
||||||
yarn lerna run prepare
|
command: yarn lerna run prepare
|
||||||
node scripts/check-types-path.js
|
- run:
|
||||||
|
name: Verify paths for types
|
||||||
|
command: node scripts/check-types-path.js
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
|
||||||
build-and-test:
|
build-and-test:
|
||||||
jobs:
|
jobs:
|
||||||
- install-dependencies
|
- install-dependencies
|
||||||
@@ -62,6 +101,9 @@ workflows:
|
|||||||
- unit-tests:
|
- unit-tests:
|
||||||
requires:
|
requires:
|
||||||
- install-dependencies
|
- install-dependencies
|
||||||
|
- integration-tests:
|
||||||
|
requires:
|
||||||
|
- install-dependencies
|
||||||
- build-packages:
|
- build-packages:
|
||||||
requires:
|
requires:
|
||||||
- install-dependencies
|
- install-dependencies
|
||||||
|
|||||||
18
.github/workflows/expo-preview.yml
vendored
18
.github/workflows/expo-preview.yml
vendored
@@ -23,20 +23,18 @@ jobs:
|
|||||||
expo-password: ${{ secrets.EXPO_CLI_PASSWORD }}
|
expo-password: ${{ secrets.EXPO_CLI_PASSWORD }}
|
||||||
expo-cache: true
|
expo-cache: true
|
||||||
|
|
||||||
- name: Get yarn cache
|
- name: Restore yarn cache
|
||||||
id: yarn-cache
|
id: yarn-cache
|
||||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
uses: actions/cache@master
|
||||||
|
|
||||||
- name: Check yarn cache
|
|
||||||
uses: actions/cache@v1
|
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.yarn-cache.outputs.dir }}
|
path: |
|
||||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
node_modules
|
||||||
restore-keys: |
|
*/*/node_modules
|
||||||
${{ runner.os }}-yarn-
|
key: ${{ runner.os }}-yarn-v1-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn
|
if: steps.yarn-cache.outputs.cache-hit != 'true'
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
- name: Publish Expo app
|
- name: Publish Expo app
|
||||||
working-directory: ./example
|
working-directory: ./example
|
||||||
|
|||||||
17
.github/workflows/expo.yml
vendored
17
.github/workflows/expo.yml
vendored
@@ -25,19 +25,18 @@ jobs:
|
|||||||
expo-password: ${{ secrets.EXPO_CLI_PASSWORD }}
|
expo-password: ${{ secrets.EXPO_CLI_PASSWORD }}
|
||||||
expo-cache: true
|
expo-cache: true
|
||||||
|
|
||||||
- name: Get yarn cache
|
- name: Restore yarn cache
|
||||||
id: yarn-cache
|
id: yarn-cache
|
||||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
uses: actions/cache@master
|
||||||
|
|
||||||
- uses: actions/cache@v1
|
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.yarn-cache.outputs.dir }}
|
path: |
|
||||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
node_modules
|
||||||
restore-keys: |
|
*/*/node_modules
|
||||||
${{ runner.os }}-yarn-
|
key: ${{ runner.os }}-yarn-v1-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn
|
if: steps.yarn-cache.outputs.cache-hit != 'true'
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
- name: Publish Expo app
|
- name: Publish Expo app
|
||||||
working-directory: ./example
|
working-directory: ./example
|
||||||
|
|||||||
27
.github/workflows/versions.yml
vendored
Normal file
27
.github/workflows/versions.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
name: Check versions
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-versions:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: react-navigation/check-versions-action@master
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
packages: |
|
||||||
|
@react-navigation/bottom-tabs
|
||||||
|
@react-navigation/compat
|
||||||
|
@react-navigation/core
|
||||||
|
@react-navigation/drawer
|
||||||
|
@react-navigation/material-bottom-tabs
|
||||||
|
@react-navigation/material-top-tabs
|
||||||
|
@react-navigation/native
|
||||||
|
@react-navigation/routers
|
||||||
|
@react-navigation/stack
|
||||||
|
react-navigation-animated-switch
|
||||||
|
react-navigation-drawer
|
||||||
|
react-navigation-material-bottom-tabs
|
||||||
|
react-navigation-stack
|
||||||
|
react-navigation-tabs
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
import 'react-native-gesture-handler';
|
import 'react-native-gesture-handler';
|
||||||
import { registerRootComponent } from 'expo';
|
import { registerRootComponent } from 'expo';
|
||||||
|
import { Asset } from 'expo-asset';
|
||||||
|
import { Assets as StackAssets } from '@react-navigation/stack';
|
||||||
|
|
||||||
import App from './src/index';
|
import App from './src/index';
|
||||||
|
|
||||||
|
Asset.loadAsync(StackAssets);
|
||||||
|
|
||||||
registerRootComponent(App);
|
registerRootComponent(App);
|
||||||
|
|||||||
41
example/CHANGELOG.md
Normal file
41
example/CHANGELOG.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Change Log
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [5.1.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/example@5.0.0-alpha.23...@react-navigation/example@5.1.0) (2020-05-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add config to enable redux devtools integration ([c9c825b](https://github.com/react-navigation/react-navigation/commit/c9c825bee61426635a28ee149eeeff3d628171cd))
|
||||||
|
* clamp interpolated styles ([67798af](https://github.com/react-navigation/react-navigation/commit/67798af869dcbbf323629fc7e7cc9062d1e12c29))
|
||||||
|
* disable screens when mode is modal on older expo versions ([94d7b28](https://github.com/react-navigation/react-navigation/commit/94d7b28c0b2ce0d56c99b224610f305be6451626))
|
||||||
|
* dispatch pop early when screen is closed with gesture ([#336](https://github.com/react-navigation/react-navigation/issues/336)) ([3d937d1](https://github.com/react-navigation/react-navigation/commit/3d937d1e6571cd613e830d64f7b2e7426076d371)), closes [#267](https://github.com/react-navigation/react-navigation/issues/267)
|
||||||
|
* provide initial values for safe area to prevent blank screen ([#238](https://github.com/react-navigation/react-navigation/issues/238)) ([77b7570](https://github.com/react-navigation/react-navigation/commit/77b757091c0451e20bca01138629669c7da544a8))
|
||||||
|
* render fallback only if linking is enabled. closes [#8161](https://github.com/react-navigation/react-navigation/issues/8161) ([1c075ff](https://github.com/react-navigation/react-navigation/commit/1c075ffb169d233ed0515efea264a5a69b4de52e))
|
||||||
|
* return onPress instead of onClick for useLinkProps ([ae5442e](https://github.com/react-navigation/react-navigation/commit/ae5442ebe812b91fa1f12164f27d1aeed918ab0e))
|
||||||
|
* rtl in native app example ([50b366e](https://github.com/react-navigation/react-navigation/commit/50b366e7341f201d29a44f20b7771b3a832b0045))
|
||||||
|
* screens integration on Android ([#294](https://github.com/react-navigation/react-navigation/issues/294)) ([9bfb295](https://github.com/react-navigation/react-navigation/commit/9bfb29562020c61b4d5c9bee278bcb1c7bdb8b67))
|
||||||
|
* spread parent params to children in compat navigator ([24febf6](https://github.com/react-navigation/react-navigation/commit/24febf6ea99be2e5f22005fdd2a82136d647255c)), closes [#6785](https://github.com/react-navigation/react-navigation/issues/6785)
|
||||||
|
* update screens for native stack ([5411816](https://github.com/react-navigation/react-navigation/commit/54118161885738a6d20b062c7e6679f3bace8424))
|
||||||
|
* wrap navigators in gesture handler root ([41a5e1a](https://github.com/react-navigation/react-navigation/commit/41a5e1a385aa5180abc3992a4c67077c37b998b9))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add `animationTypeForReplace` option ([#297](https://github.com/react-navigation/react-navigation/issues/297)) ([6262f72](https://github.com/react-navigation/react-navigation/commit/6262f7298bff843571fb4b1a677d3beabe29833e))
|
||||||
|
* add `screens` prop for nested configs ([#308](https://github.com/react-navigation/react-navigation/issues/308)) ([b931ae6](https://github.com/react-navigation/react-navigation/commit/b931ae62dfb2c5253c94ea5ace73e9070ec17c4a))
|
||||||
|
* add `useLinkBuilder` hook to build links ([2792f43](https://github.com/react-navigation/react-navigation/commit/2792f438fe45428fe193e3708fee7ad61966cbf4))
|
||||||
|
* add a useLinkProps hook ([f2291d1](https://github.com/react-navigation/react-navigation/commit/f2291d110faa2aa8e10c9133c1c0c28d54af7917))
|
||||||
|
* add action prop to Link ([942d2be](https://github.com/react-navigation/react-navigation/commit/942d2be2c72720469475ce12ec8df23825994dbf))
|
||||||
|
* add custom theme support ([#211](https://github.com/react-navigation/react-navigation/issues/211)) ([00fc616](https://github.com/react-navigation/react-navigation/commit/00fc616de0572bade8aa85052cdc8290360b1d7f))
|
||||||
|
* add deeplinking to native example ([#309](https://github.com/react-navigation/react-navigation/issues/309)) ([e55e866](https://github.com/react-navigation/react-navigation/commit/e55e866af2f2163ee89bc527997cda13ffeb2abe))
|
||||||
|
* add headerStatusBarHeight option to stack ([b201fd2](https://github.com/react-navigation/react-navigation/commit/b201fd20716a2f03cb9373c72281f5d396a9356d))
|
||||||
|
* add Link component as useLinkTo hook for navigating to links ([2573b5b](https://github.com/react-navigation/react-navigation/commit/2573b5beaac1240434e52f3f57bb29da2f541c88))
|
||||||
|
* add openByDefault option to drawer ([36689e2](https://github.com/react-navigation/react-navigation/commit/36689e24c21b474692bb7ecd0b901c8afbbe9a20))
|
||||||
|
* add permanent drawer type ([#7818](https://github.com/react-navigation/react-navigation/issues/7818)) ([6a5d0a0](https://github.com/react-navigation/react-navigation/commit/6a5d0a035afae60d91aef78401ec8826295746fe))
|
||||||
|
* add preventDefault functionality in material bottom tabs ([3dede31](https://github.com/react-navigation/react-navigation/commit/3dede316ccab3b2403a475f60ce20b5c4e4cc068))
|
||||||
|
* emit appear and dismiss events for native stack ([f1df4a0](https://github.com/react-navigation/react-navigation/commit/f1df4a080877b3642e748a41a5ffc2da8c449a8c))
|
||||||
|
* initialState should take priority over deep link ([039017b](https://github.com/react-navigation/react-navigation/commit/039017bc2af69120d2d10e8f2c8a62919c37eb65))
|
||||||
|
* integrate with history API on web ([5a3f835](https://github.com/react-navigation/react-navigation/commit/5a3f8356b05bff7ed20893a5db6804612da3e568))
|
||||||
@@ -7,12 +7,6 @@
|
|||||||
"slug": "react-navigation-example",
|
"slug": "react-navigation-example",
|
||||||
"description": "Demo app to showcase various functionality of React Navigation",
|
"description": "Demo app to showcase various functionality of React Navigation",
|
||||||
"privacy": "public",
|
"privacy": "public",
|
||||||
"sdkVersion": "37.0.0",
|
|
||||||
"platforms": [
|
|
||||||
"ios",
|
|
||||||
"android",
|
|
||||||
"web"
|
|
||||||
],
|
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"icon": "./assets/icon.png",
|
"icon": "./assets/icon.png",
|
||||||
"splash": {
|
"splash": {
|
||||||
@@ -20,15 +14,16 @@
|
|||||||
"resizeMode": "contain",
|
"resizeMode": "contain",
|
||||||
"backgroundColor": "#ffffff"
|
"backgroundColor": "#ffffff"
|
||||||
},
|
},
|
||||||
"updates": {
|
"sdkVersion": "37.0.0",
|
||||||
"fallbackToCacheTimeout": 0
|
"platforms": ["ios", "android", "web"],
|
||||||
},
|
|
||||||
"assetBundlePatterns": [
|
|
||||||
"**/*"
|
|
||||||
],
|
|
||||||
"ios": {
|
"ios": {
|
||||||
"supportsTablet": true
|
"supportsTablet": true
|
||||||
},
|
},
|
||||||
|
"updates": {
|
||||||
|
"fallbackToCacheTimeout": 0
|
||||||
|
},
|
||||||
|
"assetBundlePatterns": ["**/*"],
|
||||||
|
"scheme": "rne",
|
||||||
"entryPoint": "App.tsx"
|
"entryPoint": "App.tsx"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"settings": {
|
|
||||||
"import/core-modules": [
|
|
||||||
"detox",
|
|
||||||
"detox/runners/jest/adapter",
|
|
||||||
"detox/runners/jest/specReporter"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"env": { "jest": true, "jasmine": true }
|
|
||||||
}
|
|
||||||
44
example/e2e/__integration_tests__/Link.test.tsx
Normal file
44
example/e2e/__integration_tests__/Link.test.tsx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { page } from '../config/setup-playwright';
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await page.click('[data-testid=LinkComponent]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the article page', async () => {
|
||||||
|
expect(await page.evaluate(() => location.pathname + location.search)).toBe(
|
||||||
|
'/link-component/article/gandalf'
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
((await page.accessibility.snapshot()) as any)?.children?.find(
|
||||||
|
(it: any) => it.role === 'heading'
|
||||||
|
)?.name
|
||||||
|
).toBe('Article by Gandalf');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('goes to the album page and goes back', async () => {
|
||||||
|
await page.click('[href="/link-component/music"]');
|
||||||
|
|
||||||
|
expect(await page.evaluate(() => location.pathname + location.search)).toBe(
|
||||||
|
'/link-component/music'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
((await page.accessibility.snapshot()) as any)?.children?.find(
|
||||||
|
(it: any) => it.role === 'heading'
|
||||||
|
)?.name
|
||||||
|
).toBe('Albums');
|
||||||
|
|
||||||
|
await page.click('[aria-label="Article by Gandalf, back"]');
|
||||||
|
|
||||||
|
await page.waitForNavigation();
|
||||||
|
|
||||||
|
expect(await page.evaluate(() => location.pathname + location.search)).toBe(
|
||||||
|
'/link-component/article/gandalf'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
((await page.accessibility.snapshot()) as any)?.children?.find(
|
||||||
|
(it: any) => it.role === 'heading'
|
||||||
|
)?.name
|
||||||
|
).toBe('Article by Gandalf');
|
||||||
|
});
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { by, element, expect, device } from 'detox';
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await device.reloadReactNative();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has dark theme toggle', async () => {
|
|
||||||
await expect(element(by.text('Dark theme'))).toBeVisible();
|
|
||||||
});
|
|
||||||
13
example/e2e/__integration_tests__/index.test.tsx
Normal file
13
example/e2e/__integration_tests__/index.test.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { page } from '../config/setup-playwright';
|
||||||
|
|
||||||
|
it('loads the example app', async () => {
|
||||||
|
const snapshot = await page.accessibility.snapshot();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
expect(snapshot?.children?.find((it) => it.role === 'heading')?.name).toBe(
|
||||||
|
'Examples'
|
||||||
|
);
|
||||||
|
const title = await page.$eval('[role=heading]', (el) => el.textContent);
|
||||||
|
|
||||||
|
expect(title).toBe('Examples');
|
||||||
|
});
|
||||||
13
example/e2e/__integration_tests__/server.test.tsx
Normal file
13
example/e2e/__integration_tests__/server.test.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import fetch from 'node-fetch';
|
||||||
|
import cheerio from 'cheerio';
|
||||||
|
|
||||||
|
const server = 'http://localhost:3275';
|
||||||
|
|
||||||
|
it('renders the home page', async () => {
|
||||||
|
const res = await fetch(server);
|
||||||
|
const html = await res.text();
|
||||||
|
|
||||||
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
|
expect($('title').text()).toBe('Examples');
|
||||||
|
});
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"setupFilesAfterEnv": ["./init.js"],
|
|
||||||
"testEnvironment": "node",
|
|
||||||
"reporters": ["detox/runners/jest/streamlineReporter"],
|
|
||||||
"verbose": true
|
|
||||||
}
|
|
||||||
24
example/e2e/config/setup-playwright.tsx
Normal file
24
example/e2e/config/setup-playwright.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/* eslint-env jest */
|
||||||
|
|
||||||
|
import { chromium, Browser, BrowserContext, Page } from 'playwright';
|
||||||
|
|
||||||
|
let browser: Browser;
|
||||||
|
let context: BrowserContext;
|
||||||
|
let page: Page;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
browser = await chromium.launch();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await browser.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
context = await browser.newContext();
|
||||||
|
page = await context.newPage();
|
||||||
|
|
||||||
|
await page.goto('http://localhost:3579');
|
||||||
|
});
|
||||||
|
|
||||||
|
export { browser, context, page };
|
||||||
16
example/e2e/config/setup-server.tsx
Normal file
16
example/e2e/config/setup-server.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { setup } from 'jest-dev-server';
|
||||||
|
|
||||||
|
export default async function () {
|
||||||
|
await setup([
|
||||||
|
{
|
||||||
|
command: 'yarn serve -l 3579 web-build',
|
||||||
|
launchTimeout: 50000,
|
||||||
|
port: 3579,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: 'yarn server',
|
||||||
|
launchTimeout: 50000,
|
||||||
|
port: 3275,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
5
example/e2e/config/teardown-server.tsx
Normal file
5
example/e2e/config/teardown-server.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { teardown } from 'jest-dev-server';
|
||||||
|
|
||||||
|
export default async function () {
|
||||||
|
await teardown();
|
||||||
|
}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/* eslint-disable import/no-commonjs */
|
|
||||||
|
|
||||||
const detox = require('detox');
|
|
||||||
const config = require('../../package.json').detox;
|
|
||||||
const adapter = require('detox/runners/jest/adapter');
|
|
||||||
const specReporter = require('detox/runners/jest/specReporter');
|
|
||||||
|
|
||||||
// Set the default timeout
|
|
||||||
jest.setTimeout(120000);
|
|
||||||
|
|
||||||
jasmine.getEnv().addReporter(adapter);
|
|
||||||
|
|
||||||
// This takes care of generating status logs on a per-spec basis. By default, jest only reports at file-level.
|
|
||||||
// This is strictly optional.
|
|
||||||
jasmine.getEnv().addReporter(specReporter);
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
await detox.init(config);
|
|
||||||
}, 300000);
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await adapter.beforeEach();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
await adapter.afterAll();
|
|
||||||
await detox.cleanup();
|
|
||||||
});
|
|
||||||
@@ -1,35 +1,38 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- boost-for-react-native (1.63.0)
|
- boost-for-react-native (1.63.0)
|
||||||
- DoubleConversion (1.1.6)
|
- DoubleConversion (1.1.6)
|
||||||
- EXAppLoaderProvider (8.0.0)
|
- EXBlur (8.1.0):
|
||||||
- EXBlur (8.0.0):
|
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXConstants (8.0.0):
|
- EXConstants (9.0.0):
|
||||||
- UMConstantsInterface
|
- UMConstantsInterface
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXErrorRecovery (1.0.0):
|
- EXErrorRecovery (1.1.0):
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXFileSystem (8.0.0):
|
- EXFileSystem (8.1.0):
|
||||||
- UMCore
|
- UMCore
|
||||||
- UMFileSystemInterface
|
- UMFileSystemInterface
|
||||||
- EXFont (8.0.0):
|
- EXFont (8.1.0):
|
||||||
- UMCore
|
- UMCore
|
||||||
- UMFontInterface
|
- UMFontInterface
|
||||||
- EXKeepAwake (8.0.0):
|
- EXImageLoader (1.0.1):
|
||||||
|
- React-Core
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXLinearGradient (8.0.0):
|
- UMImageLoaderInterface
|
||||||
|
- EXKeepAwake (8.1.0):
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXLocation (8.0.0):
|
- EXLinearGradient (8.1.0):
|
||||||
|
- UMCore
|
||||||
|
- EXLocation (8.1.0):
|
||||||
- UMCore
|
- UMCore
|
||||||
- UMPermissionsInterface
|
- UMPermissionsInterface
|
||||||
- UMTaskManagerInterface
|
- UMTaskManagerInterface
|
||||||
- EXPermissions (8.0.0):
|
- EXPermissions (8.1.0):
|
||||||
- UMCore
|
- UMCore
|
||||||
- UMPermissionsInterface
|
- UMPermissionsInterface
|
||||||
- EXSQLite (8.0.0):
|
- EXSQLite (8.1.0):
|
||||||
- UMCore
|
- UMCore
|
||||||
- UMFileSystemInterface
|
- UMFileSystemInterface
|
||||||
- EXWebBrowser (8.0.0):
|
- EXWebBrowser (8.2.1):
|
||||||
- UMCore
|
- UMCore
|
||||||
- FBLazyVector (0.61.5)
|
- FBLazyVector (0.61.5)
|
||||||
- FBReactNativeSpec (0.61.5):
|
- FBReactNativeSpec (0.61.5):
|
||||||
@@ -50,8 +53,6 @@ PODS:
|
|||||||
- glog
|
- glog
|
||||||
- glog (0.3.5)
|
- glog (0.3.5)
|
||||||
- RCTRequired (0.61.5)
|
- RCTRequired (0.61.5)
|
||||||
- RCTRestart (0.0.13):
|
|
||||||
- React
|
|
||||||
- RCTTypeSafety (0.61.5):
|
- RCTTypeSafety (0.61.5):
|
||||||
- FBLazyVector (= 0.61.5)
|
- FBLazyVector (= 0.61.5)
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2018.10.22.00)
|
||||||
@@ -214,7 +215,9 @@ PODS:
|
|||||||
- React-cxxreact (= 0.61.5)
|
- React-cxxreact (= 0.61.5)
|
||||||
- React-jsi (= 0.61.5)
|
- React-jsi (= 0.61.5)
|
||||||
- React-jsinspector (0.61.5)
|
- React-jsinspector (0.61.5)
|
||||||
- react-native-safe-area-context (0.6.2):
|
- react-native-restart (0.0.15):
|
||||||
|
- React
|
||||||
|
- react-native-safe-area-context (1.0.0):
|
||||||
- React
|
- React
|
||||||
- React-RCTActionSheet (0.61.5):
|
- React-RCTActionSheet (0.61.5):
|
||||||
- React-Core/RCTActionSheetHeaders (= 0.61.5)
|
- React-Core/RCTActionSheetHeaders (= 0.61.5)
|
||||||
@@ -251,39 +254,41 @@ PODS:
|
|||||||
- React-cxxreact (= 0.61.5)
|
- React-cxxreact (= 0.61.5)
|
||||||
- React-jsi (= 0.61.5)
|
- React-jsi (= 0.61.5)
|
||||||
- ReactCommon/jscallinvoker (= 0.61.5)
|
- ReactCommon/jscallinvoker (= 0.61.5)
|
||||||
- RNCMaskedView (0.1.5):
|
- RNCMaskedView (0.1.10):
|
||||||
- React
|
- React
|
||||||
- RNGestureHandler (1.5.5):
|
- RNGestureHandler (1.6.1):
|
||||||
- React
|
- React
|
||||||
- RNReanimated (1.4.0):
|
- RNReanimated (1.8.0):
|
||||||
- React
|
- React
|
||||||
- RNScreens (2.0.0-alpha.33):
|
- RNScreens (2.7.0):
|
||||||
- React
|
- React
|
||||||
- UMBarCodeScannerInterface (5.0.0)
|
- UMAppLoader (1.0.2)
|
||||||
- UMCameraInterface (5.0.0)
|
- UMBarCodeScannerInterface (5.1.0)
|
||||||
- UMConstantsInterface (5.0.0)
|
- UMCameraInterface (5.1.0)
|
||||||
- UMCore (5.0.0)
|
- UMConstantsInterface (5.1.0)
|
||||||
- UMFaceDetectorInterface (5.0.0)
|
- UMCore (5.1.2)
|
||||||
- UMFileSystemInterface (5.0.0)
|
- UMFaceDetectorInterface (5.1.0)
|
||||||
- UMFontInterface (5.0.0)
|
- UMFileSystemInterface (5.1.0)
|
||||||
- UMImageLoaderInterface (5.0.0)
|
- UMFontInterface (5.1.0)
|
||||||
- UMPermissionsInterface (5.0.0)
|
- UMImageLoaderInterface (5.1.0)
|
||||||
- UMReactNativeAdapter (5.0.0):
|
- UMPermissionsInterface (5.1.0):
|
||||||
|
- UMCore
|
||||||
|
- UMReactNativeAdapter (5.2.0):
|
||||||
- React-Core
|
- React-Core
|
||||||
- UMCore
|
- UMCore
|
||||||
- UMFontInterface
|
- UMFontInterface
|
||||||
- UMSensorsInterface (5.0.0)
|
- UMSensorsInterface (5.1.0)
|
||||||
- UMTaskManagerInterface (5.0.0)
|
- UMTaskManagerInterface (5.1.0)
|
||||||
- Yoga (1.14.0)
|
- Yoga (1.14.0)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- DoubleConversion (from `../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
- 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`)
|
- EXBlur (from `../../node_modules/expo-blur/ios`)
|
||||||
- EXConstants (from `../../node_modules/expo-constants/ios`)
|
- EXConstants (from `../../node_modules/expo-constants/ios`)
|
||||||
- EXErrorRecovery (from `../../node_modules/expo-error-recovery/ios`)
|
- EXErrorRecovery (from `../../node_modules/expo-error-recovery/ios`)
|
||||||
- EXFileSystem (from `../../node_modules/expo-file-system/ios`)
|
- EXFileSystem (from `../../node_modules/expo-file-system/ios`)
|
||||||
- EXFont (from `../../node_modules/expo-font/ios`)
|
- EXFont (from `../../node_modules/expo-font/ios`)
|
||||||
|
- EXImageLoader (from `../../node_modules/expo-image-loader/ios`)
|
||||||
- EXKeepAwake (from `../../node_modules/expo-keep-awake/ios`)
|
- EXKeepAwake (from `../../node_modules/expo-keep-awake/ios`)
|
||||||
- EXLinearGradient (from `../../node_modules/expo-linear-gradient/ios`)
|
- EXLinearGradient (from `../../node_modules/expo-linear-gradient/ios`)
|
||||||
- EXLocation (from `../../node_modules/expo-location/ios`)
|
- EXLocation (from `../../node_modules/expo-location/ios`)
|
||||||
@@ -295,7 +300,6 @@ DEPENDENCIES:
|
|||||||
- Folly (from `../../node_modules/react-native/third-party-podspecs/Folly.podspec`)
|
- Folly (from `../../node_modules/react-native/third-party-podspecs/Folly.podspec`)
|
||||||
- glog (from `../../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
- glog (from `../../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||||
- RCTRequired (from `../../node_modules/react-native/Libraries/RCTRequired`)
|
- RCTRequired (from `../../node_modules/react-native/Libraries/RCTRequired`)
|
||||||
- RCTRestart (from `../../node_modules/react-native-restart/ios`)
|
|
||||||
- RCTTypeSafety (from `../../node_modules/react-native/Libraries/TypeSafety`)
|
- RCTTypeSafety (from `../../node_modules/react-native/Libraries/TypeSafety`)
|
||||||
- React (from `../../node_modules/react-native/`)
|
- React (from `../../node_modules/react-native/`)
|
||||||
- React-Core (from `../../node_modules/react-native/`)
|
- React-Core (from `../../node_modules/react-native/`)
|
||||||
@@ -306,6 +310,7 @@ DEPENDENCIES:
|
|||||||
- React-jsi (from `../../node_modules/react-native/ReactCommon/jsi`)
|
- React-jsi (from `../../node_modules/react-native/ReactCommon/jsi`)
|
||||||
- React-jsiexecutor (from `../../node_modules/react-native/ReactCommon/jsiexecutor`)
|
- React-jsiexecutor (from `../../node_modules/react-native/ReactCommon/jsiexecutor`)
|
||||||
- React-jsinspector (from `../../node_modules/react-native/ReactCommon/jsinspector`)
|
- React-jsinspector (from `../../node_modules/react-native/ReactCommon/jsinspector`)
|
||||||
|
- react-native-restart (from `../../node_modules/react-native-restart`)
|
||||||
- react-native-safe-area-context (from `../../node_modules/react-native-safe-area-context`)
|
- react-native-safe-area-context (from `../../node_modules/react-native-safe-area-context`)
|
||||||
- React-RCTActionSheet (from `../../node_modules/react-native/Libraries/ActionSheetIOS`)
|
- React-RCTActionSheet (from `../../node_modules/react-native/Libraries/ActionSheetIOS`)
|
||||||
- React-RCTAnimation (from `../../node_modules/react-native/Libraries/NativeAnimation`)
|
- React-RCTAnimation (from `../../node_modules/react-native/Libraries/NativeAnimation`)
|
||||||
@@ -322,10 +327,11 @@ DEPENDENCIES:
|
|||||||
- RNGestureHandler (from `../../node_modules/react-native-gesture-handler`)
|
- RNGestureHandler (from `../../node_modules/react-native-gesture-handler`)
|
||||||
- RNReanimated (from `../../node_modules/react-native-reanimated`)
|
- RNReanimated (from `../../node_modules/react-native-reanimated`)
|
||||||
- RNScreens (from `../../node_modules/react-native-screens`)
|
- RNScreens (from `../../node_modules/react-native-screens`)
|
||||||
|
- UMAppLoader (from `../../node_modules/unimodules-app-loader/ios`)
|
||||||
- UMBarCodeScannerInterface (from `../../node_modules/unimodules-barcode-scanner-interface/ios`)
|
- UMBarCodeScannerInterface (from `../../node_modules/unimodules-barcode-scanner-interface/ios`)
|
||||||
- UMCameraInterface (from `../../node_modules/unimodules-camera-interface/ios`)
|
- UMCameraInterface (from `../../node_modules/unimodules-camera-interface/ios`)
|
||||||
- UMConstantsInterface (from `../../node_modules/unimodules-constants-interface/ios`)
|
- UMConstantsInterface (from `../../node_modules/unimodules-constants-interface/ios`)
|
||||||
- "UMCore (from `../../node_modules/@unimodules/core/ios`)"
|
- "UMCore (from `../../node_modules/react-native-unimodules/node_modules/@unimodules/core/ios`)"
|
||||||
- UMFaceDetectorInterface (from `../../node_modules/unimodules-face-detector-interface/ios`)
|
- UMFaceDetectorInterface (from `../../node_modules/unimodules-face-detector-interface/ios`)
|
||||||
- UMFileSystemInterface (from `../../node_modules/unimodules-file-system-interface/ios`)
|
- UMFileSystemInterface (from `../../node_modules/unimodules-file-system-interface/ios`)
|
||||||
- UMFontInterface (from `../../node_modules/unimodules-font-interface/ios`)
|
- UMFontInterface (from `../../node_modules/unimodules-font-interface/ios`)
|
||||||
@@ -343,42 +349,30 @@ SPEC REPOS:
|
|||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
DoubleConversion:
|
DoubleConversion:
|
||||||
:podspec: "../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
|
:podspec: "../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
|
||||||
EXAppLoaderProvider:
|
|
||||||
:path: !ruby/object:Pathname
|
|
||||||
path: "../../node_modules/expo-app-loader-provider/ios"
|
|
||||||
EXBlur:
|
EXBlur:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/expo-blur/ios"
|
||||||
path: "../../node_modules/expo-blur/ios"
|
|
||||||
EXConstants:
|
EXConstants:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/expo-constants/ios"
|
||||||
path: "../../node_modules/expo-constants/ios"
|
|
||||||
EXErrorRecovery:
|
EXErrorRecovery:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/expo-error-recovery/ios"
|
||||||
path: "../../node_modules/expo-error-recovery/ios"
|
|
||||||
EXFileSystem:
|
EXFileSystem:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/expo-file-system/ios"
|
||||||
path: "../../node_modules/expo-file-system/ios"
|
|
||||||
EXFont:
|
EXFont:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/expo-font/ios"
|
||||||
path: "../../node_modules/expo-font/ios"
|
EXImageLoader:
|
||||||
|
:path: "../../node_modules/expo-image-loader/ios"
|
||||||
EXKeepAwake:
|
EXKeepAwake:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/expo-keep-awake/ios"
|
||||||
path: "../../node_modules/expo-keep-awake/ios"
|
|
||||||
EXLinearGradient:
|
EXLinearGradient:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/expo-linear-gradient/ios"
|
||||||
path: "../../node_modules/expo-linear-gradient/ios"
|
|
||||||
EXLocation:
|
EXLocation:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/expo-location/ios"
|
||||||
path: "../../node_modules/expo-location/ios"
|
|
||||||
EXPermissions:
|
EXPermissions:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/expo-permissions/ios"
|
||||||
path: "../../node_modules/expo-permissions/ios"
|
|
||||||
EXSQLite:
|
EXSQLite:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/expo-sqlite/ios"
|
||||||
path: "../../node_modules/expo-sqlite/ios"
|
|
||||||
EXWebBrowser:
|
EXWebBrowser:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/expo-web-browser/ios"
|
||||||
path: "../../node_modules/expo-web-browser/ios"
|
|
||||||
FBLazyVector:
|
FBLazyVector:
|
||||||
:path: "../../node_modules/react-native/Libraries/FBLazyVector"
|
:path: "../../node_modules/react-native/Libraries/FBLazyVector"
|
||||||
FBReactNativeSpec:
|
FBReactNativeSpec:
|
||||||
@@ -389,8 +383,6 @@ EXTERNAL SOURCES:
|
|||||||
:podspec: "../../node_modules/react-native/third-party-podspecs/glog.podspec"
|
:podspec: "../../node_modules/react-native/third-party-podspecs/glog.podspec"
|
||||||
RCTRequired:
|
RCTRequired:
|
||||||
:path: "../../node_modules/react-native/Libraries/RCTRequired"
|
:path: "../../node_modules/react-native/Libraries/RCTRequired"
|
||||||
RCTRestart:
|
|
||||||
:path: "../../node_modules/react-native-restart/ios"
|
|
||||||
RCTTypeSafety:
|
RCTTypeSafety:
|
||||||
:path: "../../node_modules/react-native/Libraries/TypeSafety"
|
:path: "../../node_modules/react-native/Libraries/TypeSafety"
|
||||||
React:
|
React:
|
||||||
@@ -407,6 +399,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../../node_modules/react-native/ReactCommon/jsiexecutor"
|
:path: "../../node_modules/react-native/ReactCommon/jsiexecutor"
|
||||||
React-jsinspector:
|
React-jsinspector:
|
||||||
:path: "../../node_modules/react-native/ReactCommon/jsinspector"
|
:path: "../../node_modules/react-native/ReactCommon/jsinspector"
|
||||||
|
react-native-restart:
|
||||||
|
:path: "../../node_modules/react-native-restart"
|
||||||
react-native-safe-area-context:
|
react-native-safe-area-context:
|
||||||
:path: "../../node_modules/react-native-safe-area-context"
|
:path: "../../node_modules/react-native-safe-area-context"
|
||||||
React-RCTActionSheet:
|
React-RCTActionSheet:
|
||||||
@@ -437,66 +431,55 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../../node_modules/react-native-reanimated"
|
:path: "../../node_modules/react-native-reanimated"
|
||||||
RNScreens:
|
RNScreens:
|
||||||
:path: "../../node_modules/react-native-screens"
|
:path: "../../node_modules/react-native-screens"
|
||||||
|
UMAppLoader:
|
||||||
|
:path: "../../node_modules/unimodules-app-loader/ios"
|
||||||
UMBarCodeScannerInterface:
|
UMBarCodeScannerInterface:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/unimodules-barcode-scanner-interface/ios"
|
||||||
path: "../../node_modules/unimodules-barcode-scanner-interface/ios"
|
|
||||||
UMCameraInterface:
|
UMCameraInterface:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/unimodules-camera-interface/ios"
|
||||||
path: "../../node_modules/unimodules-camera-interface/ios"
|
|
||||||
UMConstantsInterface:
|
UMConstantsInterface:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/unimodules-constants-interface/ios"
|
||||||
path: "../../node_modules/unimodules-constants-interface/ios"
|
|
||||||
UMCore:
|
UMCore:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/react-native-unimodules/node_modules/@unimodules/core/ios"
|
||||||
path: "../../node_modules/@unimodules/core/ios"
|
|
||||||
UMFaceDetectorInterface:
|
UMFaceDetectorInterface:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/unimodules-face-detector-interface/ios"
|
||||||
path: "../../node_modules/unimodules-face-detector-interface/ios"
|
|
||||||
UMFileSystemInterface:
|
UMFileSystemInterface:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/unimodules-file-system-interface/ios"
|
||||||
path: "../../node_modules/unimodules-file-system-interface/ios"
|
|
||||||
UMFontInterface:
|
UMFontInterface:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/unimodules-font-interface/ios"
|
||||||
path: "../../node_modules/unimodules-font-interface/ios"
|
|
||||||
UMImageLoaderInterface:
|
UMImageLoaderInterface:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/unimodules-image-loader-interface/ios"
|
||||||
path: "../../node_modules/unimodules-image-loader-interface/ios"
|
|
||||||
UMPermissionsInterface:
|
UMPermissionsInterface:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/unimodules-permissions-interface/ios"
|
||||||
path: "../../node_modules/unimodules-permissions-interface/ios"
|
|
||||||
UMReactNativeAdapter:
|
UMReactNativeAdapter:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/@unimodules/react-native-adapter/ios"
|
||||||
path: "../../node_modules/@unimodules/react-native-adapter/ios"
|
|
||||||
UMSensorsInterface:
|
UMSensorsInterface:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/unimodules-sensors-interface/ios"
|
||||||
path: "../../node_modules/unimodules-sensors-interface/ios"
|
|
||||||
UMTaskManagerInterface:
|
UMTaskManagerInterface:
|
||||||
:path: !ruby/object:Pathname
|
:path: "../../node_modules/unimodules-task-manager-interface/ios"
|
||||||
path: "../../node_modules/unimodules-task-manager-interface/ios"
|
|
||||||
Yoga:
|
Yoga:
|
||||||
:path: "../../node_modules/react-native/ReactCommon/yoga"
|
:path: "../../node_modules/react-native/ReactCommon/yoga"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
||||||
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
|
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
|
||||||
EXAppLoaderProvider: ebdb6bc2632c1ccadbe49f5e4104d8d690969c49
|
EXBlur: aa14d84bff6e9c2232fbcaf54ad809eee1cc41dc
|
||||||
EXBlur: d1604f66f89a9414f5ee65dfb23874437c1bb147
|
EXConstants: 5304709b1bea70a4828f48ba4c7fc3ec3b2d9b17
|
||||||
EXConstants: 4051b16c17ef3defa03c541d42811dc92b249146
|
EXErrorRecovery: 8f4c21ab2f51bf75defe4536f841a37de59b0661
|
||||||
EXErrorRecovery: d36db99ec6a3808f313f01b0890eb443796dd1c2
|
EXFileSystem: cf4232ba7c62dc49b78c2d36005f97b6fddf0b01
|
||||||
EXFileSystem: 6e0d9bb6cc4ea404dbb8f583c1a8a2dcdf4b83b6
|
EXFont: 8326ecf966be559f7ced7c8e221a32fc4d9ed8b0
|
||||||
EXFont: 6187b5ab46ee578d5f8e7f2ea092752e78772235
|
EXImageLoader: 5ad6896fa1ef2ee814b551873cbf7a7baccc694a
|
||||||
EXKeepAwake: 66e9f80b6d129633725a0e42f8d285c229876811
|
EXKeepAwake: d045bc2cf1ad5a04f0323cc7c894b95b414042e0
|
||||||
EXLinearGradient: 75f302f9d6484267a3f6d3252df2e7a5f00e716a
|
EXLinearGradient: 97d8095d1e4ad96f7893e010e564796ed8aeea42
|
||||||
EXLocation: 3c75d012ca92eed94d4338778d79c49d1252393a
|
EXLocation: bbd487fd96a18a3ad9725389bbb94c4a5f78edf3
|
||||||
EXPermissions: 9bc08859a675d291e89be9a0870155c27c16ac35
|
EXPermissions: 24b97f734ce9172d245a5be38ad9ccfcb6135964
|
||||||
EXSQLite: 220226a354912b100dfe913f5fe6f31762c8927e
|
EXSQLite: 877ad6c8eb169353a2f94d5ad26510ffadd46a1f
|
||||||
EXWebBrowser: db32607359fb7b55b7b7b91df32dd3d8355bb3b7
|
EXWebBrowser: 5902f99ac5ac551e5c82ff46f13a337b323aa9ea
|
||||||
FBLazyVector: aaeaf388755e4f29cd74acbc9e3b8da6d807c37f
|
FBLazyVector: aaeaf388755e4f29cd74acbc9e3b8da6d807c37f
|
||||||
FBReactNativeSpec: 118d0d177724c2d67f08a59136eb29ef5943ec75
|
FBReactNativeSpec: 118d0d177724c2d67f08a59136eb29ef5943ec75
|
||||||
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
|
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
|
||||||
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
|
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
|
||||||
RCTRequired: b153add4da6e7dbc44aebf93f3cf4fcae392ddf1
|
RCTRequired: b153add4da6e7dbc44aebf93f3cf4fcae392ddf1
|
||||||
RCTRestart: dd19aab87fc1118e05b6b5b91b959105647f56b4
|
|
||||||
RCTTypeSafety: 9aa1b91d7f9310fc6eadc3cf95126ffe818af320
|
RCTTypeSafety: 9aa1b91d7f9310fc6eadc3cf95126ffe818af320
|
||||||
React: b6a59ef847b2b40bb6e0180a97d0ca716969ac78
|
React: b6a59ef847b2b40bb6e0180a97d0ca716969ac78
|
||||||
React-Core: 688b451f7d616cc1134ac95295b593d1b5158a04
|
React-Core: 688b451f7d616cc1134ac95295b593d1b5158a04
|
||||||
@@ -505,7 +488,8 @@ SPEC CHECKSUMS:
|
|||||||
React-jsi: cb2cd74d7ccf4cffb071a46833613edc79cdf8f7
|
React-jsi: cb2cd74d7ccf4cffb071a46833613edc79cdf8f7
|
||||||
React-jsiexecutor: d5525f9ed5f782fdbacb64b9b01a43a9323d2386
|
React-jsiexecutor: d5525f9ed5f782fdbacb64b9b01a43a9323d2386
|
||||||
React-jsinspector: fa0ecc501688c3c4c34f28834a76302233e29dc0
|
React-jsinspector: fa0ecc501688c3c4c34f28834a76302233e29dc0
|
||||||
react-native-safe-area-context: 25260c5d0b9c53fd7aa88e569e2edae72af1f6a3
|
react-native-restart: fff228304625f55de2ebd4de43938110f4c888ed
|
||||||
|
react-native-safe-area-context: a346c75f2288147527365ce27b59ca6d38c27805
|
||||||
React-RCTActionSheet: 600b4d10e3aea0913b5a92256d2719c0cdd26d76
|
React-RCTActionSheet: 600b4d10e3aea0913b5a92256d2719c0cdd26d76
|
||||||
React-RCTAnimation: 791a87558389c80908ed06cc5dfc5e7920dfa360
|
React-RCTAnimation: 791a87558389c80908ed06cc5dfc5e7920dfa360
|
||||||
React-RCTBlob: d89293cc0236d9cb0933d85e430b0bbe81ad1d72
|
React-RCTBlob: d89293cc0236d9cb0933d85e430b0bbe81ad1d72
|
||||||
@@ -516,24 +500,25 @@ SPEC CHECKSUMS:
|
|||||||
React-RCTText: 9ccc88273e9a3aacff5094d2175a605efa854dbe
|
React-RCTText: 9ccc88273e9a3aacff5094d2175a605efa854dbe
|
||||||
React-RCTVibration: a49a1f42bf8f5acf1c3e297097517c6b3af377ad
|
React-RCTVibration: a49a1f42bf8f5acf1c3e297097517c6b3af377ad
|
||||||
ReactCommon: 198c7c8d3591f975e5431bec1b0b3b581aa1c5dd
|
ReactCommon: 198c7c8d3591f975e5431bec1b0b3b581aa1c5dd
|
||||||
RNCMaskedView: dd13f9f7b146a9ad82f9b7eb6c9b5548fcf6e990
|
RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f
|
||||||
RNGestureHandler: d2270608171c868581b840cfc692f2962c05cd17
|
RNGestureHandler: 8f09cd560f8d533eb36da5a6c5a843af9f056b38
|
||||||
RNReanimated: b2ab0b693dddd2339bd2f300e770f6302d2e960c
|
RNReanimated: 955cf4068714003d2f1a6e2bae3fb1118f359aff
|
||||||
RNScreens: 1c7fd499b915c77c21e8e6c327890c5af9b4cf7e
|
RNScreens: cf198f915f8a2bf163de94ca9f5bfc8d326c3706
|
||||||
UMBarCodeScannerInterface: 3802c8574ef119c150701d679ab386e2266d6a54
|
UMAppLoader: ee77a072f9e15128f777ccd6d2d00f52ab4387e6
|
||||||
UMCameraInterface: 985d301f688ed392f815728f0dd906ca34b7ccb1
|
UMBarCodeScannerInterface: 9dc692b87e5f20fe277fa57aa47f45d418c3cc6c
|
||||||
UMConstantsInterface: bda5f8bd3403ad99e663eb3c4da685d063c5653c
|
UMCameraInterface: 625878bbf2ba188a8548675e1d1d2e438a653e6d
|
||||||
UMCore: 7ab08669a8bb2e61f557c1fe9784521cb5aa28e3
|
UMConstantsInterface: 64060cf86587bcd90b1dbd804cceb6d377a308c1
|
||||||
UMFaceDetectorInterface: ce14e8e597f6a52aa66e4ab956cb5bff4fa8acf8
|
UMCore: eb200e882eadafcd31ead290770835fd648c0945
|
||||||
UMFileSystemInterface: 2ed004c9620f43f0b36b33c42ce668500850d6a4
|
UMFaceDetectorInterface: d6677d6ddc9ab95a0ca857aa7f8ba76656cc770f
|
||||||
UMFontInterface: 24fbc0a02ade6c60ad3ee3e2b5d597c8dcfc3208
|
UMFileSystemInterface: c70ea7147198b9807080f3597f26236be49b0165
|
||||||
UMImageLoaderInterface: 3976a14c588341228881ff75970fbabf122efca4
|
UMFontInterface: d9d3b27af698c5389ae9e20b99ef56a083f491fb
|
||||||
UMPermissionsInterface: 2abf9f7f4aa7110e27beaf634a7deda2d50ff3d7
|
UMImageLoaderInterface: 14dd2c46c67167491effc9e91250e9510f12709e
|
||||||
UMReactNativeAdapter: 230406e3335a8dbd4c9c0e654488a1cf3b44552f
|
UMPermissionsInterface: 5e83a9167c177e4a0f0a3539345983cc749efb3e
|
||||||
UMSensorsInterface: d708a892ef1500bdd9fc3ff03f7836c66d1634d3
|
UMReactNativeAdapter: 126da3486c1a1f11945b649d557d6c2ebb9407b2
|
||||||
UMTaskManagerInterface: a98e37a576a5220bf43b8faf33cfdc129d2f441d
|
UMSensorsInterface: 48941f70175e2975af1a9386c6d6cb16d8126805
|
||||||
|
UMTaskManagerInterface: cb890c79c63885504ddc0efd7a7d01481760aca2
|
||||||
Yoga: f2a7cd4280bfe2cca5a7aed98ba0eb3d1310f18b
|
Yoga: f2a7cd4280bfe2cca5a7aed98ba0eb3d1310f18b
|
||||||
|
|
||||||
PODFILE CHECKSUM: c48a21ff513d3eadafa50f8797207ef2be75e234
|
PODFILE CHECKSUM: c48a21ff513d3eadafa50f8797207ef2be75e234
|
||||||
|
|
||||||
COCOAPODS: 1.8.4
|
COCOAPODS: 1.9.1
|
||||||
|
|||||||
6
example/jest.config.js
Normal file
6
example/jest.config.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
testRegex: '/__integration_tests__/.*\\.(test|spec)\\.(js|tsx?)$',
|
||||||
|
globalSetup: './e2e/config/setup-server.tsx',
|
||||||
|
globalTeardown: './e2e/config/teardown-server.tsx',
|
||||||
|
setupFilesAfterEnv: ['./e2e/config/setup-playwright.tsx'],
|
||||||
|
};
|
||||||
@@ -8,26 +8,25 @@ const blacklist = require('metro-config/src/defaults/blacklist');
|
|||||||
const root = path.resolve(__dirname, '..');
|
const root = path.resolve(__dirname, '..');
|
||||||
const packages = path.resolve(root, 'packages');
|
const packages = path.resolve(root, 'packages');
|
||||||
|
|
||||||
|
const workspaces = fs
|
||||||
|
// List all packages under `packages/`
|
||||||
|
.readdirSync(packages)
|
||||||
|
// Ignore hidden files such as .DS_Store
|
||||||
|
.filter((p) => !p.startsWith('.'))
|
||||||
|
.map((p) => path.join(packages, p));
|
||||||
|
|
||||||
// Get the list of dependencies for all packages in the monorepo
|
// Get the list of dependencies for all packages in the monorepo
|
||||||
const modules = ['@expo/vector-icons']
|
const modules = ['@expo/vector-icons']
|
||||||
.concat(
|
.concat(
|
||||||
...fs
|
...workspaces.map((it) => {
|
||||||
// List all packages under `packages/`
|
const pak = JSON.parse(
|
||||||
.readdirSync(packages)
|
fs.readFileSync(path.join(it, 'package.json'), 'utf8')
|
||||||
// Ignore hidden files such as .DS_Store
|
);
|
||||||
.filter((p) => !p.startsWith('.'))
|
|
||||||
.map((p) => {
|
|
||||||
const pak = JSON.parse(
|
|
||||||
fs.readFileSync(path.join(packages, p, 'package.json'), 'utf8')
|
|
||||||
);
|
|
||||||
|
|
||||||
// We need to collect list of deps that this package imports
|
// We need to make sure that only one version is loaded for peerDependencies
|
||||||
// Collecting both dependencies are peerDependencies should do it
|
// So we blacklist them at the root, and alias them to the versions in example's node_modules
|
||||||
return Object.keys({
|
return pak.peerDependencies ? Object.keys(pak.peerDependencies) : [];
|
||||||
...pak.dependencies,
|
})
|
||||||
...pak.peerDependencies,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
.sort()
|
.sort()
|
||||||
.filter(
|
.filter(
|
||||||
@@ -45,15 +44,16 @@ module.exports = {
|
|||||||
watchFolders: [root],
|
watchFolders: [root],
|
||||||
|
|
||||||
resolver: {
|
resolver: {
|
||||||
// We need to blacklist `node_modules` of all our packages
|
// We need to blacklist the peerDependencies we've collected in packages' node_modules
|
||||||
// This will avoid Metro throwing duplicate module errors
|
|
||||||
blacklistRE: blacklist(
|
blacklistRE: blacklist(
|
||||||
fs
|
[].concat(
|
||||||
.readdirSync(packages)
|
...workspaces.map((it) =>
|
||||||
.map((p) => path.join(packages, p))
|
modules.map(
|
||||||
.map(
|
(m) =>
|
||||||
(it) => new RegExp(`^${escape(path.join(it, 'node_modules'))}\\/.*$`)
|
new RegExp(`^${escape(path.join(it, 'node_modules', m))}\\/.*$`)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
// When we import a package from the monorepo, metro won't be able to find their deps
|
// When we import a package from the monorepo, metro won't be able to find their deps
|
||||||
|
|||||||
@@ -1,41 +1,60 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/example",
|
"name": "@react-navigation/example",
|
||||||
"description": "Demo app to showcase various functionality of React Navigation",
|
"description": "Demo app to showcase various functionality of React Navigation",
|
||||||
"version": "5.0.0",
|
"version": "5.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "expo start",
|
"start": "expo start",
|
||||||
"web": "expo start:web",
|
"web": "expo start:web",
|
||||||
"native": "react-native start",
|
"native": "react-native start",
|
||||||
"android": "react-native run-android",
|
"android": "react-native run-android",
|
||||||
"ios": "react-native run-ios"
|
"ios": "react-native run-ios",
|
||||||
|
"server": "nodemon -e '.js,.ts,.tsx' --exec \"babel-node -i '/node_modules[/\\](?react-native)/' -x '.web.tsx,.web.ts,.web.js,.tsx,.ts,.js' --config-file ./server/babel.config.js server\"",
|
||||||
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/vector-icons": "^10.0.0",
|
"@expo/vector-icons": "^10.2.0",
|
||||||
"@react-native-community/masked-view": "^0.1.7",
|
"@react-native-community/masked-view": "^0.1.10",
|
||||||
"color": "^3.1.2",
|
"color": "^3.1.2",
|
||||||
"expo": "^37.0.0",
|
"expo": "^37.0.8",
|
||||||
"expo-asset": "~8.1.3",
|
"expo-asset": "~8.1.3",
|
||||||
"expo-blur": "~8.1.0",
|
"expo-blur": "~8.1.0",
|
||||||
|
"koa": "^2.12.0",
|
||||||
"react": "~16.9.0",
|
"react": "~16.9.0",
|
||||||
"react-dom": "~16.9.0",
|
"react-dom": "~16.9.0",
|
||||||
"react-native": "~0.61.5",
|
"react-native": "~0.61.5",
|
||||||
"react-native-gesture-handler": "^1.6.0",
|
"react-native-gesture-handler": "^1.6.0",
|
||||||
"react-native-paper": "^3.7.0",
|
"react-native-paper": "^3.10.1",
|
||||||
"react-native-reanimated": "^1.7.0",
|
"react-native-reanimated": "^1.8.0",
|
||||||
"react-native-restart": "^0.0.14",
|
"react-native-restart": "^0.0.15",
|
||||||
"react-native-safe-area-context": "^0.7.3",
|
"react-native-safe-area-context": "^1.0.0",
|
||||||
"react-native-screens": "^2.3.0",
|
"react-native-screens": "^2.7.0",
|
||||||
"react-native-tab-view": "2.14.0",
|
"react-native-tab-view": "2.14.0",
|
||||||
"react-native-unimodules": "~0.8.1",
|
"react-native-unimodules": "~0.9.1",
|
||||||
|
"react-native-vector-icons": "^6.6.0",
|
||||||
"react-native-web": "^0.11.7"
|
"react-native-web": "^0.11.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/node": "^7.8.7",
|
||||||
"@expo/webpack-config": "^0.11.19",
|
"@expo/webpack-config": "^0.11.19",
|
||||||
"@types/react": "^16.9.23",
|
"@types/cheerio": "^0.22.18",
|
||||||
"@types/react-native": "^0.60.22",
|
"@types/jest-dev-server": "^4.2.0",
|
||||||
|
"@types/koa": "^2.11.3",
|
||||||
|
"@types/node-fetch": "^2.5.7",
|
||||||
|
"@types/react": "^16.9.34",
|
||||||
|
"@types/react-dom": "^16.9.8",
|
||||||
|
"@types/react-native": "^0.62.7",
|
||||||
|
"babel-plugin-module-resolver": "^4.0.0",
|
||||||
"babel-preset-expo": "^8.1.0",
|
"babel-preset-expo": "^8.1.0",
|
||||||
"expo-cli": "^3.17.18",
|
"cheerio": "^1.0.0-rc.3",
|
||||||
|
"expo-cli": "^3.20.1",
|
||||||
|
"jest": "^26.0.1",
|
||||||
|
"jest-dev-server": "^4.4.0",
|
||||||
|
"mock-require-assets": "^0.0.1",
|
||||||
|
"node-fetch": "^2.6.0",
|
||||||
|
"nodemon": "^2.0.4",
|
||||||
|
"playwright": "^0.14.0",
|
||||||
|
"serve": "^11.3.0",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
40
example/server/babel.config.js
Normal file
40
example/server/babel.config.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const packages = path.resolve(__dirname, '..', '..', 'packages');
|
||||||
|
|
||||||
|
const alias = Object.fromEntries(
|
||||||
|
fs
|
||||||
|
.readdirSync(packages)
|
||||||
|
.filter((name) => !name.startsWith('.'))
|
||||||
|
.map((name) => [
|
||||||
|
`@react-navigation/${name}`,
|
||||||
|
path.resolve(
|
||||||
|
packages,
|
||||||
|
name,
|
||||||
|
require(`../../packages/${name}/package.json`).source
|
||||||
|
),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@babel/preset-env',
|
||||||
|
'@babel/preset-flow',
|
||||||
|
'@babel/preset-typescript',
|
||||||
|
'@babel/preset-react',
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
'@babel/plugin-proposal-class-properties',
|
||||||
|
[
|
||||||
|
'module-resolver',
|
||||||
|
{
|
||||||
|
root: ['..'],
|
||||||
|
alias: {
|
||||||
|
'react-native': 'react-native-web',
|
||||||
|
...alias,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
};
|
||||||
54
example/server/index.tsx
Normal file
54
example/server/index.tsx
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import './resolve-hooks';
|
||||||
|
|
||||||
|
import Koa from 'koa';
|
||||||
|
import * as React from 'react';
|
||||||
|
import ReactDOMServer from 'react-dom/server';
|
||||||
|
import { AppRegistry } from 'react-native-web';
|
||||||
|
import { ServerContainer, ServerContainerRef } from '@react-navigation/native';
|
||||||
|
import App from '../src/index';
|
||||||
|
|
||||||
|
AppRegistry.registerComponent('App', () => App);
|
||||||
|
|
||||||
|
const PORT = process.env.PORT || 3275;
|
||||||
|
|
||||||
|
const app = new Koa();
|
||||||
|
|
||||||
|
app.use(async (ctx) => {
|
||||||
|
const { element, getStyleElement } = AppRegistry.getApplication('App');
|
||||||
|
|
||||||
|
const ref = React.createRef<ServerContainerRef>();
|
||||||
|
|
||||||
|
const html = ReactDOMServer.renderToString(
|
||||||
|
<ServerContainer
|
||||||
|
ref={ref}
|
||||||
|
location={{ pathname: ctx.path, search: ctx.search }}
|
||||||
|
>
|
||||||
|
{element}
|
||||||
|
</ServerContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
const css = ReactDOMServer.renderToStaticMarkup(getStyleElement());
|
||||||
|
|
||||||
|
const document = `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html style="height: 100%">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta httpEquiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover"
|
||||||
|
>
|
||||||
|
${css}
|
||||||
|
<title>${ref.current?.getCurrentOptions()?.title}</title>
|
||||||
|
<body style="height: 100%">
|
||||||
|
<div id="root" style="display: flex; height: 100%">
|
||||||
|
${html}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
ctx.body = document;
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Running at http://localhost:${PORT}`);
|
||||||
|
});
|
||||||
12
example/server/resolve-hooks.tsx
Normal file
12
example/server/resolve-hooks.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import 'mock-require-assets';
|
||||||
|
|
||||||
|
import Module from 'module';
|
||||||
|
|
||||||
|
// We need to make sure that .web.xx extensions are resolved before .xx
|
||||||
|
// @ts-ignore
|
||||||
|
Module._extensions = Object.fromEntries(
|
||||||
|
// @ts-ignore
|
||||||
|
Object.entries(Module._extensions).sort((a, b) => {
|
||||||
|
return b[0].split('.').length - a[0].split('.').length;
|
||||||
|
})
|
||||||
|
);
|
||||||
3
example/src/AsyncStorage.native.tsx
Normal file
3
example/src/AsyncStorage.native.tsx
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { AsyncStorage } from 'react-native';
|
||||||
|
|
||||||
|
export default AsyncStorage;
|
||||||
14
example/src/AsyncStorage.tsx
Normal file
14
example/src/AsyncStorage.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
export default {
|
||||||
|
getItem(key: string) {
|
||||||
|
return Promise.resolve(localStorage.getItem(key));
|
||||||
|
},
|
||||||
|
setItem(key: string, value: string) {
|
||||||
|
return Promise.resolve(localStorage.setItem(key, value));
|
||||||
|
},
|
||||||
|
removeItem(key: string) {
|
||||||
|
return Promise.resolve(localStorage.removeItem(key));
|
||||||
|
},
|
||||||
|
clear() {
|
||||||
|
return Promise.resolve(localStorage.clear());
|
||||||
|
},
|
||||||
|
};
|
||||||
11
example/src/Restart.native.tsx
Normal file
11
example/src/Restart.native.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import RNRestart from 'react-native-restart';
|
||||||
|
import { Updates } from 'expo';
|
||||||
|
|
||||||
|
export function restartApp() {
|
||||||
|
// @ts-ignore
|
||||||
|
if (global.Expo) {
|
||||||
|
Updates.reloadFromCache();
|
||||||
|
} else {
|
||||||
|
RNRestart.Restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
1
example/src/Restart.tsx
Normal file
1
example/src/Restart.tsx
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export function restartApp() {}
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
import { View, ScrollView, StyleSheet, Platform } from 'react-native';
|
||||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
import { Button } from 'react-native-paper';
|
||||||
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
|
import {
|
||||||
|
createBottomTabNavigator,
|
||||||
|
BottomTabNavigationProp,
|
||||||
|
} from '@react-navigation/bottom-tabs';
|
||||||
import TouchableBounce from '../Shared/TouchableBounce';
|
import TouchableBounce from '../Shared/TouchableBounce';
|
||||||
import Albums from '../Shared/Albums';
|
import Albums from '../Shared/Albums';
|
||||||
import Contacts from '../Shared/Contacts';
|
import Contacts from '../Shared/Contacts';
|
||||||
@@ -22,13 +27,46 @@ type BottomTabParams = {
|
|||||||
Chat: undefined;
|
Chat: undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const scrollEnabled = Platform.select({ web: true, default: false });
|
||||||
|
|
||||||
|
const AlbumsScreen = ({
|
||||||
|
navigation,
|
||||||
|
}: {
|
||||||
|
navigation: BottomTabNavigationProp<BottomTabParams>;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<ScrollView>
|
||||||
|
<View style={styles.buttons}>
|
||||||
|
<Button
|
||||||
|
mode="outlined"
|
||||||
|
onPress={() => navigation.setOptions({ tabBarVisible: false })}
|
||||||
|
style={styles.button}
|
||||||
|
>
|
||||||
|
Hide tab bar
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
mode="outlined"
|
||||||
|
onPress={() => navigation.setOptions({ tabBarVisible: true })}
|
||||||
|
style={styles.button}
|
||||||
|
>
|
||||||
|
Show tab bar
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
<Albums scrollEnabled={scrollEnabled} />
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const BottomTabs = createBottomTabNavigator<BottomTabParams>();
|
const BottomTabs = createBottomTabNavigator<BottomTabParams>();
|
||||||
|
|
||||||
export default function BottomTabsScreen() {
|
export default function BottomTabsScreen() {
|
||||||
return (
|
return (
|
||||||
<BottomTabs.Navigator
|
<BottomTabs.Navigator
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
tabBarButton: (props) => <TouchableBounce {...props} />,
|
tabBarButton:
|
||||||
|
Platform.OS === 'web'
|
||||||
|
? undefined
|
||||||
|
: (props) => <TouchableBounce {...props} />,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<BottomTabs.Screen
|
<BottomTabs.Screen
|
||||||
@@ -58,7 +96,7 @@ export default function BottomTabsScreen() {
|
|||||||
/>
|
/>
|
||||||
<BottomTabs.Screen
|
<BottomTabs.Screen
|
||||||
name="Albums"
|
name="Albums"
|
||||||
component={Albums}
|
component={AlbumsScreen}
|
||||||
options={{
|
options={{
|
||||||
title: 'Albums',
|
title: 'Albums',
|
||||||
tabBarIcon: getTabBarIcon('image-album'),
|
tabBarIcon: getTabBarIcon('image-album'),
|
||||||
@@ -67,3 +105,13 @@ export default function BottomTabsScreen() {
|
|||||||
</BottomTabs.Navigator>
|
</BottomTabs.Navigator>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
buttons: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
padding: 8,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
margin: 8,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { View, StyleSheet } from 'react-native';
|
import { View, ScrollView, StyleSheet, Platform } from 'react-native';
|
||||||
import { Button } from 'react-native-paper';
|
import { Button } from 'react-native-paper';
|
||||||
import {
|
import {
|
||||||
createCompatNavigatorFactory,
|
createCompatNavigatorFactory,
|
||||||
@@ -11,25 +11,32 @@ import {
|
|||||||
} from '@react-navigation/stack';
|
} from '@react-navigation/stack';
|
||||||
import Article from '../Shared/Article';
|
import Article from '../Shared/Article';
|
||||||
import Albums from '../Shared/Albums';
|
import Albums from '../Shared/Albums';
|
||||||
|
import NewsFeed from '../Shared/NewsFeed';
|
||||||
|
|
||||||
type CompatStackParams = {
|
type CompatStackParams = {
|
||||||
Article: { author: string };
|
Albums: undefined;
|
||||||
Album: undefined;
|
Nested: { author: string };
|
||||||
};
|
};
|
||||||
|
|
||||||
const ArticleScreen: CompatScreenType<StackNavigationProp<
|
type NestedStackParams = {
|
||||||
CompatStackParams,
|
Feed: undefined;
|
||||||
'Article'
|
Article: { author: string };
|
||||||
|
};
|
||||||
|
|
||||||
|
const scrollEnabled = Platform.select({ web: true, default: false });
|
||||||
|
|
||||||
|
const AlbumsScreen: CompatScreenType<StackNavigationProp<
|
||||||
|
CompatStackParams
|
||||||
>> = ({ navigation }) => {
|
>> = ({ navigation }) => {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<ScrollView>
|
||||||
<View style={styles.buttons}>
|
<View style={styles.buttons}>
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
onPress={() => navigation.push('Album')}
|
onPress={() => navigation.push('Nested', { author: 'Babel fish' })}
|
||||||
style={styles.button}
|
style={styles.button}
|
||||||
>
|
>
|
||||||
Push album
|
Push nested
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
mode="outlined"
|
mode="outlined"
|
||||||
@@ -39,24 +46,20 @@ const ArticleScreen: CompatScreenType<StackNavigationProp<
|
|||||||
Go back
|
Go back
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
<Article author={{ name: navigation.getParam('author') }} />
|
<Albums scrollEnabled={scrollEnabled} />
|
||||||
</React.Fragment>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ArticleScreen.navigationOptions = ({ navigation }) => ({
|
const FeedScreen: CompatScreenType<StackNavigationProp<NestedStackParams>> = ({
|
||||||
title: `Article by ${navigation.getParam('author')}`,
|
navigation,
|
||||||
});
|
}) => {
|
||||||
|
|
||||||
const AlbumsScreen: CompatScreenType<StackNavigationProp<
|
|
||||||
CompatStackParams
|
|
||||||
>> = ({ navigation }) => {
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<ScrollView>
|
||||||
<View style={styles.buttons}>
|
<View style={styles.buttons}>
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
onPress={() => navigation.push('Article', { author: 'Babel fish' })}
|
onPress={() => navigation.push('Article')}
|
||||||
style={styles.button}
|
style={styles.button}
|
||||||
>
|
>
|
||||||
Push article
|
Push article
|
||||||
@@ -69,22 +72,69 @@ const AlbumsScreen: CompatScreenType<StackNavigationProp<
|
|||||||
Go back
|
Go back
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
<Albums />
|
<NewsFeed scrollEnabled={scrollEnabled} />
|
||||||
</React.Fragment>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const CompatStack = createCompatNavigatorFactory(createStackNavigator)<
|
const ArticleScreen: CompatScreenType<StackNavigationProp<
|
||||||
|
NestedStackParams,
|
||||||
|
'Article'
|
||||||
|
>> = ({ navigation }) => {
|
||||||
|
navigation.dangerouslyGetParent();
|
||||||
|
return (
|
||||||
|
<ScrollView>
|
||||||
|
<View style={styles.buttons}>
|
||||||
|
<Button
|
||||||
|
mode="contained"
|
||||||
|
onPress={() => navigation.push('Albums')}
|
||||||
|
style={styles.button}
|
||||||
|
>
|
||||||
|
Push albums
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
mode="outlined"
|
||||||
|
onPress={() => navigation.goBack()}
|
||||||
|
style={styles.button}
|
||||||
|
>
|
||||||
|
Go back
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
<Article
|
||||||
|
author={{ name: navigation.getParam('author') }}
|
||||||
|
scrollEnabled={scrollEnabled}
|
||||||
|
/>
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ArticleScreen.navigationOptions = ({ navigation }) => ({
|
||||||
|
title: `Article by ${navigation.getParam('author')}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createCompatStackNavigator = createCompatNavigatorFactory(
|
||||||
|
createStackNavigator
|
||||||
|
);
|
||||||
|
|
||||||
|
const CompatStack = createCompatStackNavigator<
|
||||||
StackNavigationProp<CompatStackParams>
|
StackNavigationProp<CompatStackParams>
|
||||||
>(
|
>(
|
||||||
{
|
{
|
||||||
Article: {
|
Albums: AlbumsScreen,
|
||||||
screen: ArticleScreen,
|
Nested: {
|
||||||
|
screen: createCompatStackNavigator<
|
||||||
|
StackNavigationProp<NestedStackParams>
|
||||||
|
>(
|
||||||
|
{
|
||||||
|
Feed: FeedScreen,
|
||||||
|
Article: ArticleScreen,
|
||||||
|
},
|
||||||
|
{ navigationOptions: { headerShown: false } }
|
||||||
|
),
|
||||||
params: {
|
params: {
|
||||||
author: 'Gandalf',
|
author: 'Gandalf',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Album: AlbumsScreen,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
mode: 'modal',
|
mode: 'modal',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { View, StyleSheet } from 'react-native';
|
import { View, StyleSheet } from 'react-native';
|
||||||
import { Title, Button } from 'react-native-paper';
|
import { Title, Button } from 'react-native-paper';
|
||||||
import { Feather } from '@expo/vector-icons';
|
import Feather from 'react-native-vector-icons/Feather';
|
||||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||||
|
|
||||||
type BottomTabParams = {
|
type BottomTabParams = {
|
||||||
|
|||||||
162
example/src/Screens/LinkComponent.tsx
Normal file
162
example/src/Screens/LinkComponent.tsx
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { View, StyleSheet, ScrollView, Platform } from 'react-native';
|
||||||
|
import { Button } from 'react-native-paper';
|
||||||
|
import {
|
||||||
|
Link,
|
||||||
|
StackActions,
|
||||||
|
RouteProp,
|
||||||
|
ParamListBase,
|
||||||
|
useLinkProps,
|
||||||
|
} from '@react-navigation/native';
|
||||||
|
import {
|
||||||
|
createStackNavigator,
|
||||||
|
StackNavigationProp,
|
||||||
|
} from '@react-navigation/stack';
|
||||||
|
import Article from '../Shared/Article';
|
||||||
|
import Albums from '../Shared/Albums';
|
||||||
|
|
||||||
|
type SimpleStackParams = {
|
||||||
|
Article: { author: string };
|
||||||
|
Albums: undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
|
||||||
|
|
||||||
|
const scrollEnabled = Platform.select({ web: true, default: false });
|
||||||
|
|
||||||
|
const LinkButton = ({
|
||||||
|
to,
|
||||||
|
...rest
|
||||||
|
}: React.ComponentProps<typeof Button> & { to: string }) => {
|
||||||
|
const { onPress, ...props } = useLinkProps({ to });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
{...props}
|
||||||
|
{...rest}
|
||||||
|
{...Platform.select({
|
||||||
|
web: { onClick: onPress } as any,
|
||||||
|
default: { onPress },
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ArticleScreen = ({
|
||||||
|
navigation,
|
||||||
|
route,
|
||||||
|
}: {
|
||||||
|
navigation: SimpleStackNavigation;
|
||||||
|
route: RouteProp<SimpleStackParams, 'Article'>;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<ScrollView>
|
||||||
|
<View style={styles.buttons}>
|
||||||
|
<Link
|
||||||
|
to="/link-component/music"
|
||||||
|
style={[styles.button, { padding: 8 }]}
|
||||||
|
>
|
||||||
|
Go to /link-component/music
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
to="/link-component/music"
|
||||||
|
action={StackActions.replace('Albums')}
|
||||||
|
style={[styles.button, { padding: 8 }]}
|
||||||
|
>
|
||||||
|
Replace with /link-component/music
|
||||||
|
</Link>
|
||||||
|
<LinkButton
|
||||||
|
to="/link-component/music"
|
||||||
|
mode="contained"
|
||||||
|
style={styles.button}
|
||||||
|
>
|
||||||
|
Go to /link-component/music
|
||||||
|
</LinkButton>
|
||||||
|
<Button
|
||||||
|
mode="outlined"
|
||||||
|
onPress={() => navigation.goBack()}
|
||||||
|
style={styles.button}
|
||||||
|
>
|
||||||
|
Go back
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
<Article
|
||||||
|
author={{ name: route.params.author }}
|
||||||
|
scrollEnabled={scrollEnabled}
|
||||||
|
/>
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const AlbumsScreen = ({
|
||||||
|
navigation,
|
||||||
|
}: {
|
||||||
|
navigation: SimpleStackNavigation;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<ScrollView>
|
||||||
|
<View style={styles.buttons}>
|
||||||
|
<Link
|
||||||
|
to="/link-component/article/babel"
|
||||||
|
style={[styles.button, { padding: 8 }]}
|
||||||
|
>
|
||||||
|
Go to /link-component/article
|
||||||
|
</Link>
|
||||||
|
<LinkButton
|
||||||
|
to="/link-component/article/babel"
|
||||||
|
mode="contained"
|
||||||
|
style={styles.button}
|
||||||
|
>
|
||||||
|
Go to /link-component/article
|
||||||
|
</LinkButton>
|
||||||
|
<Button
|
||||||
|
mode="outlined"
|
||||||
|
onPress={() => navigation.goBack()}
|
||||||
|
style={styles.button}
|
||||||
|
>
|
||||||
|
Go back
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
<Albums scrollEnabled={scrollEnabled} />
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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 {...rest}>
|
||||||
|
<SimpleStack.Screen
|
||||||
|
name="Article"
|
||||||
|
component={ArticleScreen}
|
||||||
|
options={({ route }) => ({
|
||||||
|
title: `Article by ${route.params.author}`,
|
||||||
|
})}
|
||||||
|
initialParams={{ author: 'Gandalf' }}
|
||||||
|
/>
|
||||||
|
<SimpleStack.Screen
|
||||||
|
name="Albums"
|
||||||
|
component={AlbumsScreen}
|
||||||
|
options={{ title: 'Albums' }}
|
||||||
|
/>
|
||||||
|
</SimpleStack.Navigator>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
buttons: {
|
||||||
|
padding: 8,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
margin: 8,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,12 +1,18 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Dimensions, ScaledSize } from 'react-native';
|
import { Dimensions, ScaledSize } from 'react-native';
|
||||||
import { Appbar } from 'react-native-paper';
|
import { Appbar } from 'react-native-paper';
|
||||||
import { ParamListBase } from '@react-navigation/native';
|
import {
|
||||||
|
useTheme,
|
||||||
|
useNavigation,
|
||||||
|
ParamListBase,
|
||||||
|
} from '@react-navigation/native';
|
||||||
import { StackNavigationProp } from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import {
|
import {
|
||||||
createDrawerNavigator,
|
createDrawerNavigator,
|
||||||
DrawerNavigationProp,
|
DrawerNavigationProp,
|
||||||
DrawerContent,
|
DrawerContent,
|
||||||
|
DrawerContentComponentProps,
|
||||||
|
DrawerContentOptions,
|
||||||
} from '@react-navigation/drawer';
|
} from '@react-navigation/drawer';
|
||||||
import Article from '../Shared/Article';
|
import Article from '../Shared/Article';
|
||||||
import Albums from '../Shared/Albums';
|
import Albums from '../Shared/Albums';
|
||||||
@@ -15,7 +21,7 @@ import NewsFeed from '../Shared/NewsFeed';
|
|||||||
type DrawerParams = {
|
type DrawerParams = {
|
||||||
Article: undefined;
|
Article: undefined;
|
||||||
NewsFeed: undefined;
|
NewsFeed: undefined;
|
||||||
Album: undefined;
|
Albums: undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DrawerNavigation = DrawerNavigationProp<DrawerParams>;
|
type DrawerNavigation = DrawerNavigationProp<DrawerParams>;
|
||||||
@@ -43,10 +49,11 @@ const Header = ({
|
|||||||
onGoBack: () => void;
|
onGoBack: () => void;
|
||||||
title: string;
|
title: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { colors } = useTheme();
|
||||||
const isLargeScreen = useIsLargeScreen();
|
const isLargeScreen = useIsLargeScreen();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Appbar.Header>
|
<Appbar.Header style={{ backgroundColor: colors.card, elevation: 1 }}>
|
||||||
{isLargeScreen ? null : <Appbar.BackAction onPress={onGoBack} />}
|
{isLargeScreen ? null : <Appbar.BackAction onPress={onGoBack} />}
|
||||||
<Appbar.Content title={title} />
|
<Appbar.Content title={title} />
|
||||||
</Appbar.Header>
|
</Appbar.Header>
|
||||||
@@ -80,6 +87,23 @@ const AlbumsScreen = ({ navigation }: { navigation: DrawerNavigation }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CustomDrawerContent = (
|
||||||
|
props: DrawerContentComponentProps<DrawerContentOptions>
|
||||||
|
) => {
|
||||||
|
const { colors } = useTheme();
|
||||||
|
const navigation = useNavigation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Appbar.Header style={{ backgroundColor: colors.card, elevation: 1 }}>
|
||||||
|
<Appbar.Action icon="close" onPress={() => navigation.goBack()} />
|
||||||
|
<Appbar.Content title="Pages" />
|
||||||
|
</Appbar.Header>
|
||||||
|
<DrawerContent {...props} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const Drawer = createDrawerNavigator<DrawerParams>();
|
const Drawer = createDrawerNavigator<DrawerParams>();
|
||||||
|
|
||||||
type Props = Partial<React.ComponentProps<typeof Drawer.Navigator>> & {
|
type Props = Partial<React.ComponentProps<typeof Drawer.Navigator>> & {
|
||||||
@@ -100,15 +124,7 @@ export default function DrawerScreen({ navigation, ...rest }: Props) {
|
|||||||
drawerType={isLargeScreen ? 'permanent' : 'back'}
|
drawerType={isLargeScreen ? 'permanent' : 'back'}
|
||||||
drawerStyle={isLargeScreen ? null : { width: '100%' }}
|
drawerStyle={isLargeScreen ? null : { width: '100%' }}
|
||||||
overlayColor="transparent"
|
overlayColor="transparent"
|
||||||
drawerContent={(props) => (
|
drawerContent={(props) => <CustomDrawerContent {...props} />}
|
||||||
<>
|
|
||||||
<Appbar.Header>
|
|
||||||
<Appbar.Action icon="close" onPress={() => navigation.goBack()} />
|
|
||||||
<Appbar.Content title="Pages" />
|
|
||||||
</Appbar.Header>
|
|
||||||
<DrawerContent {...props} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<Drawer.Screen name="Article" component={ArticleScreen} />
|
<Drawer.Screen name="Article" component={ArticleScreen} />
|
||||||
@@ -118,9 +134,9 @@ export default function DrawerScreen({ navigation, ...rest }: Props) {
|
|||||||
options={{ title: 'Feed' }}
|
options={{ title: 'Feed' }}
|
||||||
/>
|
/>
|
||||||
<Drawer.Screen
|
<Drawer.Screen
|
||||||
name="Album"
|
name="Albums"
|
||||||
component={AlbumsScreen}
|
component={AlbumsScreen}
|
||||||
options={{ title: 'Album' }}
|
options={{ title: 'Albums' }}
|
||||||
/>
|
/>
|
||||||
</Drawer.Navigator>
|
</Drawer.Navigator>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { ParamListBase } from '@react-navigation/native';
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
|
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
|
||||||
import Albums from '../Shared/Albums';
|
import Albums from '../Shared/Albums';
|
||||||
import Contacts from '../Shared/Contacts';
|
import Contacts from '../Shared/Contacts';
|
||||||
@@ -12,7 +14,15 @@ type MaterialTopTabParams = {
|
|||||||
|
|
||||||
const MaterialTopTabs = createMaterialTopTabNavigator<MaterialTopTabParams>();
|
const MaterialTopTabs = createMaterialTopTabNavigator<MaterialTopTabParams>();
|
||||||
|
|
||||||
export default function MaterialTopTabsScreen() {
|
type Props = {
|
||||||
|
navigation: StackNavigationProp<ParamListBase>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function MaterialTopTabsScreen({ navigation }: Props) {
|
||||||
|
navigation.setOptions({
|
||||||
|
cardStyle: { flex: 1 },
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MaterialTopTabs.Navigator>
|
<MaterialTopTabs.Navigator>
|
||||||
<MaterialTopTabs.Screen
|
<MaterialTopTabs.Screen
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { View, StyleSheet, ScrollView } from 'react-native';
|
import { View, StyleSheet, ScrollView, Platform } from 'react-native';
|
||||||
import { Button } from 'react-native-paper';
|
import { Button } from 'react-native-paper';
|
||||||
import { RouteProp, ParamListBase } from '@react-navigation/native';
|
import { RouteProp, ParamListBase } from '@react-navigation/native';
|
||||||
import {
|
import {
|
||||||
@@ -12,11 +12,13 @@ import Albums from '../Shared/Albums';
|
|||||||
|
|
||||||
type ModalStackParams = {
|
type ModalStackParams = {
|
||||||
Article: { author: string };
|
Article: { author: string };
|
||||||
Album: undefined;
|
Albums: undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ModalStackNavigation = StackNavigationProp<ModalStackParams>;
|
type ModalStackNavigation = StackNavigationProp<ModalStackParams>;
|
||||||
|
|
||||||
|
const scrollEnabled = Platform.select({ web: true, default: false });
|
||||||
|
|
||||||
const ArticleScreen = ({
|
const ArticleScreen = ({
|
||||||
navigation,
|
navigation,
|
||||||
route,
|
route,
|
||||||
@@ -29,7 +31,7 @@ const ArticleScreen = ({
|
|||||||
<View style={styles.buttons}>
|
<View style={styles.buttons}>
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
onPress={() => navigation.push('Album')}
|
onPress={() => navigation.push('Albums')}
|
||||||
style={styles.button}
|
style={styles.button}
|
||||||
>
|
>
|
||||||
Push album
|
Push album
|
||||||
@@ -42,7 +44,10 @@ const ArticleScreen = ({
|
|||||||
Go back
|
Go back
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
<Article author={{ name: route.params.author }} scrollEnabled={false} />
|
<Article
|
||||||
|
author={{ name: route.params.author }}
|
||||||
|
scrollEnabled={scrollEnabled}
|
||||||
|
/>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -66,7 +71,7 @@ const AlbumsScreen = ({ navigation }: { navigation: ModalStackNavigation }) => {
|
|||||||
Go back
|
Go back
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
<Albums scrollEnabled={false} />
|
<Albums scrollEnabled={scrollEnabled} />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -107,9 +112,9 @@ export default function SimpleStackScreen({ navigation, options }: Props) {
|
|||||||
initialParams={{ author: 'Gandalf' }}
|
initialParams={{ author: 'Gandalf' }}
|
||||||
/>
|
/>
|
||||||
<ModalPresentationStack.Screen
|
<ModalPresentationStack.Screen
|
||||||
name="Album"
|
name="Albums"
|
||||||
component={AlbumsScreen}
|
component={AlbumsScreen}
|
||||||
options={{ title: 'Album' }}
|
options={{ title: 'Albums' }}
|
||||||
/>
|
/>
|
||||||
</ModalPresentationStack.Navigator>
|
</ModalPresentationStack.Navigator>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { View, StyleSheet, ScrollView } from 'react-native';
|
import { View, Platform, StyleSheet, ScrollView } from 'react-native';
|
||||||
import { Button } from 'react-native-paper';
|
import { Button } from 'react-native-paper';
|
||||||
import { RouteProp, ParamListBase } from '@react-navigation/native';
|
import { RouteProp, ParamListBase } from '@react-navigation/native';
|
||||||
import {
|
import {
|
||||||
@@ -13,11 +13,13 @@ import NewsFeed from '../Shared/NewsFeed';
|
|||||||
type SimpleStackParams = {
|
type SimpleStackParams = {
|
||||||
Article: { author: string };
|
Article: { author: string };
|
||||||
NewsFeed: undefined;
|
NewsFeed: undefined;
|
||||||
Album: undefined;
|
Albums: undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
|
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
|
||||||
|
|
||||||
|
const scrollEnabled = Platform.select({ web: true, default: false });
|
||||||
|
|
||||||
const ArticleScreen = ({
|
const ArticleScreen = ({
|
||||||
navigation,
|
navigation,
|
||||||
route,
|
route,
|
||||||
@@ -43,7 +45,10 @@ const ArticleScreen = ({
|
|||||||
Pop screen
|
Pop screen
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
<Article author={{ name: route.params.author }} scrollEnabled={false} />
|
<Article
|
||||||
|
author={{ name: route.params.author }}
|
||||||
|
scrollEnabled={scrollEnabled}
|
||||||
|
/>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -58,7 +63,7 @@ const NewsFeedScreen = ({
|
|||||||
<View style={styles.buttons}>
|
<View style={styles.buttons}>
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
onPress={() => navigation.navigate('Album')}
|
onPress={() => navigation.navigate('Albums')}
|
||||||
style={styles.button}
|
style={styles.button}
|
||||||
>
|
>
|
||||||
Navigate to album
|
Navigate to album
|
||||||
@@ -71,7 +76,7 @@ const NewsFeedScreen = ({
|
|||||||
Go back
|
Go back
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
<NewsFeed scrollEnabled={false} />
|
<NewsFeed scrollEnabled={scrollEnabled} />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -99,7 +104,7 @@ const AlbumsScreen = ({
|
|||||||
Pop by 2
|
Pop by 2
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
<Albums scrollEnabled={false} />
|
<Albums scrollEnabled={scrollEnabled} />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -131,9 +136,9 @@ export default function SimpleStackScreen({ navigation, ...rest }: Props) {
|
|||||||
options={{ title: 'Feed' }}
|
options={{ title: 'Feed' }}
|
||||||
/>
|
/>
|
||||||
<SimpleStack.Screen
|
<SimpleStack.Screen
|
||||||
name="Album"
|
name="Albums"
|
||||||
component={AlbumsScreen}
|
component={AlbumsScreen}
|
||||||
options={{ title: 'Album' }}
|
options={{ title: 'Albums' }}
|
||||||
/>
|
/>
|
||||||
</SimpleStack.Navigator>
|
</SimpleStack.Navigator>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { View, StyleSheet, ScrollView, Alert, Platform } from 'react-native';
|
import { View, StyleSheet, ScrollView, Alert, Platform } from 'react-native';
|
||||||
import { Button, Appbar } from 'react-native-paper';
|
import { Button, Appbar } from 'react-native-paper';
|
||||||
import { BlurView } from 'expo-blur';
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
||||||
import { RouteProp, ParamListBase } from '@react-navigation/native';
|
import { RouteProp, ParamListBase } from '@react-navigation/native';
|
||||||
import {
|
import {
|
||||||
createStackNavigator,
|
createStackNavigator,
|
||||||
@@ -10,16 +9,19 @@ import {
|
|||||||
HeaderBackground,
|
HeaderBackground,
|
||||||
useHeaderHeight,
|
useHeaderHeight,
|
||||||
} from '@react-navigation/stack';
|
} from '@react-navigation/stack';
|
||||||
|
import BlurView from '../Shared/BlurView';
|
||||||
import Article from '../Shared/Article';
|
import Article from '../Shared/Article';
|
||||||
import Albums from '../Shared/Albums';
|
import Albums from '../Shared/Albums';
|
||||||
|
|
||||||
type SimpleStackParams = {
|
type SimpleStackParams = {
|
||||||
Article: { author: string };
|
Article: { author: string };
|
||||||
Album: undefined;
|
Albums: undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
|
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
|
||||||
|
|
||||||
|
const scrollEnabled = Platform.select({ web: true, default: false });
|
||||||
|
|
||||||
const ArticleScreen = ({
|
const ArticleScreen = ({
|
||||||
navigation,
|
navigation,
|
||||||
route,
|
route,
|
||||||
@@ -32,7 +34,7 @@ const ArticleScreen = ({
|
|||||||
<View style={styles.buttons}>
|
<View style={styles.buttons}>
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
onPress={() => navigation.push('Album')}
|
onPress={() => navigation.push('Albums')}
|
||||||
style={styles.button}
|
style={styles.button}
|
||||||
>
|
>
|
||||||
Push album
|
Push album
|
||||||
@@ -45,7 +47,10 @@ const ArticleScreen = ({
|
|||||||
Go back
|
Go back
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
<Article author={{ name: route.params.author }} scrollEnabled={false} />
|
<Article
|
||||||
|
author={{ name: route.params.author }}
|
||||||
|
scrollEnabled={scrollEnabled}
|
||||||
|
/>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -75,7 +80,7 @@ const AlbumsScreen = ({
|
|||||||
Go back
|
Go back
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
<Albums scrollEnabled={false} />
|
<Albums scrollEnabled={scrollEnabled} />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -126,10 +131,10 @@ export default function SimpleStackScreen({ navigation, ...rest }: Props) {
|
|||||||
initialParams={{ author: 'Gandalf' }}
|
initialParams={{ author: 'Gandalf' }}
|
||||||
/>
|
/>
|
||||||
<SimpleStack.Screen
|
<SimpleStack.Screen
|
||||||
name="Album"
|
name="Albums"
|
||||||
component={AlbumsScreen}
|
component={AlbumsScreen}
|
||||||
options={{
|
options={{
|
||||||
title: 'Album',
|
title: 'Albums',
|
||||||
headerBackTitle: 'Back',
|
headerBackTitle: 'Back',
|
||||||
headerTransparent: true,
|
headerTransparent: true,
|
||||||
headerBackground: () => (
|
headerBackground: () => (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { View, StyleSheet, ScrollView } from 'react-native';
|
import { View, StyleSheet, ScrollView, Platform } from 'react-native';
|
||||||
import { Button, Paragraph } from 'react-native-paper';
|
import { Button, Paragraph } from 'react-native-paper';
|
||||||
import { RouteProp, ParamListBase, useTheme } from '@react-navigation/native';
|
import { RouteProp, ParamListBase, useTheme } from '@react-navigation/native';
|
||||||
import {
|
import {
|
||||||
@@ -15,6 +15,8 @@ type SimpleStackParams = {
|
|||||||
|
|
||||||
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
|
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
|
||||||
|
|
||||||
|
const scrollEnabled = Platform.select({ web: true, default: false });
|
||||||
|
|
||||||
const ArticleScreen = ({
|
const ArticleScreen = ({
|
||||||
navigation,
|
navigation,
|
||||||
route,
|
route,
|
||||||
@@ -40,7 +42,10 @@ const ArticleScreen = ({
|
|||||||
Go back
|
Go back
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
<Article author={{ name: route.params.author }} scrollEnabled={false} />
|
<Article
|
||||||
|
author={{ name: route.params.author }}
|
||||||
|
scrollEnabled={scrollEnabled}
|
||||||
|
/>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
3
example/src/Shared/BlurView.native.tsx
Normal file
3
example/src/Shared/BlurView.native.tsx
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { BlurView } from 'expo-blur';
|
||||||
|
|
||||||
|
export default BlurView;
|
||||||
12
example/src/Shared/BlurView.tsx
Normal file
12
example/src/Shared/BlurView.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { View, ViewProps } from 'react-native';
|
||||||
|
|
||||||
|
type Props = ViewProps & {
|
||||||
|
tint: 'light' | 'dark';
|
||||||
|
intensity: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
export default function BlurView({ tint, intensity, ...rest }: Props) {
|
||||||
|
return <View {...rest} />;
|
||||||
|
}
|
||||||
@@ -1,20 +1,17 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {
|
||||||
ScrollView,
|
ScrollView,
|
||||||
AsyncStorage,
|
|
||||||
YellowBox,
|
YellowBox,
|
||||||
Platform,
|
Platform,
|
||||||
StatusBar,
|
StatusBar,
|
||||||
I18nManager,
|
I18nManager,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
ScaledSize,
|
ScaledSize,
|
||||||
|
Linking,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
// eslint-disable-next-line import/no-unresolved
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import { enableScreens } from 'react-native-screens';
|
import { enableScreens } from 'react-native-screens';
|
||||||
import RNRestart from 'react-native-restart';
|
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||||
import { Updates } from 'expo';
|
|
||||||
import { Asset } from 'expo-asset';
|
|
||||||
import { MaterialIcons } from '@expo/vector-icons';
|
|
||||||
import {
|
import {
|
||||||
Provider as PaperProvider,
|
Provider as PaperProvider,
|
||||||
DefaultTheme as PaperLightTheme,
|
DefaultTheme as PaperLightTheme,
|
||||||
@@ -22,11 +19,10 @@ import {
|
|||||||
Appbar,
|
Appbar,
|
||||||
List,
|
List,
|
||||||
Divider,
|
Divider,
|
||||||
|
Text,
|
||||||
} from 'react-native-paper';
|
} from 'react-native-paper';
|
||||||
import {
|
import {
|
||||||
InitialState,
|
InitialState,
|
||||||
useLinking,
|
|
||||||
NavigationContainerRef,
|
|
||||||
NavigationContainer,
|
NavigationContainer,
|
||||||
DefaultTheme,
|
DefaultTheme,
|
||||||
DarkTheme,
|
DarkTheme,
|
||||||
@@ -37,11 +33,12 @@ import {
|
|||||||
} from '@react-navigation/drawer';
|
} from '@react-navigation/drawer';
|
||||||
import {
|
import {
|
||||||
createStackNavigator,
|
createStackNavigator,
|
||||||
Assets as StackAssets,
|
|
||||||
StackNavigationProp,
|
StackNavigationProp,
|
||||||
HeaderStyleInterpolators,
|
HeaderStyleInterpolators,
|
||||||
} from '@react-navigation/stack';
|
} from '@react-navigation/stack';
|
||||||
|
|
||||||
|
import { restartApp } from './Restart';
|
||||||
|
import AsyncStorage from './AsyncStorage';
|
||||||
import LinkingPrefixes from './LinkingPrefixes';
|
import LinkingPrefixes from './LinkingPrefixes';
|
||||||
import SettingsItem from './Shared/SettingsItem';
|
import SettingsItem from './Shared/SettingsItem';
|
||||||
import SimpleStack from './Screens/SimpleStack';
|
import SimpleStack from './Screens/SimpleStack';
|
||||||
@@ -55,11 +52,15 @@ import DynamicTabs from './Screens/DynamicTabs';
|
|||||||
import AuthFlow from './Screens/AuthFlow';
|
import AuthFlow from './Screens/AuthFlow';
|
||||||
import CompatAPI from './Screens/CompatAPI';
|
import CompatAPI from './Screens/CompatAPI';
|
||||||
import MasterDetail from './Screens/MasterDetail';
|
import MasterDetail from './Screens/MasterDetail';
|
||||||
|
import LinkComponent from './Screens/LinkComponent';
|
||||||
|
|
||||||
YellowBox.ignoreWarnings(['Require cycle:', 'Warning: Async Storage']);
|
YellowBox.ignoreWarnings(['Require cycle:', 'Warning: Async Storage']);
|
||||||
|
|
||||||
enableScreens();
|
enableScreens();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
global.REACT_NAVIGATION_REDUX_DEVTOOLS_EXTENSION_INTEGRATION_ENABLED = true;
|
||||||
|
|
||||||
type RootDrawerParamList = {
|
type RootDrawerParamList = {
|
||||||
Root: undefined;
|
Root: undefined;
|
||||||
Another: undefined;
|
Another: undefined;
|
||||||
@@ -110,6 +111,10 @@ const SCREENS = {
|
|||||||
title: 'Compat Layer',
|
title: 'Compat Layer',
|
||||||
component: CompatAPI,
|
component: CompatAPI,
|
||||||
},
|
},
|
||||||
|
LinkComponent: {
|
||||||
|
title: '<Link />',
|
||||||
|
component: LinkComponent,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const Drawer = createDrawerNavigator<RootDrawerParamList>();
|
const Drawer = createDrawerNavigator<RootDrawerParamList>();
|
||||||
@@ -118,42 +123,10 @@ const Stack = createStackNavigator<RootStackParamList>();
|
|||||||
const NAVIGATION_PERSISTENCE_KEY = 'NAVIGATION_STATE';
|
const NAVIGATION_PERSISTENCE_KEY = 'NAVIGATION_STATE';
|
||||||
const THEME_PERSISTENCE_KEY = 'THEME_TYPE';
|
const THEME_PERSISTENCE_KEY = 'THEME_TYPE';
|
||||||
|
|
||||||
Asset.loadAsync(StackAssets);
|
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const containerRef = React.useRef<NavigationContainerRef>(null);
|
|
||||||
|
|
||||||
// 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: {
|
|
||||||
path: '',
|
|
||||||
initialRouteName: 'Home',
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
{ Home: '' }
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const [theme, setTheme] = React.useState(DefaultTheme);
|
const [theme, setTheme] = React.useState(DefaultTheme);
|
||||||
|
|
||||||
const [isReady, setIsReady] = React.useState(false);
|
const [isReady, setIsReady] = React.useState(Platform.OS === 'web');
|
||||||
const [initialState, setInitialState] = React.useState<
|
const [initialState, setInitialState] = React.useState<
|
||||||
InitialState | undefined
|
InitialState | undefined
|
||||||
>();
|
>();
|
||||||
@@ -161,17 +134,18 @@ export default function App() {
|
|||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const restoreState = async () => {
|
const restoreState = async () => {
|
||||||
try {
|
try {
|
||||||
let state = await getInitialState();
|
const initialUrl = await Linking.getInitialURL();
|
||||||
|
|
||||||
if (Platform.OS !== 'web' && state === undefined) {
|
if (Platform.OS !== 'web' || initialUrl === null) {
|
||||||
const savedState = await AsyncStorage.getItem(
|
const savedState = await AsyncStorage.getItem(
|
||||||
NAVIGATION_PERSISTENCE_KEY
|
NAVIGATION_PERSISTENCE_KEY
|
||||||
);
|
);
|
||||||
state = savedState ? JSON.parse(savedState) : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state !== undefined) {
|
const state = savedState ? JSON.parse(savedState) : undefined;
|
||||||
setInitialState(state);
|
|
||||||
|
if (state !== undefined) {
|
||||||
|
setInitialState(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
@@ -187,7 +161,7 @@ export default function App() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
restoreState();
|
restoreState();
|
||||||
}, [getInitialState]);
|
}, []);
|
||||||
|
|
||||||
const paperTheme = React.useMemo(() => {
|
const paperTheme = React.useMemo(() => {
|
||||||
const t = theme.dark ? PaperDarkTheme : PaperLightTheme;
|
const t = theme.dark ? PaperDarkTheme : PaperLightTheme;
|
||||||
@@ -227,7 +201,6 @@ export default function App() {
|
|||||||
<StatusBar barStyle={theme.dark ? 'light-content' : 'dark-content'} />
|
<StatusBar barStyle={theme.dark ? 'light-content' : 'dark-content'} />
|
||||||
)}
|
)}
|
||||||
<NavigationContainer
|
<NavigationContainer
|
||||||
ref={containerRef}
|
|
||||||
initialState={initialState}
|
initialState={initialState}
|
||||||
onStateChange={(state) =>
|
onStateChange={(state) =>
|
||||||
AsyncStorage.setItem(
|
AsyncStorage.setItem(
|
||||||
@@ -236,6 +209,50 @@ export default function App() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
linking={{
|
||||||
|
// 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`)
|
||||||
|
prefixes: LinkingPrefixes,
|
||||||
|
config: {
|
||||||
|
Root: {
|
||||||
|
path: '',
|
||||||
|
initialRouteName: 'Home',
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
{ Home: '' }
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Article: {
|
||||||
|
path: 'article/:author?',
|
||||||
|
parse: {
|
||||||
|
author: (author) =>
|
||||||
|
author.charAt(0).toUpperCase() +
|
||||||
|
author.slice(1).replace(/-/g, ' '),
|
||||||
|
},
|
||||||
|
stringify: {
|
||||||
|
author: (author: string) =>
|
||||||
|
author.toLowerCase().replace(/\s/g, '-'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Albums: 'music',
|
||||||
|
Chat: 'chat',
|
||||||
|
Contacts: 'people',
|
||||||
|
NewsFeed: 'feed',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
fallback={<Text>Loading…</Text>}
|
||||||
>
|
>
|
||||||
<Drawer.Navigator drawerType={isLargeScreen ? 'permanent' : undefined}>
|
<Drawer.Navigator drawerType={isLargeScreen ? 'permanent' : undefined}>
|
||||||
<Drawer.Screen
|
<Drawer.Screen
|
||||||
@@ -285,12 +302,7 @@ export default function App() {
|
|||||||
value={I18nManager.isRTL}
|
value={I18nManager.isRTL}
|
||||||
onValueChange={() => {
|
onValueChange={() => {
|
||||||
I18nManager.forceRTL(!I18nManager.isRTL);
|
I18nManager.forceRTL(!I18nManager.isRTL);
|
||||||
// @ts-ignore
|
restartApp();
|
||||||
if (global.Expo) {
|
|
||||||
Updates.reloadFromCache();
|
|
||||||
} else {
|
|
||||||
RNRestart.Restart();
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Divider />
|
<Divider />
|
||||||
@@ -311,6 +323,7 @@ export default function App() {
|
|||||||
(name) => (
|
(name) => (
|
||||||
<List.Item
|
<List.Item
|
||||||
key={name}
|
key={name}
|
||||||
|
testID={name}
|
||||||
title={SCREENS[name].title}
|
title={SCREENS[name].title}
|
||||||
onPress={() => navigation.navigate(name)}
|
onPress={() => navigation.navigate(name)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
16
example/types/react-native-web.d.ts
vendored
Normal file
16
example/types/react-native-web.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
declare module 'react-native-web' {
|
||||||
|
export const AppRegistry: {
|
||||||
|
registerComponent(
|
||||||
|
name: string,
|
||||||
|
callback: () => React.ComponentType<any>
|
||||||
|
): void;
|
||||||
|
|
||||||
|
getApplication(
|
||||||
|
name: string,
|
||||||
|
options?: { initialProps: object }
|
||||||
|
): {
|
||||||
|
element: React.ReactElement;
|
||||||
|
getStyleElement(): React.ReactElement;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
1
example/web/_redirects
Normal file
1
example/web/_redirects
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/* /index.html 200
|
||||||
@@ -24,19 +24,21 @@ module.exports = async function (env, argv) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Object.assign(config.resolve.alias, {
|
Object.assign(config.resolve.alias, {
|
||||||
react: path.resolve(node_modules, 'react'),
|
'react': path.resolve(node_modules, 'react'),
|
||||||
'react-native': path.resolve(node_modules, 'react-native-web'),
|
'react-native': path.resolve(node_modules, 'react-native-web'),
|
||||||
'react-native-web': path.resolve(node_modules, 'react-native-web'),
|
'react-native-web': path.resolve(node_modules, 'react-native-web'),
|
||||||
'@expo/vector-icons': path.resolve(node_modules, '@expo/vector-icons'),
|
'@expo/vector-icons': path.resolve(node_modules, '@expo/vector-icons'),
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.readdirSync(packages).forEach((name) => {
|
fs.readdirSync(packages)
|
||||||
config.resolve.alias[`@react-navigation/${name}`] = path.resolve(
|
.filter((name) => !name.startsWith('.'))
|
||||||
packages,
|
.forEach((name) => {
|
||||||
name,
|
config.resolve.alias[`@react-navigation/${name}`] = path.resolve(
|
||||||
'src'
|
packages,
|
||||||
);
|
name,
|
||||||
});
|
require(`../packages/${name}/package.json`).source
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
|
|||||||
18
package.json
18
package.json
@@ -13,7 +13,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/satya164/react-navigation.git"
|
"url": "git+https://github.com/react-navigation/react-navigation.git"
|
||||||
},
|
},
|
||||||
"author": "Satyajit Sahoo <satyajit.happy@gmail.com> (https://github.com/satya164/), Michał Osadnik <micosa97@gmail.com> (https://github.com/osdnk/)",
|
"author": "Satyajit Sahoo <satyajit.happy@gmail.com> (https://github.com/satya164/), Michał Osadnik <micosa97@gmail.com> (https://github.com/osdnk/)",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -27,23 +27,23 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
||||||
"@babel/plugin-proposal-optional-chaining": "^7.9.0",
|
"@babel/plugin-proposal-optional-chaining": "^7.9.0",
|
||||||
"@babel/preset-env": "^7.9.0",
|
"@babel/preset-env": "^7.9.6",
|
||||||
"@babel/preset-flow": "^7.9.0",
|
"@babel/preset-flow": "^7.9.0",
|
||||||
"@babel/preset-react": "^7.9.4",
|
"@babel/preset-react": "^7.9.4",
|
||||||
"@babel/preset-typescript": "^7.9.0",
|
"@babel/preset-typescript": "^7.9.0",
|
||||||
"@babel/runtime": "^7.9.2",
|
"@babel/runtime": "^7.9.6",
|
||||||
"@commitlint/config-conventional": "^8.3.4",
|
"@commitlint/config-conventional": "^8.3.4",
|
||||||
"@types/jest": "^25.2.1",
|
"@types/jest": "^25.2.1",
|
||||||
"babel-jest": "^25.2.6",
|
"babel-jest": "^26.0.1",
|
||||||
"codecov": "^3.6.5",
|
"codecov": "^3.6.5",
|
||||||
"commitlint": "^8.3.5",
|
"commitlint": "^8.3.5",
|
||||||
"core-js": "^3.6.4",
|
"core-js": "^3.6.5",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"eslint-config-satya164": "^3.1.6",
|
"eslint-config-satya164": "^3.1.7",
|
||||||
"husky": "^4.2.3",
|
"husky": "^4.2.5",
|
||||||
"jest": "^25.2.7",
|
"jest": "^26.0.1",
|
||||||
"lerna": "^3.20.2",
|
"lerna": "^3.20.2",
|
||||||
"prettier": "^2.0.4",
|
"prettier": "^2.0.5",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.8.3"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
|
|||||||
@@ -3,6 +3,151 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [5.5.1](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.5.0...@react-navigation/bottom-tabs@5.5.1) (2020-05-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix type of style for various options ([9d822b9](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/9d822b95a6df797e2e63e481573e64ea7d0f9386))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.5.0](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.4.7...@react-navigation/bottom-tabs@5.5.0) (2020-05-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* animate changes to tabBarVisible in BottomTabBar ([#8286](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/issues/8286)) ([c1e46f8](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/c1e46f8e331e0054995aa476455af204d02d4170))
|
||||||
|
* update react-native-safe-area-context to 1.0.0 ([#8182](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/issues/8182)) ([d62fbfe](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/d62fbfe255140f16b182e8b54b276a7c96f2aec6))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.4.7](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.4.6...@react-navigation/bottom-tabs@5.4.7) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.4.6](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.4.5...@react-navigation/bottom-tabs@5.4.6) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.4.5](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.4.4...@react-navigation/bottom-tabs@5.4.5) (2020-05-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.4.4](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.4.3...@react-navigation/bottom-tabs@5.4.4) (2020-05-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.4.3](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.4.2...@react-navigation/bottom-tabs@5.4.3) (2020-05-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.4.2](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.4.1...@react-navigation/bottom-tabs@5.4.2) (2020-05-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.4.1](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.4.0...@react-navigation/bottom-tabs@5.4.1) (2020-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix building typescript definitions. closes [#8216](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/issues/8216) ([47a1229](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/47a12298378747edd2d22e54dc1c8677f98c49b4))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.4.0](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.3.4...@react-navigation/bottom-tabs@5.4.0) (2020-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add generic type aliases for screen props ([bea14aa](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/bea14aa26fd5cbfebc7973733c5cf1f44fd323aa)), closes [#7971](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/issues/7971)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.4](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.3.3...@react-navigation/bottom-tabs@5.3.4) (2020-05-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.3](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.3.2...@react-navigation/bottom-tabs@5.3.3) (2020-05-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.2](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.3.1...@react-navigation/bottom-tabs@5.3.2) (2020-05-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.1](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.3.0...@react-navigation/bottom-tabs@5.3.1) (2020-04-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.3.0](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.8...@react-navigation/bottom-tabs@5.3.0) (2020-04-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add `useLinkBuilder` hook to build links ([2792f43](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/2792f438fe45428fe193e3708fee7ad61966cbf4))
|
||||||
|
* add action prop to Link ([942d2be](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/942d2be2c72720469475ce12ec8df23825994dbf))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.8](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.7...@react-navigation/bottom-tabs@5.2.8) (2020-04-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.2.7](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.6...@react-navigation/bottom-tabs@5.2.7) (2020-04-17)
|
## [5.2.7](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.6...@react-navigation/bottom-tabs@5.2.7) (2020-04-17)
|
||||||
|
|
||||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/bottom-tabs",
|
"name": "@react-navigation/bottom-tabs",
|
||||||
"description": "Bottom tab navigator following iOS design guidelines",
|
"description": "Bottom tab navigator following iOS design guidelines",
|
||||||
"version": "5.2.7",
|
"version": "5.5.1",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -15,11 +15,13 @@
|
|||||||
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs",
|
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs",
|
||||||
"main": "lib/commonjs/index.js",
|
"main": "lib/commonjs/index.js",
|
||||||
"react-native": "src/index.tsx",
|
"react-native": "src/index.tsx",
|
||||||
|
"source": "src/index.tsx",
|
||||||
"module": "lib/module/index.js",
|
"module": "lib/module/index.js",
|
||||||
"types": "lib/typescript/src/index.d.ts",
|
"types": "lib/typescript/src/index.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"src",
|
||||||
"lib"
|
"lib",
|
||||||
|
"!**/__tests__"
|
||||||
],
|
],
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
@@ -34,16 +36,16 @@
|
|||||||
"react-native-iphone-x-helper": "^1.2.1"
|
"react-native-iphone-x-helper": "^1.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.10.0",
|
"@react-native-community/bob": "^0.14.3",
|
||||||
"@react-navigation/native": "^5.1.6",
|
"@react-navigation/native": "^5.5.0",
|
||||||
"@types/color": "^3.0.1",
|
"@types/color": "^3.0.1",
|
||||||
"@types/react": "^16.9.23",
|
"@types/react": "^16.9.34",
|
||||||
"@types/react-native": "^0.61.22",
|
"@types/react-native": "^0.62.7",
|
||||||
"del-cli": "^3.0.0",
|
"del-cli": "^3.0.0",
|
||||||
"react": "~16.9.0",
|
"react": "~16.9.0",
|
||||||
"react-native": "~0.61.5",
|
"react-native": "~0.61.5",
|
||||||
"react-native-safe-area-context": "^0.7.3",
|
"react-native-safe-area-context": "^1.0.0",
|
||||||
"react-native-screens": "^2.3.0",
|
"react-native-screens": "^2.7.0",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.8.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export { default as BottomTabBar } from './views/BottomTabBar';
|
|||||||
export type {
|
export type {
|
||||||
BottomTabNavigationOptions,
|
BottomTabNavigationOptions,
|
||||||
BottomTabNavigationProp,
|
BottomTabNavigationProp,
|
||||||
|
BottomTabScreenProps,
|
||||||
BottomTabBarProps,
|
BottomTabBarProps,
|
||||||
BottomTabBarOptions,
|
BottomTabBarOptions,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {
|
||||||
|
Animated,
|
||||||
TouchableWithoutFeedbackProps,
|
TouchableWithoutFeedbackProps,
|
||||||
StyleProp,
|
StyleProp,
|
||||||
TextStyle,
|
TextStyle,
|
||||||
ViewStyle,
|
ViewStyle,
|
||||||
|
GestureResponderEvent,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import {
|
import {
|
||||||
NavigationHelpers,
|
NavigationHelpers,
|
||||||
@@ -12,6 +14,7 @@ import {
|
|||||||
Descriptor,
|
Descriptor,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
TabActionHelpers,
|
TabActionHelpers,
|
||||||
|
RouteProp,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
export type BottomTabNavigationEventMap = {
|
export type BottomTabNavigationEventMap = {
|
||||||
@@ -44,6 +47,14 @@ export type BottomTabNavigationProp<
|
|||||||
> &
|
> &
|
||||||
TabActionHelpers<ParamList>;
|
TabActionHelpers<ParamList>;
|
||||||
|
|
||||||
|
export type BottomTabScreenProps<
|
||||||
|
ParamList extends ParamListBase,
|
||||||
|
RouteName extends keyof ParamList = string
|
||||||
|
> = {
|
||||||
|
navigation: BottomTabNavigationProp<ParamList, RouteName>;
|
||||||
|
route: RouteProp<ParamList, RouteName>;
|
||||||
|
};
|
||||||
|
|
||||||
export type BottomTabNavigationOptions = {
|
export type BottomTabNavigationOptions = {
|
||||||
/**
|
/**
|
||||||
* Title text for the screen.
|
* Title text for the screen.
|
||||||
@@ -187,7 +198,7 @@ export type BottomTabBarOptions = {
|
|||||||
/**
|
/**
|
||||||
* Style object for the tab bar container.
|
* Style object for the tab bar container.
|
||||||
*/
|
*/
|
||||||
style?: StyleProp<ViewStyle>;
|
style?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BottomTabBarProps = BottomTabBarOptions & {
|
export type BottomTabBarProps = BottomTabBarOptions & {
|
||||||
@@ -196,6 +207,13 @@ export type BottomTabBarProps = BottomTabBarOptions & {
|
|||||||
navigation: NavigationHelpers<ParamListBase, BottomTabNavigationEventMap>;
|
navigation: NavigationHelpers<ParamListBase, BottomTabNavigationEventMap>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BottomTabBarButtonProps = TouchableWithoutFeedbackProps & {
|
export type BottomTabBarButtonProps = Omit<
|
||||||
|
TouchableWithoutFeedbackProps,
|
||||||
|
'onPress'
|
||||||
|
> & {
|
||||||
|
to?: string;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
onPress?: (
|
||||||
|
e: React.MouseEvent<HTMLAnchorElement, MouseEvent> | GestureResponderEvent
|
||||||
|
) => void;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
NavigationRouteContext,
|
NavigationRouteContext,
|
||||||
CommonActions,
|
CommonActions,
|
||||||
useTheme,
|
useTheme,
|
||||||
|
useLinkBuilder,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
import { useSafeArea } from 'react-native-safe-area-context';
|
import { useSafeArea } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
@@ -50,6 +51,45 @@ export default function BottomTabBar({
|
|||||||
tabStyle,
|
tabStyle,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
const buildLink = useLinkBuilder();
|
||||||
|
|
||||||
|
const focusedRoute = state.routes[state.index];
|
||||||
|
const focusedDescriptor = descriptors[focusedRoute.key];
|
||||||
|
const focusedOptions = focusedDescriptor.options;
|
||||||
|
|
||||||
|
const [isKeyboardShown, setIsKeyboardShown] = React.useState(false);
|
||||||
|
|
||||||
|
const shouldShowTabBar =
|
||||||
|
focusedOptions.tabBarVisible !== false &&
|
||||||
|
!(keyboardHidesTabBar && isKeyboardShown);
|
||||||
|
|
||||||
|
const [isTabBarHidden, setIsTabBarHidden] = React.useState(!shouldShowTabBar);
|
||||||
|
|
||||||
|
const [visible] = React.useState(
|
||||||
|
() => new Animated.Value(shouldShowTabBar ? 1 : 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (shouldShowTabBar) {
|
||||||
|
Animated.timing(visible, {
|
||||||
|
toValue: 1,
|
||||||
|
duration: 250,
|
||||||
|
useNativeDriver,
|
||||||
|
}).start(({ finished }) => {
|
||||||
|
if (finished) {
|
||||||
|
setIsTabBarHidden(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setIsTabBarHidden(true);
|
||||||
|
|
||||||
|
Animated.timing(visible, {
|
||||||
|
toValue: 0,
|
||||||
|
duration: 200,
|
||||||
|
useNativeDriver,
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
}, [shouldShowTabBar, visible]);
|
||||||
|
|
||||||
const [dimensions, setDimensions] = React.useState(() => {
|
const [dimensions, setDimensions] = React.useState(() => {
|
||||||
const { height = 0, width = 0 } = Dimensions.get('window');
|
const { height = 0, width = 0 } = Dimensions.get('window');
|
||||||
@@ -57,46 +97,16 @@ export default function BottomTabBar({
|
|||||||
return { height, width };
|
return { height, width };
|
||||||
});
|
});
|
||||||
|
|
||||||
const [layout, setLayout] = React.useState({
|
|
||||||
height: 0,
|
|
||||||
width: dimensions.width,
|
|
||||||
});
|
|
||||||
const [keyboardShown, setKeyboardShown] = React.useState(false);
|
|
||||||
|
|
||||||
const [visible] = React.useState(() => new Animated.Value(1));
|
|
||||||
|
|
||||||
const { routes } = state;
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (keyboardShown) {
|
|
||||||
Animated.timing(visible, {
|
|
||||||
toValue: 0,
|
|
||||||
duration: 200,
|
|
||||||
useNativeDriver,
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
}, [keyboardShown, visible]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const handleOrientationChange = ({ window }: { window: ScaledSize }) => {
|
const handleOrientationChange = ({ window }: { window: ScaledSize }) => {
|
||||||
setDimensions(window);
|
setDimensions(window);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyboardShow = () => setKeyboardShown(true);
|
|
||||||
|
|
||||||
const handleKeyboardHide = () =>
|
|
||||||
Animated.timing(visible, {
|
|
||||||
toValue: 1,
|
|
||||||
duration: 250,
|
|
||||||
useNativeDriver,
|
|
||||||
}).start(({ finished }) => {
|
|
||||||
if (finished) {
|
|
||||||
setKeyboardShown(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Dimensions.addEventListener('change', handleOrientationChange);
|
Dimensions.addEventListener('change', handleOrientationChange);
|
||||||
|
|
||||||
|
const handleKeyboardShow = () => setIsKeyboardShown(true);
|
||||||
|
const handleKeyboardHide = () => setIsKeyboardShown(false);
|
||||||
|
|
||||||
if (Platform.OS === 'ios') {
|
if (Platform.OS === 'ios') {
|
||||||
Keyboard.addListener('keyboardWillShow', handleKeyboardShow);
|
Keyboard.addListener('keyboardWillShow', handleKeyboardShow);
|
||||||
Keyboard.addListener('keyboardWillHide', handleKeyboardHide);
|
Keyboard.addListener('keyboardWillHide', handleKeyboardHide);
|
||||||
@@ -116,7 +126,12 @@ export default function BottomTabBar({
|
|||||||
Keyboard.removeListener('keyboardDidHide', handleKeyboardHide);
|
Keyboard.removeListener('keyboardDidHide', handleKeyboardHide);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [visible]);
|
}, []);
|
||||||
|
|
||||||
|
const [layout, setLayout] = React.useState({
|
||||||
|
height: 0,
|
||||||
|
width: dimensions.width,
|
||||||
|
});
|
||||||
|
|
||||||
const handleLayout = (e: LayoutChangeEvent) => {
|
const handleLayout = (e: LayoutChangeEvent) => {
|
||||||
const { height, width } = e.nativeEvent.layout;
|
const { height, width } = e.nativeEvent.layout;
|
||||||
@@ -133,6 +148,7 @@ export default function BottomTabBar({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { routes } = state;
|
||||||
const shouldUseHorizontalLabels = () => {
|
const shouldUseHorizontalLabels = () => {
|
||||||
if (labelPosition) {
|
if (labelPosition) {
|
||||||
return labelPosition === 'beside-icon';
|
return labelPosition === 'beside-icon';
|
||||||
@@ -181,22 +197,19 @@ export default function BottomTabBar({
|
|||||||
backgroundColor: colors.card,
|
backgroundColor: colors.card,
|
||||||
borderTopColor: colors.border,
|
borderTopColor: colors.border,
|
||||||
},
|
},
|
||||||
keyboardHidesTabBar
|
{
|
||||||
? {
|
transform: [
|
||||||
// When the keyboard is shown, slide down the tab bar
|
{
|
||||||
transform: [
|
translateY: visible.interpolate({
|
||||||
{
|
inputRange: [0, 1],
|
||||||
translateY: visible.interpolate({
|
outputRange: [layout.height + insets.bottom, 0],
|
||||||
inputRange: [0, 1],
|
}),
|
||||||
outputRange: [layout.height, 0],
|
},
|
||||||
}),
|
],
|
||||||
},
|
// Absolutely position the tab bar so that the content is below it
|
||||||
],
|
// This is needed to avoid gap at bottom when the tab bar is hidden
|
||||||
// Absolutely position the tab bar so that the content is below it
|
position: isTabBarHidden ? 'absolute' : null,
|
||||||
// This is needed to avoid gap at bottom when the tab bar is hidden
|
},
|
||||||
position: keyboardShown ? 'absolute' : null,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
{
|
{
|
||||||
height: DEFAULT_TABBAR_HEIGHT + insets.bottom,
|
height: DEFAULT_TABBAR_HEIGHT + insets.bottom,
|
||||||
paddingBottom: insets.bottom,
|
paddingBottom: insets.bottom,
|
||||||
@@ -204,7 +217,7 @@ export default function BottomTabBar({
|
|||||||
},
|
},
|
||||||
style,
|
style,
|
||||||
]}
|
]}
|
||||||
pointerEvents={keyboardHidesTabBar && keyboardShown ? 'none' : 'auto'}
|
pointerEvents={isTabBarHidden ? 'none' : 'auto'}
|
||||||
>
|
>
|
||||||
<View style={styles.content} onLayout={handleLayout}>
|
<View style={styles.content} onLayout={handleLayout}>
|
||||||
{routes.map((route, index) => {
|
{routes.map((route, index) => {
|
||||||
@@ -260,6 +273,7 @@ export default function BottomTabBar({
|
|||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
onLongPress={onLongPress}
|
onLongPress={onLongPress}
|
||||||
accessibilityLabel={accessibilityLabel}
|
accessibilityLabel={accessibilityLabel}
|
||||||
|
to={buildLink(route.name, route.params)}
|
||||||
testID={options.tabBarTestID}
|
testID={options.tabBarTestID}
|
||||||
allowFontScaling={allowFontScaling}
|
allowFontScaling={allowFontScaling}
|
||||||
activeTintColor={activeTintColor}
|
activeTintColor={activeTintColor}
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
|
Text,
|
||||||
TouchableWithoutFeedback,
|
TouchableWithoutFeedback,
|
||||||
Animated,
|
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
|
Platform,
|
||||||
StyleProp,
|
StyleProp,
|
||||||
ViewStyle,
|
ViewStyle,
|
||||||
TextStyle,
|
TextStyle,
|
||||||
|
GestureResponderEvent,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { Route, useTheme } from '@react-navigation/native';
|
import { Link, Route, useTheme } from '@react-navigation/native';
|
||||||
import Color from 'color';
|
import Color from 'color';
|
||||||
|
|
||||||
import TabBarIcon from './TabBarIcon';
|
import TabBarIcon from './TabBarIcon';
|
||||||
@@ -37,6 +39,10 @@ type Props = {
|
|||||||
size: number;
|
size: number;
|
||||||
color: string;
|
color: string;
|
||||||
}) => React.ReactNode;
|
}) => React.ReactNode;
|
||||||
|
/**
|
||||||
|
* URL to use for the link to the tab.
|
||||||
|
*/
|
||||||
|
to?: string;
|
||||||
/**
|
/**
|
||||||
* The button for the tab. Uses a `TouchableWithoutFeedback` by default.
|
* The button for the tab. Uses a `TouchableWithoutFeedback` by default.
|
||||||
*/
|
*/
|
||||||
@@ -50,13 +56,16 @@ type Props = {
|
|||||||
*/
|
*/
|
||||||
testID?: string;
|
testID?: string;
|
||||||
/**
|
/**
|
||||||
* Function to execute on press.
|
* Function to execute on press in React Native.
|
||||||
|
* On the web, this will use onClick.
|
||||||
*/
|
*/
|
||||||
onPress: () => void;
|
onPress: (
|
||||||
|
e: React.MouseEvent<HTMLElement, MouseEvent> | GestureResponderEvent
|
||||||
|
) => void;
|
||||||
/**
|
/**
|
||||||
* Function to execute on long press.
|
* Function to execute on long press.
|
||||||
*/
|
*/
|
||||||
onLongPress: () => void;
|
onLongPress: (e: GestureResponderEvent) => void;
|
||||||
/**
|
/**
|
||||||
* Whether the label should be aligned with the icon horizontally.
|
* Whether the label should be aligned with the icon horizontally.
|
||||||
*/
|
*/
|
||||||
@@ -104,11 +113,48 @@ export default function BottomTabBarItem({
|
|||||||
route,
|
route,
|
||||||
label,
|
label,
|
||||||
icon,
|
icon,
|
||||||
button = ({ children, style, ...rest }: BottomTabBarButtonProps) => (
|
to,
|
||||||
<TouchableWithoutFeedback {...rest}>
|
button = ({
|
||||||
<View style={style}>{children}</View>
|
children,
|
||||||
</TouchableWithoutFeedback>
|
style,
|
||||||
),
|
onPress,
|
||||||
|
to,
|
||||||
|
accessibilityRole,
|
||||||
|
...rest
|
||||||
|
}: BottomTabBarButtonProps) => {
|
||||||
|
if (Platform.OS === 'web' && to) {
|
||||||
|
// React Native Web doesn't forward `onClick` if we use `TouchableWithoutFeedback`.
|
||||||
|
// We need to use `onClick` to be able to prevent default browser handling of links.
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
{...rest}
|
||||||
|
to={to}
|
||||||
|
style={[styles.button, style]}
|
||||||
|
onPress={(e: any) => {
|
||||||
|
if (
|
||||||
|
!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) && // ignore clicks with modifier keys
|
||||||
|
(e.button == null || e.button === 0) // ignore everything but left clicks
|
||||||
|
) {
|
||||||
|
e.preventDefault();
|
||||||
|
onPress?.(e);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<TouchableWithoutFeedback
|
||||||
|
{...rest}
|
||||||
|
accessibilityRole={accessibilityRole}
|
||||||
|
onPress={onPress}
|
||||||
|
>
|
||||||
|
<View style={style}>{children}</View>
|
||||||
|
</TouchableWithoutFeedback>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
accessibilityLabel,
|
accessibilityLabel,
|
||||||
testID,
|
testID,
|
||||||
onPress,
|
onPress,
|
||||||
@@ -145,7 +191,7 @@ export default function BottomTabBarItem({
|
|||||||
|
|
||||||
if (typeof label === 'string') {
|
if (typeof label === 'string') {
|
||||||
return (
|
return (
|
||||||
<Animated.Text
|
<Text
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
style={[
|
style={[
|
||||||
styles.label,
|
styles.label,
|
||||||
@@ -156,14 +202,10 @@ export default function BottomTabBarItem({
|
|||||||
allowFontScaling={allowFontScaling}
|
allowFontScaling={allowFontScaling}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</Animated.Text>
|
</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof label === 'string') {
|
|
||||||
return label;
|
|
||||||
}
|
|
||||||
|
|
||||||
return label({ focused, color });
|
return label({ focused, color });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -196,6 +238,7 @@ export default function BottomTabBarItem({
|
|||||||
: inactiveBackgroundColor;
|
: inactiveBackgroundColor;
|
||||||
|
|
||||||
return button({
|
return button({
|
||||||
|
to,
|
||||||
onPress,
|
onPress,
|
||||||
onLongPress,
|
onLongPress,
|
||||||
testID,
|
testID,
|
||||||
@@ -248,4 +291,7 @@ const styles = StyleSheet.create({
|
|||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
marginLeft: 20,
|
marginLeft: 20,
|
||||||
},
|
},
|
||||||
|
button: {
|
||||||
|
display: 'flex',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { View, StyleSheet } from 'react-native';
|
import { View, StyleSheet } from 'react-native';
|
||||||
|
|
||||||
import { TabNavigationState, useTheme } from '@react-navigation/native';
|
import {
|
||||||
|
NavigationHelpersContext,
|
||||||
|
TabNavigationState,
|
||||||
|
useTheme,
|
||||||
|
} from '@react-navigation/native';
|
||||||
// eslint-disable-next-line import/no-unresolved
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import { ScreenContainer } from 'react-native-screens';
|
import { ScreenContainer } from 'react-native-screens';
|
||||||
|
|
||||||
@@ -71,17 +75,8 @@ export default class BottomTabView extends React.Component<Props, State> {
|
|||||||
tabBarOptions,
|
tabBarOptions,
|
||||||
state,
|
state,
|
||||||
navigation,
|
navigation,
|
||||||
|
descriptors,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { descriptors } = this.props;
|
|
||||||
const route = state.routes[state.index];
|
|
||||||
const descriptor = descriptors[route.key];
|
|
||||||
const options = descriptor.options;
|
|
||||||
|
|
||||||
if (options.tabBarVisible === false) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tabBar({
|
return tabBar({
|
||||||
...tabBarOptions,
|
...tabBarOptions,
|
||||||
state: state,
|
state: state,
|
||||||
@@ -91,44 +86,46 @@ export default class BottomTabView extends React.Component<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { state, descriptors, lazy } = this.props;
|
const { state, descriptors, navigation, lazy } = this.props;
|
||||||
const { routes } = state;
|
const { routes } = state;
|
||||||
const { loaded } = this.state;
|
const { loaded } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaProviderCompat>
|
<NavigationHelpersContext.Provider value={navigation}>
|
||||||
<View style={styles.container}>
|
<SafeAreaProviderCompat>
|
||||||
<ScreenContainer style={styles.pages}>
|
<View style={styles.container}>
|
||||||
{routes.map((route, index) => {
|
<ScreenContainer style={styles.pages}>
|
||||||
const descriptor = descriptors[route.key];
|
{routes.map((route, index) => {
|
||||||
const { unmountOnBlur } = descriptor.options;
|
const descriptor = descriptors[route.key];
|
||||||
const isFocused = state.index === index;
|
const { unmountOnBlur } = descriptor.options;
|
||||||
|
const isFocused = state.index === index;
|
||||||
|
|
||||||
if (unmountOnBlur && !isFocused) {
|
if (unmountOnBlur && !isFocused) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lazy && !loaded.includes(index) && !isFocused) {
|
if (lazy && !loaded.includes(index) && !isFocused) {
|
||||||
// Don't render a screen if we've never navigated to it
|
// Don't render a screen if we've never navigated to it
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResourceSavingScene
|
<ResourceSavingScene
|
||||||
key={route.key}
|
key={route.key}
|
||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
isVisible={isFocused}
|
isVisible={isFocused}
|
||||||
>
|
>
|
||||||
<SceneContent isFocused={isFocused}>
|
<SceneContent isFocused={isFocused}>
|
||||||
{descriptor.render()}
|
{descriptor.render()}
|
||||||
</SceneContent>
|
</SceneContent>
|
||||||
</ResourceSavingScene>
|
</ResourceSavingScene>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ScreenContainer>
|
</ScreenContainer>
|
||||||
{this.renderTabBar()}
|
{this.renderTabBar()}
|
||||||
</View>
|
</View>
|
||||||
</SafeAreaProviderCompat>
|
</SafeAreaProviderCompat>
|
||||||
|
</NavigationHelpersContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,141 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [5.1.25](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.24...@react-navigation/compat@5.1.25) (2020-05-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.24](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.23...@react-navigation/compat@5.1.24) (2020-05-23)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.23](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.22...@react-navigation/compat@5.1.23) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.22](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.21...@react-navigation/compat@5.1.22) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.21](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.20...@react-navigation/compat@5.1.21) (2020-05-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.20](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.19...@react-navigation/compat@5.1.20) (2020-05-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.19](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.18...@react-navigation/compat@5.1.19) (2020-05-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.18](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.17...@react-navigation/compat@5.1.18) (2020-05-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.17](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.16...@react-navigation/compat@5.1.17) (2020-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix building typescript definitions. closes [#8216](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/issues/8216) ([47a1229](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/commit/47a12298378747edd2d22e54dc1c8677f98c49b4))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.16](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.15...@react-navigation/compat@5.1.16) (2020-05-08)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.15](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.14...@react-navigation/compat@5.1.15) (2020-05-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.14](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.13...@react-navigation/compat@5.1.14) (2020-05-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.13](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.12...@react-navigation/compat@5.1.13) (2020-05-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.12](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.11...@react-navigation/compat@5.1.12) (2020-04-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.11](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.10...@react-navigation/compat@5.1.11) (2020-04-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.10](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.9...@react-navigation/compat@5.1.10) (2020-04-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix typo in navigationOptions ([8cbb201](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/commit/8cbb201f1a7fb90e45a078df6bc42ce4771cc6a6))
|
||||||
|
* spread parent params to children in compat navigator ([24febf6](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/commit/24febf6ea99be2e5f22005fdd2a82136d647255c)), closes [#6785](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/issues/6785)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.1.9](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.8...@react-navigation/compat@5.1.9) (2020-04-17)
|
## [5.1.9](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.8...@react-navigation/compat@5.1.9) (2020-04-17)
|
||||||
|
|
||||||
**Note:** Version bump only for package @react-navigation/compat
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/compat",
|
"name": "@react-navigation/compat",
|
||||||
"description": "Compatibility layer to write navigator definitions in static configuration format",
|
"description": "Compatibility layer to write navigator definitions in static configuration format",
|
||||||
"version": "5.1.9",
|
"version": "5.1.25",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/compat",
|
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/compat",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
@@ -10,11 +10,13 @@
|
|||||||
"homepage": "https://reactnavigation.org/docs/compatibility.html",
|
"homepage": "https://reactnavigation.org/docs/compatibility.html",
|
||||||
"main": "lib/commonjs/index.js",
|
"main": "lib/commonjs/index.js",
|
||||||
"react-native": "src/index.tsx",
|
"react-native": "src/index.tsx",
|
||||||
|
"source": "src/index.tsx",
|
||||||
"module": "lib/module/index.js",
|
"module": "lib/module/index.js",
|
||||||
"types": "lib/typescript/src/index.d.ts",
|
"types": "lib/typescript/src/index.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"src",
|
||||||
"lib"
|
"lib",
|
||||||
|
"!**/__tests__"
|
||||||
],
|
],
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
@@ -25,9 +27,9 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.10.0",
|
"@react-native-community/bob": "^0.14.3",
|
||||||
"@react-navigation/native": "^5.1.6",
|
"@react-navigation/native": "^5.5.0",
|
||||||
"@types/react": "^16.9.23",
|
"@types/react": "^16.9.34",
|
||||||
"react": "~16.9.0",
|
"react": "~16.9.0",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.8.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
NavigationProp,
|
NavigationProp,
|
||||||
RouteProp,
|
RouteProp,
|
||||||
EventMapBase,
|
EventMapBase,
|
||||||
|
NavigationRouteContext,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
import CompatScreen from './CompatScreen';
|
import CompatScreen from './CompatScreen';
|
||||||
import ScreenPropsContext from './ScreenPropsContext';
|
import ScreenPropsContext from './ScreenPropsContext';
|
||||||
@@ -67,6 +68,9 @@ export default function createCompatNavigatorFactory<
|
|||||||
const routeNames = order !== undefined ? order : Object.keys(routeConfig);
|
const routeNames = order !== undefined ? order : Object.keys(routeConfig);
|
||||||
|
|
||||||
function Navigator({ screenProps }: { screenProps?: unknown }) {
|
function Navigator({ screenProps }: { screenProps?: unknown }) {
|
||||||
|
const parentRouteParams = React.useContext(NavigationRouteContext)
|
||||||
|
?.params;
|
||||||
|
|
||||||
const screens = React.useMemo(
|
const screens = React.useMemo(
|
||||||
() =>
|
() =>
|
||||||
routeNames.map((name) => {
|
routeNames.map((name) => {
|
||||||
@@ -135,7 +139,7 @@ export default function createCompatNavigatorFactory<
|
|||||||
<Pair.Screen
|
<Pair.Screen
|
||||||
key={name}
|
key={name}
|
||||||
name={name}
|
name={name}
|
||||||
initialParams={initialParams}
|
initialParams={{ ...parentRouteParams, ...initialParams }}
|
||||||
options={screenOptions}
|
options={screenOptions}
|
||||||
>
|
>
|
||||||
{({ navigation, route }) => (
|
{({ navigation, route }) => (
|
||||||
@@ -148,7 +152,7 @@ export default function createCompatNavigatorFactory<
|
|||||||
</Pair.Screen>
|
</Pair.Screen>
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
[screenProps]
|
[parentRouteParams, screenProps]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -163,7 +167,7 @@ export default function createCompatNavigatorFactory<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Navigator.navigationOtions = parentNavigationOptions;
|
Navigator.navigationOptions = parentNavigationOptions;
|
||||||
|
|
||||||
return Navigator;
|
return Navigator;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,151 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [5.9.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.8.2...@react-navigation/core@5.9.0) (2020-05-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add ref to get current options in `ServerContainer` ([#8333](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8333)) ([0b1a718](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/0b1a718756e208d84b20e45ca56004332308ad54))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.8.2](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.8.1...@react-navigation/core@5.8.2) (2020-05-23)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.8.1](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.8.0...@react-navigation/core@5.8.1) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.8.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.7.0...@react-navigation/core@5.8.0) (2020-05-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add getCurrentOptions ([#8277](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8277)) ([d024ec6](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/d024ec6d74dffe481ce6fde732c729e20c1668f4))
|
||||||
|
* add getCurrentRoute ([#8254](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8254)) ([7b25c8e](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/7b25c8eb2e6f96128fd86b92615346ce55bedeca))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.7.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.6.1...@react-navigation/core@5.7.0) (2020-05-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* don't use Object.fromEntries ([51f4d11](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/51f4d11fdf4bd2bb06f8cd4094f051816590e62c))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add a PathConfig type ([60cb3c9](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/60cb3c9ba76d7ef166c9fe8b55f23728975b5b6e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.6.1](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.6.0...@react-navigation/core@5.6.1) (2020-05-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* don't use flat since it's not supported in node ([21b397f](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/21b397f0d6b96ec4875d3172f47533130bb08009))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.6.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.5.2...@react-navigation/core@5.6.0) (2020-05-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* ignore extra slashes in the pattern ([3c47716](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/3c47716826d0dfa69dfa6112141c116723372ea1))
|
||||||
|
* ignore state updates when we're not mounted ([0149e85](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/0149e85a95b90c6a9d487fa753ddbf5d01c03e3d)), closes [#8226](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8226)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* merge path patterns for nested screens ([#8253](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8253)) ([acc9646](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/acc9646426fee53558d686dfbe5fd0e35361d8c0))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.5.2](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.5.1...@react-navigation/core@5.5.2) (2020-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix building typescript definitions. closes [#8216](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8216) ([47a1229](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/47a12298378747edd2d22e54dc1c8677f98c49b4))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.5.1](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.5.0...@react-navigation/core@5.5.1) (2020-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* avoid cleaning up state when a new navigator is mounted. fixes [#8195](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8195) ([f6d0676](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/f6d06768d3c36d1f5beaffcb660f3c259209f2e7))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.5.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.4.0...@react-navigation/core@5.5.0) (2020-05-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add support for optional params to linking ([#8196](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8196)) ([fcd1cc6](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/fcd1cc64c151e4941f3f544a54b5048d853821f6))
|
||||||
|
* support params anywhere in path segement ([#8184](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8184)) ([3999fc2](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/3999fc28365c3a06a17d963c7be7fb7e897f99e0))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.4.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.3.5...@react-navigation/core@5.4.0) (2020-04-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* handle empty paths when parsing ([c3fa83e](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/c3fa83efe0d73db76365f8be3d6a8ca1d1289b71))
|
||||||
|
* parsing url ([bd35b4f](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/bd35b4fc202c3868fb75c3675b62de67557089e1))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add `useLinkBuilder` hook to build links ([2792f43](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/2792f438fe45428fe193e3708fee7ad61966cbf4))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.5](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.3.4...@react-navigation/core@5.3.5) (2020-04-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add config to enable redux devtools integration ([c9c825b](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/c9c825bee61426635a28ee149eeeff3d628171cd))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.3.4](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.3.3...@react-navigation/core@5.3.4) (2020-04-17)
|
## [5.3.4](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.3.3...@react-navigation/core@5.3.4) (2020-04-17)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/core",
|
"name": "@react-navigation/core",
|
||||||
"description": "Core utilities for building navigators",
|
"description": "Core utilities for building navigators",
|
||||||
"version": "5.3.4",
|
"version": "5.9.0",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react",
|
"react",
|
||||||
"react-native",
|
"react-native",
|
||||||
@@ -15,11 +15,13 @@
|
|||||||
"homepage": "https://reactnavigation.org",
|
"homepage": "https://reactnavigation.org",
|
||||||
"main": "lib/commonjs/index.js",
|
"main": "lib/commonjs/index.js",
|
||||||
"react-native": "src/index.tsx",
|
"react-native": "src/index.tsx",
|
||||||
|
"source": "src/index.tsx",
|
||||||
"module": "lib/module/index.js",
|
"module": "lib/module/index.js",
|
||||||
"types": "lib/typescript/src/index.d.ts",
|
"types": "lib/typescript/src/index.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"src",
|
||||||
"lib"
|
"lib",
|
||||||
|
"!**/__tests__"
|
||||||
],
|
],
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
@@ -29,21 +31,21 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-navigation/routers": "^5.4.0",
|
"@react-navigation/routers": "^5.4.7",
|
||||||
"escape-string-regexp": "^2.0.0",
|
"escape-string-regexp": "^4.0.0",
|
||||||
"nanoid": "^3.0.2",
|
"nanoid": "^3.1.5",
|
||||||
"query-string": "^6.12.0",
|
"query-string": "^6.12.1",
|
||||||
"react-is": "^16.13.0",
|
"react-is": "^16.13.0",
|
||||||
"use-subscription": "^1.4.0"
|
"use-subscription": "^1.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.10.0",
|
"@react-native-community/bob": "^0.14.3",
|
||||||
"@types/react": "^16.9.23",
|
"@types/react": "^16.9.34",
|
||||||
"@types/react-is": "^16.7.1",
|
"@types/react-is": "^16.7.1",
|
||||||
"@types/use-subscription": "^1.0.0",
|
"@types/use-subscription": "^1.0.0",
|
||||||
"del-cli": "^3.0.0",
|
"del-cli": "^3.0.0",
|
||||||
"react": "~16.9.0",
|
"react": "~16.9.0",
|
||||||
"react-native-testing-library": "^1.12.0",
|
"react-native-testing-library": "^1.13.2",
|
||||||
"react-test-renderer": "~16.13.1",
|
"react-test-renderer": "~16.13.1",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.8.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,46 +13,22 @@ import { ScheduleUpdateContext } from './useScheduleUpdate';
|
|||||||
import useFocusedListeners from './useFocusedListeners';
|
import useFocusedListeners from './useFocusedListeners';
|
||||||
import useDevTools from './useDevTools';
|
import useDevTools from './useDevTools';
|
||||||
import useStateGetters from './useStateGetters';
|
import useStateGetters from './useStateGetters';
|
||||||
|
import useOptionsGetters from './useOptionsGetters';
|
||||||
import useEventEmitter from './useEventEmitter';
|
import useEventEmitter from './useEventEmitter';
|
||||||
import useSyncState from './useSyncState';
|
import useSyncState from './useSyncState';
|
||||||
import isSerializable from './isSerializable';
|
import isSerializable from './isSerializable';
|
||||||
|
|
||||||
import { NavigationContainerRef, NavigationContainerProps } from './types';
|
import { NavigationContainerRef, NavigationContainerProps } from './types';
|
||||||
|
import NavigationStateContext from './NavigationStateContext';
|
||||||
|
|
||||||
type State = NavigationState | PartialState<NavigationState> | undefined;
|
type State = NavigationState | PartialState<NavigationState> | undefined;
|
||||||
|
|
||||||
const MISSING_CONTEXT_ERROR =
|
const DEVTOOLS_CONFIG_KEY =
|
||||||
"Couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'? See https://reactnavigation.org/docs/getting-started for setup instructions.";
|
'REACT_NAVIGATION_REDUX_DEVTOOLS_EXTENSION_INTEGRATION_ENABLED';
|
||||||
|
|
||||||
const NOT_INITIALIZED_ERROR =
|
const NOT_INITIALIZED_ERROR =
|
||||||
"The 'navigation' object hasn't been initialized yet. This might happen if you don't have a navigator mounted, or if the navigator hasn't finished mounting. See https://reactnavigation.org/docs/navigating-without-navigation-prop#handling-initialization for more details.";
|
"The 'navigation' object hasn't been initialized yet. This might happen if you don't have a navigator mounted, or if the navigator hasn't finished mounting. See https://reactnavigation.org/docs/navigating-without-navigation-prop#handling-initialization for more details.";
|
||||||
|
|
||||||
export const NavigationStateContext = React.createContext<{
|
|
||||||
isDefault?: true;
|
|
||||||
state?: NavigationState | PartialState<NavigationState>;
|
|
||||||
getKey: () => string | undefined;
|
|
||||||
setKey: (key: string) => void;
|
|
||||||
getState: () => NavigationState | PartialState<NavigationState> | undefined;
|
|
||||||
setState: (
|
|
||||||
state: NavigationState | PartialState<NavigationState> | undefined
|
|
||||||
) => void;
|
|
||||||
}>({
|
|
||||||
isDefault: true,
|
|
||||||
|
|
||||||
get getKey(): any {
|
|
||||||
throw new Error(MISSING_CONTEXT_ERROR);
|
|
||||||
},
|
|
||||||
get setKey(): any {
|
|
||||||
throw new Error(MISSING_CONTEXT_ERROR);
|
|
||||||
},
|
|
||||||
get getState(): any {
|
|
||||||
throw new Error(MISSING_CONTEXT_ERROR);
|
|
||||||
},
|
|
||||||
get setState(): any {
|
|
||||||
throw new Error(MISSING_CONTEXT_ERROR);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
let hasWarnedForSerialization = false;
|
let hasWarnedForSerialization = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -143,7 +119,9 @@ const BaseNavigationContainer = React.forwardRef(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { trackState, trackAction } = useDevTools({
|
const { trackState, trackAction } = useDevTools({
|
||||||
enabled: false,
|
enabled:
|
||||||
|
// @ts-ignore
|
||||||
|
DEVTOOLS_CONFIG_KEY in global ? global[DEVTOOLS_CONFIG_KEY] : false,
|
||||||
name: '@react-navigation',
|
name: '@react-navigation',
|
||||||
reset,
|
reset,
|
||||||
state,
|
state,
|
||||||
@@ -194,8 +172,21 @@ const BaseNavigationContainer = React.forwardRef(
|
|||||||
return getStateForRoute('root');
|
return getStateForRoute('root');
|
||||||
}, [getStateForRoute]);
|
}, [getStateForRoute]);
|
||||||
|
|
||||||
|
const getCurrentRoute = React.useCallback(() => {
|
||||||
|
let state = getRootState();
|
||||||
|
if (state === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
while (state.routes[state.index].state !== undefined) {
|
||||||
|
state = state.routes[state.index].state as NavigationState;
|
||||||
|
}
|
||||||
|
return state.routes[state.index];
|
||||||
|
}, [getRootState]);
|
||||||
|
|
||||||
const emitter = useEventEmitter();
|
const emitter = useEventEmitter();
|
||||||
|
|
||||||
|
const { addOptionsGetter, getCurrentOptions } = useOptionsGetters({});
|
||||||
|
|
||||||
React.useImperativeHandle(ref, () => ({
|
React.useImperativeHandle(ref, () => ({
|
||||||
...(Object.keys(CommonActions) as (keyof typeof CommonActions)[]).reduce<
|
...(Object.keys(CommonActions) as (keyof typeof CommonActions)[]).reduce<
|
||||||
any
|
any
|
||||||
@@ -214,6 +205,10 @@ const BaseNavigationContainer = React.forwardRef(
|
|||||||
dispatch,
|
dispatch,
|
||||||
canGoBack,
|
canGoBack,
|
||||||
getRootState,
|
getRootState,
|
||||||
|
dangerouslyGetState: () => state,
|
||||||
|
dangerouslyGetParent: () => undefined,
|
||||||
|
getCurrentRoute,
|
||||||
|
getCurrentOptions,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const builderContext = React.useMemo(
|
const builderContext = React.useMemo(
|
||||||
@@ -237,8 +232,9 @@ const BaseNavigationContainer = React.forwardRef(
|
|||||||
setState,
|
setState,
|
||||||
getKey,
|
getKey,
|
||||||
setKey,
|
setKey,
|
||||||
|
addOptionsGetter,
|
||||||
}),
|
}),
|
||||||
[getKey, getState, setKey, setState, state]
|
[getKey, getState, setKey, setState, state, addOptionsGetter]
|
||||||
);
|
);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|||||||
11
packages/core/src/CurrentRenderContext.tsx
Normal file
11
packages/core/src/CurrentRenderContext.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context which holds the values for the current navigation tree.
|
||||||
|
* Intended for use in SSR. This is not safe to use on the client.
|
||||||
|
*/
|
||||||
|
const CurrentRenderContext = React.createContext<
|
||||||
|
{ options?: object } | undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
|
export default CurrentRenderContext;
|
||||||
13
packages/core/src/NavigationHelpersContext.tsx
Normal file
13
packages/core/src/NavigationHelpersContext.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { ParamListBase } from '@react-navigation/routers';
|
||||||
|
import { NavigationHelpers } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context which holds the navigation helpers of the parent navigator.
|
||||||
|
* Navigators should use this context in their view component.
|
||||||
|
*/
|
||||||
|
const NavigationHelpersContext = React.createContext<
|
||||||
|
NavigationHelpers<ParamListBase> | undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
|
export default NavigationHelpersContext;
|
||||||
35
packages/core/src/NavigationStateContext.tsx
Normal file
35
packages/core/src/NavigationStateContext.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { NavigationState, PartialState } from '@react-navigation/routers';
|
||||||
|
|
||||||
|
const MISSING_CONTEXT_ERROR =
|
||||||
|
"Couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'? See https://reactnavigation.org/docs/getting-started for setup instructions.";
|
||||||
|
|
||||||
|
export default React.createContext<{
|
||||||
|
isDefault?: true;
|
||||||
|
state?: NavigationState | PartialState<NavigationState>;
|
||||||
|
getKey: () => string | undefined;
|
||||||
|
setKey: (key: string) => void;
|
||||||
|
getState: () => NavigationState | PartialState<NavigationState> | undefined;
|
||||||
|
setState: (
|
||||||
|
state: NavigationState | PartialState<NavigationState> | undefined
|
||||||
|
) => void;
|
||||||
|
addOptionsGetter?: (
|
||||||
|
key: string,
|
||||||
|
getter: () => object | undefined | null
|
||||||
|
) => void;
|
||||||
|
}>({
|
||||||
|
isDefault: true,
|
||||||
|
|
||||||
|
get getKey(): any {
|
||||||
|
throw new Error(MISSING_CONTEXT_ERROR);
|
||||||
|
},
|
||||||
|
get setKey(): any {
|
||||||
|
throw new Error(MISSING_CONTEXT_ERROR);
|
||||||
|
},
|
||||||
|
get getState(): any {
|
||||||
|
throw new Error(MISSING_CONTEXT_ERROR);
|
||||||
|
},
|
||||||
|
get setState(): any {
|
||||||
|
throw new Error(MISSING_CONTEXT_ERROR);
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -5,12 +5,13 @@ import {
|
|||||||
NavigationState,
|
NavigationState,
|
||||||
PartialState,
|
PartialState,
|
||||||
} from '@react-navigation/routers';
|
} from '@react-navigation/routers';
|
||||||
import { NavigationStateContext } from './BaseNavigationContainer';
|
import NavigationStateContext from './NavigationStateContext';
|
||||||
import NavigationContext from './NavigationContext';
|
import NavigationContext from './NavigationContext';
|
||||||
import NavigationRouteContext from './NavigationRouteContext';
|
import NavigationRouteContext from './NavigationRouteContext';
|
||||||
import StaticContainer from './StaticContainer';
|
import StaticContainer from './StaticContainer';
|
||||||
import EnsureSingleNavigator from './EnsureSingleNavigator';
|
import EnsureSingleNavigator from './EnsureSingleNavigator';
|
||||||
import { NavigationProp, RouteConfig, EventMapBase } from './types';
|
import { NavigationProp, RouteConfig, EventMapBase } from './types';
|
||||||
|
import useOptionsGetters from './useOptionsGetters';
|
||||||
|
|
||||||
type Props<
|
type Props<
|
||||||
State extends NavigationState,
|
State extends NavigationState,
|
||||||
@@ -24,6 +25,7 @@ type Props<
|
|||||||
};
|
};
|
||||||
getState: () => State;
|
getState: () => State;
|
||||||
setState: (state: State) => void;
|
setState: (state: State) => void;
|
||||||
|
options: object;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,11 +42,24 @@ export default function SceneView<
|
|||||||
navigation,
|
navigation,
|
||||||
getState,
|
getState,
|
||||||
setState,
|
setState,
|
||||||
|
options,
|
||||||
}: Props<State, ScreenOptions, EventMap>) {
|
}: Props<State, ScreenOptions, EventMap>) {
|
||||||
const navigatorKeyRef = React.useRef<string | undefined>();
|
const navigatorKeyRef = React.useRef<string | undefined>();
|
||||||
|
|
||||||
const getKey = React.useCallback(() => navigatorKeyRef.current, []);
|
const getKey = React.useCallback(() => navigatorKeyRef.current, []);
|
||||||
|
|
||||||
|
const optionsRef = React.useRef<object | undefined>(options);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
optionsRef.current = options;
|
||||||
|
}, [options]);
|
||||||
|
|
||||||
|
const getOptions = React.useCallback(() => optionsRef.current, []);
|
||||||
|
|
||||||
|
const { addOptionsGetter } = useOptionsGetters({
|
||||||
|
key: route.key,
|
||||||
|
getOptions,
|
||||||
|
});
|
||||||
|
|
||||||
const setKey = React.useCallback((key: string) => {
|
const setKey = React.useCallback((key: string) => {
|
||||||
navigatorKeyRef.current = key;
|
navigatorKeyRef.current = key;
|
||||||
}, []);
|
}, []);
|
||||||
@@ -77,8 +92,16 @@ export default function SceneView<
|
|||||||
setState: setCurrentState,
|
setState: setCurrentState,
|
||||||
getKey,
|
getKey,
|
||||||
setKey,
|
setKey,
|
||||||
|
addOptionsGetter,
|
||||||
}),
|
}),
|
||||||
[getCurrentState, getKey, route.state, setCurrentState, setKey]
|
[
|
||||||
|
getCurrentState,
|
||||||
|
getKey,
|
||||||
|
route.state,
|
||||||
|
setCurrentState,
|
||||||
|
setKey,
|
||||||
|
addOptionsGetter,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ import {
|
|||||||
NavigationState,
|
NavigationState,
|
||||||
Router,
|
Router,
|
||||||
} from '@react-navigation/routers';
|
} from '@react-navigation/routers';
|
||||||
import BaseNavigationContainer, {
|
import BaseNavigationContainer from '../BaseNavigationContainer';
|
||||||
NavigationStateContext,
|
import NavigationStateContext from '../NavigationStateContext';
|
||||||
} from '../BaseNavigationContainer';
|
|
||||||
import MockRouter, { MockActions } from './__fixtures__/MockRouter';
|
import MockRouter, { MockActions } from './__fixtures__/MockRouter';
|
||||||
import useNavigationBuilder from '../useNavigationBuilder';
|
import useNavigationBuilder from '../useNavigationBuilder';
|
||||||
import Screen from '../Screen';
|
import Screen from '../Screen';
|
||||||
|
|||||||
@@ -117,7 +117,8 @@ it("doesn't add query param for empty params", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('handles state with config with nested screens', () => {
|
it('handles state with config with nested screens', () => {
|
||||||
const path = '/foe/bar/sweet/apple/baz/jane?answer=42&count=10&valid=true';
|
const path =
|
||||||
|
'/foo/foe/bar/sweet/apple/baz/jane?answer=42&count=10&valid=true';
|
||||||
const config = {
|
const config = {
|
||||||
Foo: {
|
Foo: {
|
||||||
path: 'foo',
|
path: 'foo',
|
||||||
@@ -182,8 +183,77 @@ it('handles state with config with nested screens', () => {
|
|||||||
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('handles state with config with nested screens and exact', () => {
|
||||||
|
const path = '/foe/bar/sweet/apple/baz/jane?answer=42&count=10&valid=true';
|
||||||
|
const config = {
|
||||||
|
Foo: {
|
||||||
|
path: 'foo',
|
||||||
|
screens: {
|
||||||
|
Foe: {
|
||||||
|
path: 'foe',
|
||||||
|
exact: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Bar: 'bar/:type/:fruit',
|
||||||
|
Baz: {
|
||||||
|
path: 'baz/:author',
|
||||||
|
parse: {
|
||||||
|
author: (author: string) =>
|
||||||
|
author.replace(/^\w/, (c) => c.toUpperCase()),
|
||||||
|
count: Number,
|
||||||
|
valid: Boolean,
|
||||||
|
},
|
||||||
|
stringify: {
|
||||||
|
author: (author: string) => author.toLowerCase(),
|
||||||
|
id: (id: number) => `x${id}`,
|
||||||
|
unknown: (_: unknown) => 'x',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Foo',
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Foe',
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Bar',
|
||||||
|
params: { fruit: 'apple', type: 'sweet' },
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Baz',
|
||||||
|
params: {
|
||||||
|
author: 'Jane',
|
||||||
|
count: '10',
|
||||||
|
answer: '42',
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getPathFromState(state, config)).toBe(path);
|
||||||
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||||
|
});
|
||||||
|
|
||||||
it('handles state with config with nested screens and unused configs', () => {
|
it('handles state with config with nested screens and unused configs', () => {
|
||||||
const path = '/foe/baz/jane?answer=42&count=10&valid=true';
|
const path = '/foo/foe/baz/jane?answer=42&count=10&valid=true';
|
||||||
const config = {
|
const config = {
|
||||||
Foo: {
|
Foo: {
|
||||||
path: 'foo',
|
path: 'foo',
|
||||||
@@ -239,6 +309,66 @@ it('handles state with config with nested screens and unused configs', () => {
|
|||||||
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('handles state with config with nested screens and unused configs with exact', () => {
|
||||||
|
const path = '/foe/baz/jane?answer=42&count=10&valid=true';
|
||||||
|
const config = {
|
||||||
|
Foo: {
|
||||||
|
path: 'foo',
|
||||||
|
screens: {
|
||||||
|
Foe: {
|
||||||
|
path: 'foe',
|
||||||
|
exact: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baz: {
|
||||||
|
path: 'baz/:author',
|
||||||
|
parse: {
|
||||||
|
author: (author: string) =>
|
||||||
|
author.replace(/^\w/, (c) => c.toUpperCase()),
|
||||||
|
count: Number,
|
||||||
|
valid: Boolean,
|
||||||
|
},
|
||||||
|
stringify: {
|
||||||
|
author: (author: string) =>
|
||||||
|
author.replace(/^\w/, (c) => c.toLowerCase()),
|
||||||
|
unknown: (_: unknown) => 'x',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Foo',
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Foe',
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Baz',
|
||||||
|
params: {
|
||||||
|
author: 'Jane',
|
||||||
|
count: 10,
|
||||||
|
answer: '42',
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getPathFromState(state, config)).toBe(path);
|
||||||
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||||
|
});
|
||||||
|
|
||||||
it('handles nested object with stringify in it', () => {
|
it('handles nested object with stringify in it', () => {
|
||||||
const path = '/bar/sweet/apple/foo/bis/jane?answer=42&count=10&valid=true';
|
const path = '/bar/sweet/apple/foo/bis/jane?answer=42&count=10&valid=true';
|
||||||
const config = {
|
const config = {
|
||||||
@@ -252,7 +382,6 @@ it('handles nested object with stringify in it', () => {
|
|||||||
},
|
},
|
||||||
Bar: 'bar/:type/:fruit',
|
Bar: 'bar/:type/:fruit',
|
||||||
Baz: {
|
Baz: {
|
||||||
path: 'baz',
|
|
||||||
screens: {
|
screens: {
|
||||||
Bos: 'bos',
|
Bos: 'bos',
|
||||||
Bis: {
|
Bis: {
|
||||||
@@ -312,8 +441,82 @@ it('handles nested object with stringify in it', () => {
|
|||||||
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('handles nested object with stringify in it with exact', () => {
|
||||||
|
const path = '/bar/sweet/apple/foo/bis/jane?answer=42&count=10&valid=true';
|
||||||
|
const config = {
|
||||||
|
Foo: {
|
||||||
|
path: 'foo',
|
||||||
|
screens: {
|
||||||
|
Foe: {
|
||||||
|
path: 'foe',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Bar: 'bar/:type/:fruit',
|
||||||
|
Baz: {
|
||||||
|
path: 'baz',
|
||||||
|
screens: {
|
||||||
|
Bos: 'bos',
|
||||||
|
Bis: {
|
||||||
|
path: 'bis/:author',
|
||||||
|
exact: true,
|
||||||
|
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: 'Baz',
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Bis',
|
||||||
|
params: {
|
||||||
|
author: 'Jane',
|
||||||
|
count: 10,
|
||||||
|
answer: '42',
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getPathFromState(state, config)).toBe(path);
|
||||||
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||||
|
});
|
||||||
|
|
||||||
it('handles nested object for second route depth', () => {
|
it('handles nested object for second route depth', () => {
|
||||||
const path = '/baz';
|
const path = '/foo/bar/baz';
|
||||||
const config = {
|
const config = {
|
||||||
Foo: {
|
Foo: {
|
||||||
path: 'foo',
|
path: 'foo',
|
||||||
@@ -351,7 +554,95 @@ it('handles nested object for second route depth', () => {
|
|||||||
expect(getPathFromState(getStateFromPath(path, config), config)).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', () => {
|
it('handles nested object for second route depth with exact', () => {
|
||||||
|
const path = '/baz';
|
||||||
|
const config = {
|
||||||
|
Foo: {
|
||||||
|
path: 'foo',
|
||||||
|
screens: {
|
||||||
|
Foe: 'foe',
|
||||||
|
Bar: {
|
||||||
|
path: 'bar',
|
||||||
|
screens: {
|
||||||
|
Baz: {
|
||||||
|
path: 'baz',
|
||||||
|
exact: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Foo',
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Bar',
|
||||||
|
state: {
|
||||||
|
routes: [{ name: 'Baz' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getPathFromState(state, config)).toBe(path);
|
||||||
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles nested object for second route depth and path and stringify in roots', () => {
|
||||||
|
const path = '/foo/dathomir/bar/42/baz';
|
||||||
|
const config = {
|
||||||
|
Foo: {
|
||||||
|
path: 'foo/:planet',
|
||||||
|
stringify: {
|
||||||
|
id: (id: number) => `planet=${id}`,
|
||||||
|
},
|
||||||
|
screens: {
|
||||||
|
Foe: 'foe',
|
||||||
|
Bar: {
|
||||||
|
path: 'bar/:id',
|
||||||
|
parse: {
|
||||||
|
id: Number,
|
||||||
|
},
|
||||||
|
screens: {
|
||||||
|
Baz: 'baz',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Foo',
|
||||||
|
params: { planet: 'dathomir' },
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Bar',
|
||||||
|
state: {
|
||||||
|
routes: [{ name: 'Baz', params: { id: 42 } }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getPathFromState(state, config)).toBe(path);
|
||||||
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles nested object for second route depth and path and stringify in roots with exact', () => {
|
||||||
const path = '/baz';
|
const path = '/baz';
|
||||||
const config = {
|
const config = {
|
||||||
Foo: {
|
Foo: {
|
||||||
@@ -370,7 +661,10 @@ it('handles nested object for second route depth and and path and stringify in r
|
|||||||
id: Number,
|
id: Number,
|
||||||
},
|
},
|
||||||
screens: {
|
screens: {
|
||||||
Baz: 'baz',
|
Baz: {
|
||||||
|
path: 'baz',
|
||||||
|
exact: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -426,8 +720,51 @@ it('ignores empty string paths', () => {
|
|||||||
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('keeps query params if path is empty', () => {
|
||||||
|
const path = '/?foo=42';
|
||||||
|
const config = {
|
||||||
|
Foo: {
|
||||||
|
screens: {
|
||||||
|
Foe: 'foe',
|
||||||
|
Bar: {
|
||||||
|
screens: {
|
||||||
|
Qux: {
|
||||||
|
path: '',
|
||||||
|
parse: { foo: Number },
|
||||||
|
},
|
||||||
|
Baz: 'baz',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Foo',
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Bar',
|
||||||
|
state: {
|
||||||
|
routes: [{ name: 'Qux', params: { foo: 42 } }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getPathFromState(state, config)).toBe(path);
|
||||||
|
expect(getPathFromState(getStateFromPath(path, config), config)).toEqual(
|
||||||
|
path
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('cuts nested configs too', () => {
|
it('cuts nested configs too', () => {
|
||||||
const path = '/baz';
|
const path = '/foo/baz';
|
||||||
const config = {
|
const config = {
|
||||||
Foo: {
|
Foo: {
|
||||||
path: 'foo',
|
path: 'foo',
|
||||||
@@ -435,7 +772,48 @@ it('cuts nested configs too', () => {
|
|||||||
Bar: '',
|
Bar: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Baz: { path: 'baz' },
|
Baz: {
|
||||||
|
path: 'baz',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Foo',
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Bar',
|
||||||
|
state: {
|
||||||
|
routes: [{ name: 'Baz' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getPathFromState(state, config)).toBe(path);
|
||||||
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cuts nested configs too with exact', () => {
|
||||||
|
const path = '/baz';
|
||||||
|
const config = {
|
||||||
|
Foo: {
|
||||||
|
path: 'foo',
|
||||||
|
screens: {
|
||||||
|
Bar: {
|
||||||
|
path: '',
|
||||||
|
exact: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baz: {
|
||||||
|
path: 'baz',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
@@ -461,7 +839,7 @@ it('cuts nested configs too', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('handles empty path at the end', () => {
|
it('handles empty path at the end', () => {
|
||||||
const path = '/bar';
|
const path = '/foo/bar';
|
||||||
const config = {
|
const config = {
|
||||||
Foo: {
|
Foo: {
|
||||||
path: 'foo',
|
path: 'foo',
|
||||||
@@ -495,6 +873,8 @@ it('handles empty path at the end', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('returns "/" for empty path', () => {
|
it('returns "/" for empty path', () => {
|
||||||
|
const path = '/';
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
Foo: {
|
Foo: {
|
||||||
path: '',
|
path: '',
|
||||||
@@ -519,7 +899,8 @@ it('returns "/" for empty path', () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(getPathFromState(state, config)).toBe('/');
|
expect(getPathFromState(state, config)).toBe(path);
|
||||||
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('parses no path specified', () => {
|
it('parses no path specified', () => {
|
||||||
@@ -595,7 +976,6 @@ it('strips undefined query params', () => {
|
|||||||
},
|
},
|
||||||
Bar: 'bar/:type/:fruit',
|
Bar: 'bar/:type/:fruit',
|
||||||
Baz: {
|
Baz: {
|
||||||
path: 'baz',
|
|
||||||
screens: {
|
screens: {
|
||||||
Bos: 'bos',
|
Bos: 'bos',
|
||||||
Bis: {
|
Bis: {
|
||||||
@@ -635,7 +1015,79 @@ it('strips undefined query params', () => {
|
|||||||
params: {
|
params: {
|
||||||
author: 'Jane',
|
author: 'Jane',
|
||||||
count: 10,
|
count: 10,
|
||||||
answer: undefined,
|
valid: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getPathFromState(state, config)).toBe(path);
|
||||||
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('strips undefined query params with exact', () => {
|
||||||
|
const path = '/bar/sweet/apple/foo/bis/jane?count=10&valid=true';
|
||||||
|
const config = {
|
||||||
|
Foo: {
|
||||||
|
path: 'foo',
|
||||||
|
screens: {
|
||||||
|
Foe: {
|
||||||
|
path: 'foe',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Bar: 'bar/:type/:fruit',
|
||||||
|
Baz: {
|
||||||
|
path: 'baz',
|
||||||
|
screens: {
|
||||||
|
Bos: 'bos',
|
||||||
|
Bis: {
|
||||||
|
path: 'bis/:author',
|
||||||
|
exact: true,
|
||||||
|
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: 'Baz',
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Bis',
|
||||||
|
params: {
|
||||||
|
author: 'Jane',
|
||||||
|
count: 10,
|
||||||
valid: true,
|
valid: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -668,7 +1120,6 @@ it('handles stripping all query params', () => {
|
|||||||
},
|
},
|
||||||
Bar: 'bar/:type/:fruit',
|
Bar: 'bar/:type/:fruit',
|
||||||
Baz: {
|
Baz: {
|
||||||
path: 'baz',
|
|
||||||
screens: {
|
screens: {
|
||||||
Bos: 'bos',
|
Bos: 'bos',
|
||||||
Bis: {
|
Bis: {
|
||||||
@@ -707,9 +1158,6 @@ it('handles stripping all query params', () => {
|
|||||||
name: 'Bis',
|
name: 'Bis',
|
||||||
params: {
|
params: {
|
||||||
author: 'Jane',
|
author: 'Jane',
|
||||||
count: undefined,
|
|
||||||
answer: undefined,
|
|
||||||
valid: undefined,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -727,3 +1175,93 @@ it('handles stripping all query params', () => {
|
|||||||
expect(getPathFromState(state, config)).toBe(path);
|
expect(getPathFromState(state, config)).toBe(path);
|
||||||
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('handles stripping all query params with exact', () => {
|
||||||
|
const path = '/bar/sweet/apple/foo/bis/jane';
|
||||||
|
const config = {
|
||||||
|
Foo: {
|
||||||
|
path: 'foo',
|
||||||
|
screens: {
|
||||||
|
Foe: {
|
||||||
|
path: 'foe',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Bar: 'bar/:type/:fruit',
|
||||||
|
Baz: {
|
||||||
|
path: 'baz',
|
||||||
|
screens: {
|
||||||
|
Bos: 'bos',
|
||||||
|
Bis: {
|
||||||
|
path: 'bis/:author',
|
||||||
|
exact: true,
|
||||||
|
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: 'Baz',
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Bis',
|
||||||
|
params: {
|
||||||
|
author: 'Jane',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getPathFromState(state, config)).toBe(path);
|
||||||
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('replaces undefined query params', () => {
|
||||||
|
const path = '/bar/undefined/apple';
|
||||||
|
const config = {
|
||||||
|
Bar: 'bar/:type/:fruit',
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Bar',
|
||||||
|
params: { fruit: 'apple' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getPathFromState(state, config)).toBe(path);
|
||||||
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||||
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -379,6 +379,8 @@ it("doesn't update state if action wasn't handled", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('cleans up state when the navigator unmounts', () => {
|
it('cleans up state when the navigator unmounts', () => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
|
||||||
const TestNavigator = (props: any) => {
|
const TestNavigator = (props: any) => {
|
||||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||||
|
|
||||||
@@ -426,6 +428,8 @@ it('cleans up state when the navigator unmounts', () => {
|
|||||||
<BaseNavigationContainer onStateChange={onStateChange} children={null} />
|
<BaseNavigationContainer onStateChange={onStateChange} children={null} />
|
||||||
);
|
);
|
||||||
|
|
||||||
|
act(() => jest.runAllTimers());
|
||||||
|
|
||||||
expect(onStateChange).toBeCalledTimes(2);
|
expect(onStateChange).toBeCalledTimes(2);
|
||||||
expect(onStateChange).lastCalledWith(undefined);
|
expect(onStateChange).lastCalledWith(undefined);
|
||||||
});
|
});
|
||||||
@@ -1486,3 +1490,128 @@ it("doesn't throw if children is null", () => {
|
|||||||
|
|
||||||
expect(() => render(element).update(element)).not.toThrowError();
|
expect(() => render(element).update(element)).not.toThrowError();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns currently focused route with getCurrentRoute', () => {
|
||||||
|
const TestNavigator = (props: any): any => {
|
||||||
|
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||||
|
|
||||||
|
return descriptors[state.routes[state.index].key].render();
|
||||||
|
};
|
||||||
|
|
||||||
|
const TestScreen = () => null;
|
||||||
|
|
||||||
|
const navigation = React.createRef<NavigationContainerRef>();
|
||||||
|
|
||||||
|
const container = (
|
||||||
|
<BaseNavigationContainer ref={navigation}>
|
||||||
|
<TestNavigator>
|
||||||
|
<Screen name="bar" options={{ a: 'b' }}>
|
||||||
|
{() => (
|
||||||
|
<TestNavigator initialRouteName="bar-a">
|
||||||
|
<Screen
|
||||||
|
name="bar-a"
|
||||||
|
component={TestScreen}
|
||||||
|
options={{ sample: 'data' }}
|
||||||
|
/>
|
||||||
|
</TestNavigator>
|
||||||
|
)}
|
||||||
|
</Screen>
|
||||||
|
<Screen name="xux" component={TestScreen} />
|
||||||
|
</TestNavigator>
|
||||||
|
</BaseNavigationContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
render(container).update(container);
|
||||||
|
|
||||||
|
expect(navigation.current?.getCurrentRoute()).toEqual({
|
||||||
|
key: 'bar-a',
|
||||||
|
name: 'bar-a',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns currently focused route's options with getCurrentOptions", () => {
|
||||||
|
const TestNavigator = (props: any): any => {
|
||||||
|
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||||
|
|
||||||
|
return descriptors[state.routes[state.index].key].render();
|
||||||
|
};
|
||||||
|
|
||||||
|
const TestScreen = () => null;
|
||||||
|
|
||||||
|
const navigation = React.createRef<NavigationContainerRef>();
|
||||||
|
|
||||||
|
const container = (
|
||||||
|
<BaseNavigationContainer ref={navigation}>
|
||||||
|
<TestNavigator>
|
||||||
|
<Screen name="bar" options={{ a: 'b' }}>
|
||||||
|
{() => (
|
||||||
|
<TestNavigator
|
||||||
|
initialRouteName="bar-a"
|
||||||
|
screenOptions={() => ({ sample2: 'data' })}
|
||||||
|
>
|
||||||
|
<Screen
|
||||||
|
name="bar-a"
|
||||||
|
component={TestScreen}
|
||||||
|
options={{ sample: 'data' }}
|
||||||
|
/>
|
||||||
|
</TestNavigator>
|
||||||
|
)}
|
||||||
|
</Screen>
|
||||||
|
<Screen name="xux" component={TestScreen} />
|
||||||
|
</TestNavigator>
|
||||||
|
</BaseNavigationContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
render(container).update(container);
|
||||||
|
|
||||||
|
expect(navigation.current?.getCurrentOptions()).toEqual({
|
||||||
|
sample: 'data',
|
||||||
|
sample2: 'data',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not throw if while getting current options with no options defined', () => {
|
||||||
|
const TestNavigator = (props: any): any => {
|
||||||
|
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||||
|
|
||||||
|
return descriptors[state.routes[state.index].key].render();
|
||||||
|
};
|
||||||
|
|
||||||
|
const TestScreen = () => null;
|
||||||
|
|
||||||
|
const navigation = React.createRef<NavigationContainerRef>();
|
||||||
|
|
||||||
|
const container = (
|
||||||
|
<BaseNavigationContainer ref={navigation}>
|
||||||
|
<TestNavigator>
|
||||||
|
<Screen name="bar" options={{ a: 'b' }}>
|
||||||
|
{() => (
|
||||||
|
<TestNavigator initialRouteName="bar-a">
|
||||||
|
<Screen
|
||||||
|
name="bar-b"
|
||||||
|
component={TestScreen}
|
||||||
|
options={{ wrongKey: true }}
|
||||||
|
/>
|
||||||
|
<Screen name="bar-a" component={TestScreen} />
|
||||||
|
</TestNavigator>
|
||||||
|
)}
|
||||||
|
</Screen>
|
||||||
|
</TestNavigator>
|
||||||
|
</BaseNavigationContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
render(container).update(container);
|
||||||
|
|
||||||
|
expect(navigation.current?.getCurrentOptions()).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not throw if while getting current options with empty container', () => {
|
||||||
|
const navigation = React.createRef<NavigationContainerRef>();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const container = <BaseNavigationContainer ref={navigation} />;
|
||||||
|
|
||||||
|
render(container).update(container);
|
||||||
|
|
||||||
|
expect(navigation.current?.getCurrentOptions()).toEqual(undefined);
|
||||||
|
});
|
||||||
|
|||||||
@@ -4,19 +4,18 @@ import {
|
|||||||
PartialState,
|
PartialState,
|
||||||
Route,
|
Route,
|
||||||
} from '@react-navigation/routers';
|
} from '@react-navigation/routers';
|
||||||
|
import { PathConfig } from './types';
|
||||||
|
|
||||||
type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>;
|
type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>;
|
||||||
|
|
||||||
type StringifyConfig = Record<string, (value: any) => string>;
|
type StringifyConfig = Record<string, (value: any) => string>;
|
||||||
|
|
||||||
type Options = {
|
type OptionsItem = PathConfig[string];
|
||||||
[routeName: string]:
|
|
||||||
| string
|
type ConfigItem = {
|
||||||
| {
|
pattern?: string;
|
||||||
path?: string;
|
stringify?: StringifyConfig;
|
||||||
stringify?: StringifyConfig;
|
screens?: Record<string, ConfigItem>;
|
||||||
screens?: Options;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,137 +47,192 @@ type Options = {
|
|||||||
*/
|
*/
|
||||||
export default function getPathFromState(
|
export default function getPathFromState(
|
||||||
state?: State,
|
state?: State,
|
||||||
options: Options = {}
|
options: PathConfig = {}
|
||||||
): string {
|
): string {
|
||||||
if (state === undefined) {
|
if (state === undefined) {
|
||||||
throw Error('NavigationState not passed');
|
throw Error('NavigationState not passed');
|
||||||
}
|
}
|
||||||
let path = '/';
|
|
||||||
|
|
||||||
|
// Create a normalized configs array which will be easier to use
|
||||||
|
const configs = createNormalizedConfigs(options);
|
||||||
|
|
||||||
|
let path = '/';
|
||||||
let current: State | undefined = state;
|
let current: State | undefined = state;
|
||||||
|
|
||||||
|
const allParams: Record<string, any> = {};
|
||||||
|
|
||||||
while (current) {
|
while (current) {
|
||||||
let index = typeof current.index === 'number' ? current.index : 0;
|
let index = typeof current.index === 'number' ? current.index : 0;
|
||||||
let route = current.routes[index] as Route<string> & {
|
let route = current.routes[index] as Route<string> & {
|
||||||
state?: State;
|
state?: State;
|
||||||
};
|
};
|
||||||
let currentOptions = options;
|
|
||||||
let pattern = route.name;
|
|
||||||
// we keep all the route names that appeared during going deeper in config in case the pattern is resolved to undefined
|
|
||||||
let nestedRouteNames = '';
|
|
||||||
|
|
||||||
while (route.name in currentOptions) {
|
let pattern: string | undefined;
|
||||||
if (typeof currentOptions[route.name] === 'string') {
|
|
||||||
pattern = currentOptions[route.name] as string;
|
let currentParams: Record<string, any> = { ...route.params };
|
||||||
break;
|
let currentOptions = configs;
|
||||||
} else if (typeof currentOptions[route.name] === 'object') {
|
|
||||||
// if there is no `screens` property, we return pattern
|
// Keep all the route names that appeared during going deeper in config in case the pattern is resolved to undefined
|
||||||
if (
|
let nestedRouteNames = [];
|
||||||
!(currentOptions[route.name] as {
|
|
||||||
screens: Options;
|
let hasNext = true;
|
||||||
}).screens
|
|
||||||
) {
|
while (route.name in currentOptions && hasNext) {
|
||||||
pattern = (currentOptions[route.name] as { path: string }).path;
|
pattern = currentOptions[route.name].pattern;
|
||||||
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
|
|
||||||
break;
|
nestedRouteNames.push(route.name);
|
||||||
|
|
||||||
|
if (route.params) {
|
||||||
|
const stringify = currentOptions[route.name]?.stringify;
|
||||||
|
|
||||||
|
currentParams = fromEntries(
|
||||||
|
Object.entries(route.params).map(([key, value]) => [
|
||||||
|
key,
|
||||||
|
stringify?.[key] ? stringify[key](value) : String(value),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pattern) {
|
||||||
|
Object.assign(allParams, currentParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no `screens` property or no nested state, we return pattern
|
||||||
|
if (!currentOptions[route.name].screens || route.state === undefined) {
|
||||||
|
hasNext = false;
|
||||||
|
} else {
|
||||||
|
index =
|
||||||
|
typeof route.state.index === 'number'
|
||||||
|
? route.state.index
|
||||||
|
: route.state.routes.length - 1;
|
||||||
|
|
||||||
|
const nextRoute = route.state.routes[index];
|
||||||
|
const nestedConfig = currentOptions[route.name].screens;
|
||||||
|
|
||||||
|
// if there is config for next route name, we go deeper
|
||||||
|
if (nestedConfig && nextRoute.name in nestedConfig) {
|
||||||
|
route = nextRoute as Route<string> & { state?: State };
|
||||||
|
currentOptions = nestedConfig;
|
||||||
} else {
|
} else {
|
||||||
// if it is the end of state, we return pattern
|
// If not, there is no sense in going deeper in config
|
||||||
if (route.state === undefined) {
|
hasNext = false;
|
||||||
pattern = (currentOptions[route.name] as { path: string }).path;
|
|
||||||
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
index =
|
|
||||||
typeof route.state.index === 'number' ? route.state.index : 0;
|
|
||||||
const nextRoute = route.state.routes[index];
|
|
||||||
const deeperConfig = (currentOptions[route.name] as {
|
|
||||||
screens: Options;
|
|
||||||
}).screens;
|
|
||||||
// if there is config for next route name, we go deeper
|
|
||||||
if (nextRoute.name in deeperConfig) {
|
|
||||||
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
|
|
||||||
route = nextRoute as Route<string> & { state?: State };
|
|
||||||
currentOptions = deeperConfig;
|
|
||||||
} else {
|
|
||||||
// if not, there is no sense in going deeper in config
|
|
||||||
pattern = (currentOptions[route.name] as { path: string }).path;
|
|
||||||
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pattern === undefined) {
|
if (pattern === undefined) {
|
||||||
// cut the first `/`
|
pattern = nestedRouteNames.join('/');
|
||||||
pattern = nestedRouteNames.substring(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// we don't add empty path strings to path
|
if (currentOptions[route.name] !== undefined) {
|
||||||
if (pattern !== '') {
|
path += pattern
|
||||||
const config =
|
.split('/')
|
||||||
currentOptions[route.name] !== undefined
|
.map((p) => {
|
||||||
? (currentOptions[route.name] as { stringify?: StringifyConfig })
|
const name = p.replace(/^:/, '').replace(/\?$/, '');
|
||||||
.stringify
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const params = route.params
|
// If the path has a pattern for a param, put the param in the path
|
||||||
? // Stringify all of the param values before we use them
|
if (p.startsWith(':')) {
|
||||||
Object.entries(route.params).reduce<{
|
const value = allParams[name];
|
||||||
[key: string]: string;
|
|
||||||
}>((acc, [key, value]) => {
|
|
||||||
acc[key] = config?.[key] ? config[key](value) : String(value);
|
|
||||||
return acc;
|
|
||||||
}, {})
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
if (currentOptions[route.name] !== undefined) {
|
// Remove the used value from the params object since we'll use the rest for query string
|
||||||
path += pattern
|
if (currentParams) {
|
||||||
.split('/')
|
|
||||||
.map((p) => {
|
|
||||||
const name = p.replace(/^:/, '');
|
|
||||||
|
|
||||||
// If the path has a pattern for a param, put the param in the path
|
|
||||||
if (params && name in params && p.startsWith(':')) {
|
|
||||||
const value = params[name];
|
|
||||||
// Remove the used value from the params object since we'll use the rest for query string
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
delete params[name];
|
delete currentParams[name];
|
||||||
return encodeURIComponent(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return encodeURIComponent(p);
|
if (value === undefined && p.endsWith('?')) {
|
||||||
})
|
// Optional params without value assigned in route.params should be ignored
|
||||||
.join('/');
|
return '';
|
||||||
} else {
|
}
|
||||||
path += encodeURIComponent(route.name);
|
|
||||||
|
return encodeURIComponent(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeURIComponent(p);
|
||||||
|
})
|
||||||
|
.join('/');
|
||||||
|
} else {
|
||||||
|
path += encodeURIComponent(route.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (route.state) {
|
||||||
|
path += '/';
|
||||||
|
} else if (currentParams) {
|
||||||
|
for (let param in currentParams) {
|
||||||
|
if (currentParams[param] === 'undefined') {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
|
delete currentParams[param];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (route.state) {
|
const query = queryString.stringify(currentParams);
|
||||||
path += '/';
|
|
||||||
} else if (params) {
|
|
||||||
for (let param in params) {
|
|
||||||
if (params[param] === 'undefined') {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
||||||
delete params[param];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const query = queryString.stringify(params);
|
|
||||||
|
|
||||||
if (query) {
|
if (query) {
|
||||||
path += `?${query}`;
|
path += `?${query}`;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
current = route.state;
|
current = route.state;
|
||||||
}
|
}
|
||||||
|
|
||||||
path =
|
// Remove multiple as well as trailing slashes
|
||||||
path !== '/' && path.slice(path.length - 1) === '/'
|
path = path.replace(/\/+/g, '/');
|
||||||
? path.slice(0, -1)
|
path = path.length > 1 ? path.replace(/\/$/, '') : path;
|
||||||
: path;
|
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Object.fromEntries is not available in older iOS versions
|
||||||
|
const fromEntries = <K extends string, V>(entries: (readonly [K, V])[]) =>
|
||||||
|
entries.reduce((acc, [k, v]) => {
|
||||||
|
acc[k] = v;
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<K, V>);
|
||||||
|
|
||||||
|
const joinPaths = (...paths: string[]): string =>
|
||||||
|
([] as string[])
|
||||||
|
.concat(...paths.map((p) => p.split('/')))
|
||||||
|
.filter(Boolean)
|
||||||
|
.join('/');
|
||||||
|
|
||||||
|
const createConfigItem = (
|
||||||
|
config: OptionsItem | string,
|
||||||
|
parentPattern?: string
|
||||||
|
): ConfigItem => {
|
||||||
|
if (typeof config === 'string') {
|
||||||
|
// If a string is specified as the value of the key(e.g. Foo: '/path'), use it as the pattern
|
||||||
|
const pattern = parentPattern ? joinPaths(parentPattern, config) : config;
|
||||||
|
|
||||||
|
return { pattern };
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an object is specified as the value (e.g. Foo: { ... }),
|
||||||
|
// It can have `path` property and `screens` prop which has nested configs
|
||||||
|
const pattern =
|
||||||
|
config.exact !== true && parentPattern && config.path
|
||||||
|
? joinPaths(parentPattern, config.path)
|
||||||
|
: config.path;
|
||||||
|
|
||||||
|
const screens = config.screens
|
||||||
|
? createNormalizedConfigs(config.screens, pattern)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Normalize pattern to remove any leading, trailing slashes, duplicate slashes etc.
|
||||||
|
pattern: pattern?.split('/').filter(Boolean).join('/'),
|
||||||
|
stringify: config.stringify,
|
||||||
|
screens,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const createNormalizedConfigs = (
|
||||||
|
options: PathConfig,
|
||||||
|
pattern?: string
|
||||||
|
): Record<string, ConfigItem> =>
|
||||||
|
fromEntries(
|
||||||
|
Object.entries(options).map(([name, c]) => {
|
||||||
|
const result = createConfigItem(c, pattern);
|
||||||
|
|
||||||
|
return [name, result];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|||||||
@@ -5,25 +5,17 @@ import {
|
|||||||
PartialState,
|
PartialState,
|
||||||
InitialState,
|
InitialState,
|
||||||
} from '@react-navigation/routers';
|
} from '@react-navigation/routers';
|
||||||
|
import { PathConfig } from './types';
|
||||||
|
|
||||||
type ParseConfig = Record<string, (value: string) => any>;
|
type ParseConfig = Record<string, (value: string) => any>;
|
||||||
|
|
||||||
type Options = {
|
|
||||||
[routeName: string]:
|
|
||||||
| string
|
|
||||||
| {
|
|
||||||
path?: string;
|
|
||||||
parse?: ParseConfig;
|
|
||||||
screens?: Options;
|
|
||||||
initialRouteName?: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type RouteConfig = {
|
type RouteConfig = {
|
||||||
match: RegExp;
|
screen: string;
|
||||||
|
regex?: RegExp;
|
||||||
|
path: string;
|
||||||
pattern: string;
|
pattern: string;
|
||||||
routeNames: string[];
|
routeNames: string[];
|
||||||
parse: ParseConfig | undefined;
|
parse?: ParseConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
type InitialRouteConfig = {
|
type InitialRouteConfig = {
|
||||||
@@ -56,34 +48,73 @@ type ResultState = PartialState<NavigationState> & {
|
|||||||
*/
|
*/
|
||||||
export default function getStateFromPath(
|
export default function getStateFromPath(
|
||||||
path: string,
|
path: string,
|
||||||
options: Options = {}
|
options: PathConfig = {}
|
||||||
): ResultState | undefined {
|
): ResultState | undefined {
|
||||||
if (path === '') {
|
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, [], initialRoutes)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.sort(
|
||||||
|
(a, b) =>
|
||||||
|
// Sort configs so the most exhaustive is always first to be chosen
|
||||||
|
b.pattern.split('/').length - a.pattern.split('/').length
|
||||||
|
);
|
||||||
|
|
||||||
|
let remaining = path
|
||||||
|
.replace(/\/+/g, '/') // Replace multiple slash (//) with single ones
|
||||||
|
.replace(/^\//, '') // Remove extra leading slash
|
||||||
|
.replace(/\?.*$/, ''); // Remove query params which we will handle later
|
||||||
|
|
||||||
|
// Make sure there is a trailing slash
|
||||||
|
remaining = remaining.endsWith('/') ? remaining : `${remaining}/`;
|
||||||
|
|
||||||
|
if (remaining === '/') {
|
||||||
|
// We need to add special handling of empty path so navigation to empty path also works
|
||||||
|
// When handling empty path, we should only look at the root level config
|
||||||
|
const match = configs.find(
|
||||||
|
(config) =>
|
||||||
|
config.path === '' &&
|
||||||
|
config.routeNames.every(
|
||||||
|
// Make sure that none of the parent configs have a non-empty path defined
|
||||||
|
(name) => !configs.find((c) => c.screen === name)?.path
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
return createNestedStateObject(
|
||||||
|
match.routeNames.map((name, i, self) => {
|
||||||
|
if (i === self.length - 1) {
|
||||||
|
return { name, params: parseQueryParams(path, match.parse) };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { name };
|
||||||
|
}),
|
||||||
|
initialRoutes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return undefined;
|
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, [], initialRoutes)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
let result: PartialState<NavigationState> | undefined;
|
let result: PartialState<NavigationState> | undefined;
|
||||||
let current: PartialState<NavigationState> | undefined;
|
let current: PartialState<NavigationState> | undefined;
|
||||||
|
|
||||||
let remaining = path
|
|
||||||
.replace(/[/]+/, '/') // Replace multiple slash (//) with single ones
|
|
||||||
.replace(/^\//, '') // Remove extra leading slash
|
|
||||||
.replace(/\?.*/, ''); // Remove query params which we will handle later
|
|
||||||
|
|
||||||
while (remaining) {
|
while (remaining) {
|
||||||
let routeNames: string[] | undefined;
|
let routeNames: string[] | undefined;
|
||||||
let params: Record<string, any> | undefined;
|
let allParams: Record<string, any> | undefined;
|
||||||
|
|
||||||
// Go through all configs, and see if the next path segment matches our regex
|
// Go through all configs, and see if the next path segment matches our regex
|
||||||
for (const config of configs) {
|
for (const config of configs) {
|
||||||
const match = remaining.match(config.match);
|
if (!config.regex) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const match = remaining.match(config.regex);
|
||||||
|
|
||||||
// If our regex matches, we need to extract params from the path
|
// If our regex matches, we need to extract params from the path
|
||||||
if (match) {
|
if (match) {
|
||||||
@@ -94,21 +125,16 @@ export default function getStateFromPath(
|
|||||||
.filter((p) => p.startsWith(':'));
|
.filter((p) => p.startsWith(':'));
|
||||||
|
|
||||||
if (paramPatterns.length) {
|
if (paramPatterns.length) {
|
||||||
params = paramPatterns.reduce<Record<string, any>>((acc, p, i) => {
|
allParams = paramPatterns.reduce<Record<string, any>>((acc, p, i) => {
|
||||||
const key = p.replace(/^:/, '');
|
const value = match![(i + 1) * 2].replace(/\//, ''); // The param segments appear every second item starting from 2 in the regex match result
|
||||||
const value = match[i + 1]; // The param segments start from index 1 in the regex match result
|
|
||||||
|
|
||||||
acc[key] =
|
acc[p] = value;
|
||||||
config.parse && config.parse[key]
|
|
||||||
? config.parse[key](value)
|
|
||||||
: value;
|
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the matched segment from the remaining path
|
remaining = remaining.replace(match[1], '');
|
||||||
remaining = remaining.replace(match[0], '');
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -123,34 +149,46 @@ export default function getStateFromPath(
|
|||||||
remaining = segments.join('/');
|
remaining = segments.join('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
let state: InitialState;
|
const state = createNestedStateObject(
|
||||||
let routeName = routeNames.shift() as string;
|
routeNames.map((name) => {
|
||||||
let initialRoute = findInitialRoute(routeName, initialRoutes);
|
const config = configs.find((c) => c.screen === name);
|
||||||
|
|
||||||
state = createNestedState(
|
let params: object | undefined;
|
||||||
initialRoute,
|
|
||||||
routeName,
|
|
||||||
routeNames.length === 0,
|
|
||||||
params
|
|
||||||
);
|
|
||||||
|
|
||||||
if (routeNames.length > 0) {
|
if (allParams && config?.path) {
|
||||||
let nestedState = state;
|
const pattern = config.path;
|
||||||
|
|
||||||
while ((routeName = routeNames.shift() as string)) {
|
if (pattern) {
|
||||||
initialRoute = findInitialRoute(routeName, initialRoutes);
|
const paramPatterns = pattern
|
||||||
nestedState.routes[nestedState.index || 0].state = createNestedState(
|
.split('/')
|
||||||
initialRoute,
|
.filter((p) => p.startsWith(':'));
|
||||||
routeName,
|
|
||||||
routeNames.length === 0,
|
if (paramPatterns.length) {
|
||||||
params
|
params = paramPatterns.reduce<Record<string, any>>((acc, p) => {
|
||||||
);
|
const key = p.replace(/^:/, '').replace(/\?$/, '');
|
||||||
if (routeNames.length > 0) {
|
const value = allParams![p];
|
||||||
nestedState = nestedState.routes[nestedState.index || 0]
|
|
||||||
.state as InitialState;
|
if (value) {
|
||||||
|
acc[key] =
|
||||||
|
config.parse && config.parse[key]
|
||||||
|
? config.parse[key](value)
|
||||||
|
: value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
if (params && Object.keys(params).length) {
|
||||||
|
return { name, params };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { name };
|
||||||
|
}),
|
||||||
|
initialRoutes
|
||||||
|
);
|
||||||
|
|
||||||
if (current) {
|
if (current) {
|
||||||
// The state should be nested inside the deepest route we parsed before
|
// The state should be nested inside the deepest route we parsed before
|
||||||
@@ -172,74 +210,78 @@ export default function getStateFromPath(
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const query = path.split('?')[1];
|
const route = findFocusedRoute(current);
|
||||||
|
const params = parseQueryParams(
|
||||||
if (query) {
|
path,
|
||||||
while (current?.routes[current.index || 0].state) {
|
findParseConfigForRoute(route.name, configs)
|
||||||
// The query params apply to the deepest route
|
);
|
||||||
current = current.routes[current.index || 0].state;
|
|
||||||
}
|
|
||||||
|
|
||||||
const route = (current as PartialState<NavigationState>).routes[
|
|
||||||
current?.index || 0
|
|
||||||
];
|
|
||||||
|
|
||||||
const params = queryString.parse(query);
|
|
||||||
const parseFunction = findParseConfigForRoute(route.name, configs);
|
|
||||||
|
|
||||||
if (parseFunction) {
|
|
||||||
Object.keys(params).forEach((name) => {
|
|
||||||
if (parseFunction[name] && typeof params[name] === 'string') {
|
|
||||||
params[name] = parseFunction[name](params[name] as string);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (params) {
|
||||||
route.params = { ...route.params, ...params };
|
route.params = { ...route.params, ...params };
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNormalizedConfigs(
|
const joinPaths = (...paths: string[]): string =>
|
||||||
key: string,
|
([] as string[])
|
||||||
routeConfig: Options,
|
.concat(...paths.map((p) => p.split('/')))
|
||||||
|
.filter(Boolean)
|
||||||
|
.join('/');
|
||||||
|
|
||||||
|
const createNormalizedConfigs = (
|
||||||
|
screen: string,
|
||||||
|
routeConfig: PathConfig,
|
||||||
routeNames: string[] = [],
|
routeNames: string[] = [],
|
||||||
initials: InitialRouteConfig[]
|
initials: InitialRouteConfig[],
|
||||||
): RouteConfig[] {
|
parentPattern?: string
|
||||||
|
): RouteConfig[] => {
|
||||||
const configs: RouteConfig[] = [];
|
const configs: RouteConfig[] = [];
|
||||||
|
|
||||||
routeNames.push(key);
|
routeNames.push(screen);
|
||||||
|
|
||||||
const value = routeConfig[key];
|
const config = routeConfig[screen];
|
||||||
|
|
||||||
if (typeof value === 'string') {
|
if (typeof config === 'string') {
|
||||||
// If a string is specified as the value of the key(e.g. Foo: '/path'), use it as the pattern
|
// If a string is specified as the value of the key(e.g. Foo: '/path'), use it as the pattern
|
||||||
if (value !== '') {
|
const pattern = parentPattern ? joinPaths(parentPattern, config) : config;
|
||||||
configs.push(createConfigItem(routeNames, value));
|
|
||||||
}
|
configs.push(createConfigItem(screen, routeNames, pattern, config));
|
||||||
} else if (typeof value === 'object') {
|
} else if (typeof config === 'object') {
|
||||||
|
let pattern: string | undefined;
|
||||||
|
|
||||||
// if an object is specified as the value (e.g. Foo: { ... }),
|
// if an object is specified as the value (e.g. Foo: { ... }),
|
||||||
// it can have `path` property and
|
// it can have `path` property and
|
||||||
// it could have `screens` prop which has nested configs
|
// it could have `screens` prop which has nested configs
|
||||||
if (value.path && value.path !== '') {
|
if (typeof config.path === 'string') {
|
||||||
configs.push(createConfigItem(routeNames, value.path, value.parse));
|
pattern =
|
||||||
|
config.exact !== true && parentPattern
|
||||||
|
? joinPaths(parentPattern, config.path)
|
||||||
|
: config.path;
|
||||||
|
|
||||||
|
configs.push(
|
||||||
|
createConfigItem(screen, routeNames, pattern, config.path, config.parse)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (value.screens) {
|
|
||||||
|
if (config.screens) {
|
||||||
// property `initialRouteName` without `screens` has no purpose
|
// property `initialRouteName` without `screens` has no purpose
|
||||||
if (value.initialRouteName) {
|
if (config.initialRouteName) {
|
||||||
initials.push({
|
initials.push({
|
||||||
initialRouteName: value.initialRouteName,
|
initialRouteName: config.initialRouteName,
|
||||||
connectedRoutes: Object.keys(value.screens),
|
connectedRoutes: Object.keys(config.screens),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Object.keys(value.screens).forEach((nestedConfig) => {
|
|
||||||
|
Object.keys(config.screens).forEach((nestedConfig) => {
|
||||||
const result = createNormalizedConfigs(
|
const result = createNormalizedConfigs(
|
||||||
nestedConfig,
|
nestedConfig,
|
||||||
value.screens as Options,
|
config.screens as PathConfig,
|
||||||
routeNames,
|
routeNames,
|
||||||
initials
|
initials,
|
||||||
|
pattern
|
||||||
);
|
);
|
||||||
|
|
||||||
configs.push(...result);
|
configs.push(...result);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -248,43 +290,62 @@ function createNormalizedConfigs(
|
|||||||
routeNames.pop();
|
routeNames.pop();
|
||||||
|
|
||||||
return configs;
|
return configs;
|
||||||
}
|
};
|
||||||
|
|
||||||
function createConfigItem(
|
const createConfigItem = (
|
||||||
|
screen: string,
|
||||||
routeNames: string[],
|
routeNames: string[],
|
||||||
pattern: string,
|
pattern: string,
|
||||||
|
path: string,
|
||||||
parse?: ParseConfig
|
parse?: ParseConfig
|
||||||
): RouteConfig {
|
): RouteConfig => {
|
||||||
const match = new RegExp(
|
// Normalize pattern to remove any leading, trailing slashes, duplicate slashes etc.
|
||||||
'^' + escape(pattern).replace(/:[a-z0-9]+/gi, '([^/]+)') + '/?'
|
pattern = pattern.split('/').filter(Boolean).join('/');
|
||||||
);
|
|
||||||
|
const regex = pattern
|
||||||
|
? new RegExp(
|
||||||
|
`^(${pattern
|
||||||
|
.split('/')
|
||||||
|
.map((it) => {
|
||||||
|
if (it.startsWith(':')) {
|
||||||
|
return `(([^/]+\\/)${it.endsWith('?') ? '?' : ''})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${escape(it)}\\/`;
|
||||||
|
})
|
||||||
|
.join('')})`
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
match,
|
screen,
|
||||||
|
regex,
|
||||||
pattern,
|
pattern,
|
||||||
|
path,
|
||||||
// The routeNames array is mutated, so copy it to keep the current state
|
// The routeNames array is mutated, so copy it to keep the current state
|
||||||
routeNames: [...routeNames],
|
routeNames: [...routeNames],
|
||||||
parse,
|
parse,
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
function findParseConfigForRoute(
|
const findParseConfigForRoute = (
|
||||||
routeName: string,
|
routeName: string,
|
||||||
flatConfig: RouteConfig[]
|
flatConfig: RouteConfig[]
|
||||||
): ParseConfig | undefined {
|
): ParseConfig | undefined => {
|
||||||
for (const config of flatConfig) {
|
for (const config of flatConfig) {
|
||||||
if (routeName === config.routeNames[config.routeNames.length - 1]) {
|
if (routeName === config.routeNames[config.routeNames.length - 1]) {
|
||||||
return config.parse;
|
return config.parse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// tries to find an initial route connected with the one passed
|
return undefined;
|
||||||
function findInitialRoute(
|
};
|
||||||
|
|
||||||
|
// Try to find an initial route connected with the one passed
|
||||||
|
const findInitialRoute = (
|
||||||
routeName: string,
|
routeName: string,
|
||||||
initialRoutes: InitialRouteConfig[]
|
initialRoutes: InitialRouteConfig[]
|
||||||
): string | undefined {
|
): string | undefined => {
|
||||||
for (const config of initialRoutes) {
|
for (const config of initialRoutes) {
|
||||||
if (config.connectedRoutes.includes(routeName)) {
|
if (config.connectedRoutes.includes(routeName)) {
|
||||||
return config.initialRouteName === routeName
|
return config.initialRouteName === routeName
|
||||||
@@ -293,28 +354,25 @@ function findInitialRoute(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
};
|
||||||
|
|
||||||
// returns nested state object with values depending on whether
|
// returns state object with values depending on whether
|
||||||
// it is the end of state and if there is initialRoute for this level
|
// it is the end of state and if there is initialRoute for this level
|
||||||
function createNestedState(
|
const createStateObject = (
|
||||||
initialRoute: string | undefined,
|
initialRoute: string | undefined,
|
||||||
routeName: string,
|
routeName: string,
|
||||||
isEmpty: boolean,
|
params: Record<string, any> | undefined,
|
||||||
params?: Record<string, any> | undefined
|
isEmpty: boolean
|
||||||
): InitialState {
|
): InitialState => {
|
||||||
if (isEmpty) {
|
if (isEmpty) {
|
||||||
if (initialRoute) {
|
if (initialRoute) {
|
||||||
return {
|
return {
|
||||||
index: 1,
|
index: 1,
|
||||||
routes: [
|
routes: [{ name: initialRoute }, { name: routeName as string, params }],
|
||||||
{ name: initialRoute },
|
|
||||||
{ name: routeName as string, ...(params && { params }) },
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
routes: [{ name: routeName as string, ...(params && { params }) }],
|
routes: [{ name: routeName as string, params }],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -323,11 +381,87 @@ function createNestedState(
|
|||||||
index: 1,
|
index: 1,
|
||||||
routes: [
|
routes: [
|
||||||
{ name: initialRoute },
|
{ name: initialRoute },
|
||||||
{ name: routeName as string, state: { routes: [] } },
|
{ name: routeName as string, params, state: { routes: [] } },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return { routes: [{ name: routeName as string, state: { routes: [] } }] };
|
return {
|
||||||
|
routes: [{ name: routeName as string, params, state: { routes: [] } }],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
const createNestedStateObject = (
|
||||||
|
routes: { name: string; params?: object }[],
|
||||||
|
initialRoutes: InitialRouteConfig[]
|
||||||
|
) => {
|
||||||
|
let state: InitialState;
|
||||||
|
let route = routes.shift() as { name: string; params?: object };
|
||||||
|
let initialRoute = findInitialRoute(route.name, initialRoutes);
|
||||||
|
|
||||||
|
state = createStateObject(
|
||||||
|
initialRoute,
|
||||||
|
route.name,
|
||||||
|
route.params,
|
||||||
|
routes.length === 0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (routes.length > 0) {
|
||||||
|
let nestedState = state;
|
||||||
|
|
||||||
|
while ((route = routes.shift() as { name: string; params?: object })) {
|
||||||
|
initialRoute = findInitialRoute(route.name, initialRoutes);
|
||||||
|
|
||||||
|
const nestedStateIndex =
|
||||||
|
nestedState.index || nestedState.routes.length - 1;
|
||||||
|
|
||||||
|
nestedState.routes[nestedStateIndex].state = createStateObject(
|
||||||
|
initialRoute,
|
||||||
|
route.name,
|
||||||
|
route.params,
|
||||||
|
routes.length === 0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (routes.length > 0) {
|
||||||
|
nestedState = nestedState.routes[nestedStateIndex]
|
||||||
|
.state as InitialState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
const findFocusedRoute = (state: InitialState) => {
|
||||||
|
let current: InitialState | undefined = state;
|
||||||
|
|
||||||
|
while (current?.routes[current.index || 0].state) {
|
||||||
|
// The query params apply to the deepest route
|
||||||
|
current = current.routes[current.index || 0].state;
|
||||||
|
}
|
||||||
|
|
||||||
|
const route = (current as PartialState<NavigationState>).routes[
|
||||||
|
current?.index || 0
|
||||||
|
];
|
||||||
|
|
||||||
|
return route;
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseQueryParams = (
|
||||||
|
path: string,
|
||||||
|
parseConfig?: Record<string, (value: string) => any>
|
||||||
|
) => {
|
||||||
|
const query = path.split('?')[1];
|
||||||
|
const params = queryString.parse(query);
|
||||||
|
|
||||||
|
if (parseConfig) {
|
||||||
|
Object.keys(params).forEach((name) => {
|
||||||
|
if (parseConfig[name] && typeof params[name] === 'string') {
|
||||||
|
params[name] = parseConfig[name](params[name] as string);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.keys(params).length ? params : undefined;
|
||||||
|
};
|
||||||
|
|||||||
@@ -3,9 +3,12 @@ export * from '@react-navigation/routers';
|
|||||||
export { default as BaseNavigationContainer } from './BaseNavigationContainer';
|
export { default as BaseNavigationContainer } from './BaseNavigationContainer';
|
||||||
export { default as createNavigatorFactory } from './createNavigatorFactory';
|
export { default as createNavigatorFactory } from './createNavigatorFactory';
|
||||||
|
|
||||||
|
export { default as NavigationHelpersContext } from './NavigationHelpersContext';
|
||||||
export { default as NavigationContext } from './NavigationContext';
|
export { default as NavigationContext } from './NavigationContext';
|
||||||
export { default as NavigationRouteContext } from './NavigationRouteContext';
|
export { default as NavigationRouteContext } from './NavigationRouteContext';
|
||||||
|
|
||||||
|
export { default as CurrentRenderContext } from './CurrentRenderContext';
|
||||||
|
|
||||||
export { default as useNavigationBuilder } from './useNavigationBuilder';
|
export { default as useNavigationBuilder } from './useNavigationBuilder';
|
||||||
export { default as useNavigation } from './useNavigation';
|
export { default as useNavigation } from './useNavigation';
|
||||||
export { default as useRoute } from './useRoute';
|
export { default as useRoute } from './useRoute';
|
||||||
|
|||||||
@@ -193,6 +193,20 @@ type NavigationHelpersCommon<
|
|||||||
* Note that this method doesn't re-render screen when the result changes. So don't use it in `render`.
|
* Note that this method doesn't re-render screen when the result changes. So don't use it in `render`.
|
||||||
*/
|
*/
|
||||||
canGoBack(): boolean;
|
canGoBack(): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parent navigator, if any. Reason why the function is called
|
||||||
|
* dangerouslyGetParent is to warn developers against overusing it to eg. get parent
|
||||||
|
* of parent and other hard-to-follow patterns.
|
||||||
|
*/
|
||||||
|
dangerouslyGetParent<T = NavigationProp<ParamListBase> | undefined>(): T;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the navigator's state. Reason why the function is called
|
||||||
|
* dangerouslyGetState is to discourage developers to use internal navigation's state.
|
||||||
|
* Note that this method doesn't re-render screen when the result changes. So don't use it in `render`.
|
||||||
|
*/
|
||||||
|
dangerouslyGetState(): State;
|
||||||
} & PrivateValueStore<ParamList, keyof ParamList, {}>;
|
} & PrivateValueStore<ParamList, keyof ParamList, {}>;
|
||||||
|
|
||||||
export type NavigationHelpers<
|
export type NavigationHelpers<
|
||||||
@@ -254,20 +268,6 @@ export type NavigationProp<
|
|||||||
* @param options Options object for the route.
|
* @param options Options object for the route.
|
||||||
*/
|
*/
|
||||||
setOptions(options: Partial<ScreenOptions>): void;
|
setOptions(options: Partial<ScreenOptions>): void;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the parent navigator, if any. Reason why the function is called
|
|
||||||
* dangerouslyGetParent is to warn developers against overusing it to eg. get parent
|
|
||||||
* of parent and other hard-to-follow patterns.
|
|
||||||
*/
|
|
||||||
dangerouslyGetParent<T = NavigationProp<ParamListBase> | undefined>(): T;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the navigator's state. Reason why the function is called
|
|
||||||
* dangerouslyGetState is to discourage developers to use internal navigation's state.
|
|
||||||
* 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<State>> &
|
} & EventConsumer<EventMap & EventMapCore<State>> &
|
||||||
PrivateValueStore<ParamList, RouteName, EventMap>;
|
PrivateValueStore<ParamList, RouteName, EventMap>;
|
||||||
|
|
||||||
@@ -422,6 +422,14 @@ export type NavigationContainerRef = NavigationHelpers<ParamListBase> &
|
|||||||
* Get the rehydrated navigation state of the navigation tree.
|
* Get the rehydrated navigation state of the navigation tree.
|
||||||
*/
|
*/
|
||||||
getRootState(): NavigationState;
|
getRootState(): NavigationState;
|
||||||
|
/**
|
||||||
|
* Get the currently focused navigation route.
|
||||||
|
*/
|
||||||
|
getCurrentRoute(): Route<string> | undefined;
|
||||||
|
/**
|
||||||
|
* Get the currently focused route's options.
|
||||||
|
*/
|
||||||
|
getCurrentOptions(): object | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TypedNavigator<
|
export type TypedNavigator<
|
||||||
@@ -462,3 +470,16 @@ export type TypedNavigator<
|
|||||||
_: RouteConfig<ParamList, RouteName, State, ScreenOptions, EventMap>
|
_: RouteConfig<ParamList, RouteName, State, ScreenOptions, EventMap>
|
||||||
) => null;
|
) => null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PathConfig = {
|
||||||
|
[routeName: string]:
|
||||||
|
| string
|
||||||
|
| {
|
||||||
|
path?: string;
|
||||||
|
exact?: boolean;
|
||||||
|
parse?: Record<string, (value: string) => any>;
|
||||||
|
stringify?: Record<string, (value: any) => string>;
|
||||||
|
screens?: PathConfig;
|
||||||
|
initialRouteName?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
28
packages/core/src/useCurrentRender.tsx
Normal file
28
packages/core/src/useCurrentRender.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { NavigationState, ParamListBase } from '@react-navigation/routers';
|
||||||
|
import CurrentRenderContext from './CurrentRenderContext';
|
||||||
|
import { Descriptor, NavigationHelpers } from './types';
|
||||||
|
|
||||||
|
type Options = {
|
||||||
|
state: NavigationState;
|
||||||
|
navigation: NavigationHelpers<ParamListBase>;
|
||||||
|
descriptors: {
|
||||||
|
[key: string]: Descriptor<ParamListBase, string, NavigationState, object>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the current options, so that server renderer can get current values
|
||||||
|
* Mutating values like this is not safe in async mode, but it doesn't apply to SSR
|
||||||
|
*/
|
||||||
|
export default function useCurrentRender({
|
||||||
|
state,
|
||||||
|
navigation,
|
||||||
|
descriptors,
|
||||||
|
}: Options) {
|
||||||
|
const current = React.useContext(CurrentRenderContext);
|
||||||
|
|
||||||
|
if (current && navigation.isFocused()) {
|
||||||
|
current.options = descriptors[state.routes[state.index].key].options;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -117,6 +117,28 @@ export default function useDescriptors<
|
|||||||
const screen = screens[route.name];
|
const screen = screens[route.name];
|
||||||
const navigation = navigations[route.key];
|
const navigation = navigations[route.key];
|
||||||
|
|
||||||
|
const routeOptions = {
|
||||||
|
// The default `screenOptions` passed to the navigator
|
||||||
|
...(typeof screenOptions === 'object' || screenOptions == null
|
||||||
|
? screenOptions
|
||||||
|
: screenOptions({
|
||||||
|
// @ts-ignore
|
||||||
|
route,
|
||||||
|
navigation,
|
||||||
|
})),
|
||||||
|
// The `options` prop passed to `Screen` elements
|
||||||
|
...(typeof screen.options === 'object' || screen.options == null
|
||||||
|
? screen.options
|
||||||
|
: screen.options({
|
||||||
|
// @ts-ignore
|
||||||
|
route,
|
||||||
|
// @ts-ignore
|
||||||
|
navigation,
|
||||||
|
})),
|
||||||
|
// The options set via `navigation.setOptions`
|
||||||
|
...options[route.key],
|
||||||
|
};
|
||||||
|
|
||||||
acc[route.key] = {
|
acc[route.key] = {
|
||||||
navigation,
|
navigation,
|
||||||
render() {
|
render() {
|
||||||
@@ -128,31 +150,12 @@ export default function useDescriptors<
|
|||||||
screen={screen}
|
screen={screen}
|
||||||
getState={getState}
|
getState={getState}
|
||||||
setState={setState}
|
setState={setState}
|
||||||
|
options={routeOptions}
|
||||||
/>
|
/>
|
||||||
</NavigationBuilderContext.Provider>
|
</NavigationBuilderContext.Provider>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
options: {
|
options: routeOptions,
|
||||||
// The default `screenOptions` passed to the navigator
|
|
||||||
...(typeof screenOptions === 'object' || screenOptions == null
|
|
||||||
? screenOptions
|
|
||||||
: screenOptions({
|
|
||||||
// @ts-ignore
|
|
||||||
route,
|
|
||||||
navigation,
|
|
||||||
})),
|
|
||||||
// The `options` prop passed to `Screen` elements
|
|
||||||
...(typeof screen.options === 'object' || screen.options == null
|
|
||||||
? screen.options
|
|
||||||
: screen.options({
|
|
||||||
// @ts-ignore
|
|
||||||
route,
|
|
||||||
// @ts-ignore
|
|
||||||
navigation,
|
|
||||||
})),
|
|
||||||
// The options set via `navigation.setOptions`
|
|
||||||
...options[route.key],
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
|
|||||||
@@ -9,16 +9,13 @@ import useNavigation from './useNavigation';
|
|||||||
*/
|
*/
|
||||||
export default function useIsFocused(): boolean {
|
export default function useIsFocused(): boolean {
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
const getCurrentValue = React.useCallback(navigation.isFocused, [navigation]);
|
const getCurrentValue = React.useCallback(navigation.isFocused, [navigation]);
|
||||||
const subscribe = React.useCallback(
|
const subscribe = React.useCallback(
|
||||||
(callback: (value: boolean) => void) => {
|
(callback: () => void) => {
|
||||||
const unsubscribeFocus = navigation.addListener('focus', () =>
|
const unsubscribeFocus = navigation.addListener('focus', callback);
|
||||||
callback(true)
|
|
||||||
);
|
|
||||||
|
|
||||||
const unsubscribeBlur = navigation.addListener('blur', () =>
|
const unsubscribeBlur = navigation.addListener('blur', callback);
|
||||||
callback(false)
|
|
||||||
);
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unsubscribeFocus();
|
unsubscribeFocus();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
NavigationAction,
|
NavigationAction,
|
||||||
Route,
|
Route,
|
||||||
} from '@react-navigation/routers';
|
} from '@react-navigation/routers';
|
||||||
import { NavigationStateContext } from './BaseNavigationContainer';
|
import NavigationStateContext from './NavigationStateContext';
|
||||||
import NavigationRouteContext from './NavigationRouteContext';
|
import NavigationRouteContext from './NavigationRouteContext';
|
||||||
import Screen from './Screen';
|
import Screen from './Screen';
|
||||||
import useEventEmitter from './useEventEmitter';
|
import useEventEmitter from './useEventEmitter';
|
||||||
@@ -33,6 +33,7 @@ import {
|
|||||||
import useStateGetters from './useStateGetters';
|
import useStateGetters from './useStateGetters';
|
||||||
import useOnGetState from './useOnGetState';
|
import useOnGetState from './useOnGetState';
|
||||||
import useScheduleUpdate from './useScheduleUpdate';
|
import useScheduleUpdate from './useScheduleUpdate';
|
||||||
|
import useCurrentRender from './useCurrentRender';
|
||||||
|
|
||||||
// This is to make TypeScript compiler happy
|
// This is to make TypeScript compiler happy
|
||||||
// eslint-disable-next-line babel/no-unused-expressions
|
// eslint-disable-next-line babel/no-unused-expressions
|
||||||
@@ -264,41 +265,36 @@ export default function useNavigationBuilder<
|
|||||||
getKey,
|
getKey,
|
||||||
} = React.useContext(NavigationStateContext);
|
} = React.useContext(NavigationStateContext);
|
||||||
|
|
||||||
const previousStateRef = React.useRef<
|
const [initializedState, isFirstStateInitialization] = React.useMemo(() => {
|
||||||
NavigationState | PartialState<NavigationState> | undefined
|
|
||||||
>();
|
|
||||||
const initializedStateRef = React.useRef<State>();
|
|
||||||
|
|
||||||
let isFirstStateInitialization = false;
|
|
||||||
|
|
||||||
if (
|
|
||||||
initializedStateRef.current === undefined ||
|
|
||||||
currentState !== previousStateRef.current
|
|
||||||
) {
|
|
||||||
// If the current state isn't initialized on first render, we initialize it
|
// If the current state isn't initialized on first render, we initialize it
|
||||||
// We also need to re-initialize it if the state passed from parent was changed (maybe due to reset)
|
// We also need to re-initialize it if the state passed from parent was changed (maybe due to reset)
|
||||||
// Otherwise assume that the state was provided as initial state
|
// Otherwise assume that the state was provided as initial state
|
||||||
// So we need to rehydrate it to make it usable
|
// So we need to rehydrate it to make it usable
|
||||||
if (currentState === undefined || !isStateValid(currentState)) {
|
if (currentState === undefined || !isStateValid(currentState)) {
|
||||||
isFirstStateInitialization = true;
|
return [
|
||||||
initializedStateRef.current = router.getInitialState({
|
router.getInitialState({
|
||||||
routeNames,
|
|
||||||
routeParamList,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
initializedStateRef.current = router.getRehydratedState(
|
|
||||||
currentState as PartialState<State>,
|
|
||||||
{
|
|
||||||
routeNames,
|
routeNames,
|
||||||
routeParamList,
|
routeParamList,
|
||||||
}
|
}),
|
||||||
);
|
true,
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
router.getRehydratedState(currentState as PartialState<State>, {
|
||||||
|
routeNames,
|
||||||
|
routeParamList,
|
||||||
|
}),
|
||||||
|
false,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
// We explicitly don't include routeNames/routeParamList in the dep list
|
||||||
|
// below. We want to avoid forcing a new state to be calculated in cases
|
||||||
React.useEffect(() => {
|
// where routeConfigs change without affecting routeNames/routeParamList.
|
||||||
previousStateRef.current = currentState;
|
// Instead, we handle changes to these in the nextState code below. Note
|
||||||
}, [currentState]);
|
// that some changes to routeConfigs are explicitly ignored, such as changes
|
||||||
|
// to initialParams
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [currentState, router, isStateValid]);
|
||||||
|
|
||||||
let state =
|
let state =
|
||||||
// If the state isn't initialized, or stale, use the state we initialized instead
|
// If the state isn't initialized, or stale, use the state we initialized instead
|
||||||
@@ -306,7 +302,7 @@ export default function useNavigationBuilder<
|
|||||||
// So it'll be `undefined` or stale untill the first navigation event happens
|
// So it'll be `undefined` or stale untill the first navigation event happens
|
||||||
isStateInitialized(currentState)
|
isStateInitialized(currentState)
|
||||||
? (currentState as State)
|
? (currentState as State)
|
||||||
: (initializedStateRef.current as State);
|
: (initializedState as State);
|
||||||
|
|
||||||
let nextState: State = state;
|
let nextState: State = state;
|
||||||
|
|
||||||
@@ -362,13 +358,24 @@ export default function useNavigationBuilder<
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
// We need to clean up state for this navigator on unmount
|
// We need to clean up state for this navigator on unmount
|
||||||
if (getCurrentState() !== undefined && getKey() === navigatorKey) {
|
// We do it in a timeout because we need to detect if another navigator mounted in the meantime
|
||||||
setState(undefined);
|
// For example, if another navigator has started rendering, we should skip cleanup
|
||||||
}
|
// Otherwise, our cleanup step will cleanup state for the other navigator and re-initialize it
|
||||||
|
setTimeout(() => {
|
||||||
|
if (getCurrentState() !== undefined && getKey() === navigatorKey) {
|
||||||
|
setState(undefined);
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// We initialize this ref here to avoid a new getState getting initialized
|
||||||
|
// whenever initializedState changes. We want getState to have access to the
|
||||||
|
// latest initializedState, but don't need it to change when that happens
|
||||||
|
const initializedStateRef = React.useRef<State>();
|
||||||
|
initializedStateRef.current = initializedState;
|
||||||
|
|
||||||
const getState = React.useCallback((): State => {
|
const getState = React.useCallback((): State => {
|
||||||
const currentState = getCurrentState();
|
const currentState = getCurrentState();
|
||||||
|
|
||||||
@@ -492,6 +499,12 @@ export default function useNavigationBuilder<
|
|||||||
emitter,
|
emitter,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useCurrentRender({
|
||||||
|
state,
|
||||||
|
navigation,
|
||||||
|
descriptors,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
state,
|
state,
|
||||||
navigation,
|
navigation,
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
Router,
|
Router,
|
||||||
} from '@react-navigation/routers';
|
} from '@react-navigation/routers';
|
||||||
import { NavigationEventEmitter } from './useEventEmitter';
|
import { NavigationEventEmitter } from './useEventEmitter';
|
||||||
import NavigationContext from './NavigationContext';
|
|
||||||
|
|
||||||
import { NavigationHelpers, NavigationProp } from './types';
|
import { NavigationHelpers, NavigationProp } from './types';
|
||||||
|
|
||||||
@@ -49,12 +48,10 @@ export default function useNavigationCache<
|
|||||||
// Cache object which holds navigation objects for each screen
|
// Cache object which holds navigation objects for each screen
|
||||||
// We use `React.useMemo` instead of `React.useRef` coz we want to invalidate it when deps change
|
// We use `React.useMemo` instead of `React.useRef` coz we want to invalidate it when deps change
|
||||||
// In reality, these deps will rarely change, if ever
|
// In reality, these deps will rarely change, if ever
|
||||||
const parentNavigation = React.useContext(NavigationContext);
|
|
||||||
|
|
||||||
const cache = React.useMemo(
|
const cache = React.useMemo(
|
||||||
() => ({ current: {} as NavigationCache<State, ScreenOptions> }),
|
() => ({ current: {} as NavigationCache<State, ScreenOptions> }),
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[getState, navigation, setOptions, router, emitter, parentNavigation]
|
[getState, navigation, setOptions, router, emitter]
|
||||||
);
|
);
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
@@ -99,8 +96,6 @@ export default function useNavigationCache<
|
|||||||
...rest,
|
...rest,
|
||||||
...helpers,
|
...helpers,
|
||||||
...emitter.create(route.key),
|
...emitter.create(route.key),
|
||||||
dangerouslyGetParent: () => parentNavigation as any,
|
|
||||||
dangerouslyGetState: getState,
|
|
||||||
dispatch,
|
dispatch,
|
||||||
setOptions: (options: object) =>
|
setOptions: (options: object) =>
|
||||||
setOptions((o) => ({
|
setOptions((o) => ({
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ export default function useNavigationHelpers<
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
dangerouslyGetParent: () => parentNavigationHelpers as any,
|
||||||
|
dangerouslyGetState: getState,
|
||||||
} as NavigationHelpers<ParamListBase, EventMap> &
|
} as NavigationHelpers<ParamListBase, EventMap> &
|
||||||
(NavigationProp<ParamListBase, string, any, any, any> | undefined);
|
(NavigationProp<ParamListBase, string, any, any, any> | undefined);
|
||||||
}, [router, getState, parentNavigationHelpers, emitter.emit, onAction]);
|
}, [router, getState, parentNavigationHelpers, emitter.emit, onAction]);
|
||||||
|
|||||||
70
packages/core/src/useOptionsGetters.tsx
Normal file
70
packages/core/src/useOptionsGetters.tsx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import NavigationStateContext from './NavigationStateContext';
|
||||||
|
import { NavigationState } from '@react-navigation/routers';
|
||||||
|
|
||||||
|
export default function useOptionsGetters({
|
||||||
|
key,
|
||||||
|
getOptions,
|
||||||
|
getState,
|
||||||
|
}: {
|
||||||
|
key?: string;
|
||||||
|
getOptions?: () => object | undefined;
|
||||||
|
getState?: () => NavigationState;
|
||||||
|
}) {
|
||||||
|
const optionsGettersFromChild = React.useRef<
|
||||||
|
Record<string, (() => object | undefined | null) | undefined>
|
||||||
|
>({});
|
||||||
|
|
||||||
|
const { addOptionsGetter: parentAddOptionsGetter } = React.useContext(
|
||||||
|
NavigationStateContext
|
||||||
|
);
|
||||||
|
|
||||||
|
const getOptionsFromListener = React.useCallback(() => {
|
||||||
|
for (let key in optionsGettersFromChild.current) {
|
||||||
|
if (optionsGettersFromChild.current.hasOwnProperty(key)) {
|
||||||
|
const result = optionsGettersFromChild.current[key]?.();
|
||||||
|
// null means unfocused route
|
||||||
|
if (result !== null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getCurrentOptions = React.useCallback(() => {
|
||||||
|
if (getState) {
|
||||||
|
const state = getState();
|
||||||
|
if (state.routes[state.index].key !== key) {
|
||||||
|
// null means unfocused route
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const optionsFromListener = getOptionsFromListener();
|
||||||
|
if (optionsFromListener !== null) {
|
||||||
|
return optionsFromListener;
|
||||||
|
}
|
||||||
|
return getOptions?.() ?? undefined;
|
||||||
|
}, [getState, getOptionsFromListener, getOptions, key]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
return parentAddOptionsGetter?.(key!, getCurrentOptions);
|
||||||
|
}, [getCurrentOptions, parentAddOptionsGetter, key]);
|
||||||
|
|
||||||
|
const addOptionsGetter = React.useCallback(
|
||||||
|
(key: string, getter: () => object | undefined | null) => {
|
||||||
|
optionsGettersFromChild.current[key] = getter;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
optionsGettersFromChild.current[key] = undefined;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
addOptionsGetter,
|
||||||
|
getCurrentOptions,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -8,6 +8,15 @@ const UNINTIALIZED_STATE = {};
|
|||||||
export default function useSyncState<T>(initialState?: (() => T) | T) {
|
export default function useSyncState<T>(initialState?: (() => T) | T) {
|
||||||
const stateRef = React.useRef<T>(UNINTIALIZED_STATE as any);
|
const stateRef = React.useRef<T>(UNINTIALIZED_STATE as any);
|
||||||
const isSchedulingRef = React.useRef(false);
|
const isSchedulingRef = React.useRef(false);
|
||||||
|
const isMountedRef = React.useRef(true);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
isMountedRef.current = true;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
isMountedRef.current = false;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (stateRef.current === UNINTIALIZED_STATE) {
|
if (stateRef.current === UNINTIALIZED_STATE) {
|
||||||
stateRef.current =
|
stateRef.current =
|
||||||
@@ -20,7 +29,7 @@ export default function useSyncState<T>(initialState?: (() => T) | T) {
|
|||||||
const getState = React.useCallback(() => stateRef.current, []);
|
const getState = React.useCallback(() => stateRef.current, []);
|
||||||
|
|
||||||
const setState = React.useCallback((state: T) => {
|
const setState = React.useCallback((state: T) => {
|
||||||
if (state === stateRef.current) {
|
if (state === stateRef.current || !isMountedRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +51,10 @@ export default function useSyncState<T>(initialState?: (() => T) | T) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const flushUpdates = React.useCallback(() => {
|
const flushUpdates = React.useCallback(() => {
|
||||||
|
if (!isMountedRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure that the tracking state is up-to-date.
|
// Make sure that the tracking state is up-to-date.
|
||||||
// We call it unconditionally, but React should skip the update if state is unchanged.
|
// We call it unconditionally, but React should skip the update if state is unchanged.
|
||||||
setTrackingState(stateRef.current);
|
setTrackingState(stateRef.current);
|
||||||
|
|||||||
@@ -3,6 +3,153 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [5.8.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.8.0...@react-navigation/drawer@5.8.1) (2020-05-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.8.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.7.7...@react-navigation/drawer@5.8.0) (2020-05-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* update react-native-safe-area-context to 1.0.0 ([#8182](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/8182)) ([d62fbfe](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/d62fbfe255140f16b182e8b54b276a7c96f2aec6))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.7.7](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.7.6...@react-navigation/drawer@5.7.7) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.7.6](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.7.5...@react-navigation/drawer@5.7.6) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.7.5](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.7.4...@react-navigation/drawer@5.7.5) (2020-05-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.7.4](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.7.3...@react-navigation/drawer@5.7.4) (2020-05-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.7.3](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.7.2...@react-navigation/drawer@5.7.3) (2020-05-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.7.2](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.7.1...@react-navigation/drawer@5.7.2) (2020-05-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.7.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.7.0...@react-navigation/drawer@5.7.1) (2020-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix building typescript definitions. closes [#8216](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/8216) ([47a1229](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/47a12298378747edd2d22e54dc1c8677f98c49b4))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.7.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.6.4...@react-navigation/drawer@5.7.0) (2020-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add generic type aliases for screen props ([bea14aa](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/bea14aa26fd5cbfebc7973733c5cf1f44fd323aa)), closes [#7971](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/7971)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.6.4](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.6.3...@react-navigation/drawer@5.6.4) (2020-05-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.6.3](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.6.2...@react-navigation/drawer@5.6.3) (2020-05-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.6.2](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.6.1...@react-navigation/drawer@5.6.2) (2020-05-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.6.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.6.0...@react-navigation/drawer@5.6.1) (2020-04-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.6.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.5.1...@react-navigation/drawer@5.6.0) (2020-04-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix closing drawer on web with tap on overlay ([70be3f6](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/70be3f6d863c56211e2f90bdf743bd8526338248))
|
||||||
|
* make sure the address bar hides when scrolling on web ([0a19e94](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/0a19e94b23a4d2b5f22d1d9deb0544f586f475ee))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add `useLinkBuilder` hook to build links ([2792f43](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/2792f438fe45428fe193e3708fee7ad61966cbf4))
|
||||||
|
* add action prop to Link ([942d2be](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/942d2be2c72720469475ce12ec8df23825994dbf))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.5.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.5.0...@react-navigation/drawer@5.5.1) (2020-04-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [5.5.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.4.1...@react-navigation/drawer@5.5.0) (2020-04-17)
|
# [5.5.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.4.1...@react-navigation/drawer@5.5.0) (2020-04-17)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/drawer",
|
"name": "@react-navigation/drawer",
|
||||||
"description": "Drawer navigator component with animated transitions and gesturess",
|
"description": "Drawer navigator component with animated transitions and gesturess",
|
||||||
"version": "5.5.0",
|
"version": "5.8.1",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -20,11 +20,13 @@
|
|||||||
"homepage": "https://reactnavigation.org/docs/drawer-navigator.html",
|
"homepage": "https://reactnavigation.org/docs/drawer-navigator.html",
|
||||||
"main": "lib/commonjs/index.js",
|
"main": "lib/commonjs/index.js",
|
||||||
"react-native": "src/index.tsx",
|
"react-native": "src/index.tsx",
|
||||||
|
"source": "src/index.tsx",
|
||||||
"module": "lib/module/index.js",
|
"module": "lib/module/index.js",
|
||||||
"types": "lib/typescript/src/index.d.ts",
|
"types": "lib/typescript/src/index.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"src",
|
||||||
"lib"
|
"lib",
|
||||||
|
"!**/__tests__"
|
||||||
],
|
],
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
@@ -39,17 +41,17 @@
|
|||||||
"react-native-iphone-x-helper": "^1.2.1"
|
"react-native-iphone-x-helper": "^1.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.10.0",
|
"@react-native-community/bob": "^0.14.3",
|
||||||
"@react-navigation/native": "^5.1.6",
|
"@react-navigation/native": "^5.5.0",
|
||||||
"@types/react": "^16.9.23",
|
"@types/react": "^16.9.34",
|
||||||
"@types/react-native": "^0.61.22",
|
"@types/react-native": "^0.62.7",
|
||||||
"del-cli": "^3.0.0",
|
"del-cli": "^3.0.0",
|
||||||
"react": "~16.9.0",
|
"react": "~16.9.0",
|
||||||
"react-native": "~0.61.5",
|
"react-native": "~0.61.5",
|
||||||
"react-native-gesture-handler": "^1.6.0",
|
"react-native-gesture-handler": "^1.6.0",
|
||||||
"react-native-reanimated": "^1.7.0",
|
"react-native-reanimated": "^1.8.0",
|
||||||
"react-native-safe-area-context": "^0.7.3",
|
"react-native-safe-area-context": "^1.0.0",
|
||||||
"react-native-screens": "^2.3.0",
|
"react-native-screens": "^2.7.0",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.8.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export { default as useIsDrawerOpen } from './utils/useIsDrawerOpen';
|
|||||||
export type {
|
export type {
|
||||||
DrawerNavigationOptions,
|
DrawerNavigationOptions,
|
||||||
DrawerNavigationProp,
|
DrawerNavigationProp,
|
||||||
|
DrawerScreenProps,
|
||||||
DrawerContentOptions,
|
DrawerContentOptions,
|
||||||
DrawerContentComponentProps,
|
DrawerContentComponentProps,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ import {
|
|||||||
NavigationHelpers,
|
NavigationHelpers,
|
||||||
DrawerNavigationState,
|
DrawerNavigationState,
|
||||||
DrawerActionHelpers,
|
DrawerActionHelpers,
|
||||||
|
RouteProp,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
import { PanGestureHandler } from 'react-native-gesture-handler';
|
import type { PanGestureHandlerProperties } from 'react-native-gesture-handler';
|
||||||
|
|
||||||
export type Scene = {
|
export type Scene = {
|
||||||
route: Route<string>;
|
route: Route<string>;
|
||||||
@@ -32,6 +33,7 @@ export type DrawerNavigationConfig<T = DrawerContentOptions> = {
|
|||||||
drawerType?: 'front' | 'back' | 'slide' | 'permanent';
|
drawerType?: 'front' | 'back' | 'slide' | 'permanent';
|
||||||
/**
|
/**
|
||||||
* How far from the edge of the screen the swipe gesture should activate.
|
* How far from the edge of the screen the swipe gesture should activate.
|
||||||
|
* Not supported on Web.
|
||||||
*/
|
*/
|
||||||
edgeWidth?: number;
|
edgeWidth?: number;
|
||||||
/**
|
/**
|
||||||
@@ -58,8 +60,9 @@ export type DrawerNavigationConfig<T = DrawerContentOptions> = {
|
|||||||
statusBarAnimation?: 'slide' | 'none' | 'fade';
|
statusBarAnimation?: 'slide' | 'none' | 'fade';
|
||||||
/**
|
/**
|
||||||
* Props to pass to the underlying pan gesture handler.
|
* Props to pass to the underlying pan gesture handler.
|
||||||
|
* Not supported on Web.
|
||||||
*/
|
*/
|
||||||
gestureHandlerProps?: React.ComponentProps<typeof PanGestureHandler>;
|
gestureHandlerProps?: PanGestureHandlerProperties;
|
||||||
/**
|
/**
|
||||||
* Whether the screens should render the first time they are accessed. Defaults to `true`.
|
* Whether the screens should render the first time they are accessed. Defaults to `true`.
|
||||||
* Set it to `false` if you want to render all screens on initial render.
|
* Set it to `false` if you want to render all screens on initial render.
|
||||||
@@ -113,13 +116,15 @@ export type DrawerNavigationOptions = {
|
|||||||
* Whether you can use gestures to open or close the drawer.
|
* Whether you can use gestures to open or close the drawer.
|
||||||
* Setting this to `false` disables swipe gestures as well as tap on overlay to close.
|
* Setting this to `false` disables swipe gestures as well as tap on overlay to close.
|
||||||
* See `swipeEnabled` to disable only the swipe gesture.
|
* See `swipeEnabled` to disable only the swipe gesture.
|
||||||
* Defaults to `true`
|
* Defaults to `true`.
|
||||||
|
* Not supported on Web.
|
||||||
*/
|
*/
|
||||||
gestureEnabled?: boolean;
|
gestureEnabled?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether you can use swipe gestures to open or close the drawer.
|
* Whether you can use swipe gestures to open or close the drawer.
|
||||||
* Defaults to `true`
|
* Defaults to `true`.
|
||||||
|
* Not supported on Web.
|
||||||
*/
|
*/
|
||||||
swipeEnabled?: boolean;
|
swipeEnabled?: boolean;
|
||||||
|
|
||||||
@@ -204,6 +209,14 @@ export type DrawerNavigationProp<
|
|||||||
> &
|
> &
|
||||||
DrawerActionHelpers<ParamList>;
|
DrawerActionHelpers<ParamList>;
|
||||||
|
|
||||||
|
export type DrawerScreenProps<
|
||||||
|
ParamList extends ParamListBase,
|
||||||
|
RouteName extends keyof ParamList = string
|
||||||
|
> = {
|
||||||
|
navigation: DrawerNavigationProp<ParamList, RouteName>;
|
||||||
|
route: RouteProp<ParamList, RouteName>;
|
||||||
|
};
|
||||||
|
|
||||||
export type DrawerDescriptor = Descriptor<
|
export type DrawerDescriptor = Descriptor<
|
||||||
ParamListBase,
|
ParamListBase,
|
||||||
string,
|
string,
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ import {
|
|||||||
StyleProp,
|
StyleProp,
|
||||||
View,
|
View,
|
||||||
InteractionManager,
|
InteractionManager,
|
||||||
|
TouchableWithoutFeedback,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import Animated from 'react-native-reanimated';
|
||||||
import {
|
import {
|
||||||
PanGestureHandler,
|
PanGestureHandler,
|
||||||
TapGestureHandler,
|
TapGestureHandler,
|
||||||
State as GestureState,
|
GestureState,
|
||||||
TapGestureHandlerStateChangeEvent,
|
} from './GestureHandler';
|
||||||
} from 'react-native-gesture-handler';
|
|
||||||
import Animated from 'react-native-reanimated';
|
|
||||||
import Overlay from './Overlay';
|
import Overlay from './Overlay';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -79,7 +79,6 @@ type Props = {
|
|||||||
open: boolean;
|
open: boolean;
|
||||||
onOpen: () => void;
|
onOpen: () => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onGestureRef?: (ref: PanGestureHandler | null) => void;
|
|
||||||
gestureEnabled: boolean;
|
gestureEnabled: boolean;
|
||||||
swipeEnabled: boolean;
|
swipeEnabled: boolean;
|
||||||
drawerPosition: 'left' | 'right';
|
drawerPosition: 'left' | 'right';
|
||||||
@@ -511,28 +510,17 @@ export default class DrawerView extends React.Component<Props> {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
private handleTapStateChange =
|
private handleTapStateChange = event([
|
||||||
Platform.OS === 'web'
|
{
|
||||||
? // FIXME: Drawer doesn't close on Web with the same code that we use for native
|
nativeEvent: {
|
||||||
({ nativeEvent }: TapGestureHandlerStateChangeEvent) => {
|
oldState: (s: Animated.Value<number>) =>
|
||||||
if (
|
cond(
|
||||||
nativeEvent.state === GestureState.END &&
|
eq(s, GestureState.ACTIVE),
|
||||||
nativeEvent.oldState === GestureState.ACTIVE
|
set(this.manuallyTriggerSpring, TRUE)
|
||||||
) {
|
),
|
||||||
this.toggleDrawer(false);
|
},
|
||||||
}
|
},
|
||||||
}
|
]);
|
||||||
: event([
|
|
||||||
{
|
|
||||||
nativeEvent: {
|
|
||||||
oldState: (s: Animated.Value<number>) =>
|
|
||||||
cond(
|
|
||||||
eq(s, GestureState.ACTIVE),
|
|
||||||
set(this.manuallyTriggerSpring, TRUE)
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
private handleContainerLayout = (e: LayoutChangeEvent) =>
|
private handleContainerLayout = (e: LayoutChangeEvent) =>
|
||||||
this.containerWidth.setValue(e.nativeEvent.layout.width);
|
this.containerWidth.setValue(e.nativeEvent.layout.width);
|
||||||
@@ -579,7 +567,6 @@ export default class DrawerView extends React.Component<Props> {
|
|||||||
sceneContainerStyle,
|
sceneContainerStyle,
|
||||||
drawerStyle,
|
drawerStyle,
|
||||||
overlayStyle,
|
overlayStyle,
|
||||||
onGestureRef,
|
|
||||||
renderDrawerContent,
|
renderDrawerContent,
|
||||||
renderSceneContent,
|
renderSceneContent,
|
||||||
gestureHandlerProps,
|
gestureHandlerProps,
|
||||||
@@ -624,7 +611,6 @@ export default class DrawerView extends React.Component<Props> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PanGestureHandler
|
<PanGestureHandler
|
||||||
ref={onGestureRef}
|
|
||||||
activeOffsetX={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
|
activeOffsetX={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
|
||||||
failOffsetY={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
|
failOffsetY={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
|
||||||
onGestureEvent={this.handleGestureEvent}
|
onGestureEvent={this.handleGestureEvent}
|
||||||
@@ -663,12 +649,20 @@ export default class DrawerView extends React.Component<Props> {
|
|||||||
</View>
|
</View>
|
||||||
{
|
{
|
||||||
// Disable overlay if sidebar is permanent
|
// Disable overlay if sidebar is permanent
|
||||||
drawerType === 'permanent' ? null : (
|
drawerType === 'permanent' ? null : Platform.OS === 'web' ? (
|
||||||
|
<TouchableWithoutFeedback
|
||||||
|
onPress={
|
||||||
|
gestureEnabled ? () => this.toggleDrawer(false) : undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Overlay progress={progress} style={overlayStyle as any} />
|
||||||
|
</TouchableWithoutFeedback>
|
||||||
|
) : (
|
||||||
<TapGestureHandler
|
<TapGestureHandler
|
||||||
enabled={gestureEnabled}
|
enabled={gestureEnabled}
|
||||||
onHandlerStateChange={this.handleTapStateChange}
|
onHandlerStateChange={this.handleTapStateChange}
|
||||||
>
|
>
|
||||||
<Overlay progress={progress} style={overlayStyle} />
|
<Overlay progress={progress} style={overlayStyle as any} />
|
||||||
</TapGestureHandler>
|
</TapGestureHandler>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -737,6 +731,11 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
main: {
|
main: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
overflow: 'hidden',
|
...Platform.select({
|
||||||
|
// FIXME: We need to hide `overflowX` on Web so the translated content doesn't show offscreen.
|
||||||
|
// But adding `overflowX: 'hidden'` prevents content from collapsing the URL bar.
|
||||||
|
web: null,
|
||||||
|
default: { overflow: 'hidden' },
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import {
|
|||||||
StyleProp,
|
StyleProp,
|
||||||
ViewStyle,
|
ViewStyle,
|
||||||
TextStyle,
|
TextStyle,
|
||||||
|
Platform,
|
||||||
|
TouchableWithoutFeedbackProps,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { useTheme } from '@react-navigation/native';
|
import { Link, useTheme } from '@react-navigation/native';
|
||||||
import Color from 'color';
|
import Color from 'color';
|
||||||
import TouchableItem from './TouchableItem';
|
import TouchableItem from './TouchableItem';
|
||||||
|
|
||||||
@@ -26,6 +28,10 @@ type Props = {
|
|||||||
size: number;
|
size: number;
|
||||||
color: string;
|
color: string;
|
||||||
}) => React.ReactNode;
|
}) => React.ReactNode;
|
||||||
|
/**
|
||||||
|
* URL to use for the link to the tab.
|
||||||
|
*/
|
||||||
|
to?: string;
|
||||||
/**
|
/**
|
||||||
* Whether to highlight the drawer item as active.
|
* Whether to highlight the drawer item as active.
|
||||||
*/
|
*/
|
||||||
@@ -60,6 +66,54 @@ type Props = {
|
|||||||
style?: StyleProp<ViewStyle>;
|
style?: StyleProp<ViewStyle>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Touchable = ({
|
||||||
|
children,
|
||||||
|
style,
|
||||||
|
onPress,
|
||||||
|
to,
|
||||||
|
accessibilityRole,
|
||||||
|
delayPressIn,
|
||||||
|
...rest
|
||||||
|
}: TouchableWithoutFeedbackProps & {
|
||||||
|
to?: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
onPress?: () => void;
|
||||||
|
}) => {
|
||||||
|
if (Platform.OS === 'web' && to) {
|
||||||
|
// React Native Web doesn't forward `onClick` if we use `TouchableWithoutFeedback`.
|
||||||
|
// We need to use `onClick` to be able to prevent default browser handling of links.
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
{...rest}
|
||||||
|
to={to}
|
||||||
|
style={[styles.button, style]}
|
||||||
|
onPress={(e: any) => {
|
||||||
|
if (
|
||||||
|
!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) && // ignore clicks with modifier keys
|
||||||
|
(e.button == null || e.button === 0) // ignore everything but left clicks
|
||||||
|
) {
|
||||||
|
e.preventDefault();
|
||||||
|
onPress?.(e);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<TouchableItem
|
||||||
|
{...rest}
|
||||||
|
accessibilityRole={accessibilityRole}
|
||||||
|
delayPressIn={delayPressIn}
|
||||||
|
onPress={onPress}
|
||||||
|
>
|
||||||
|
<View style={style}>{children}</View>
|
||||||
|
</TouchableItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A component used to show an action item with an icon and a label in a navigation drawer.
|
* A component used to show an action item with an icon and a label in a navigation drawer.
|
||||||
*/
|
*/
|
||||||
@@ -70,6 +124,7 @@ export default function DrawerItem(props: Props) {
|
|||||||
icon,
|
icon,
|
||||||
label,
|
label,
|
||||||
labelStyle,
|
labelStyle,
|
||||||
|
to,
|
||||||
focused = false,
|
focused = false,
|
||||||
activeTintColor = colors.primary,
|
activeTintColor = colors.primary,
|
||||||
inactiveTintColor = Color(colors.text).alpha(0.68).rgb().string(),
|
inactiveTintColor = Color(colors.text).alpha(0.68).rgb().string(),
|
||||||
@@ -94,7 +149,7 @@ export default function DrawerItem(props: Props) {
|
|||||||
{...rest}
|
{...rest}
|
||||||
style={[styles.container, { borderRadius, backgroundColor }, style]}
|
style={[styles.container, { borderRadius, backgroundColor }, style]}
|
||||||
>
|
>
|
||||||
<TouchableItem
|
<Touchable
|
||||||
delayPressIn={0}
|
delayPressIn={0}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
style={[styles.wrapper, { borderRadius }]}
|
style={[styles.wrapper, { borderRadius }]}
|
||||||
@@ -102,6 +157,7 @@ export default function DrawerItem(props: Props) {
|
|||||||
accessibilityComponentType="button"
|
accessibilityComponentType="button"
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityStates={focused ? ['selected'] : []}
|
accessibilityStates={focused ? ['selected'] : []}
|
||||||
|
to={to}
|
||||||
>
|
>
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{iconNode}
|
{iconNode}
|
||||||
@@ -129,7 +185,7 @@ export default function DrawerItem(props: Props) {
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
</TouchableItem>
|
</Touchable>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -148,4 +204,7 @@ const styles = StyleSheet.create({
|
|||||||
label: {
|
label: {
|
||||||
marginRight: 32,
|
marginRight: 32,
|
||||||
},
|
},
|
||||||
|
button: {
|
||||||
|
display: 'flex',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
CommonActions,
|
CommonActions,
|
||||||
DrawerActions,
|
DrawerActions,
|
||||||
DrawerNavigationState,
|
DrawerNavigationState,
|
||||||
|
useLinkBuilder,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
import DrawerItem from './DrawerItem';
|
import DrawerItem from './DrawerItem';
|
||||||
import {
|
import {
|
||||||
@@ -31,6 +32,8 @@ export default function DrawerItemList({
|
|||||||
itemStyle,
|
itemStyle,
|
||||||
labelStyle,
|
labelStyle,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
|
const buildLink = useLinkBuilder();
|
||||||
|
|
||||||
return (state.routes.map((route, i) => {
|
return (state.routes.map((route, i) => {
|
||||||
const focused = i === state.index;
|
const focused = i === state.index;
|
||||||
const { title, drawerLabel, drawerIcon } = descriptors[route.key].options;
|
const { title, drawerLabel, drawerIcon } = descriptors[route.key].options;
|
||||||
@@ -53,6 +56,7 @@ export default function DrawerItemList({
|
|||||||
inactiveBackgroundColor={inactiveBackgroundColor}
|
inactiveBackgroundColor={inactiveBackgroundColor}
|
||||||
labelStyle={labelStyle}
|
labelStyle={labelStyle}
|
||||||
style={itemStyle}
|
style={itemStyle}
|
||||||
|
to={buildLink(route.name, route.params)}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
navigation.dispatch({
|
navigation.dispatch({
|
||||||
...(focused
|
...(focused
|
||||||
|
|||||||
@@ -12,16 +12,13 @@ import {
|
|||||||
// eslint-disable-next-line import/no-unresolved
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import { ScreenContainer } from 'react-native-screens';
|
import { ScreenContainer } from 'react-native-screens';
|
||||||
import {
|
import {
|
||||||
PanGestureHandler,
|
NavigationHelpersContext,
|
||||||
GestureHandlerRootView,
|
|
||||||
} from 'react-native-gesture-handler';
|
|
||||||
import {
|
|
||||||
DrawerNavigationState,
|
DrawerNavigationState,
|
||||||
DrawerActions,
|
DrawerActions,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
import DrawerGestureContext from '../utils/DrawerGestureContext';
|
import { GestureHandlerRootView } from './GestureHandler';
|
||||||
import SafeAreaProviderCompat from './SafeAreaProviderCompat';
|
import SafeAreaProviderCompat from './SafeAreaProviderCompat';
|
||||||
import ResourceSavingScene from './ResourceSavingScene';
|
import ResourceSavingScene from './ResourceSavingScene';
|
||||||
import DrawerContent from './DrawerContent';
|
import DrawerContent from './DrawerContent';
|
||||||
@@ -93,8 +90,6 @@ export default function DrawerView({
|
|||||||
Dimensions.get('window')
|
Dimensions.get('window')
|
||||||
);
|
);
|
||||||
|
|
||||||
const drawerGestureRef = React.useRef<PanGestureHandler>(null);
|
|
||||||
|
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
|
||||||
const isDrawerOpen = state.history.some((it) => it.type === 'drawer');
|
const isDrawerOpen = state.history.some((it) => it.type === 'drawer');
|
||||||
@@ -201,9 +196,9 @@ export default function DrawerView({
|
|||||||
const { gestureEnabled, swipeEnabled } = descriptors[activeKey].options;
|
const { gestureEnabled, swipeEnabled } = descriptors[activeKey].options;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GestureHandlerWrapper style={styles.content}>
|
<NavigationHelpersContext.Provider value={navigation}>
|
||||||
<SafeAreaProviderCompat>
|
<GestureHandlerWrapper style={styles.content}>
|
||||||
<DrawerGestureContext.Provider value={drawerGestureRef}>
|
<SafeAreaProviderCompat>
|
||||||
<DrawerOpenContext.Provider value={isDrawerOpen}>
|
<DrawerOpenContext.Provider value={isDrawerOpen}>
|
||||||
<Drawer
|
<Drawer
|
||||||
open={isDrawerOpen}
|
open={isDrawerOpen}
|
||||||
@@ -211,10 +206,6 @@ export default function DrawerView({
|
|||||||
swipeEnabled={swipeEnabled}
|
swipeEnabled={swipeEnabled}
|
||||||
onOpen={handleDrawerOpen}
|
onOpen={handleDrawerOpen}
|
||||||
onClose={handleDrawerClose}
|
onClose={handleDrawerClose}
|
||||||
onGestureRef={(ref) => {
|
|
||||||
// @ts-ignore
|
|
||||||
drawerGestureRef.current = ref;
|
|
||||||
}}
|
|
||||||
gestureHandlerProps={gestureHandlerProps}
|
gestureHandlerProps={gestureHandlerProps}
|
||||||
drawerType={drawerType}
|
drawerType={drawerType}
|
||||||
drawerPosition={drawerPosition}
|
drawerPosition={drawerPosition}
|
||||||
@@ -251,9 +242,9 @@ export default function DrawerView({
|
|||||||
dimensions={dimensions}
|
dimensions={dimensions}
|
||||||
/>
|
/>
|
||||||
</DrawerOpenContext.Provider>
|
</DrawerOpenContext.Provider>
|
||||||
</DrawerGestureContext.Provider>
|
</SafeAreaProviderCompat>
|
||||||
</SafeAreaProviderCompat>
|
</GestureHandlerWrapper>
|
||||||
</GestureHandlerWrapper>
|
</NavigationHelpersContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
packages/drawer/src/views/GestureHandler.native.tsx
Normal file
23
packages/drawer/src/views/GestureHandler.native.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import {
|
||||||
|
PanGestureHandler as PanGestureHandlerNative,
|
||||||
|
PanGestureHandlerProperties,
|
||||||
|
} from 'react-native-gesture-handler';
|
||||||
|
import DrawerGestureContext from '../utils/DrawerGestureContext';
|
||||||
|
|
||||||
|
export function PanGestureHandler(props: PanGestureHandlerProperties) {
|
||||||
|
const gestureRef = React.useRef<PanGestureHandlerNative>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DrawerGestureContext.Provider value={gestureRef}>
|
||||||
|
<PanGestureHandlerNative {...props} />
|
||||||
|
</DrawerGestureContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
GestureHandlerRootView,
|
||||||
|
TapGestureHandler,
|
||||||
|
State as GestureState,
|
||||||
|
PanGestureHandlerGestureEvent,
|
||||||
|
} from 'react-native-gesture-handler';
|
||||||
31
packages/drawer/src/views/GestureHandler.tsx
Normal file
31
packages/drawer/src/views/GestureHandler.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { View } from 'react-native';
|
||||||
|
import type {
|
||||||
|
PanGestureHandlerProperties,
|
||||||
|
TapGestureHandlerProperties,
|
||||||
|
} from 'react-native-gesture-handler';
|
||||||
|
|
||||||
|
const Dummy: any = ({ children }: { children: React.ReactNode }) => (
|
||||||
|
<>{children}</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const PanGestureHandler = Dummy as React.ComponentType<
|
||||||
|
PanGestureHandlerProperties
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const TapGestureHandler = Dummy as React.ComponentType<
|
||||||
|
TapGestureHandlerProperties
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const GestureHandlerRootView = View;
|
||||||
|
|
||||||
|
export const GestureState = {
|
||||||
|
UNDETERMINED: 0,
|
||||||
|
FAILED: 1,
|
||||||
|
BEGAN: 2,
|
||||||
|
CANCELLED: 3,
|
||||||
|
ACTIVE: 4,
|
||||||
|
END: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type { PanGestureHandlerGestureEvent } from 'react-native-gesture-handler';
|
||||||
@@ -29,7 +29,7 @@ export default class ResourceSavingScene extends React.Component<Props> {
|
|||||||
styles.container,
|
styles.container,
|
||||||
Platform.OS === 'web'
|
Platform.OS === 'web'
|
||||||
? { display: isVisible ? 'flex' : 'none' }
|
? { display: isVisible ? 'flex' : 'none' }
|
||||||
: null,
|
: { overflow: 'hidden' },
|
||||||
style,
|
style,
|
||||||
]}
|
]}
|
||||||
collapsable={false}
|
collapsable={false}
|
||||||
@@ -52,7 +52,6 @@ export default class ResourceSavingScene extends React.Component<Props> {
|
|||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
overflow: 'hidden',
|
|
||||||
},
|
},
|
||||||
attached: {
|
attached: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
|||||||
@@ -3,6 +3,144 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [5.2.9](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.8...@react-navigation/material-bottom-tabs@5.2.9) (2020-05-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.8](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.7...@react-navigation/material-bottom-tabs@5.2.8) (2020-05-23)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.7](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.6...@react-navigation/material-bottom-tabs@5.2.7) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.6](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.5...@react-navigation/material-bottom-tabs@5.2.6) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.5](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.4...@react-navigation/material-bottom-tabs@5.2.5) (2020-05-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* center icons in material tab bar. fixes [#8248](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/issues/8248) ([51b4087](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/commit/51b40879bdb9cea5462a2291955513a88eb87340))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.4](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.3...@react-navigation/material-bottom-tabs@5.2.4) (2020-05-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.3](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.2...@react-navigation/material-bottom-tabs@5.2.3) (2020-05-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.2](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.1...@react-navigation/material-bottom-tabs@5.2.2) (2020-05-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.1](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.0...@react-navigation/material-bottom-tabs@5.2.1) (2020-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix building typescript definitions. closes [#8216](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/issues/8216) ([47a1229](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/commit/47a12298378747edd2d22e54dc1c8677f98c49b4))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.2.0](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.15...@react-navigation/material-bottom-tabs@5.2.0) (2020-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add generic type aliases for screen props ([bea14aa](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/commit/bea14aa26fd5cbfebc7973733c5cf1f44fd323aa)), closes [#7971](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/issues/7971)
|
||||||
|
* use links in bottom navigation tabs ([f384706](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/commit/f384706741f7e2422c284b65da10425f7af680c0))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.15](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.14...@react-navigation/material-bottom-tabs@5.1.15) (2020-05-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.14](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.13...@react-navigation/material-bottom-tabs@5.1.14) (2020-05-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.13](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.12...@react-navigation/material-bottom-tabs@5.1.13) (2020-05-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.12](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.11...@react-navigation/material-bottom-tabs@5.1.12) (2020-04-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.11](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.10...@react-navigation/material-bottom-tabs@5.1.11) (2020-04-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.10](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.9...@react-navigation/material-bottom-tabs@5.1.10) (2020-04-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.1.9](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.8...@react-navigation/material-bottom-tabs@5.1.9) (2020-04-17)
|
## [5.1.9](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.8...@react-navigation/material-bottom-tabs@5.1.9) (2020-04-17)
|
||||||
|
|
||||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/material-bottom-tabs",
|
"name": "@react-navigation/material-bottom-tabs",
|
||||||
"description": "Integration for bottom navigation component from react-native-paper",
|
"description": "Integration for bottom navigation component from react-native-paper",
|
||||||
"version": "5.1.9",
|
"version": "5.2.9",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -20,11 +20,13 @@
|
|||||||
"homepage": "https://reactnavigation.org/docs/material-bottom-tab-navigator.html",
|
"homepage": "https://reactnavigation.org/docs/material-bottom-tab-navigator.html",
|
||||||
"main": "lib/commonjs/index.js",
|
"main": "lib/commonjs/index.js",
|
||||||
"react-native": "src/index.tsx",
|
"react-native": "src/index.tsx",
|
||||||
|
"source": "src/index.tsx",
|
||||||
"module": "lib/module/index.js",
|
"module": "lib/module/index.js",
|
||||||
"types": "lib/typescript/src/index.d.ts",
|
"types": "lib/typescript/src/index.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"src",
|
||||||
"lib"
|
"lib",
|
||||||
|
"!**/__tests__"
|
||||||
],
|
],
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
@@ -35,15 +37,15 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.10.0",
|
"@react-native-community/bob": "^0.14.3",
|
||||||
"@react-navigation/native": "^5.1.6",
|
"@react-navigation/native": "^5.5.0",
|
||||||
"@types/react": "^16.9.23",
|
"@types/react": "^16.9.34",
|
||||||
"@types/react-native": "^0.61.22",
|
"@types/react-native": "^0.62.7",
|
||||||
"@types/react-native-vector-icons": "^6.4.5",
|
"@types/react-native-vector-icons": "^6.4.5",
|
||||||
"del-cli": "^3.0.0",
|
"del-cli": "^3.0.0",
|
||||||
"react": "~16.9.0",
|
"react": "~16.9.0",
|
||||||
"react-native": "~0.61.5",
|
"react-native": "~0.61.5",
|
||||||
"react-native-paper": "^3.7.0",
|
"react-native-paper": "^3.10.1",
|
||||||
"react-native-vector-icons": "^6.6.0",
|
"react-native-vector-icons": "^6.6.0",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.8.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,4 +14,5 @@ export { default as MaterialBottomTabView } from './views/MaterialBottomTabView'
|
|||||||
export type {
|
export type {
|
||||||
MaterialBottomTabNavigationOptions,
|
MaterialBottomTabNavigationOptions,
|
||||||
MaterialBottomTabNavigationProp,
|
MaterialBottomTabNavigationProp,
|
||||||
|
MaterialBottomTabScreenProps,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
NavigationHelpers,
|
NavigationHelpers,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
TabActionHelpers,
|
TabActionHelpers,
|
||||||
|
RouteProp,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
export type MaterialBottomTabNavigationEventMap = {
|
export type MaterialBottomTabNavigationEventMap = {
|
||||||
@@ -32,6 +33,14 @@ export type MaterialBottomTabNavigationProp<
|
|||||||
> &
|
> &
|
||||||
TabActionHelpers<ParamList>;
|
TabActionHelpers<ParamList>;
|
||||||
|
|
||||||
|
export type MaterialBottomTabScreenProps<
|
||||||
|
ParamList extends ParamListBase,
|
||||||
|
RouteName extends keyof ParamList = string
|
||||||
|
> = {
|
||||||
|
navigation: MaterialBottomTabNavigationProp<ParamList, RouteName>;
|
||||||
|
route: RouteProp<ParamList, RouteName>;
|
||||||
|
};
|
||||||
|
|
||||||
export type MaterialBottomTabNavigationOptions = {
|
export type MaterialBottomTabNavigationOptions = {
|
||||||
/**
|
/**
|
||||||
* Title text for the screen.
|
* Title text for the screen.
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet, Platform } from 'react-native';
|
||||||
import { BottomNavigation, DefaultTheme, DarkTheme } from 'react-native-paper';
|
import { BottomNavigation, DefaultTheme, DarkTheme } from 'react-native-paper';
|
||||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import {
|
import {
|
||||||
|
NavigationHelpersContext,
|
||||||
Route,
|
Route,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
TabActions,
|
TabActions,
|
||||||
useTheme,
|
useTheme,
|
||||||
|
useLinkBuilder,
|
||||||
|
Link,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -23,13 +26,14 @@ type Props = MaterialBottomTabNavigationConfig & {
|
|||||||
|
|
||||||
type Scene = { route: { key: string } };
|
type Scene = { route: { key: string } };
|
||||||
|
|
||||||
export default function MaterialBottomTabView({
|
function MaterialBottomTabViewInner({
|
||||||
state,
|
state,
|
||||||
navigation,
|
navigation,
|
||||||
descriptors,
|
descriptors,
|
||||||
...rest
|
...rest
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { dark, colors } = useTheme();
|
const { dark, colors } = useTheme();
|
||||||
|
const buildLink = useLinkBuilder();
|
||||||
|
|
||||||
const theme = React.useMemo(() => {
|
const theme = React.useMemo(() => {
|
||||||
const t = dark ? DarkTheme : DefaultTheme;
|
const t = dark ? DarkTheme : DefaultTheme;
|
||||||
@@ -56,6 +60,39 @@ export default function MaterialBottomTabView({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
renderScene={({ route }) => descriptors[route.key].render()}
|
renderScene={({ route }) => descriptors[route.key].render()}
|
||||||
|
renderTouchable={
|
||||||
|
Platform.OS === 'web'
|
||||||
|
? ({
|
||||||
|
onPress,
|
||||||
|
route,
|
||||||
|
accessibilityRole: _0,
|
||||||
|
borderless: _1,
|
||||||
|
centered: _2,
|
||||||
|
rippleColor: _3,
|
||||||
|
style,
|
||||||
|
...rest
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
{...rest}
|
||||||
|
// @ts-ignore
|
||||||
|
to={buildLink(route.name, route.params)}
|
||||||
|
accessibilityRole="link"
|
||||||
|
onPress={(e: any) => {
|
||||||
|
if (
|
||||||
|
!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) && // ignore clicks with modifier keys
|
||||||
|
(e.button == null || e.button === 0) // ignore everything but left clicks
|
||||||
|
) {
|
||||||
|
e.preventDefault();
|
||||||
|
onPress?.(e);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={[styles.touchable, style]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
renderIcon={({ route, focused, color }) => {
|
renderIcon={({ route, focused, color }) => {
|
||||||
const { options } = descriptors[route.key];
|
const { options } = descriptors[route.key];
|
||||||
|
|
||||||
@@ -66,8 +103,6 @@ export default function MaterialBottomTabView({
|
|||||||
color={color}
|
color={color}
|
||||||
size={24}
|
size={24}
|
||||||
style={styles.icon}
|
style={styles.icon}
|
||||||
importantForAccessibility="no-hide-descendants"
|
|
||||||
accessibilityElementsHidden
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -108,8 +143,20 @@ export default function MaterialBottomTabView({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function MaterialBottomTabView(props: Props) {
|
||||||
|
return (
|
||||||
|
<NavigationHelpersContext.Provider value={props.navigation}>
|
||||||
|
<MaterialBottomTabViewInner {...props} />
|
||||||
|
</NavigationHelpersContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
icon: {
|
icon: {
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
},
|
},
|
||||||
|
touchable: {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,140 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [5.2.9](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.8...@react-navigation/material-top-tabs@5.2.9) (2020-05-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.8](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.7...@react-navigation/material-top-tabs@5.2.8) (2020-05-23)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.7](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.6...@react-navigation/material-top-tabs@5.2.7) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.6](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.5...@react-navigation/material-top-tabs@5.2.6) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.5](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.4...@react-navigation/material-top-tabs@5.2.5) (2020-05-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.4](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.3...@react-navigation/material-top-tabs@5.2.4) (2020-05-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.3](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.2...@react-navigation/material-top-tabs@5.2.3) (2020-05-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.2](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.1...@react-navigation/material-top-tabs@5.2.2) (2020-05-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.1](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.0...@react-navigation/material-top-tabs@5.2.1) (2020-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix building typescript definitions. closes [#8216](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/issues/8216) ([47a1229](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/commit/47a12298378747edd2d22e54dc1c8677f98c49b4))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.2.0](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.15...@react-navigation/material-top-tabs@5.2.0) (2020-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add generic type aliases for screen props ([bea14aa](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/commit/bea14aa26fd5cbfebc7973733c5cf1f44fd323aa)), closes [#7971](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/issues/7971)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.15](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.14...@react-navigation/material-top-tabs@5.1.15) (2020-05-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.14](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.13...@react-navigation/material-top-tabs@5.1.14) (2020-05-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.13](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.12...@react-navigation/material-top-tabs@5.1.13) (2020-05-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.12](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.11...@react-navigation/material-top-tabs@5.1.12) (2020-04-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.11](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.10...@react-navigation/material-top-tabs@5.1.11) (2020-04-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.10](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.9...@react-navigation/material-top-tabs@5.1.10) (2020-04-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.1.9](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.8...@react-navigation/material-top-tabs@5.1.9) (2020-04-17)
|
## [5.1.9](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.8...@react-navigation/material-top-tabs@5.1.9) (2020-04-17)
|
||||||
|
|
||||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/material-top-tabs",
|
"name": "@react-navigation/material-top-tabs",
|
||||||
"description": "Integration for the animated tab view component from react-native-tab-view",
|
"description": "Integration for the animated tab view component from react-native-tab-view",
|
||||||
"version": "5.1.9",
|
"version": "5.2.9",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -20,11 +20,13 @@
|
|||||||
"homepage": "https://reactnavigation.org/docs/material-top-tab-navigator.html",
|
"homepage": "https://reactnavigation.org/docs/material-top-tab-navigator.html",
|
||||||
"main": "lib/commonjs/index.js",
|
"main": "lib/commonjs/index.js",
|
||||||
"react-native": "src/index.tsx",
|
"react-native": "src/index.tsx",
|
||||||
|
"source": "src/index.tsx",
|
||||||
"module": "lib/module/index.js",
|
"module": "lib/module/index.js",
|
||||||
"types": "lib/typescript/src/index.d.ts",
|
"types": "lib/typescript/src/index.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"src",
|
||||||
"lib"
|
"lib",
|
||||||
|
"!**/__tests__"
|
||||||
],
|
],
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
@@ -38,15 +40,15 @@
|
|||||||
"color": "^3.1.2"
|
"color": "^3.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.10.0",
|
"@react-native-community/bob": "^0.14.3",
|
||||||
"@react-navigation/native": "^5.1.6",
|
"@react-navigation/native": "^5.5.0",
|
||||||
"@types/react": "^16.9.23",
|
"@types/react": "^16.9.34",
|
||||||
"@types/react-native": "^0.61.22",
|
"@types/react-native": "^0.62.7",
|
||||||
"del-cli": "^3.0.0",
|
"del-cli": "^3.0.0",
|
||||||
"react": "~16.9.0",
|
"react": "~16.9.0",
|
||||||
"react-native": "~0.61.5",
|
"react-native": "~0.61.5",
|
||||||
"react-native-gesture-handler": "^1.6.0",
|
"react-native-gesture-handler": "^1.6.0",
|
||||||
"react-native-reanimated": "^1.7.0",
|
"react-native-reanimated": "^1.8.0",
|
||||||
"react-native-tab-view": "^2.14.0",
|
"react-native-tab-view": "^2.14.0",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.8.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export { default as MaterialTopTabBar } from './views/MaterialTopTabBar';
|
|||||||
export type {
|
export type {
|
||||||
MaterialTopTabNavigationOptions,
|
MaterialTopTabNavigationOptions,
|
||||||
MaterialTopTabNavigationProp,
|
MaterialTopTabNavigationProp,
|
||||||
|
MaterialTopTabScreenProps,
|
||||||
MaterialTopTabBarProps,
|
MaterialTopTabBarProps,
|
||||||
MaterialTopTabBarOptions,
|
MaterialTopTabBarOptions,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
NavigationProp,
|
NavigationProp,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
TabActionHelpers,
|
TabActionHelpers,
|
||||||
|
RouteProp,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
export type MaterialTopTabNavigationEventMap = {
|
export type MaterialTopTabNavigationEventMap = {
|
||||||
@@ -46,6 +47,14 @@ export type MaterialTopTabNavigationProp<
|
|||||||
> &
|
> &
|
||||||
TabActionHelpers<ParamList>;
|
TabActionHelpers<ParamList>;
|
||||||
|
|
||||||
|
export type MaterialTopTabScreenProps<
|
||||||
|
ParamList extends ParamListBase,
|
||||||
|
RouteName extends keyof ParamList = string
|
||||||
|
> = {
|
||||||
|
navigation: MaterialTopTabNavigationProp<ParamList, RouteName>;
|
||||||
|
route: RouteProp<ParamList, RouteName>;
|
||||||
|
};
|
||||||
|
|
||||||
export type MaterialTopTabNavigationOptions = {
|
export type MaterialTopTabNavigationOptions = {
|
||||||
/**
|
/**
|
||||||
* Title text for the screen.
|
* Title text for the screen.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { TabView, SceneRendererProps } from 'react-native-tab-view';
|
import { TabView, SceneRendererProps } from 'react-native-tab-view';
|
||||||
import {
|
import {
|
||||||
|
NavigationHelpersContext,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
TabActions,
|
TabActions,
|
||||||
useTheme,
|
useTheme,
|
||||||
@@ -45,25 +46,27 @@ export default function MaterialTopTabView({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TabView
|
<NavigationHelpersContext.Provider value={navigation}>
|
||||||
{...rest}
|
<TabView
|
||||||
onIndexChange={(index) =>
|
{...rest}
|
||||||
navigation.dispatch({
|
onIndexChange={(index) =>
|
||||||
...TabActions.jumpTo(state.routes[index].name),
|
navigation.dispatch({
|
||||||
target: state.key,
|
...TabActions.jumpTo(state.routes[index].name),
|
||||||
})
|
target: state.key,
|
||||||
}
|
})
|
||||||
renderScene={({ route }) => descriptors[route.key].render()}
|
}
|
||||||
navigationState={state}
|
renderScene={({ route }) => descriptors[route.key].render()}
|
||||||
renderTabBar={renderTabBar}
|
navigationState={state}
|
||||||
renderPager={pager}
|
renderTabBar={renderTabBar}
|
||||||
renderLazyPlaceholder={lazyPlaceholder}
|
renderPager={pager}
|
||||||
onSwipeStart={() => navigation.emit({ type: 'swipeStart' })}
|
renderLazyPlaceholder={lazyPlaceholder}
|
||||||
onSwipeEnd={() => navigation.emit({ type: 'swipeEnd' })}
|
onSwipeStart={() => navigation.emit({ type: 'swipeStart' })}
|
||||||
sceneContainerStyle={[
|
onSwipeEnd={() => navigation.emit({ type: 'swipeEnd' })}
|
||||||
{ backgroundColor: colors.background },
|
sceneContainerStyle={[
|
||||||
sceneContainerStyle,
|
{ backgroundColor: colors.background },
|
||||||
]}
|
sceneContainerStyle,
|
||||||
/>
|
]}
|
||||||
|
/>
|
||||||
|
</NavigationHelpersContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,184 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [5.5.0](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.4.3...@react-navigation/native@5.5.0) (2020-05-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* export types from /native ([af1722d](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/af1722d1e915f3ec234df202f74c4b4c631472c7))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add a `ServerContainer` component for SSR ([#8297](https://github.com/react-navigation/react-navigation/tree/master/packages/native/issues/8297)) ([68e750d](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/68e750d5a6d198a2f5bdb86ba631de0a27732943))
|
||||||
|
* add ref to get current options in `ServerContainer` ([#8333](https://github.com/react-navigation/react-navigation/tree/master/packages/native/issues/8333)) ([0b1a718](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/0b1a718756e208d84b20e45ca56004332308ad54))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.4.3](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.4.2...@react-navigation/native@5.4.3) (2020-05-23)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/native
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.4.2](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.4.1...@react-navigation/native@5.4.2) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/native
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.4.1](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.4.0...@react-navigation/native@5.4.1) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/native
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.4.0](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.3.2...@react-navigation/native@5.4.0) (2020-05-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix types for linking options ([d14f38b](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/d14f38b80ad569d5828c1919cea426c659173924))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add a PathConfig type ([60cb3c9](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/60cb3c9ba76d7ef166c9fe8b55f23728975b5b6e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.2](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.3.1...@react-navigation/native@5.3.2) (2020-05-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/native
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.1](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.3.0...@react-navigation/native@5.3.1) (2020-05-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/native
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.3.0](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.2.6...@react-navigation/native@5.3.0) (2020-05-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* initialState should take priority over deep link ([039017b](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/039017bc2af69120d2d10e8f2c8a62919c37eb65))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.6](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.2.5...@react-navigation/native@5.2.6) (2020-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix building typescript definitions. closes [#8216](https://github.com/react-navigation/react-navigation/tree/master/packages/native/issues/8216) ([47a1229](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/47a12298378747edd2d22e54dc1c8677f98c49b4))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.5](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.2.4...@react-navigation/native@5.2.5) (2020-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* return a promise-like from getInitialState ([#8210](https://github.com/react-navigation/react-navigation/tree/master/packages/native/issues/8210)) ([85ae378](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/85ae378d8cb1073895b281e13ebccee881d4c062))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.4](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.2.3...@react-navigation/native@5.2.4) (2020-05-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* return undefined for buildLink if linking is not enabled ([9fd2635](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/9fd2635756362c8da79656b4d9b101bebaaf7003))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.3](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.2.2...@react-navigation/native@5.2.3) (2020-05-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* default linking enabled to true ([c7b8e2e](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/c7b8e2e9666733143eef156b27f3e4995c36b856))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.2](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.2.1...@react-navigation/native@5.2.2) (2020-05-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* don't throw when using 'useLinking'. fixes [#8171](https://github.com/react-navigation/react-navigation/tree/master/packages/native/issues/8171) ([10eca8b](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/10eca8b92edbce6dbef8abaf189e4b59a29b3748))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.1](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.2.0...@react-navigation/native@5.2.1) (2020-04-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* render fallback only if linking is enabled. closes [#8161](https://github.com/react-navigation/react-navigation/tree/master/packages/native/issues/8161) ([1c075ff](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/1c075ffb169d233ed0515efea264a5a69b4de52e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.2.0](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.7...@react-navigation/native@5.2.0) (2020-04-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add catch to thenable returned by getInitialState ([d6fa279](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/d6fa279d9371c7a6403d10d209a2a64147891c63))
|
||||||
|
* return onPress instead of onClick for useLinkProps ([ae5442e](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/ae5442ebe812b91fa1f12164f27d1aeed918ab0e))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add `useLinkBuilder` hook to build links ([2792f43](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/2792f438fe45428fe193e3708fee7ad61966cbf4))
|
||||||
|
* add a useLinkProps hook ([f2291d1](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/f2291d110faa2aa8e10c9133c1c0c28d54af7917))
|
||||||
|
* add action prop to Link ([942d2be](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/942d2be2c72720469475ce12ec8df23825994dbf))
|
||||||
|
* add Link component as useLinkTo hook for navigating to links ([2573b5b](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/2573b5beaac1240434e52f3f57bb29da2f541c88))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.7](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.6...@react-navigation/native@5.1.7) (2020-04-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/native
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.1.6](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.5...@react-navigation/native@5.1.6) (2020-04-17)
|
## [5.1.6](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.5...@react-navigation/native@5.1.6) (2020-04-17)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user