mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-16 17:42:33 +08:00
Compare commits
85 Commits
@react-nav
...
@react-nav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21f61d6eeb | ||
|
|
8774ca97e1 | ||
|
|
e653d55479 | ||
|
|
78afbffe97 | ||
|
|
762cc44578 | ||
|
|
c3bd349d77 | ||
|
|
5dcaf903f3 | ||
|
|
2d66ef93ec | ||
|
|
4fe72e3ce7 | ||
|
|
ab1f79c096 | ||
|
|
9305bfa939 | ||
|
|
0c3c450f5f | ||
|
|
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 |
@@ -1,46 +1,55 @@
|
|||||||
version: 2
|
version: 2.1
|
||||||
|
|
||||||
defaults: &defaults
|
executors:
|
||||||
|
default:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:10
|
- image: circleci/node:10
|
||||||
working_directory: ~/project
|
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 }}-
|
||||||
|
- yarn-packages-v1-
|
||||||
- run:
|
- run:
|
||||||
name: Install project dependencies
|
name: Install project dependencies
|
||||||
command: yarn install --frozen-lockfile
|
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
|
name: Lint files
|
||||||
command: yarn lint
|
command: yarn lint
|
||||||
- run:
|
- run:
|
||||||
name: Typecheck files
|
name: Typecheck files
|
||||||
command: yarn typescript
|
command: yarn typescript
|
||||||
|
|
||||||
unit-tests:
|
unit-tests:
|
||||||
<<: *defaults
|
executor: default
|
||||||
steps:
|
steps:
|
||||||
- attach_workspace:
|
- attach_project
|
||||||
at: ~/project
|
|
||||||
- run:
|
- run:
|
||||||
name: Run unit tests
|
name: Run unit tests
|
||||||
command: yarn test --coverage
|
command: yarn test --coverage
|
||||||
@@ -50,11 +59,11 @@ jobs:
|
|||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: coverage
|
path: coverage
|
||||||
destination: coverage
|
destination: coverage
|
||||||
|
|
||||||
integration-tests:
|
integration-tests:
|
||||||
<<: *defaults
|
executor: default
|
||||||
steps:
|
steps:
|
||||||
- attach_workspace:
|
- attach_project
|
||||||
at: ~/project
|
|
||||||
- run:
|
- run:
|
||||||
name: Install Headless Chrome dependencies
|
name: Install Headless Chrome dependencies
|
||||||
command: |
|
command: |
|
||||||
@@ -70,11 +79,11 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Run integration tests
|
name: Run integration tests
|
||||||
command: yarn example test --maxWorkers=2
|
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
|
name: Build packages in the monorepo
|
||||||
command: yarn lerna run prepare
|
command: yarn lerna run prepare
|
||||||
@@ -83,7 +92,6 @@ jobs:
|
|||||||
command: node scripts/check-types-path.js
|
command: node scripts/check-types-path.js
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
|
||||||
build-and-test:
|
build-and-test:
|
||||||
jobs:
|
jobs:
|
||||||
- install-dependencies
|
- install-dependencies
|
||||||
|
|||||||
14
.github/workflows/expo-preview.yml
vendored
14
.github/workflows/expo-preview.yml
vendored
@@ -23,20 +23,16 @@ 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: '**/node_modules'
|
||||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-yarn-
|
|
||||||
|
|
||||||
- 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
|
||||||
|
|||||||
13
.github/workflows/expo.yml
vendored
13
.github/workflows/expo.yml
vendored
@@ -25,19 +25,16 @@ 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: '**/node_modules'
|
||||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-yarn-
|
|
||||||
|
|
||||||
- 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
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ beforeEach(async () => {
|
|||||||
|
|
||||||
it('loads the article page', async () => {
|
it('loads the article page', async () => {
|
||||||
expect(await page.evaluate(() => location.pathname + location.search)).toBe(
|
expect(await page.evaluate(() => location.pathname + location.search)).toBe(
|
||||||
'/link-component/Article?author=Gandalf'
|
'/link-component/article/gandalf'
|
||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
((await page.accessibility.snapshot()) as any)?.children?.find(
|
((await page.accessibility.snapshot()) as any)?.children?.find(
|
||||||
@@ -16,24 +16,24 @@ it('loads the article page', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('goes to the album page and goes back', async () => {
|
it('goes to the album page and goes back', async () => {
|
||||||
await page.click('[href="/link-component/Album"]');
|
await page.click('[href="/link-component/music"]');
|
||||||
|
|
||||||
expect(await page.evaluate(() => location.pathname + location.search)).toBe(
|
expect(await page.evaluate(() => location.pathname + location.search)).toBe(
|
||||||
'/link-component/Album'
|
'/link-component/music'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
((await page.accessibility.snapshot()) as any)?.children?.find(
|
((await page.accessibility.snapshot()) as any)?.children?.find(
|
||||||
(it: any) => it.role === 'heading'
|
(it: any) => it.role === 'heading'
|
||||||
)?.name
|
)?.name
|
||||||
).toBe('Album');
|
).toBe('Albums');
|
||||||
|
|
||||||
await page.click('[aria-label="Article by Gandalf, back"]');
|
await page.click('[aria-label="Article by Gandalf, back"]');
|
||||||
|
|
||||||
await page.waitForNavigation();
|
await page.waitForNavigation();
|
||||||
|
|
||||||
expect(await page.evaluate(() => location.pathname + location.search)).toBe(
|
expect(await page.evaluate(() => location.pathname + location.search)).toBe(
|
||||||
'/link-component/Article?author=Gandalf'
|
'/link-component/article/gandalf'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
|||||||
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,8 +1,16 @@
|
|||||||
import { setup } from 'jest-dev-server';
|
import { setup } from 'jest-dev-server';
|
||||||
|
|
||||||
export default async function () {
|
export default async function () {
|
||||||
await setup({
|
await setup([
|
||||||
|
{
|
||||||
command: 'yarn serve -l 3579 web-build',
|
command: 'yarn serve -l 3579 web-build',
|
||||||
|
launchTimeout: 50000,
|
||||||
port: 3579,
|
port: 3579,
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
command: 'yarn server',
|
||||||
|
launchTimeout: 50000,
|
||||||
|
port: 3275,
|
||||||
|
},
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -8,25 +8,24 @@ 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');
|
||||||
|
|
||||||
// Get the list of dependencies for all packages in the monorepo
|
const workspaces = fs
|
||||||
const modules = ['@expo/vector-icons']
|
|
||||||
.concat(
|
|
||||||
...fs
|
|
||||||
// List all packages under `packages/`
|
// List all packages under `packages/`
|
||||||
.readdirSync(packages)
|
.readdirSync(packages)
|
||||||
// Ignore hidden files such as .DS_Store
|
// Ignore hidden files such as .DS_Store
|
||||||
.filter((p) => !p.startsWith('.'))
|
.filter((p) => !p.startsWith('.'))
|
||||||
.map((p) => {
|
.map((p) => path.join(packages, p));
|
||||||
|
|
||||||
|
// Get the list of dependencies for all packages in the monorepo
|
||||||
|
const modules = ['@expo/vector-icons']
|
||||||
|
.concat(
|
||||||
|
...workspaces.map((it) => {
|
||||||
const pak = JSON.parse(
|
const pak = JSON.parse(
|
||||||
fs.readFileSync(path.join(packages, p, 'package.json'), 'utf8')
|
fs.readFileSync(path.join(it, '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()
|
||||||
@@ -45,14 +44,15 @@ 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))}\\/.*$`)
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"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",
|
||||||
@@ -9,37 +9,50 @@
|
|||||||
"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"
|
"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/cheerio": "^0.22.18",
|
||||||
"@types/jest-dev-server": "^4.2.0",
|
"@types/jest-dev-server": "^4.2.0",
|
||||||
"@types/react": "^16.9.23",
|
"@types/koa": "^2.11.3",
|
||||||
"@types/react-native": "^0.60.22",
|
"@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",
|
||||||
"jest": "^25.2.7",
|
"expo-cli": "^3.20.1",
|
||||||
|
"jest": "^26.0.1",
|
||||||
"jest-dev-server": "^4.4.0",
|
"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",
|
"playwright": "^0.14.0",
|
||||||
"serve": "^11.3.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="min-height: 100%">
|
||||||
|
<div id="root" style="display: flex; min-height: 100vh">
|
||||||
|
${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;
|
||||||
|
})
|
||||||
|
);
|
||||||
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,7 +1,11 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Platform } from 'react-native';
|
import { View, ScrollView, StyleSheet, Platform } from 'react-native';
|
||||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
import { Button } from 'react-native-paper';
|
||||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
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';
|
||||||
@@ -23,6 +27,36 @@ 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() {
|
||||||
@@ -62,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'),
|
||||||
@@ -71,3 +105,13 @@ export default function BottomTabsScreen() {
|
|||||||
</BottomTabs.Navigator>
|
</BottomTabs.Navigator>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
buttons: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
padding: 8,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
margin: 8,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ 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>;
|
||||||
@@ -53,24 +53,24 @@ const ArticleScreen = ({
|
|||||||
<ScrollView>
|
<ScrollView>
|
||||||
<View style={styles.buttons}>
|
<View style={styles.buttons}>
|
||||||
<Link
|
<Link
|
||||||
to="/link-component/Album"
|
to="/link-component/music"
|
||||||
style={[styles.button, { padding: 8 }]}
|
style={[styles.button, { padding: 8 }]}
|
||||||
>
|
>
|
||||||
Go to /link-component/Album
|
Go to /link-component/music
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/link-component/Album"
|
to="/link-component/music"
|
||||||
action={StackActions.replace('Album')}
|
action={StackActions.replace('Albums')}
|
||||||
style={[styles.button, { padding: 8 }]}
|
style={[styles.button, { padding: 8 }]}
|
||||||
>
|
>
|
||||||
Replace with /link-component/Album
|
Replace with /link-component/music
|
||||||
</Link>
|
</Link>
|
||||||
<LinkButton
|
<LinkButton
|
||||||
to="/link-component/Album"
|
to="/link-component/music"
|
||||||
mode="contained"
|
mode="contained"
|
||||||
style={styles.button}
|
style={styles.button}
|
||||||
>
|
>
|
||||||
Go to /link-component/Album
|
Go to /link-component/music
|
||||||
</LinkButton>
|
</LinkButton>
|
||||||
<Button
|
<Button
|
||||||
mode="outlined"
|
mode="outlined"
|
||||||
@@ -97,17 +97,17 @@ const AlbumsScreen = ({
|
|||||||
<ScrollView>
|
<ScrollView>
|
||||||
<View style={styles.buttons}>
|
<View style={styles.buttons}>
|
||||||
<Link
|
<Link
|
||||||
to="/link-component/Article?author=Babel"
|
to="/link-component/article/babel"
|
||||||
style={[styles.button, { padding: 8 }]}
|
style={[styles.button, { padding: 8 }]}
|
||||||
>
|
>
|
||||||
Go to /link-component/Article
|
Go to /link-component/article
|
||||||
</Link>
|
</Link>
|
||||||
<LinkButton
|
<LinkButton
|
||||||
to="/link-component/Article?author=Babel"
|
to="/link-component/article/babel"
|
||||||
mode="contained"
|
mode="contained"
|
||||||
style={styles.button}
|
style={styles.button}
|
||||||
>
|
>
|
||||||
Go to /link-component/Article
|
Go to /link-component/article
|
||||||
</LinkButton>
|
</LinkButton>
|
||||||
<Button
|
<Button
|
||||||
mode="outlined"
|
mode="outlined"
|
||||||
@@ -144,9 +144,9 @@ 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={{ title: 'Album' }}
|
options={{ title: 'Albums' }}
|
||||||
/>
|
/>
|
||||||
</SimpleStack.Navigator>
|
</SimpleStack.Navigator>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ 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>;
|
||||||
@@ -31,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
|
||||||
@@ -112,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>
|
||||||
);
|
);
|
||||||
|
|||||||
40
example/src/Screens/NotFound.tsx
Normal file
40
example/src/Screens/NotFound.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
|
import { Button } from 'react-native-paper';
|
||||||
|
|
||||||
|
const NotFoundScreen = ({
|
||||||
|
navigation,
|
||||||
|
}: {
|
||||||
|
navigation: StackNavigationProp<{ Home: undefined }>;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.title}>404 Not Found</Text>
|
||||||
|
<Button
|
||||||
|
mode="contained"
|
||||||
|
onPress={() => navigation.navigate('Home')}
|
||||||
|
style={styles.button}
|
||||||
|
>
|
||||||
|
Go to home
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NotFoundScreen;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
title: {
|
||||||
|
fontSize: 36,
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: 8,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
margin: 24,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -13,7 +13,7 @@ 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>;
|
||||||
@@ -63,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
|
||||||
@@ -136,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,21 +1,30 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { View, StyleSheet, ScrollView, Alert, Platform } from 'react-native';
|
import {
|
||||||
|
Animated,
|
||||||
|
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,
|
||||||
StackNavigationProp,
|
StackNavigationProp,
|
||||||
HeaderBackground,
|
HeaderBackground,
|
||||||
useHeaderHeight,
|
useHeaderHeight,
|
||||||
|
Header,
|
||||||
|
StackHeaderProps,
|
||||||
} 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>;
|
||||||
@@ -34,7 +43,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
|
||||||
@@ -91,6 +100,25 @@ type Props = Partial<React.ComponentProps<typeof SimpleStack.Navigator>> & {
|
|||||||
navigation: StackNavigationProp<ParamListBase>;
|
navigation: StackNavigationProp<ParamListBase>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function CustomHeader(props: StackHeaderProps) {
|
||||||
|
const { current, next } = props.scene.progress;
|
||||||
|
|
||||||
|
const progress = Animated.add(current, next || 0);
|
||||||
|
const opacity = progress.interpolate({
|
||||||
|
inputRange: [0, 1, 2],
|
||||||
|
outputRange: [0, 1, 0],
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header {...props} />
|
||||||
|
<Animated.Text style={[styles.banner, { opacity }]}>
|
||||||
|
Why hello there, pardner!
|
||||||
|
</Animated.Text>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function SimpleStackScreen({ navigation, ...rest }: Props) {
|
export default function SimpleStackScreen({ navigation, ...rest }: Props) {
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
@@ -103,6 +131,7 @@ export default function SimpleStackScreen({ navigation, ...rest }: Props) {
|
|||||||
component={ArticleScreen}
|
component={ArticleScreen}
|
||||||
options={({ route }) => ({
|
options={({ route }) => ({
|
||||||
title: `Article by ${route.params?.author}`,
|
title: `Article by ${route.params?.author}`,
|
||||||
|
header: CustomHeader,
|
||||||
headerTintColor: '#fff',
|
headerTintColor: '#fff',
|
||||||
headerStyle: { backgroundColor: '#ff005d' },
|
headerStyle: { backgroundColor: '#ff005d' },
|
||||||
headerBackTitleVisible: false,
|
headerBackTitleVisible: false,
|
||||||
@@ -131,10 +160,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: () => (
|
||||||
@@ -160,4 +189,10 @@ const styles = StyleSheet.create({
|
|||||||
button: {
|
button: {
|
||||||
margin: 8,
|
margin: 8,
|
||||||
},
|
},
|
||||||
|
banner: {
|
||||||
|
textAlign: 'center',
|
||||||
|
color: 'tomato',
|
||||||
|
backgroundColor: 'papayawhip',
|
||||||
|
padding: 4,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
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} />;
|
||||||
|
}
|
||||||
@@ -7,13 +7,11 @@ import {
|
|||||||
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,
|
||||||
@@ -28,6 +26,7 @@ import {
|
|||||||
NavigationContainer,
|
NavigationContainer,
|
||||||
DefaultTheme,
|
DefaultTheme,
|
||||||
DarkTheme,
|
DarkTheme,
|
||||||
|
PathConfig,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
import {
|
import {
|
||||||
createDrawerNavigator,
|
createDrawerNavigator,
|
||||||
@@ -35,11 +34,11 @@ 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 AsyncStorage from './AsyncStorage';
|
||||||
import LinkingPrefixes from './LinkingPrefixes';
|
import LinkingPrefixes from './LinkingPrefixes';
|
||||||
import SettingsItem from './Shared/SettingsItem';
|
import SettingsItem from './Shared/SettingsItem';
|
||||||
@@ -50,6 +49,7 @@ import StackHeaderCustomization from './Screens/StackHeaderCustomization';
|
|||||||
import BottomTabs from './Screens/BottomTabs';
|
import BottomTabs from './Screens/BottomTabs';
|
||||||
import MaterialTopTabsScreen from './Screens/MaterialTopTabs';
|
import MaterialTopTabsScreen from './Screens/MaterialTopTabs';
|
||||||
import MaterialBottomTabs from './Screens/MaterialBottomTabs';
|
import MaterialBottomTabs from './Screens/MaterialBottomTabs';
|
||||||
|
import NotFound from './Screens/NotFound';
|
||||||
import DynamicTabs from './Screens/DynamicTabs';
|
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';
|
||||||
@@ -70,6 +70,7 @@ type RootDrawerParamList = {
|
|||||||
|
|
||||||
type RootStackParamList = {
|
type RootStackParamList = {
|
||||||
Home: undefined;
|
Home: undefined;
|
||||||
|
NotFound: undefined;
|
||||||
} & {
|
} & {
|
||||||
[P in keyof typeof SCREENS]: undefined;
|
[P in keyof typeof SCREENS]: undefined;
|
||||||
};
|
};
|
||||||
@@ -125,12 +126,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 [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
|
||||||
>();
|
>();
|
||||||
@@ -138,19 +137,19 @@ export default function App() {
|
|||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const restoreState = async () => {
|
const restoreState = async () => {
|
||||||
try {
|
try {
|
||||||
let state;
|
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;
|
const state = savedState ? JSON.parse(savedState) : undefined;
|
||||||
}
|
|
||||||
|
|
||||||
if (state !== undefined) {
|
if (state !== undefined) {
|
||||||
setInitialState(state);
|
setInitialState(state);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
const themeName = await AsyncStorage.getItem(THEME_PERSISTENCE_KEY);
|
const themeName = await AsyncStorage.getItem(THEME_PERSISTENCE_KEY);
|
||||||
@@ -225,17 +224,43 @@ export default function App() {
|
|||||||
Root: {
|
Root: {
|
||||||
path: '',
|
path: '',
|
||||||
initialRouteName: 'Home',
|
initialRouteName: 'Home',
|
||||||
screens: Object.keys(SCREENS).reduce<{ [key: string]: string }>(
|
screens: Object.keys(SCREENS).reduce<PathConfig>(
|
||||||
(acc, name) => {
|
(acc, name) => {
|
||||||
// Convert screen names such as SimpleStack to kebab case (simple-stack)
|
// Convert screen names such as SimpleStack to kebab case (simple-stack)
|
||||||
acc[name] = name
|
const path = name
|
||||||
.replace(/([A-Z]+)/g, '-$1')
|
.replace(/([A-Z]+)/g, '-$1')
|
||||||
.replace(/^-/, '')
|
.replace(/^-/, '')
|
||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
|
|
||||||
|
acc[name] = {
|
||||||
|
path,
|
||||||
|
screens: {
|
||||||
|
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',
|
||||||
|
Dialog: 'dialog',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
},
|
},
|
||||||
{ Home: '' }
|
{
|
||||||
|
Home: '',
|
||||||
|
NotFound: '*',
|
||||||
|
}
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -290,12 +315,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 />
|
||||||
@@ -325,6 +345,11 @@ export default function App() {
|
|||||||
</ScrollView>
|
</ScrollView>
|
||||||
)}
|
)}
|
||||||
</Stack.Screen>
|
</Stack.Screen>
|
||||||
|
<Stack.Screen
|
||||||
|
name="NotFound"
|
||||||
|
component={NotFound}
|
||||||
|
options={{ title: 'Oops!' }}
|
||||||
|
/>
|
||||||
{(Object.keys(SCREENS) as (keyof typeof SCREENS)[]).map(
|
{(Object.keys(SCREENS) as (keyof typeof SCREENS)[]).map(
|
||||||
(name) => (
|
(name) => (
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
|
|||||||
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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -24,17 +24,19 @@ 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)
|
||||||
|
.filter((name) => !name.startsWith('.'))
|
||||||
|
.forEach((name) => {
|
||||||
config.resolve.alias[`@react-navigation/${name}`] = path.resolve(
|
config.resolve.alias[`@react-navigation/${name}`] = path.resolve(
|
||||||
packages,
|
packages,
|
||||||
name,
|
name,
|
||||||
'src'
|
require(`../packages/${name}/package.json`).source
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
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,131 @@
|
|||||||
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.2](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.5.1...@react-navigation/bottom-tabs@5.5.2) (2020-06-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [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)
|
## [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
|
**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.3.1",
|
"version": "5.5.2",
|
||||||
"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.2.1",
|
"@react-navigation/native": "^5.5.1",
|
||||||
"@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,5 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {
|
||||||
|
Animated,
|
||||||
TouchableWithoutFeedbackProps,
|
TouchableWithoutFeedbackProps,
|
||||||
StyleProp,
|
StyleProp,
|
||||||
TextStyle,
|
TextStyle,
|
||||||
@@ -13,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 = {
|
||||||
@@ -45,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.
|
||||||
@@ -188,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 & {
|
||||||
|
|||||||
@@ -53,52 +53,60 @@ export default function BottomTabBar({
|
|||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
const buildLink = useLinkBuilder();
|
const buildLink = useLinkBuilder();
|
||||||
|
|
||||||
const [dimensions, setDimensions] = React.useState(() => {
|
const focusedRoute = state.routes[state.index];
|
||||||
const { height = 0, width = 0 } = Dimensions.get('window');
|
const focusedDescriptor = descriptors[focusedRoute.key];
|
||||||
|
const focusedOptions = focusedDescriptor.options;
|
||||||
|
|
||||||
return { height, width };
|
const [isKeyboardShown, setIsKeyboardShown] = React.useState(false);
|
||||||
});
|
|
||||||
|
|
||||||
const [layout, setLayout] = React.useState({
|
const shouldShowTabBar =
|
||||||
height: 0,
|
focusedOptions.tabBarVisible !== false &&
|
||||||
width: dimensions.width,
|
!(keyboardHidesTabBar && isKeyboardShown);
|
||||||
});
|
|
||||||
const [keyboardShown, setKeyboardShown] = React.useState(false);
|
|
||||||
|
|
||||||
const [visible] = React.useState(() => new Animated.Value(1));
|
const [isTabBarHidden, setIsTabBarHidden] = React.useState(!shouldShowTabBar);
|
||||||
|
|
||||||
const { routes } = state;
|
const [visible] = React.useState(
|
||||||
|
() => new Animated.Value(shouldShowTabBar ? 1 : 0)
|
||||||
|
);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (keyboardShown) {
|
if (shouldShowTabBar) {
|
||||||
Animated.timing(visible, {
|
|
||||||
toValue: 0,
|
|
||||||
duration: 200,
|
|
||||||
useNativeDriver,
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
}, [keyboardShown, visible]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const handleOrientationChange = ({ window }: { window: ScaledSize }) => {
|
|
||||||
setDimensions(window);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleKeyboardShow = () => setKeyboardShown(true);
|
|
||||||
|
|
||||||
const handleKeyboardHide = () =>
|
|
||||||
Animated.timing(visible, {
|
Animated.timing(visible, {
|
||||||
toValue: 1,
|
toValue: 1,
|
||||||
duration: 250,
|
duration: 250,
|
||||||
useNativeDriver,
|
useNativeDriver,
|
||||||
}).start(({ finished }) => {
|
}).start(({ finished }) => {
|
||||||
if (finished) {
|
if (finished) {
|
||||||
setKeyboardShown(false);
|
setIsTabBarHidden(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
setIsTabBarHidden(true);
|
||||||
|
|
||||||
|
Animated.timing(visible, {
|
||||||
|
toValue: 0,
|
||||||
|
duration: 200,
|
||||||
|
useNativeDriver,
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
}, [shouldShowTabBar, visible]);
|
||||||
|
|
||||||
|
const [dimensions, setDimensions] = React.useState(() => {
|
||||||
|
const { height = 0, width = 0 } = Dimensions.get('window');
|
||||||
|
|
||||||
|
return { height, width };
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const handleOrientationChange = ({ window }: { window: ScaledSize }) => {
|
||||||
|
setDimensions(window);
|
||||||
|
};
|
||||||
|
|
||||||
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);
|
||||||
@@ -118,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;
|
||||||
@@ -135,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';
|
||||||
@@ -183,22 +197,19 @@ export default function BottomTabBar({
|
|||||||
backgroundColor: colors.card,
|
backgroundColor: colors.card,
|
||||||
borderTopColor: colors.border,
|
borderTopColor: colors.border,
|
||||||
},
|
},
|
||||||
keyboardHidesTabBar
|
{
|
||||||
? {
|
|
||||||
// When the keyboard is shown, slide down the tab bar
|
|
||||||
transform: [
|
transform: [
|
||||||
{
|
{
|
||||||
translateY: visible.interpolate({
|
translateY: visible.interpolate({
|
||||||
inputRange: [0, 1],
|
inputRange: [0, 1],
|
||||||
outputRange: [layout.height, 0],
|
outputRange: [layout.height + insets.bottom, 0],
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
// Absolutely position the tab bar so that the content is below it
|
// 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
|
// This is needed to avoid gap at bottom when the tab bar is hidden
|
||||||
position: keyboardShown ? 'absolute' : null,
|
position: isTabBarHidden ? 'absolute' : null,
|
||||||
}
|
},
|
||||||
: null,
|
|
||||||
{
|
{
|
||||||
height: DEFAULT_TABBAR_HEIGHT + insets.bottom,
|
height: DEFAULT_TABBAR_HEIGHT + insets.bottom,
|
||||||
paddingBottom: insets.bottom,
|
paddingBottom: insets.bottom,
|
||||||
@@ -206,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) => {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
|
Text,
|
||||||
TouchableWithoutFeedback,
|
TouchableWithoutFeedback,
|
||||||
Animated,
|
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
Platform,
|
Platform,
|
||||||
StyleProp,
|
StyleProp,
|
||||||
@@ -191,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,
|
||||||
@@ -202,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 });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -75,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,
|
||||||
|
|||||||
@@ -3,6 +3,121 @@
|
|||||||
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.26](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.25...@react-navigation/compat@5.1.26) (2020-06-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [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)
|
## [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
|
**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.12",
|
"version": "5.1.26",
|
||||||
"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.2.1",
|
"@react-navigation/native": "^5.5.1",
|
||||||
"@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"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,142 @@
|
|||||||
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.10.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.9.0...@react-navigation/core@5.10.0) (2020-06-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* catch missing params when they are required in navigate ([#8389](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8389)) ([8774ca9](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/8774ca97e1da91e97677ecd816c85f66af296b93))
|
||||||
|
* make sure the wildcard pattern catches nested unmatched routes ([c3bd349](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/c3bd349d77688011c9c55027edd66c6f39de2ade))
|
||||||
|
* only use the query params for focused route in path ([2d66ef9](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/2d66ef93ec9923a452415c482c40e7c6b769917c))
|
||||||
|
* prevent state change being emitted unnecessarily ([ab1f79c](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/ab1f79c096e94475a4da1acf1c850d04fb1bc4cf))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add wildcard patterns for paths ([4fe72e3](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/4fe72e3ce7bae9120d04e490401f3bad58ebdf5c)), closes [#8019](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8019)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [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)
|
# [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)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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.4.0",
|
"version": "5.10.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.2",
|
"@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,49 +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 DEVTOOLS_CONFIG_KEY =
|
const DEVTOOLS_CONFIG_KEY =
|
||||||
'REACT_NAVIGATION_REDUX_DEVTOOLS_EXTENSION_INTEGRATION_ENABLED';
|
'REACT_NAVIGATION_REDUX_DEVTOOLS_EXTENSION_INTEGRATION_ENABLED';
|
||||||
|
|
||||||
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.";
|
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -199,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
|
||||||
@@ -221,6 +207,8 @@ const BaseNavigationContainer = React.forwardRef(
|
|||||||
getRootState,
|
getRootState,
|
||||||
dangerouslyGetState: () => state,
|
dangerouslyGetState: () => state,
|
||||||
dangerouslyGetParent: () => undefined,
|
dangerouslyGetParent: () => undefined,
|
||||||
|
getCurrentRoute,
|
||||||
|
getCurrentOptions,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const builderContext = React.useMemo(
|
const builderContext = React.useMemo(
|
||||||
@@ -244,10 +232,17 @@ const BaseNavigationContainer = React.forwardRef(
|
|||||||
setState,
|
setState,
|
||||||
getKey,
|
getKey,
|
||||||
setKey,
|
setKey,
|
||||||
|
addOptionsGetter,
|
||||||
}),
|
}),
|
||||||
[getKey, getState, setKey, setState, state]
|
[getKey, getState, setKey, setState, state, addOptionsGetter]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onStateChangeRef = React.useRef(onStateChange);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
onStateChangeRef.current = onStateChange;
|
||||||
|
});
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
if (
|
if (
|
||||||
@@ -274,12 +269,12 @@ const BaseNavigationContainer = React.forwardRef(
|
|||||||
trackState(getRootState);
|
trackState(getRootState);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isFirstMountRef.current && onStateChange) {
|
if (!isFirstMountRef.current && onStateChangeRef.current) {
|
||||||
onStateChange(getRootState());
|
onStateChangeRef.current(getRootState());
|
||||||
}
|
}
|
||||||
|
|
||||||
isFirstMountRef.current = false;
|
isFirstMountRef.current = false;
|
||||||
}, [onStateChange, trackState, getRootState, emitter, state]);
|
}, [trackState, getRootState, emitter, state]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScheduleUpdateContext.Provider value={scheduleContext}>
|
<ScheduleUpdateContext.Provider value={scheduleContext}>
|
||||||
|
|||||||
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;
|
||||||
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,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -470,7 +764,7 @@ it('keeps query params if path is empty', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
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',
|
||||||
@@ -478,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 = {
|
||||||
@@ -504,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',
|
||||||
@@ -641,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: {
|
||||||
@@ -681,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,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -714,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: {
|
||||||
@@ -753,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,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -773,3 +1175,265 @@ 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);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('matches wildcard patterns at root', () => {
|
||||||
|
const path = '/test/bar/42/whatever';
|
||||||
|
const config = {
|
||||||
|
404: '*',
|
||||||
|
Foo: {
|
||||||
|
screens: {
|
||||||
|
Bar: {
|
||||||
|
path: '/bar/:id/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
routes: [{ name: '404' }],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getPathFromState(state, config)).toBe('/404');
|
||||||
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe('/404');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('matches wildcard patterns at nested level', () => {
|
||||||
|
const path = '/bar/42/whatever/baz/initt';
|
||||||
|
const config = {
|
||||||
|
Foo: {
|
||||||
|
screens: {
|
||||||
|
Bar: {
|
||||||
|
path: '/bar/:id/',
|
||||||
|
screens: {
|
||||||
|
404: '*',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Foo',
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Bar',
|
||||||
|
params: { id: '42' },
|
||||||
|
state: {
|
||||||
|
routes: [{ name: '404' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getPathFromState(state, config)).toBe('/bar/42/404');
|
||||||
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(
|
||||||
|
'/bar/42/404'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('matches wildcard patterns at nested level with exact', () => {
|
||||||
|
const path = '/whatever';
|
||||||
|
const config = {
|
||||||
|
Foo: {
|
||||||
|
screens: {
|
||||||
|
Bar: {
|
||||||
|
path: '/bar/:id/',
|
||||||
|
screens: {
|
||||||
|
404: {
|
||||||
|
path: '*',
|
||||||
|
exact: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baz: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Foo',
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Bar',
|
||||||
|
state: {
|
||||||
|
routes: [{ name: '404' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getPathFromState(state, config)).toBe('/404');
|
||||||
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe('/404');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('tries to match wildcard patterns at the end', () => {
|
||||||
|
const path = '/bar/42/test';
|
||||||
|
const config = {
|
||||||
|
Foo: {
|
||||||
|
screens: {
|
||||||
|
Bar: {
|
||||||
|
path: '/bar/:id/',
|
||||||
|
screens: {
|
||||||
|
404: '*',
|
||||||
|
Test: 'test',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Foo',
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Bar',
|
||||||
|
params: { id: '42' },
|
||||||
|
state: {
|
||||||
|
routes: [{ name: 'Test' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getPathFromState(state, config)).toBe(path);
|
||||||
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses nearest parent wildcard match for unmatched paths', () => {
|
||||||
|
const path = '/bar/42/baz/test';
|
||||||
|
const config = {
|
||||||
|
Foo: {
|
||||||
|
screens: {
|
||||||
|
Bar: {
|
||||||
|
path: '/bar/:id/',
|
||||||
|
screens: {
|
||||||
|
Baz: 'baz',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: '*',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Foo',
|
||||||
|
state: {
|
||||||
|
routes: [{ name: '404' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getPathFromState(state, config)).toBe('/404');
|
||||||
|
expect(getPathFromState(getStateFromPath(path, config), config)).toBe('/404');
|
||||||
|
});
|
||||||
|
|||||||
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,31 @@ 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?: Options;
|
screens?: Record<string, ConfigItem>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getActiveRoute = (state: State): { name: string; params?: object } => {
|
||||||
|
const route =
|
||||||
|
typeof state.index === 'number'
|
||||||
|
? state.routes[state.index]
|
||||||
|
: state.routes[state.routes.length - 1];
|
||||||
|
|
||||||
|
if (route.state) {
|
||||||
|
return getActiveRoute(route.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return route;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,101 +60,126 @@ 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 focusedParams: Record<string, any> | undefined;
|
||||||
break;
|
let focusedRoute = getActiveRoute(state);
|
||||||
} else if (typeof currentOptions[route.name] === 'object') {
|
let currentOptions = configs;
|
||||||
// if there is no `screens` property, we return pattern
|
|
||||||
if (
|
// Keep all the route names that appeared during going deeper in config in case the pattern is resolved to undefined
|
||||||
!(currentOptions[route.name] as {
|
let nestedRouteNames = [];
|
||||||
screens: Options;
|
|
||||||
}).screens
|
let hasNext = true;
|
||||||
) {
|
|
||||||
pattern = (currentOptions[route.name] as { path: string }).path;
|
while (route.name in currentOptions && hasNext) {
|
||||||
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
|
pattern = currentOptions[route.name].pattern;
|
||||||
break;
|
|
||||||
} else {
|
nestedRouteNames.push(route.name);
|
||||||
// if it is the end of state, we return pattern
|
|
||||||
if (route.state === undefined) {
|
if (route.params) {
|
||||||
pattern = (currentOptions[route.name] as { path: string }).path;
|
const stringify = currentOptions[route.name]?.stringify;
|
||||||
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
|
|
||||||
break;
|
const currentParams = fromEntries(
|
||||||
|
Object.entries(route.params).map(([key, value]) => [
|
||||||
|
key,
|
||||||
|
stringify?.[key] ? stringify[key](value) : String(value),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pattern) {
|
||||||
|
Object.assign(allParams, currentParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (focusedRoute === route) {
|
||||||
|
// If this is the focused route, keep the params for later use
|
||||||
|
// We save it here since it's been stringified already
|
||||||
|
focusedParams = { ...currentParams };
|
||||||
|
|
||||||
|
pattern
|
||||||
|
?.split('/')
|
||||||
|
.filter((p) => p.startsWith(':'))
|
||||||
|
// eslint-disable-next-line no-loop-func
|
||||||
|
.forEach((p) => {
|
||||||
|
const name = getParamName(p);
|
||||||
|
|
||||||
|
// Remove the params present in the pattern since we'll only use the rest for query string
|
||||||
|
if (focusedParams) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
|
delete focusedParams[name];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no `screens` property or no nested state, we return pattern
|
||||||
|
if (!currentOptions[route.name].screens || route.state === undefined) {
|
||||||
|
hasNext = false;
|
||||||
} else {
|
} else {
|
||||||
index =
|
index =
|
||||||
typeof route.state.index === 'number' ? route.state.index : 0;
|
typeof route.state.index === 'number'
|
||||||
|
? route.state.index
|
||||||
|
: route.state.routes.length - 1;
|
||||||
|
|
||||||
const nextRoute = route.state.routes[index];
|
const nextRoute = route.state.routes[index];
|
||||||
const deeperConfig = (currentOptions[route.name] as {
|
const nestedConfig = currentOptions[route.name].screens;
|
||||||
screens: Options;
|
|
||||||
}).screens;
|
|
||||||
// if there is config for next route name, we go deeper
|
// if there is config for next route name, we go deeper
|
||||||
if (nextRoute.name in deeperConfig) {
|
if (nestedConfig && nextRoute.name in nestedConfig) {
|
||||||
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
|
|
||||||
route = nextRoute as Route<string> & { state?: State };
|
route = nextRoute as Route<string> & { state?: State };
|
||||||
currentOptions = deeperConfig;
|
currentOptions = nestedConfig;
|
||||||
} else {
|
} else {
|
||||||
// if not, there is no sense in going deeper in config
|
// If not, there is no sense in going deeper in config
|
||||||
pattern = (currentOptions[route.name] as { path: string }).path;
|
hasNext = false;
|
||||||
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pattern === undefined) {
|
if (pattern === undefined) {
|
||||||
// cut the first `/`
|
pattern = nestedRouteNames.join('/');
|
||||||
pattern = nestedRouteNames.substring(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const config =
|
|
||||||
currentOptions[route.name] !== undefined
|
|
||||||
? (currentOptions[route.name] as { stringify?: StringifyConfig })
|
|
||||||
.stringify
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const params = route.params
|
|
||||||
? // Stringify all of the param values before we use them
|
|
||||||
Object.entries(route.params).reduce<{
|
|
||||||
[key: string]: string;
|
|
||||||
}>((acc, [key, value]) => {
|
|
||||||
acc[key] = config?.[key] ? config[key](value) : String(value);
|
|
||||||
return acc;
|
|
||||||
}, {})
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
if (currentOptions[route.name] !== undefined) {
|
if (currentOptions[route.name] !== undefined) {
|
||||||
path += pattern
|
path += pattern
|
||||||
.split('/')
|
.split('/')
|
||||||
.map((p) => {
|
.map((p) => {
|
||||||
const name = p.replace(/^:/, '');
|
const name = getParamName(p);
|
||||||
|
|
||||||
|
// We don't know what to show for wildcard patterns
|
||||||
|
// Showing the route name seems ok, though whatever we show here will be incorrect
|
||||||
|
// Since the page doesn't actually exist
|
||||||
|
if (p === '*') {
|
||||||
|
return route.name;
|
||||||
|
}
|
||||||
|
|
||||||
// If the path has a pattern for a param, put the param in the path
|
// If the path has a pattern for a param, put the param in the path
|
||||||
if (params && name in params && p.startsWith(':')) {
|
if (p.startsWith(':')) {
|
||||||
const value = params[name];
|
const value = allParams[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
|
if (value === undefined && p.endsWith('?')) {
|
||||||
delete params[name];
|
// Optional params without value assigned in route.params should be ignored
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
return encodeURIComponent(value);
|
return encodeURIComponent(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,16 +190,21 @@ export default function getPathFromState(
|
|||||||
path += encodeURIComponent(route.name);
|
path += encodeURIComponent(route.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!focusedParams) {
|
||||||
|
focusedParams = focusedRoute.params;
|
||||||
|
}
|
||||||
|
|
||||||
if (route.state) {
|
if (route.state) {
|
||||||
path += '/';
|
path += '/';
|
||||||
} else if (params) {
|
} else if (focusedParams) {
|
||||||
for (let param in params) {
|
for (let param in focusedParams) {
|
||||||
if (params[param] === 'undefined') {
|
if (focusedParams[param] === 'undefined') {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
delete params[param];
|
delete focusedParams[param];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const query = queryString.stringify(params);
|
|
||||||
|
const query = queryString.stringify(focusedParams);
|
||||||
|
|
||||||
if (query) {
|
if (query) {
|
||||||
path += `?${query}`;
|
path += `?${query}`;
|
||||||
@@ -173,8 +215,66 @@ export default function getPathFromState(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove multiple as well as trailing slashes
|
// Remove multiple as well as trailing slashes
|
||||||
path = path.replace(/\/+/, '/');
|
path = path.replace(/\/+/g, '/');
|
||||||
path = path.length > 1 ? path.replace(/\/$/, '') : path;
|
path = path.length > 1 ? path.replace(/\/$/, '') : 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 getParamName = (pattern: string) =>
|
||||||
|
pattern.replace(/^:/, '').replace(/\?$/, '');
|
||||||
|
|
||||||
|
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];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,29 +1,21 @@
|
|||||||
|
import escape from 'escape-string-regexp';
|
||||||
import queryString from 'query-string';
|
import queryString from 'query-string';
|
||||||
import {
|
import {
|
||||||
NavigationState,
|
NavigationState,
|
||||||
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 = {
|
||||||
screen: string;
|
screen: string;
|
||||||
match: string | null;
|
regex?: RegExp;
|
||||||
|
path: string;
|
||||||
pattern: string;
|
pattern: string;
|
||||||
routeNames: string[];
|
routeNames: string[];
|
||||||
parse: ParseConfig | undefined;
|
parse?: ParseConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
type InitialRouteConfig = {
|
type InitialRouteConfig = {
|
||||||
@@ -56,45 +48,88 @@ type ResultState = PartialState<NavigationState> & {
|
|||||||
*/
|
*/
|
||||||
export default function getStateFromPath(
|
export default function getStateFromPath(
|
||||||
path: string,
|
path: string,
|
||||||
options: Options = {}
|
options: PathConfig = {}
|
||||||
): ResultState | undefined {
|
): ResultState | undefined {
|
||||||
let initialRoutes: InitialRouteConfig[] = [];
|
let initialRoutes: InitialRouteConfig[] = [];
|
||||||
|
|
||||||
// Create a normalized configs array which will be easier to use
|
// Create a normalized configs array which will be easier to use
|
||||||
const configs = ([] as RouteConfig[]).concat(
|
const configs = ([] as RouteConfig[])
|
||||||
|
.concat(
|
||||||
...Object.keys(options).map((key) =>
|
...Object.keys(options).map((key) =>
|
||||||
createNormalizedConfigs(key, options, [], initialRoutes)
|
createNormalizedConfigs(key, options, [], initialRoutes)
|
||||||
)
|
)
|
||||||
);
|
)
|
||||||
|
.sort((a, b) => {
|
||||||
|
// Sort config so that:
|
||||||
|
// - the most exhaustive ones are always at the beginning
|
||||||
|
// - patterns with wildcard are always at the end
|
||||||
|
|
||||||
// sort configs so the most exhaustive is always first to be chosen
|
// If one of the patterns starts with the other, it's more exhaustive
|
||||||
configs.sort(
|
// So move it up
|
||||||
(config1, config2) =>
|
if (a.pattern.startsWith(b.pattern)) {
|
||||||
config2.pattern.split('/').length - config1.pattern.split('/').length
|
return 1;
|
||||||
);
|
}
|
||||||
|
|
||||||
|
if (b.pattern.startsWith(a.pattern)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const aParts = a.pattern.split('/');
|
||||||
|
const bParts = b.pattern.split('/');
|
||||||
|
|
||||||
|
const aWildcardIndex = aParts.indexOf('*');
|
||||||
|
const bWildcardIndex = bParts.indexOf('*');
|
||||||
|
|
||||||
|
// If only one of the patterns has a wildcard, move it down in the list
|
||||||
|
if (aWildcardIndex === -1 && bWildcardIndex !== -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aWildcardIndex !== -1 && bWildcardIndex === -1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aWildcardIndex === bWildcardIndex) {
|
||||||
|
// If `b` has more `/`, it's more exhaustive
|
||||||
|
// So we move it up in the list
|
||||||
|
return bParts.length - aParts.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the wildcard appears later in the pattern (has higher index), it's more specific
|
||||||
|
// So we move it up in the list
|
||||||
|
return bWildcardIndex - aWildcardIndex;
|
||||||
|
});
|
||||||
|
|
||||||
let remaining = path
|
let remaining = path
|
||||||
.replace(/[/]+/, '/') // Replace multiple slash (//) with single ones
|
.replace(/\/+/g, '/') // Replace multiple slash (//) with single ones
|
||||||
.replace(/^\//, '') // Remove extra leading slash
|
.replace(/^\//, '') // Remove extra leading slash
|
||||||
.replace(/\?.*/, ''); // Remove query params which we will handle later
|
.replace(/\?.*$/, ''); // Remove query params which we will handle later
|
||||||
|
|
||||||
if (remaining === '') {
|
// 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
|
// 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
|
// When handling empty path, we should only look at the root level config
|
||||||
const match = configs.find(
|
const match = configs.find(
|
||||||
(config) =>
|
(config) =>
|
||||||
config.pattern === '' &&
|
config.path === '' &&
|
||||||
config.routeNames.every(
|
config.routeNames.every(
|
||||||
// make sure that none of the parent configs have a non-empty path defined
|
// Make sure that none of the parent configs have a non-empty path defined
|
||||||
(name) => !configs.find((c) => c.screen === name)?.pattern
|
(name) => !configs.find((c) => c.screen === name)?.path
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
return createNestedStateObject(
|
return createNestedStateObject(
|
||||||
match.routeNames,
|
match.routeNames.map((name, i, self) => {
|
||||||
initialRoutes,
|
if (i === self.length - 1) {
|
||||||
parseQueryParams(path, match.parse)
|
return { name, params: parseQueryParams(path, match.parse) };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { name };
|
||||||
|
}),
|
||||||
|
initialRoutes
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,70 +139,37 @@ export default function getStateFromPath(
|
|||||||
let result: PartialState<NavigationState> | undefined;
|
let result: PartialState<NavigationState> | undefined;
|
||||||
let current: PartialState<NavigationState> | undefined;
|
let current: PartialState<NavigationState> | undefined;
|
||||||
|
|
||||||
|
// We try to match the paths in 2 passes
|
||||||
|
// In first pass, we match the whole path against the regex instead of segments
|
||||||
|
// This makes sure matches such as wildcard will catch any unmatched routes, even if nested
|
||||||
|
const { routeNames, allParams, remainingPath } = matchAgainstConfigs(
|
||||||
|
remaining,
|
||||||
|
configs.map((c) => ({
|
||||||
|
...c,
|
||||||
|
// Add `$` to the regex to make sure it matches till end of the path and not just beginning
|
||||||
|
regex: c.regex ? new RegExp(c.regex.source + '$') : undefined,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (routeNames !== undefined) {
|
||||||
|
// This will always be empty if full path matched
|
||||||
|
remaining = remainingPath;
|
||||||
|
current = createNestedStateObject(
|
||||||
|
createRouteObjects(configs, routeNames, allParams),
|
||||||
|
initialRoutes
|
||||||
|
);
|
||||||
|
result = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In second pass, we divide the path into segments and match piece by piece
|
||||||
|
// This preserves the old behaviour, but we should remove it in next major
|
||||||
while (remaining) {
|
while (remaining) {
|
||||||
let routeNames: string[] | undefined;
|
let { routeNames, allParams, remainingPath } = matchAgainstConfigs(
|
||||||
let params: Record<string, any> | undefined;
|
remaining,
|
||||||
|
configs
|
||||||
|
);
|
||||||
|
|
||||||
// Go through all configs, and see if the next path segment matches our regex
|
remaining = remainingPath;
|
||||||
for (const config of configs) {
|
|
||||||
if (!config.match) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let didMatch = true;
|
|
||||||
const matchParts = config.match.split('/');
|
|
||||||
const remainingParts = remaining.split('/');
|
|
||||||
|
|
||||||
// we check if remaining path has enough segments to be handled with this pattern
|
|
||||||
if (config.pattern.split('/').length > remainingParts.length) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we keep info about the index of segment on which the params start
|
|
||||||
let paramsIndex = 0;
|
|
||||||
// the beginning of the remaining path should be the same as the part of config before params
|
|
||||||
for (paramsIndex; paramsIndex < matchParts.length; paramsIndex++) {
|
|
||||||
if (matchParts[paramsIndex] !== remainingParts[paramsIndex]) {
|
|
||||||
didMatch = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the first part of the path matches, we need to extract params from the path
|
|
||||||
if (didMatch) {
|
|
||||||
routeNames = [...config.routeNames];
|
|
||||||
|
|
||||||
const paramPatterns = config.pattern
|
|
||||||
.split('/')
|
|
||||||
.filter((p) => p.startsWith(':'));
|
|
||||||
|
|
||||||
if (paramPatterns.length) {
|
|
||||||
params = paramPatterns.reduce<Record<string, any>>((acc, p, i) => {
|
|
||||||
const key = p.replace(/^:/, '');
|
|
||||||
const value = remainingParts[i + paramsIndex]; // The param segments start from the end of matched part
|
|
||||||
acc[key] =
|
|
||||||
config.parse && config.parse[key]
|
|
||||||
? config.parse[key](value)
|
|
||||||
: value;
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
// if pattern and remaining path have same amount of segments, there should be nothing left
|
|
||||||
if (config.pattern.split('/').length === remainingParts.length) {
|
|
||||||
remaining = '';
|
|
||||||
} else {
|
|
||||||
// For each segment of the pattern, remove one segment from remaining path
|
|
||||||
let i = config.pattern.split('/').length;
|
|
||||||
while (i--) {
|
|
||||||
remaining = remaining.substr(remaining.indexOf('/') + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we hadn't matched any segments earlier, use the path as route name
|
// If we hadn't matched any segments earlier, use the path as route name
|
||||||
if (routeNames === undefined) {
|
if (routeNames === undefined) {
|
||||||
@@ -178,7 +180,10 @@ export default function getStateFromPath(
|
|||||||
remaining = segments.join('/');
|
remaining = segments.join('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = createNestedStateObject(routeNames, initialRoutes, params);
|
const state = createNestedStateObject(
|
||||||
|
createRouteObjects(configs, routeNames, allParams),
|
||||||
|
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
|
||||||
@@ -213,44 +218,105 @@ export default function getStateFromPath(
|
|||||||
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 matchAgainstConfigs = (remaining: string, configs: RouteConfig[]) => {
|
||||||
|
let routeNames: string[] | undefined;
|
||||||
|
let allParams: Record<string, any> | undefined;
|
||||||
|
let remainingPath = remaining;
|
||||||
|
|
||||||
|
// Go through all configs, and see if the next path segment matches our regex
|
||||||
|
for (const config of configs) {
|
||||||
|
if (!config.regex) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const match = remainingPath.match(config.regex);
|
||||||
|
|
||||||
|
// If our regex matches, we need to extract params from the path
|
||||||
|
if (match) {
|
||||||
|
routeNames = [...config.routeNames];
|
||||||
|
|
||||||
|
const paramPatterns = config.pattern
|
||||||
|
.split('/')
|
||||||
|
.filter((p) => p.startsWith(':'));
|
||||||
|
|
||||||
|
if (paramPatterns.length) {
|
||||||
|
allParams = paramPatterns.reduce<Record<string, any>>((acc, p, i) => {
|
||||||
|
const value = match![(i + 1) * 2].replace(/\//, ''); // The param segments appear every second item starting from 2 in the regex match result
|
||||||
|
|
||||||
|
acc[p] = value;
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
remainingPath = remainingPath.replace(match[1], '');
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { routeNames, allParams, remainingPath };
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
configs.push(createConfigItem(key, routeNames, value));
|
const pattern = parentPattern ? joinPaths(parentPattern, config) : config;
|
||||||
} else if (typeof value === 'object') {
|
|
||||||
|
configs.push(createConfigItem(screen, routeNames, pattern, config));
|
||||||
|
} 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 (typeof value.path === 'string') {
|
if (typeof config.path === 'string') {
|
||||||
configs.push(createConfigItem(key, 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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -259,44 +325,62 @@ function createNormalizedConfigs(
|
|||||||
routeNames.pop();
|
routeNames.pop();
|
||||||
|
|
||||||
return configs;
|
return configs;
|
||||||
}
|
};
|
||||||
|
|
||||||
function createConfigItem(
|
const createConfigItem = (
|
||||||
screen: string,
|
screen: string,
|
||||||
routeNames: string[],
|
routeNames: string[],
|
||||||
pattern: string,
|
pattern: string,
|
||||||
|
path: string,
|
||||||
parse?: ParseConfig
|
parse?: ParseConfig
|
||||||
): RouteConfig {
|
): RouteConfig => {
|
||||||
// part being matched ends on the first param
|
// Normalize pattern to remove any leading, trailing slashes, duplicate slashes etc.
|
||||||
const match = pattern !== '' ? pattern.split('/:')[0] : null;
|
pattern = pattern.split('/').filter(Boolean).join('/');
|
||||||
|
|
||||||
|
const regex = pattern
|
||||||
|
? new RegExp(
|
||||||
|
`^(${pattern
|
||||||
|
.split('/')
|
||||||
|
.map((it) => {
|
||||||
|
if (it.startsWith(':')) {
|
||||||
|
return `(([^/]+\\/)${it.endsWith('?') ? '?' : ''})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${it === '*' ? '.*' : escape(it)}\\/`;
|
||||||
|
})
|
||||||
|
.join('')})`
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
screen,
|
screen,
|
||||||
match,
|
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
|
||||||
@@ -305,28 +389,25 @@ function findInitialRoute(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
};
|
||||||
|
|
||||||
// returns 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 createStateObject(
|
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 {
|
||||||
@@ -335,53 +416,102 @@ function createStateObject(
|
|||||||
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: [] } }],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function createNestedStateObject(
|
const createNestedStateObject = (
|
||||||
routeNames: string[],
|
routes: { name: string; params?: object }[],
|
||||||
initialRoutes: InitialRouteConfig[],
|
initialRoutes: InitialRouteConfig[]
|
||||||
params: object | undefined
|
) => {
|
||||||
) {
|
|
||||||
let state: InitialState;
|
let state: InitialState;
|
||||||
let routeName = routeNames.shift() as string;
|
let route = routes.shift() as { name: string; params?: object };
|
||||||
let initialRoute = findInitialRoute(routeName, initialRoutes);
|
let initialRoute = findInitialRoute(route.name, initialRoutes);
|
||||||
|
|
||||||
state = createStateObject(
|
state = createStateObject(
|
||||||
initialRoute,
|
initialRoute,
|
||||||
routeName,
|
route.name,
|
||||||
routeNames.length === 0,
|
route.params,
|
||||||
params
|
routes.length === 0
|
||||||
);
|
);
|
||||||
|
|
||||||
if (routeNames.length > 0) {
|
if (routes.length > 0) {
|
||||||
let nestedState = state;
|
let nestedState = state;
|
||||||
|
|
||||||
while ((routeName = routeNames.shift() as string)) {
|
while ((route = routes.shift() as { name: string; params?: object })) {
|
||||||
initialRoute = findInitialRoute(routeName, initialRoutes);
|
initialRoute = findInitialRoute(route.name, initialRoutes);
|
||||||
nestedState.routes[nestedState.index || 0].state = createStateObject(
|
|
||||||
|
const nestedStateIndex =
|
||||||
|
nestedState.index || nestedState.routes.length - 1;
|
||||||
|
|
||||||
|
nestedState.routes[nestedStateIndex].state = createStateObject(
|
||||||
initialRoute,
|
initialRoute,
|
||||||
routeName,
|
route.name,
|
||||||
routeNames.length === 0,
|
route.params,
|
||||||
params
|
routes.length === 0
|
||||||
);
|
);
|
||||||
if (routeNames.length > 0) {
|
|
||||||
nestedState = nestedState.routes[nestedState.index || 0]
|
if (routes.length > 0) {
|
||||||
|
nestedState = nestedState.routes[nestedStateIndex]
|
||||||
.state as InitialState;
|
.state as InitialState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createRouteObjects = (
|
||||||
|
configs: RouteConfig[],
|
||||||
|
routeNames: string[],
|
||||||
|
allParams?: Record<string, any>
|
||||||
|
) =>
|
||||||
|
routeNames.map((name) => {
|
||||||
|
const config = configs.find((c) => c.screen === name);
|
||||||
|
|
||||||
|
let params: object | undefined;
|
||||||
|
|
||||||
|
if (allParams && config?.path) {
|
||||||
|
const pattern = config.path;
|
||||||
|
|
||||||
|
if (pattern) {
|
||||||
|
const paramPatterns = pattern
|
||||||
|
.split('/')
|
||||||
|
.filter((p) => p.startsWith(':'));
|
||||||
|
|
||||||
|
if (paramPatterns.length) {
|
||||||
|
params = paramPatterns.reduce<Record<string, any>>((acc, p) => {
|
||||||
|
const key = p.replace(/^:/, '').replace(/\?$/, '');
|
||||||
|
const value = allParams![p];
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
acc[key] =
|
||||||
|
config.parse && config.parse[key]
|
||||||
|
? config.parse[key](value)
|
||||||
|
: value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findFocusedRoute(state: InitialState) {
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params && Object.keys(params).length) {
|
||||||
|
return { name, params };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { name };
|
||||||
|
});
|
||||||
|
|
||||||
|
const findFocusedRoute = (state: InitialState) => {
|
||||||
let current: InitialState | undefined = state;
|
let current: InitialState | undefined = state;
|
||||||
|
|
||||||
while (current?.routes[current.index || 0].state) {
|
while (current?.routes[current.index || 0].state) {
|
||||||
@@ -394,12 +524,12 @@ function findFocusedRoute(state: InitialState) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return route;
|
return route;
|
||||||
}
|
};
|
||||||
|
|
||||||
function parseQueryParams(
|
const parseQueryParams = (
|
||||||
path: string,
|
path: string,
|
||||||
parseConfig?: Record<string, (value: string) => any>
|
parseConfig?: Record<string, (value: string) => any>
|
||||||
) {
|
) => {
|
||||||
const query = path.split('?')[1];
|
const query = path.split('?')[1];
|
||||||
const params = queryString.parse(query);
|
const params = queryString.parse(query);
|
||||||
|
|
||||||
@@ -412,4 +542,4 @@ function parseQueryParams(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Object.keys(params).length ? params : undefined;
|
return Object.keys(params).length ? params : undefined;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ 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';
|
||||||
|
|||||||
7
packages/core/src/isArrayEqual.tsx
Normal file
7
packages/core/src/isArrayEqual.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Compare two arrays with primitive values as the content.
|
||||||
|
* We need to make sure that both values and order match.
|
||||||
|
*/
|
||||||
|
export default function isArrayEqual(a: any[], b: any[]) {
|
||||||
|
return a.length === b.length && a.every((it, index) => it === b[index]);
|
||||||
|
}
|
||||||
@@ -152,7 +152,7 @@ type NavigationHelpersCommon<
|
|||||||
* @param [params] Params object for the route.
|
* @param [params] Params object for the route.
|
||||||
*/
|
*/
|
||||||
navigate<RouteName extends keyof ParamList>(
|
navigate<RouteName extends keyof ParamList>(
|
||||||
...args: ParamList[RouteName] extends undefined | any
|
...args: undefined extends ParamList[RouteName]
|
||||||
? [RouteName] | [RouteName, ParamList[RouteName]]
|
? [RouteName] | [RouteName, ParamList[RouteName]]
|
||||||
: [RouteName, ParamList[RouteName]]
|
: [RouteName, ParamList[RouteName]]
|
||||||
): void;
|
): void;
|
||||||
@@ -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,22 +117,7 @@ export default function useDescriptors<
|
|||||||
const screen = screens[route.name];
|
const screen = screens[route.name];
|
||||||
const navigation = navigations[route.key];
|
const navigation = navigations[route.key];
|
||||||
|
|
||||||
acc[route.key] = {
|
const routeOptions = {
|
||||||
navigation,
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<NavigationBuilderContext.Provider key={route.key} value={context}>
|
|
||||||
<SceneView
|
|
||||||
navigation={navigation}
|
|
||||||
route={route}
|
|
||||||
screen={screen}
|
|
||||||
getState={getState}
|
|
||||||
setState={setState}
|
|
||||||
/>
|
|
||||||
</NavigationBuilderContext.Provider>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
// The default `screenOptions` passed to the navigator
|
// The default `screenOptions` passed to the navigator
|
||||||
...(typeof screenOptions === 'object' || screenOptions == null
|
...(typeof screenOptions === 'object' || screenOptions == null
|
||||||
? screenOptions
|
? screenOptions
|
||||||
@@ -152,7 +137,25 @@ export default function useDescriptors<
|
|||||||
})),
|
})),
|
||||||
// The options set via `navigation.setOptions`
|
// The options set via `navigation.setOptions`
|
||||||
...options[route.key],
|
...options[route.key],
|
||||||
|
};
|
||||||
|
|
||||||
|
acc[route.key] = {
|
||||||
|
navigation,
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<NavigationBuilderContext.Provider key={route.key} value={context}>
|
||||||
|
<SceneView
|
||||||
|
navigation={navigation}
|
||||||
|
route={route}
|
||||||
|
screen={screen}
|
||||||
|
getState={getState}
|
||||||
|
setState={setState}
|
||||||
|
options={routeOptions}
|
||||||
|
/>
|
||||||
|
</NavigationBuilderContext.Provider>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
options: routeOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
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,8 @@ 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';
|
||||||
|
import isArrayEqual from './isArrayEqual';
|
||||||
|
|
||||||
// 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
|
||||||
@@ -47,13 +49,6 @@ type NavigatorRoute = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare two arrays with primitive values as the content.
|
|
||||||
* We need to make sure that both values and order match.
|
|
||||||
*/
|
|
||||||
const isArrayEqual = (a: any[], b: any[]) =>
|
|
||||||
a.length === b.length && a.every((it, index) => it === b[index]);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract route config object from React children elements.
|
* Extract route config object from React children elements.
|
||||||
*
|
*
|
||||||
@@ -264,41 +259,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,
|
routeNames,
|
||||||
routeParamList,
|
routeParamList,
|
||||||
});
|
}),
|
||||||
|
true,
|
||||||
|
];
|
||||||
} else {
|
} else {
|
||||||
initializedStateRef.current = router.getRehydratedState(
|
return [
|
||||||
currentState as PartialState<State>,
|
router.getRehydratedState(currentState as PartialState<State>, {
|
||||||
{
|
|
||||||
routeNames,
|
routeNames,
|
||||||
routeParamList,
|
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
|
||||||
}
|
// where routeConfigs change without affecting routeNames/routeParamList.
|
||||||
|
// Instead, we handle changes to these in the nextState code below. Note
|
||||||
React.useEffect(() => {
|
// that some changes to routeConfigs are explicitly ignored, such as changes
|
||||||
previousStateRef.current = currentState;
|
// to initialParams
|
||||||
}, [currentState]);
|
// 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 +296,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 +352,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
|
||||||
|
// We do it in a timeout because we need to detect if another navigator mounted in the meantime
|
||||||
|
// 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) {
|
if (getCurrentState() !== undefined && getKey() === navigatorKey) {
|
||||||
setState(undefined);
|
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 +493,12 @@ export default function useNavigationBuilder<
|
|||||||
emitter,
|
emitter,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useCurrentRender({
|
||||||
|
state,
|
||||||
|
navigation,
|
||||||
|
descriptors,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
state,
|
state,
|
||||||
navigation,
|
navigation,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import * as React from 'react';
|
|||||||
import { NavigationState } from '@react-navigation/routers';
|
import { NavigationState } from '@react-navigation/routers';
|
||||||
import NavigationBuilderContext from './NavigationBuilderContext';
|
import NavigationBuilderContext from './NavigationBuilderContext';
|
||||||
import NavigationRouteContext from './NavigationRouteContext';
|
import NavigationRouteContext from './NavigationRouteContext';
|
||||||
|
import isArrayEqual from './isArrayEqual';
|
||||||
|
|
||||||
export default function useOnGetState({
|
export default function useOnGetState({
|
||||||
getStateForRoute,
|
getStateForRoute,
|
||||||
@@ -16,13 +17,23 @@ export default function useOnGetState({
|
|||||||
|
|
||||||
const getRehydratedState = React.useCallback(() => {
|
const getRehydratedState = React.useCallback(() => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
return {
|
|
||||||
...state,
|
// Avoid returning new route objects if we don't need to
|
||||||
routes: state.routes.map((route) => ({
|
const routes = state.routes.map((route) => {
|
||||||
...route,
|
const childState = getStateForRoute(route.key);
|
||||||
state: getStateForRoute(route.key),
|
|
||||||
})),
|
if (route.state === childState) {
|
||||||
};
|
return route;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...route, state: childState };
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isArrayEqual(state.routes, routes)) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...state, routes };
|
||||||
}, [getState, getStateForRoute]);
|
}, [getState, getStateForRoute]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|||||||
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,130 @@
|
|||||||
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.2](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.8.1...@react-navigation/drawer@5.8.2) (2020-06-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* typo on drawerPosition default props ([#8357](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/8357)) ([762cc44](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/762cc4457842182189eeac84aedbb88169452e1e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [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)
|
## [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
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|||||||
@@ -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.6.1",
|
"version": "5.8.2",
|
||||||
"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.2.1",
|
"@react-navigation/native": "^5.5.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-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,6 +8,7 @@ import {
|
|||||||
NavigationHelpers,
|
NavigationHelpers,
|
||||||
DrawerNavigationState,
|
DrawerNavigationState,
|
||||||
DrawerActionHelpers,
|
DrawerActionHelpers,
|
||||||
|
RouteProp,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
import type { PanGestureHandlerProperties } from 'react-native-gesture-handler';
|
import type { PanGestureHandlerProperties } from 'react-native-gesture-handler';
|
||||||
|
|
||||||
@@ -208,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,
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ type Props = {
|
|||||||
|
|
||||||
export default class DrawerView extends React.Component<Props> {
|
export default class DrawerView extends React.Component<Props> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
drawerPostion: I18nManager.isRTL ? 'left' : 'right',
|
drawerPosition: I18nManager.isRTL ? 'left' : 'right',
|
||||||
drawerType: 'front',
|
drawerType: 'front',
|
||||||
gestureEnabled: true,
|
gestureEnabled: true,
|
||||||
swipeEnabled: Platform.OS !== 'web',
|
swipeEnabled: Platform.OS !== 'web',
|
||||||
@@ -655,14 +655,14 @@ export default class DrawerView extends React.Component<Props> {
|
|||||||
gestureEnabled ? () => this.toggleDrawer(false) : undefined
|
gestureEnabled ? () => this.toggleDrawer(false) : undefined
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Overlay progress={progress} style={overlayStyle} />
|
<Overlay progress={progress} style={overlayStyle as any} />
|
||||||
</TouchableWithoutFeedback>
|
</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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -238,7 +238,6 @@ export default function DrawerView({
|
|||||||
renderDrawerContent={renderNavigationView}
|
renderDrawerContent={renderNavigationView}
|
||||||
renderSceneContent={renderContent}
|
renderSceneContent={renderContent}
|
||||||
keyboardDismissMode={keyboardDismissMode}
|
keyboardDismissMode={keyboardDismissMode}
|
||||||
drawerPostion={drawerPosition}
|
|
||||||
dimensions={dimensions}
|
dimensions={dimensions}
|
||||||
/>
|
/>
|
||||||
</DrawerOpenContext.Provider>
|
</DrawerOpenContext.Provider>
|
||||||
|
|||||||
@@ -3,6 +3,128 @@
|
|||||||
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.10](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.9...@react-navigation/material-bottom-tabs@5.2.10) (2020-06-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [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)
|
## [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
|
**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.12",
|
"version": "5.2.10",
|
||||||
"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.2.1",
|
"@react-navigation/native": "^5.5.1",
|
||||||
"@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,5 +1,5 @@
|
|||||||
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 {
|
||||||
@@ -8,6 +8,8 @@ import {
|
|||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
TabActions,
|
TabActions,
|
||||||
useTheme,
|
useTheme,
|
||||||
|
useLinkBuilder,
|
||||||
|
Link,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -24,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;
|
||||||
@@ -46,7 +49,6 @@ export default function MaterialBottomTabView({
|
|||||||
}, [colors, dark]);
|
}, [colors, dark]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavigationHelpersContext.Provider value={navigation}>
|
|
||||||
<BottomNavigation
|
<BottomNavigation
|
||||||
{...rest}
|
{...rest}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
@@ -58,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];
|
||||||
|
|
||||||
@@ -68,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
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -107,6 +140,13 @@ export default function MaterialBottomTabView({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MaterialBottomTabView(props: Props) {
|
||||||
|
return (
|
||||||
|
<NavigationHelpersContext.Provider value={props.navigation}>
|
||||||
|
<MaterialBottomTabViewInner {...props} />
|
||||||
</NavigationHelpersContext.Provider>
|
</NavigationHelpersContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -115,4 +155,8 @@ const styles = StyleSheet.create({
|
|||||||
icon: {
|
icon: {
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
},
|
},
|
||||||
|
touchable: {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,124 @@
|
|||||||
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.10](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.9...@react-navigation/material-top-tabs@5.2.10) (2020-06-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [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)
|
## [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
|
**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.12",
|
"version": "5.2.10",
|
||||||
"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.2.1",
|
"@react-navigation/native": "^5.5.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-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.
|
||||||
|
|||||||
@@ -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.5.1](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.5.0...@react-navigation/native@5.5.1) (2020-06-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/native
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [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)
|
## [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)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/native",
|
"name": "@react-navigation/native",
|
||||||
"description": "React Native integration for React Navigation",
|
"description": "React Native integration for React Navigation",
|
||||||
"version": "5.2.1",
|
"version": "5.5.1",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native",
|
"react-native",
|
||||||
"react-navigation",
|
"react-navigation",
|
||||||
@@ -16,11 +16,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__"
|
||||||
],
|
],
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
@@ -31,16 +33,19 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-navigation/core": "^5.4.0"
|
"@react-navigation/core": "^5.10.0",
|
||||||
|
"nanoid": "^3.1.9"
|
||||||
},
|
},
|
||||||
"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-native": "^0.61.22",
|
"@types/react-dom": "^16.9.8",
|
||||||
|
"@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-dom": "^16.13.1",
|
||||||
"react-native": "~0.61.5",
|
"react-native": "~0.61.5",
|
||||||
"react-native-testing-library": "^1.12.0",
|
"react-native-testing-library": "^1.13.2",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.8.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ type Props = NavigationContainerProps & {
|
|||||||
* Container component which holds the navigation state designed for React Native apps.
|
* Container component which holds the navigation state designed for React Native apps.
|
||||||
* This should be rendered at the root wrapping the whole app.
|
* This should be rendered at the root wrapping the whole app.
|
||||||
*
|
*
|
||||||
* @param props.initialState Initial state object for the navigation tree. When deep link handling is enabled, this will be ignored if there's an incoming link.
|
* @param props.initialState Initial state object for the navigation tree. When deep link handling is enabled, this will override deep links when specified. Make sure that you don't specify an `initialState` when there's a deep link (`Linking.getInitialURL()`).
|
||||||
* @param props.onStateChange Callback which is called with the latest navigation state when it changes.
|
* @param props.onStateChange Callback which is called with the latest navigation state when it changes.
|
||||||
* @param props.theme Theme object for the navigators.
|
* @param props.theme Theme object for the navigators.
|
||||||
* @param props.linking Options for deep linking. Deep link handling is enabled when this prop is provided, unless `linking.enabled` is `false`.
|
* @param props.linking Options for deep linking. Deep link handling is enabled when this prop is provided, unless `linking.enabled` is `false`.
|
||||||
@@ -46,15 +46,13 @@ const NavigationContainer = React.forwardRef(function NavigationContainer(
|
|||||||
...linking,
|
...linking,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [isReady, initialState = rest.initialState] = useThenable(
|
const [isReady, initialState] = useThenable(getInitialState);
|
||||||
getInitialState
|
|
||||||
);
|
|
||||||
|
|
||||||
React.useImperativeHandle(ref, () => refContainer.current);
|
React.useImperativeHandle(ref, () => refContainer.current);
|
||||||
|
|
||||||
const linkingContext = React.useMemo(() => ({ options: linking }), [linking]);
|
const linkingContext = React.useMemo(() => ({ options: linking }), [linking]);
|
||||||
|
|
||||||
if (isLinkingEnabled && !isReady) {
|
if (rest.initialState == null && isLinkingEnabled && !isReady) {
|
||||||
// This is temporary until we have Suspense for data-fetching
|
// This is temporary until we have Suspense for data-fetching
|
||||||
// Then the fallback will be handled by a parent `Suspense` component
|
// Then the fallback will be handled by a parent `Suspense` component
|
||||||
return fallback as React.ReactElement;
|
return fallback as React.ReactElement;
|
||||||
@@ -65,7 +63,9 @@ const NavigationContainer = React.forwardRef(function NavigationContainer(
|
|||||||
<ThemeProvider value={theme}>
|
<ThemeProvider value={theme}>
|
||||||
<BaseNavigationContainer
|
<BaseNavigationContainer
|
||||||
{...rest}
|
{...rest}
|
||||||
initialState={initialState}
|
initialState={
|
||||||
|
rest.initialState == null ? initialState : rest.initialState
|
||||||
|
}
|
||||||
ref={refContainer}
|
ref={refContainer}
|
||||||
/>
|
/>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|||||||
55
packages/native/src/ServerContainer.tsx
Normal file
55
packages/native/src/ServerContainer.tsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { CurrentRenderContext } from '@react-navigation/core';
|
||||||
|
import ServerContext, { ServerContextType } from './ServerContext';
|
||||||
|
import { ServerContainerRef } from './types';
|
||||||
|
|
||||||
|
type Props = ServerContextType & {
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container component for server rendering.
|
||||||
|
*
|
||||||
|
* @param props.location Location object to base the initial URL for SSR.
|
||||||
|
* @param props.children Child elements to render the content.
|
||||||
|
* @param props.ref Ref object which contains helper methods.
|
||||||
|
*/
|
||||||
|
export default React.forwardRef(function ServerContainer(
|
||||||
|
{ children, location }: Props,
|
||||||
|
ref: React.Ref<ServerContainerRef>
|
||||||
|
) {
|
||||||
|
React.useEffect(() => {
|
||||||
|
console.error(
|
||||||
|
"'ServerContainer' should only be used on the server with 'react-dom/server' for SSR."
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const current: { options?: object } = {};
|
||||||
|
|
||||||
|
if (ref) {
|
||||||
|
const value = {
|
||||||
|
getCurrentOptions() {
|
||||||
|
return current.options;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// We write to the `ref` during render instead of `React.useImperativeHandle`
|
||||||
|
// This is because `useImperativeHandle` will update the ref after 'commit',
|
||||||
|
// and there's no 'commit' phase during SSR.
|
||||||
|
// Mutating ref during render is unsafe in concurrent mode, but we don't care about it for SSR.
|
||||||
|
if (typeof ref === 'function') {
|
||||||
|
ref(value);
|
||||||
|
} else {
|
||||||
|
// @ts-ignore: the TS types are incorrect and say that ref.current is readonly
|
||||||
|
ref.current = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ServerContext.Provider value={{ location }}>
|
||||||
|
<CurrentRenderContext.Provider value={current}>
|
||||||
|
{children}
|
||||||
|
</CurrentRenderContext.Provider>
|
||||||
|
</ServerContext.Provider>
|
||||||
|
);
|
||||||
|
});
|
||||||
14
packages/native/src/ServerContext.tsx
Normal file
14
packages/native/src/ServerContext.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export type ServerContextType = {
|
||||||
|
location?: {
|
||||||
|
pathname: string;
|
||||||
|
search: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const ServerContext = React.createContext<ServerContextType | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
export default ServerContext;
|
||||||
69
packages/native/src/__mocks__/window.tsx
Normal file
69
packages/native/src/__mocks__/window.tsx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
const location = new URL('', 'http://example.com');
|
||||||
|
|
||||||
|
let listeners: (() => void)[] = [];
|
||||||
|
let entries = [{ state: null, href: location.href }];
|
||||||
|
let index = 0;
|
||||||
|
|
||||||
|
let currentState: any = null;
|
||||||
|
|
||||||
|
const history = {
|
||||||
|
get state() {
|
||||||
|
return currentState;
|
||||||
|
},
|
||||||
|
|
||||||
|
pushState(state: any, _: string, path: string) {
|
||||||
|
Object.assign(location, new URL(path, location.origin));
|
||||||
|
|
||||||
|
currentState = state;
|
||||||
|
entries = entries.slice(0, index + 1);
|
||||||
|
entries.push({ state, href: location.href });
|
||||||
|
index = entries.length - 1;
|
||||||
|
},
|
||||||
|
|
||||||
|
replaceState(state: any, _: string, path: string) {
|
||||||
|
Object.assign(location, new URL(path, location.origin));
|
||||||
|
|
||||||
|
currentState = state;
|
||||||
|
entries[index] = { state, href: location.href };
|
||||||
|
},
|
||||||
|
|
||||||
|
go(n: number) {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (
|
||||||
|
(n > 0 && n < entries.length - index) ||
|
||||||
|
(n < 0 && Math.abs(n) <= index)
|
||||||
|
) {
|
||||||
|
index += n;
|
||||||
|
Object.assign(location, new URL(entries[index].href));
|
||||||
|
listeners.forEach((cb) => cb);
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
back() {
|
||||||
|
this.go(-1);
|
||||||
|
},
|
||||||
|
|
||||||
|
forward() {
|
||||||
|
this.go(1);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const addEventListener = (type: 'popstate', listener: () => void) => {
|
||||||
|
if (type === 'popstate') {
|
||||||
|
listeners.push(listener);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeEventListener = (type: 'popstate', listener: () => void) => {
|
||||||
|
if (type === 'popstate') {
|
||||||
|
listeners = listeners.filter((cb) => cb !== listener);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
location,
|
||||||
|
history,
|
||||||
|
addEventListener,
|
||||||
|
removeEventListener,
|
||||||
|
};
|
||||||
149
packages/native/src/__tests__/NavigationContainer.test.tsx
Normal file
149
packages/native/src/__tests__/NavigationContainer.test.tsx
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import {
|
||||||
|
useNavigationBuilder,
|
||||||
|
createNavigatorFactory,
|
||||||
|
StackRouter,
|
||||||
|
TabRouter,
|
||||||
|
NavigationHelpersContext,
|
||||||
|
NavigationContainerRef,
|
||||||
|
} from '@react-navigation/core';
|
||||||
|
import { act, render } from 'react-native-testing-library';
|
||||||
|
import NavigationContainer from '../NavigationContainer';
|
||||||
|
import window from '../__mocks__/window';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
global.window = window;
|
||||||
|
|
||||||
|
// We want to use the web version of useLinking
|
||||||
|
jest.mock('../useLinking', () => require('../useLinking.tsx').default);
|
||||||
|
|
||||||
|
it('integrates with the history API', () => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
|
||||||
|
const createStackNavigator = createNavigatorFactory((props: any) => {
|
||||||
|
const { navigation, state, descriptors } = useNavigationBuilder(
|
||||||
|
StackRouter,
|
||||||
|
props
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NavigationHelpersContext.Provider value={navigation}>
|
||||||
|
{state.routes.map((route, i) => (
|
||||||
|
<div key={route.key} aria-current={state.index === i || undefined}>
|
||||||
|
{descriptors[route.key].render()}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</NavigationHelpersContext.Provider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const createTabNavigator = createNavigatorFactory((props: any) => {
|
||||||
|
const { navigation, state, descriptors } = useNavigationBuilder(
|
||||||
|
TabRouter,
|
||||||
|
props
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NavigationHelpersContext.Provider value={navigation}>
|
||||||
|
{state.routes.map((route, i) => (
|
||||||
|
<div key={route.key} aria-current={state.index === i || undefined}>
|
||||||
|
{descriptors[route.key].render()}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</NavigationHelpersContext.Provider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const Stack = createStackNavigator();
|
||||||
|
const Tab = createTabNavigator();
|
||||||
|
|
||||||
|
const TestScreen = ({ route }: any): any =>
|
||||||
|
`${route.name} ${JSON.stringify(route.params)}`;
|
||||||
|
|
||||||
|
const linking = {
|
||||||
|
prefixes: [],
|
||||||
|
config: {
|
||||||
|
Home: {
|
||||||
|
path: '',
|
||||||
|
initialRouteName: 'Feed',
|
||||||
|
screens: {
|
||||||
|
Profile: ':user',
|
||||||
|
Settings: 'edit',
|
||||||
|
Updates: 'updates',
|
||||||
|
Feed: 'feed',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Chat: 'chat',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const navigation = React.createRef<NavigationContainerRef>();
|
||||||
|
|
||||||
|
render(
|
||||||
|
<NavigationContainer ref={navigation} linking={linking}>
|
||||||
|
<Tab.Navigator>
|
||||||
|
<Tab.Screen name="Home">
|
||||||
|
{() => (
|
||||||
|
<Stack.Navigator initialRouteName="Feed">
|
||||||
|
<Stack.Screen name="Profile" component={TestScreen} />
|
||||||
|
<Stack.Screen name="Settings" component={TestScreen} />
|
||||||
|
<Stack.Screen name="Feed" component={TestScreen} />
|
||||||
|
<Stack.Screen name="Updates" component={TestScreen} />
|
||||||
|
</Stack.Navigator>
|
||||||
|
)}
|
||||||
|
</Tab.Screen>
|
||||||
|
<Tab.Screen name="Chat" component={TestScreen} />
|
||||||
|
</Tab.Navigator>
|
||||||
|
</NavigationContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(window.location.pathname).toBe('/feed');
|
||||||
|
|
||||||
|
act(() => navigation.current?.navigate('Profile', { user: 'jane' }));
|
||||||
|
|
||||||
|
expect(window.location.pathname).toBe('/jane');
|
||||||
|
|
||||||
|
act(() => navigation.current?.navigate('Updates'));
|
||||||
|
|
||||||
|
expect(window.location.pathname).toBe('/updates');
|
||||||
|
|
||||||
|
act(() => navigation.current?.goBack());
|
||||||
|
|
||||||
|
jest.runAllTimers();
|
||||||
|
|
||||||
|
expect(window.location.pathname).toBe('/jane');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
window.history.back();
|
||||||
|
jest.runAllTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(window.location.pathname).toBe('/feed');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
window.history.forward();
|
||||||
|
jest.runAllTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(window.location.pathname).toBe('/jane');
|
||||||
|
|
||||||
|
act(() => navigation.current?.navigate('Settings'));
|
||||||
|
|
||||||
|
expect(window.location.pathname).toBe('/edit');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
window.history.go(-2);
|
||||||
|
jest.runAllTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(window.location.pathname).toBe('/feed');
|
||||||
|
|
||||||
|
act(() => navigation.current?.navigate('Settings'));
|
||||||
|
act(() => navigation.current?.navigate('Chat'));
|
||||||
|
|
||||||
|
expect(window.location.pathname).toBe('/chat');
|
||||||
|
|
||||||
|
act(() => navigation.current?.navigate('Home'));
|
||||||
|
|
||||||
|
expect(window.location.pathname).toBe('/edit');
|
||||||
|
});
|
||||||
185
packages/native/src/__tests__/ServerContainer.test.tsx
Normal file
185
packages/native/src/__tests__/ServerContainer.test.tsx
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import {
|
||||||
|
useNavigationBuilder,
|
||||||
|
createNavigatorFactory,
|
||||||
|
StackRouter,
|
||||||
|
TabRouter,
|
||||||
|
NavigationHelpersContext,
|
||||||
|
} from '@react-navigation/core';
|
||||||
|
import { renderToString } from 'react-dom/server';
|
||||||
|
import NavigationContainer from '../NavigationContainer';
|
||||||
|
import ServerContainer from '../ServerContainer';
|
||||||
|
import { ServerContainerRef } from '../types';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
global.window = global;
|
||||||
|
|
||||||
|
window.addEventListener = () => {};
|
||||||
|
window.removeEventListener = () => {};
|
||||||
|
|
||||||
|
// We want to use the web version of useLinking
|
||||||
|
jest.mock('../useLinking', () => require('../useLinking.tsx').default);
|
||||||
|
|
||||||
|
it('renders correct state with location', () => {
|
||||||
|
const createStackNavigator = createNavigatorFactory((props: any) => {
|
||||||
|
const { navigation, state, descriptors } = useNavigationBuilder(
|
||||||
|
StackRouter,
|
||||||
|
props
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NavigationHelpersContext.Provider value={navigation}>
|
||||||
|
{state.routes.map((route) => (
|
||||||
|
<div key={route.key}>{descriptors[route.key].render()}</div>
|
||||||
|
))}
|
||||||
|
</NavigationHelpersContext.Provider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const Stack = createStackNavigator();
|
||||||
|
|
||||||
|
const TestScreen = ({ route }: any): any =>
|
||||||
|
`${route.name} ${JSON.stringify(route.params)}`;
|
||||||
|
|
||||||
|
const NestedStack = () => {
|
||||||
|
return (
|
||||||
|
<Stack.Navigator initialRouteName="Feed">
|
||||||
|
<Stack.Screen name="Profile" component={TestScreen} />
|
||||||
|
<Stack.Screen name="Settings" component={TestScreen} />
|
||||||
|
<Stack.Screen name="Feed" component={TestScreen} />
|
||||||
|
<Stack.Screen name="Updates" component={TestScreen} />
|
||||||
|
</Stack.Navigator>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const element = (
|
||||||
|
<NavigationContainer
|
||||||
|
linking={{
|
||||||
|
prefixes: [],
|
||||||
|
config: {
|
||||||
|
Home: {
|
||||||
|
initialRouteName: 'Profile',
|
||||||
|
screens: {
|
||||||
|
Settings: {
|
||||||
|
path: ':user/edit',
|
||||||
|
},
|
||||||
|
Updates: {
|
||||||
|
path: ':user/updates',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack.Navigator>
|
||||||
|
<Stack.Screen name="Home" component={NestedStack} />
|
||||||
|
<Stack.Screen name="Chat" component={TestScreen} />
|
||||||
|
</Stack.Navigator>
|
||||||
|
</NavigationContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
window.location = { pathname: '/jane/edit', search: '' };
|
||||||
|
|
||||||
|
const client = renderToString(element);
|
||||||
|
|
||||||
|
expect(client).toMatchInlineSnapshot(
|
||||||
|
`"<div><div>Profile undefined</div><div>Settings {"user":"jane"}</div></div>"`
|
||||||
|
);
|
||||||
|
|
||||||
|
const server = renderToString(
|
||||||
|
<ServerContainer location={{ pathname: '/john/updates', search: '' }}>
|
||||||
|
{element}
|
||||||
|
</ServerContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(server).toMatchInlineSnapshot(
|
||||||
|
`"<div><div>Profile undefined</div><div>Updates {"user":"john"}</div></div>"`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gets the current options', () => {
|
||||||
|
const createTabNavigator = createNavigatorFactory((props: any) => {
|
||||||
|
const { navigation, state, descriptors } = useNavigationBuilder(
|
||||||
|
TabRouter,
|
||||||
|
props
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NavigationHelpersContext.Provider value={navigation}>
|
||||||
|
{state.routes.map((route) => (
|
||||||
|
<div key={route.key}>{descriptors[route.key].render()}</div>
|
||||||
|
))}
|
||||||
|
</NavigationHelpersContext.Provider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const Tab = createTabNavigator();
|
||||||
|
|
||||||
|
const TestScreen = ({ route }: any): any =>
|
||||||
|
`${route.name} ${JSON.stringify(route.params)}`;
|
||||||
|
|
||||||
|
const NestedStack = () => {
|
||||||
|
return (
|
||||||
|
<Tab.Navigator initialRouteName="Feed">
|
||||||
|
<Tab.Screen
|
||||||
|
name="Profile"
|
||||||
|
component={TestScreen}
|
||||||
|
options={{ title: 'My profile' }}
|
||||||
|
/>
|
||||||
|
<Tab.Screen
|
||||||
|
name="Settings"
|
||||||
|
component={TestScreen}
|
||||||
|
options={{ title: 'Configure' }}
|
||||||
|
/>
|
||||||
|
<Tab.Screen
|
||||||
|
name="Feed"
|
||||||
|
component={TestScreen}
|
||||||
|
options={{ title: 'News feed' }}
|
||||||
|
/>
|
||||||
|
<Tab.Screen
|
||||||
|
name="Updates"
|
||||||
|
component={TestScreen}
|
||||||
|
options={{ title: 'Updates from cloud', description: 'Woah' }}
|
||||||
|
/>
|
||||||
|
</Tab.Navigator>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ref = React.createRef<ServerContainerRef>();
|
||||||
|
|
||||||
|
renderToString(
|
||||||
|
<ServerContainer ref={ref}>
|
||||||
|
<NavigationContainer
|
||||||
|
initialState={{
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Others',
|
||||||
|
state: {
|
||||||
|
routes: [{ name: 'Updates' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Tab.Navigator>
|
||||||
|
<Tab.Screen
|
||||||
|
name="Home"
|
||||||
|
component={TestScreen}
|
||||||
|
options={{ title: 'My app' }}
|
||||||
|
/>
|
||||||
|
<Tab.Screen
|
||||||
|
name="Others"
|
||||||
|
component={NestedStack}
|
||||||
|
options={{ title: 'Other stuff' }}
|
||||||
|
/>
|
||||||
|
</Tab.Navigator>
|
||||||
|
</NavigationContainer>
|
||||||
|
</ServerContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(ref.current?.getCurrentOptions()).toEqual({
|
||||||
|
title: 'Updates from cloud',
|
||||||
|
description: 'Woah',
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -17,7 +17,7 @@ it('throws if multiple instances of useLinking are used', () => {
|
|||||||
let element;
|
let element;
|
||||||
|
|
||||||
expect(() => (element = render(<Sample />))).toThrowError(
|
expect(() => (element = render(<Sample />))).toThrowError(
|
||||||
"Looks like you are using 'useLinking' in multiple components."
|
'Looks like you have configured linking in multiple places.'
|
||||||
);
|
);
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -41,9 +41,7 @@ it('throws if multiple instances of useLinking are used', () => {
|
|||||||
<B />
|
<B />
|
||||||
</>
|
</>
|
||||||
))
|
))
|
||||||
).toThrowError(
|
).toThrowError('Looks like you have configured linking in multiple places.');
|
||||||
"Looks like you are using 'useLinking' in multiple components."
|
|
||||||
);
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
element?.unmount();
|
element?.unmount();
|
||||||
@@ -57,5 +55,19 @@ it('throws if multiple instances of useLinking are used', () => {
|
|||||||
|
|
||||||
render(wrapper2).unmount();
|
render(wrapper2).unmount();
|
||||||
|
|
||||||
expect(() => render(wrapper2)).not.toThrow();
|
expect(() => (element = render(wrapper2))).not.toThrow();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
element?.unmount();
|
||||||
|
|
||||||
|
function Sample3() {
|
||||||
|
useLinking(ref, options);
|
||||||
|
useLinking(ref, { ...options, enabled: false });
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => (element = render(<Sample3 />))).not.toThrowError();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
element?.unmount();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,3 +15,7 @@ export { default as useLinking } from './useLinking';
|
|||||||
export { default as useLinkTo } from './useLinkTo';
|
export { default as useLinkTo } from './useLinkTo';
|
||||||
export { default as useLinkProps } from './useLinkProps';
|
export { default as useLinkProps } from './useLinkProps';
|
||||||
export { default as useLinkBuilder } from './useLinkBuilder';
|
export { default as useLinkBuilder } from './useLinkBuilder';
|
||||||
|
|
||||||
|
export { default as ServerContainer } from './ServerContainer';
|
||||||
|
|
||||||
|
export * from './types';
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
getStateFromPath as getStateFromPathDefault,
|
getStateFromPath as getStateFromPathDefault,
|
||||||
getPathFromState as getPathFromStateDefault,
|
getPathFromState as getPathFromStateDefault,
|
||||||
|
PathConfig,
|
||||||
} from '@react-navigation/core';
|
} from '@react-navigation/core';
|
||||||
|
|
||||||
export type Theme = {
|
export type Theme = {
|
||||||
@@ -39,7 +40,7 @@ export type LinkingOptions = {
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
config?: Parameters<typeof getStateFromPathDefault>[1];
|
config?: PathConfig;
|
||||||
/**
|
/**
|
||||||
* Custom function to parse the URL to a valid navigation state (advanced).
|
* Custom function to parse the URL to a valid navigation state (advanced).
|
||||||
* Only applicable on Web.
|
* Only applicable on Web.
|
||||||
@@ -50,3 +51,7 @@ export type LinkingOptions = {
|
|||||||
*/
|
*/
|
||||||
getPathFromState?: typeof getPathFromStateDefault;
|
getPathFromState?: typeof getPathFromStateDefault;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ServerContainerRef = {
|
||||||
|
getCurrentOptions(): Record<string, any> | undefined;
|
||||||
|
};
|
||||||
|
|||||||
@@ -52,14 +52,18 @@ export default function useLinkBuilder() {
|
|||||||
(name: string, params?: object) => {
|
(name: string, params?: object) => {
|
||||||
const { options } = linking;
|
const { options } = linking;
|
||||||
|
|
||||||
// If we couldn't find a navigation object in context, we're at root
|
if (options?.enabled === false) {
|
||||||
// So we'll construct a basic state object to use
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const state = navigation
|
const state = navigation
|
||||||
? getRootStateForNavigate(navigation, {
|
? getRootStateForNavigate(navigation, {
|
||||||
index: 0,
|
index: 0,
|
||||||
routes: [{ name, params }],
|
routes: [{ name, params }],
|
||||||
})
|
})
|
||||||
: {
|
: // If we couldn't find a navigation object in context, we're at root
|
||||||
|
// So we'll construct a basic state object to use
|
||||||
|
{
|
||||||
index: 0,
|
index: 0,
|
||||||
routes: [{ name, params }],
|
routes: [{ name, params }],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -49,6 +49,14 @@ export default function useLinkProps({ to, action }: Props) {
|
|||||||
throw new Error("Couldn't find a navigation object.");
|
throw new Error("Couldn't find a navigation object.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (typeof to !== 'string') {
|
||||||
|
throw new Error(
|
||||||
|
`To 'to' option is invalid (found '${String(
|
||||||
|
to
|
||||||
|
)}'. It must be a valid string for navigation.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
linkTo(to);
|
linkTo(to);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,22 +12,28 @@ let isUsingLinking = false;
|
|||||||
export default function useLinking(
|
export default function useLinking(
|
||||||
ref: React.RefObject<NavigationContainerRef>,
|
ref: React.RefObject<NavigationContainerRef>,
|
||||||
{
|
{
|
||||||
enabled,
|
enabled = true,
|
||||||
prefixes,
|
prefixes,
|
||||||
config,
|
config,
|
||||||
getStateFromPath = getStateFromPathDefault,
|
getStateFromPath = getStateFromPathDefault,
|
||||||
}: LinkingOptions
|
}: LinkingOptions
|
||||||
) {
|
) {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (isUsingLinking) {
|
if (enabled !== false && isUsingLinking) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Looks like you are using 'useLinking' in multiple components. This is likely an error since deep links should only be handled in one place to avoid conflicts." +
|
[
|
||||||
(Platform.OS === 'android'
|
'Looks like you have configured linking in multiple places. This is likely an error since deep links should only be handled in one place to avoid conflicts. Make sure that:',
|
||||||
? "\n\nIf you're not using it in multiple components, ensure that you have set 'android:launchMode=singleTask' in the '<activity />' section of the 'AndroidManifest.xml' file to avoid launching multiple activities which run multiple instances of the root component."
|
"- You are not using both 'linking' prop and 'useLinking'",
|
||||||
: '')
|
"- You don't have 'useLinking' in multiple components",
|
||||||
|
Platform.OS === 'android'
|
||||||
|
? "- You have set 'android:launchMode=singleTask' in the '<activity />' section of the 'AndroidManifest.xml' file to avoid launching multiple instances"
|
||||||
|
: '',
|
||||||
|
]
|
||||||
|
.join('\n')
|
||||||
|
.trim()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
isUsingLinking = true;
|
isUsingLinking = enabled !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
@@ -6,36 +6,226 @@ import {
|
|||||||
NavigationState,
|
NavigationState,
|
||||||
getActionFromState,
|
getActionFromState,
|
||||||
} from '@react-navigation/core';
|
} from '@react-navigation/core';
|
||||||
|
import { nanoid } from 'nanoid/non-secure';
|
||||||
|
import ServerContext from './ServerContext';
|
||||||
import { LinkingOptions } from './types';
|
import { LinkingOptions } from './types';
|
||||||
|
|
||||||
type ResultState = ReturnType<typeof getStateFromPathDefault>;
|
type ResultState = ReturnType<typeof getStateFromPathDefault>;
|
||||||
|
|
||||||
type HistoryState = { index: number };
|
type HistoryRecord = {
|
||||||
|
// Unique identifier for this record to match it with window.history.state
|
||||||
declare const history: {
|
id: string;
|
||||||
state?: HistoryState;
|
// Navigation state object for the history entry
|
||||||
go(delta: number): void;
|
state: NavigationState;
|
||||||
pushState(state: HistoryState, title: string, url: string): void;
|
// Path of the history entry
|
||||||
replaceState(state: HistoryState, title: string, url: string): void;
|
path: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStateLength = (state: NavigationState) => {
|
const createMemoryHistory = () => {
|
||||||
let length = 0;
|
let index = 0;
|
||||||
|
let items: HistoryRecord[] = [];
|
||||||
|
|
||||||
if (state.history) {
|
// Whether there's a `history.go(n)` pending
|
||||||
length = state.history.length;
|
let pending = false;
|
||||||
|
|
||||||
|
const history = {
|
||||||
|
get index(): number {
|
||||||
|
// We store an id in the state instead of an index
|
||||||
|
// Index could get out of sync with in-memory values if page reloads
|
||||||
|
const id = window.history.state?.id;
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
const index = items.findIndex((item) => item.id === id);
|
||||||
|
|
||||||
|
return index > -1 ? index : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
get(index: number) {
|
||||||
|
return items[index]?.state;
|
||||||
|
},
|
||||||
|
|
||||||
|
backIndex({ path }: { path: string }) {
|
||||||
|
// We need to find the index from the element before current to get closest path to go back to
|
||||||
|
for (let i = index - 1; i >= 0; i--) {
|
||||||
|
const item = items[i];
|
||||||
|
|
||||||
|
if (item.path === path) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
push({ path, state }: { path: string; state: NavigationState }) {
|
||||||
|
const id = nanoid();
|
||||||
|
|
||||||
|
// When a new entry is pushed, all the existing entries after index will be inaccessible
|
||||||
|
// So we remove any existing entries after the current index to clean them up
|
||||||
|
items = items.slice(0, index + 1);
|
||||||
|
|
||||||
|
items.push({ path, state, id });
|
||||||
|
index = items.length - 1;
|
||||||
|
|
||||||
|
// We pass empty string for title because it's ignored in all browsers except safari
|
||||||
|
// We don't store state object in history.state because:
|
||||||
|
// - browsers have limits on how big it can be, and we don't control the size
|
||||||
|
// - while not recommended, there could be non-serializable data in state
|
||||||
|
window.history.pushState({ id }, '', path);
|
||||||
|
},
|
||||||
|
|
||||||
|
replace({ path, state }: { path: string; state: NavigationState }) {
|
||||||
|
const id = window.history.state?.id ?? nanoid();
|
||||||
|
|
||||||
|
if (items.length) {
|
||||||
|
items[index] = { path, state, id };
|
||||||
} else {
|
} else {
|
||||||
length = state.index + 1;
|
// This is the first time any state modifications are done
|
||||||
|
// So we need to push the entry as there's nothing to replace
|
||||||
|
items.push({ path, state, id });
|
||||||
}
|
}
|
||||||
|
|
||||||
const focusedState = state.routes[state.index].state;
|
window.history.replaceState({ id }, '', path);
|
||||||
|
},
|
||||||
|
|
||||||
if (focusedState && !focusedState.stale) {
|
// `history.go(n)` is asynchronous, there are couple of things to keep in mind:
|
||||||
// If the focused route has history entries, we need to count them as well
|
// - it won't do anything if we can't go `n` steps, the `popstate` event won't fire.
|
||||||
length += getStateLength(focusedState as NavigationState) - 1;
|
// - each `history.go(n)` call will trigger a separate `popstate` event with correct location.
|
||||||
|
// - the `popstate` event fires before the next frame after calling `history.go(n)`.
|
||||||
|
// This method differs from `history.go(n)` in the sense that it'll go back as many steps it can.
|
||||||
|
go(n: number) {
|
||||||
|
if (n > 0) {
|
||||||
|
// We shouldn't go forward more than available index
|
||||||
|
n = Math.min(n, items.length - 1);
|
||||||
|
} else if (n < 0) {
|
||||||
|
// We shouldn't go back more than the index
|
||||||
|
// Otherwise we'll exit the page
|
||||||
|
n = Math.max(n, -Math.max(index + 1, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
return length;
|
if (n === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
index += n;
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
pending = true;
|
||||||
|
|
||||||
|
const done = () => {
|
||||||
|
pending = false;
|
||||||
|
|
||||||
|
window.removeEventListener('popstate', done);
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Resolve the promise in the next frame
|
||||||
|
// If `popstate` hasn't fired by then, then it wasn't handled
|
||||||
|
requestAnimationFrame(() => requestAnimationFrame(done));
|
||||||
|
|
||||||
|
window.addEventListener('popstate', done);
|
||||||
|
window.history.go(n);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// The `popstate` event is triggered when history changes, except `pushState` and `replaceState`
|
||||||
|
// If we call `history.go(n)` ourselves, we don't want it to trigger the listener
|
||||||
|
// Here we normalize it so that only external changes (e.g. user pressing back/forward) trigger the listener
|
||||||
|
listen(listener: () => void) {
|
||||||
|
const onPopState = () => {
|
||||||
|
if (pending) {
|
||||||
|
// This was triggered by `history.go(n)`, we shouldn't call the listener
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listener();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('popstate', onPopState);
|
||||||
|
|
||||||
|
return () => window.removeEventListener('popstate', onPopState);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return history;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the matching navigation state that changed between 2 navigation states
|
||||||
|
* e.g.: a -> b -> c -> d and a -> b -> c -> e -> f, if history in b changed, b is the matching state
|
||||||
|
*/
|
||||||
|
const findMatchingState = <T extends NavigationState>(
|
||||||
|
a: T | undefined,
|
||||||
|
b: T | undefined
|
||||||
|
): [T | undefined, T | undefined] => {
|
||||||
|
if (a === undefined || b === undefined || a.key !== b.key) {
|
||||||
|
return [undefined, undefined];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tab and drawer will have `history` property, but stack will have history in `routes`
|
||||||
|
const aHistoryLength = a.history ? a.history.length : a.routes.length;
|
||||||
|
const bHistoryLength = b.history ? b.history.length : b.routes.length;
|
||||||
|
|
||||||
|
const aRoute = a.routes[a.index];
|
||||||
|
const bRoute = b.routes[b.index];
|
||||||
|
|
||||||
|
const aChildState = aRoute.state as T | undefined;
|
||||||
|
const bChildState = bRoute.state as T | undefined;
|
||||||
|
|
||||||
|
// Stop here if this is the state object that changed:
|
||||||
|
// - history length is different
|
||||||
|
// - focused routes are different
|
||||||
|
// - one of them doesn't have child state
|
||||||
|
// - child state keys are different
|
||||||
|
if (
|
||||||
|
aHistoryLength !== bHistoryLength ||
|
||||||
|
aRoute.key !== bRoute.key ||
|
||||||
|
aChildState === undefined ||
|
||||||
|
bChildState === undefined ||
|
||||||
|
aChildState.key !== bChildState.key
|
||||||
|
) {
|
||||||
|
return [a, b];
|
||||||
|
}
|
||||||
|
|
||||||
|
return findMatchingState(aChildState, bChildState);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run async function in series as it's called.
|
||||||
|
*/
|
||||||
|
const series = (cb: () => Promise<void>) => {
|
||||||
|
// Whether we're currently handling a callback
|
||||||
|
let handling = false;
|
||||||
|
let queue: (() => Promise<void>)[] = [];
|
||||||
|
|
||||||
|
const callback = async () => {
|
||||||
|
try {
|
||||||
|
if (handling) {
|
||||||
|
// If we're currently handling a previous event, wait before handling this one
|
||||||
|
// Add the callback to the beginning of the queue
|
||||||
|
queue.unshift(callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handling = true;
|
||||||
|
|
||||||
|
await cb();
|
||||||
|
} finally {
|
||||||
|
handling = false;
|
||||||
|
|
||||||
|
if (queue.length) {
|
||||||
|
// If we have queued items, handle the last one
|
||||||
|
const last = queue.pop();
|
||||||
|
|
||||||
|
last?.();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return callback;
|
||||||
};
|
};
|
||||||
|
|
||||||
let isUsingLinking = false;
|
let isUsingLinking = false;
|
||||||
@@ -43,19 +233,25 @@ let isUsingLinking = false;
|
|||||||
export default function useLinking(
|
export default function useLinking(
|
||||||
ref: React.RefObject<NavigationContainerRef>,
|
ref: React.RefObject<NavigationContainerRef>,
|
||||||
{
|
{
|
||||||
enabled,
|
enabled = true,
|
||||||
config,
|
config,
|
||||||
getStateFromPath = getStateFromPathDefault,
|
getStateFromPath = getStateFromPathDefault,
|
||||||
getPathFromState = getPathFromStateDefault,
|
getPathFromState = getPathFromStateDefault,
|
||||||
}: LinkingOptions
|
}: LinkingOptions
|
||||||
) {
|
) {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (isUsingLinking) {
|
if (enabled !== false && isUsingLinking) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Looks like you are using 'useLinking' in multiple components. This is likely an error since URL integration should only be handled in one place to avoid conflicts. Also ensure that you set your android activity launchMode to single task in your AndroiManifest.xml file."
|
[
|
||||||
|
'Looks like you have configured linking in multiple places. This is likely an error since URL integration should only be handled in one place to avoid conflicts. Make sure that:',
|
||||||
|
"- You are not using both 'linking' prop and 'useLinking'",
|
||||||
|
"- You don't have 'useLinking' in multiple components",
|
||||||
|
]
|
||||||
|
.join('\n')
|
||||||
|
.trim()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
isUsingLinking = true;
|
isUsingLinking = enabled !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@@ -63,6 +259,8 @@ export default function useLinking(
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [history] = React.useState(createMemoryHistory);
|
||||||
|
|
||||||
// We store these options in ref to avoid re-creating getInitialState and re-subscribing listeners
|
// We store these options in ref to avoid re-creating getInitialState and re-subscribing listeners
|
||||||
// This lets user avoid wrapping the items in `React.useCallback` or `React.useMemo`
|
// This lets user avoid wrapping the items in `React.useCallback` or `React.useMemo`
|
||||||
// Not re-creating `getInitialState` is important coz it makes it easier for the user to use in an effect
|
// Not re-creating `getInitialState` is important coz it makes it easier for the user to use in an effect
|
||||||
@@ -78,228 +276,174 @@ export default function useLinking(
|
|||||||
getPathFromStateRef.current = getPathFromState;
|
getPathFromStateRef.current = getPathFromState;
|
||||||
}, [config, enabled, getPathFromState, getStateFromPath]);
|
}, [config, enabled, getPathFromState, getStateFromPath]);
|
||||||
|
|
||||||
|
const server = React.useContext(ServerContext);
|
||||||
|
|
||||||
const getInitialState = React.useCallback(() => {
|
const getInitialState = React.useCallback(() => {
|
||||||
let value: ResultState | undefined;
|
let value: ResultState | undefined;
|
||||||
|
|
||||||
if (enabledRef.current) {
|
if (enabledRef.current) {
|
||||||
const path = location.pathname + location.search;
|
const location =
|
||||||
|
server?.location ??
|
||||||
|
(typeof window !== 'undefined' ? window.location : undefined);
|
||||||
|
|
||||||
|
const path = location ? location.pathname + location.search : undefined;
|
||||||
|
|
||||||
if (path) {
|
if (path) {
|
||||||
value = getStateFromPathRef.current(path, configRef.current);
|
value = getStateFromPathRef.current(path, configRef.current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const then = (callback: (state: ResultState | undefined) => void) =>
|
|
||||||
Promise.resolve(callback(value));
|
|
||||||
|
|
||||||
// Make it a thenable to keep consistent with the native impl
|
// Make it a thenable to keep consistent with the native impl
|
||||||
const thenable = {
|
const thenable = {
|
||||||
then,
|
then(onfulfilled?: (state: ResultState | undefined) => void) {
|
||||||
|
return Promise.resolve(onfulfilled ? onfulfilled(value) : value);
|
||||||
|
},
|
||||||
catch() {
|
catch() {
|
||||||
return thenable;
|
return thenable;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return thenable;
|
return thenable as PromiseLike<ResultState | undefined>;
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const previousStateLengthRef = React.useRef<number | undefined>(undefined);
|
const previousStateRef = React.useRef<NavigationState | undefined>(undefined);
|
||||||
const previousHistoryIndexRef = React.useRef(0);
|
const pendingPopStatePathRef = React.useRef<string | undefined>(undefined);
|
||||||
|
|
||||||
const pendingIndexChangeRef = React.useRef<number | undefined>();
|
|
||||||
const pendingStateUpdateRef = React.useRef<boolean>(false);
|
|
||||||
const pendingStateMultiUpdateRef = React.useRef<boolean>(false);
|
|
||||||
|
|
||||||
// If we're navigating ahead >1, we're not restoring whole state,
|
|
||||||
// but just navigate to the selected route not caring about previous routes
|
|
||||||
// therefore if we need to go back, we need to pop screen and navigate to the new one
|
|
||||||
// Possibly, we will need to reuse the same mechanism.
|
|
||||||
// E.g. if we went ahead+4 (numberOfIndicesAhead = 3), and back-2,
|
|
||||||
// actually we need to pop the screen we navigated
|
|
||||||
// and navigate again, setting numberOfIndicesAhead to 1.
|
|
||||||
const numberOfIndicesAhead = React.useRef(0);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const onPopState = () => {
|
return history.listen(() => {
|
||||||
const navigation = ref.current;
|
const navigation = ref.current;
|
||||||
|
|
||||||
if (!navigation || !enabled) {
|
if (!navigation || !enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const previousHistoryIndex = previousHistoryIndexRef.current;
|
const path = location.pathname + location.search;
|
||||||
const historyIndex = history.state?.index ?? 0;
|
|
||||||
|
|
||||||
previousHistoryIndexRef.current = historyIndex;
|
pendingPopStatePathRef.current = path;
|
||||||
|
|
||||||
if (pendingIndexChangeRef.current === historyIndex) {
|
// When browser back/forward is clicked, we first need to check if state object for this index exists
|
||||||
pendingIndexChangeRef.current = undefined;
|
// If it does we'll reset to that state object
|
||||||
|
// Otherwise, we'll handle it like a regular deep link
|
||||||
|
const recordedState = history.get(history.index);
|
||||||
|
|
||||||
|
if (recordedState) {
|
||||||
|
navigation.resetRoot(recordedState);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = navigation.getRootState();
|
const state = getStateFromPathRef.current(path, configRef.current);
|
||||||
const path = getPathFromStateRef.current(state, configRef.current);
|
|
||||||
|
|
||||||
let canGoBack = true;
|
|
||||||
let numberOfBacks = 0;
|
|
||||||
|
|
||||||
if (previousHistoryIndex === historyIndex) {
|
|
||||||
if (location.pathname + location.search !== path) {
|
|
||||||
pendingStateUpdateRef.current = true;
|
|
||||||
history.replaceState({ index: historyIndex }, '', path);
|
|
||||||
}
|
|
||||||
} else if (previousHistoryIndex > historyIndex) {
|
|
||||||
numberOfBacks =
|
|
||||||
previousHistoryIndex - historyIndex - numberOfIndicesAhead.current;
|
|
||||||
|
|
||||||
if (numberOfBacks > 0) {
|
|
||||||
pendingStateMultiUpdateRef.current = true;
|
|
||||||
|
|
||||||
if (numberOfBacks > 1) {
|
|
||||||
pendingStateMultiUpdateRef.current = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pendingStateUpdateRef.current = true;
|
|
||||||
|
|
||||||
for (let i = 0; i < numberOfBacks; i++) {
|
|
||||||
navigation.goBack();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
canGoBack = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (previousHistoryIndex < historyIndex || !canGoBack) {
|
|
||||||
if (canGoBack) {
|
|
||||||
numberOfIndicesAhead.current =
|
|
||||||
historyIndex - previousHistoryIndex - 1;
|
|
||||||
} else {
|
|
||||||
navigation.goBack();
|
|
||||||
numberOfIndicesAhead.current -= previousHistoryIndex - historyIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
const state = getStateFromPathRef.current(
|
|
||||||
location.pathname + location.search,
|
|
||||||
configRef.current
|
|
||||||
);
|
|
||||||
|
|
||||||
pendingStateMultiUpdateRef.current = true;
|
|
||||||
|
|
||||||
if (state) {
|
if (state) {
|
||||||
const action = getActionFromState(state);
|
const action = getActionFromState(state);
|
||||||
|
|
||||||
pendingStateUpdateRef.current = true;
|
|
||||||
|
|
||||||
if (action !== undefined) {
|
if (action !== undefined) {
|
||||||
navigation.dispatch(action);
|
navigation.dispatch(action);
|
||||||
} else {
|
} else {
|
||||||
navigation.resetRoot(state);
|
navigation.resetRoot(state);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// if current path didn't return any state, we should revert to initial state
|
||||||
|
navigation.resetRoot(state);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
};
|
}, [enabled, history, ref]);
|
||||||
|
|
||||||
window.addEventListener('popstate', onPopState);
|
|
||||||
|
|
||||||
return () => window.removeEventListener('popstate', onPopState);
|
|
||||||
}, [enabled, ref]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ref.current && previousStateLengthRef.current === undefined) {
|
if (ref.current) {
|
||||||
previousStateLengthRef.current = getStateLength(
|
// We need to record the current metadata on the first render if they aren't set
|
||||||
ref.current.getRootState()
|
// This will allow the initial state to be in the history entry
|
||||||
);
|
const state = ref.current.getRootState();
|
||||||
}
|
|
||||||
|
|
||||||
if (ref.current && location.pathname + location.search === '/') {
|
|
||||||
history.replaceState(
|
|
||||||
{ index: history.state?.index ?? 0 },
|
|
||||||
'',
|
|
||||||
getPathFromStateRef.current(
|
|
||||||
ref.current.getRootState(),
|
|
||||||
configRef.current
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsubscribe = ref.current?.addListener('state', () => {
|
|
||||||
const navigation = ref.current;
|
|
||||||
|
|
||||||
if (!navigation) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const state = navigation.getRootState();
|
|
||||||
const path = getPathFromStateRef.current(state, configRef.current);
|
const path = getPathFromStateRef.current(state, configRef.current);
|
||||||
|
|
||||||
const previousStateLength = previousStateLengthRef.current ?? 1;
|
if (previousStateRef.current === undefined) {
|
||||||
const stateLength = getStateLength(state);
|
previousStateRef.current = state;
|
||||||
|
|
||||||
if (pendingStateMultiUpdateRef.current) {
|
|
||||||
if (location.pathname + location.search === path) {
|
|
||||||
pendingStateMultiUpdateRef.current = false;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
previousStateLengthRef.current = stateLength;
|
history.replace({ path, state });
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
const onStateChange = async () => {
|
||||||
pendingStateUpdateRef.current &&
|
const navigation = ref.current;
|
||||||
location.pathname + location.search === path
|
|
||||||
) {
|
if (!navigation || !enabled) {
|
||||||
pendingStateUpdateRef.current = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let index = history.state?.index ?? 0;
|
const previousState = previousStateRef.current;
|
||||||
|
const state = navigation.getRootState();
|
||||||
|
|
||||||
if (previousStateLength === stateLength) {
|
const pendingPath = pendingPopStatePathRef.current;
|
||||||
// If no new entries were added to history in our navigation state, we want to replaceState
|
const path = getPathFromStateRef.current(state, configRef.current);
|
||||||
if (location.pathname + location.search !== path) {
|
|
||||||
history.replaceState({ index }, '', path);
|
|
||||||
previousHistoryIndexRef.current = index;
|
|
||||||
}
|
|
||||||
} else if (stateLength > previousStateLength) {
|
|
||||||
// If new entries were added, pushState until we have same length
|
|
||||||
// This won't be accurate if multiple entries were added at once, but that's the best we can do
|
|
||||||
for (let i = 0, l = stateLength - previousStateLength; i < l; i++) {
|
|
||||||
index++;
|
|
||||||
history.pushState({ index }, '', path);
|
|
||||||
}
|
|
||||||
|
|
||||||
previousHistoryIndexRef.current = index;
|
previousStateRef.current = state;
|
||||||
} else if (previousStateLength > stateLength) {
|
pendingPopStatePathRef.current = undefined;
|
||||||
const delta = Math.min(
|
|
||||||
previousStateLength - stateLength,
|
// To detect the kind of state change, we need to:
|
||||||
// We need to keep at least one item in the history
|
// - Find the common focused navigation state in previous and current state
|
||||||
// Otherwise we'll exit the page
|
// - If only the route keys changed, compare history/routes.length to check if we go back/forward/replace
|
||||||
previousHistoryIndexRef.current - 1
|
// - If no common focused navigation state found, it's a replace
|
||||||
|
const [previousFocusedState, focusedState] = findMatchingState(
|
||||||
|
previousState,
|
||||||
|
state
|
||||||
);
|
);
|
||||||
|
|
||||||
if (delta > 0) {
|
if (
|
||||||
// We need to set this to ignore the `popstate` event
|
previousFocusedState &&
|
||||||
pendingIndexChangeRef.current = index - delta;
|
focusedState &&
|
||||||
|
// We should only handle push/pop if path changed from what was in last `popstate`
|
||||||
|
// Otherwise it's likely a change triggered by `popstate`
|
||||||
|
path !== pendingPath
|
||||||
|
) {
|
||||||
|
const historyDelta =
|
||||||
|
(focusedState.history
|
||||||
|
? focusedState.history.length
|
||||||
|
: focusedState.routes.length) -
|
||||||
|
(previousFocusedState.history
|
||||||
|
? previousFocusedState.history.length
|
||||||
|
: previousFocusedState.routes.length);
|
||||||
|
|
||||||
// If new entries were removed, go back so that we have same length
|
if (historyDelta > 0) {
|
||||||
history.go(-delta);
|
// If history length is increased, we should pushState
|
||||||
|
// Note that path might not actually change here, for example, drawer open should pushState
|
||||||
|
history.push({ path, state });
|
||||||
|
} else if (historyDelta < 0) {
|
||||||
|
// If history length is decreased, i.e. entries were removed, we want to go back
|
||||||
|
|
||||||
|
const nextIndex = history.backIndex({ path });
|
||||||
|
const currentIndex = history.index;
|
||||||
|
|
||||||
|
if (nextIndex !== -1 && nextIndex < currentIndex) {
|
||||||
|
// An existing entry for this path exists and it's less than current index, go back to that
|
||||||
|
await history.go(nextIndex - currentIndex);
|
||||||
} else {
|
} else {
|
||||||
// We're not going back in history, but the navigation state changed
|
// We couldn't find an existing entry to go back to, so we'll go back by the delta
|
||||||
// The URL probably also changed, so we need to re-sync the URL
|
// This won't be correct if multiple routes were pushed in one go before
|
||||||
if (location.pathname + location.search !== path) {
|
// Usually this shouldn't happen and this is a fallback for that
|
||||||
history.replaceState({ index }, '', path);
|
await history.go(historyDelta);
|
||||||
previousHistoryIndexRef.current = index;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return unsubscribe;
|
// Store the updated state as well as fix the path if incorrect
|
||||||
|
history.replace({ path, state });
|
||||||
|
} else {
|
||||||
|
// If history length is unchanged, we want to replaceState
|
||||||
|
history.replace({ path, state });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If no common navigation state was found, assume it's a replace
|
||||||
|
// This would happen if the user did a reset/conditionally changed navigators
|
||||||
|
history.replace({ path, state });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// We debounce onStateChange coz we don't want multiple state changes to be handled at one time
|
||||||
|
// This could happen since `history.go(n)` is asynchronous
|
||||||
|
// If `pushState` or `replaceState` were called before `history.go(n)` completes, it'll mess stuff up
|
||||||
|
return ref.current?.addListener('state', series(onStateChange));
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
type Thenable<T> = { then(cb: (result: T) => void): void };
|
export default function useThenable<T>(create: () => PromiseLike<T>) {
|
||||||
|
|
||||||
export default function useThenable<T>(create: () => Thenable<T>) {
|
|
||||||
const [promise] = React.useState(create);
|
const [promise] = React.useState(create);
|
||||||
|
|
||||||
// Check if our thenable is synchronous
|
// Check if our thenable is synchronous
|
||||||
|
|||||||
@@ -3,6 +3,49 @@
|
|||||||
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.4.7](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.4.6...@react-navigation/routers@5.4.7) (2020-05-23)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/routers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.4.6](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.4.5...@react-navigation/routers@5.4.6) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/routers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.4.5](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.4.4...@react-navigation/routers@5.4.5) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/routers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.4.4](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.4.3...@react-navigation/routers@5.4.4) (2020-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix building typescript definitions. closes [#8216](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/issues/8216) ([47a1229](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/commit/47a12298378747edd2d22e54dc1c8677f98c49b4))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.4.3](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.4.2...@react-navigation/routers@5.4.3) (2020-05-08)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/routers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.4.2](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.4.1...@react-navigation/routers@5.4.2) (2020-04-30)
|
## [5.4.2](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.4.1...@react-navigation/routers@5.4.2) (2020-04-30)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/routers",
|
"name": "@react-navigation/routers",
|
||||||
"description": "Routers to help build custom navigators",
|
"description": "Routers to help build custom navigators",
|
||||||
"version": "5.4.2",
|
"version": "5.4.7",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react",
|
"react",
|
||||||
"react-native",
|
"react-native",
|
||||||
@@ -15,11 +15,13 @@
|
|||||||
"homepage": "https://reactnavigation.org/docs/custom-routers.html",
|
"homepage": "https://reactnavigation.org/docs/custom-routers.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": {
|
||||||
@@ -30,10 +32,10 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.0.2"
|
"nanoid": "^3.1.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.10.0",
|
"@react-native-community/bob": "^0.14.3",
|
||||||
"del-cli": "^3.0.0",
|
"del-cli": "^3.0.0",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.8.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,168 @@
|
|||||||
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.4.2](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.4.1...@react-navigation/stack@5.4.2) (2020-06-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* relatively position float Header if !headerTransparent ([#8285](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/8285)) ([78afbff](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/78afbffe976b14bb60666a2b1230127db0dc24f6))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.4.1](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.4.0...@react-navigation/stack@5.4.1) (2020-05-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* allow HeaderBackground's subViews to be touchable ([#8317](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/8317)) ([00c23f2](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/00c23f2c9ed22fa4d010ffb427f2b52e061d8df4))
|
||||||
|
* fix type of style for various options ([9d822b9](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/9d822b95a6df797e2e63e481573e64ea7d0f9386))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.4.0](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.3.9...@react-navigation/stack@5.4.0) (2020-05-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* allow HeaderBackground's subViews to be touchable ([#8314](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/8314)) ([021a911](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/021a9111d76b9b0d940c8829f7caebeb292fec2a))
|
||||||
|
* don't ignore previous header heights on layout update ([6dd45fc](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/6dd45fcff98a0c222d120d6f76a37130de45b92f))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* update react-native-safe-area-context to 1.0.0 ([#8182](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/8182)) ([d62fbfe](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/d62fbfe255140f16b182e8b54b276a7c96f2aec6))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.9](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.3.8...@react-navigation/stack@5.3.9) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/stack
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.8](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.3.7...@react-navigation/stack@5.3.8) (2020-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/stack
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.7](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.3.6...@react-navigation/stack@5.3.7) (2020-05-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/stack
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.6](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.3.5...@react-navigation/stack@5.3.6) (2020-05-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* reduce header title margin. fixes [#8267](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/8267) ([d45dbe9](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/d45dbe97dc6625c6a8e80b5e658ab5a95045e5e8))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.5](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.3.4...@react-navigation/stack@5.3.5) (2020-05-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/stack
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.4](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.3.3...@react-navigation/stack@5.3.4) (2020-05-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/stack
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.3](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.3.2...@react-navigation/stack@5.3.3) (2020-05-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix ios transitionspec settle time ([#8028](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/8028)) ([dd7cff2](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/dd7cff201608365a80f1d50a006df3e0d18e94a1))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.2](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.3.1...@react-navigation/stack@5.3.2) (2020-05-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/stack
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.1](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.3.0...@react-navigation/stack@5.3.1) (2020-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix building typescript definitions. closes [#8216](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/8216) ([47a1229](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/47a12298378747edd2d22e54dc1c8677f98c49b4))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.3.0](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.19...@react-navigation/stack@5.3.0) (2020-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add proper margins to the header title ([f07cd13](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/f07cd135619d635e8841aa0df0b6e687636e7408))
|
||||||
|
* include safe are insets in title's margins ([4d1e102](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/4d1e102f8c3ffab116d0195fbab3086f6345a077))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add generic type aliases for screen props ([bea14aa](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/bea14aa26fd5cbfebc7973733c5cf1f44fd323aa)), closes [#7971](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7971)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.19](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.18...@react-navigation/stack@5.2.19) (2020-05-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/stack
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.18](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.17...@react-navigation/stack@5.2.18) (2020-05-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/stack
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.2.17](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.16...@react-navigation/stack@5.2.17) (2020-05-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/stack
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.2.16](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.15...@react-navigation/stack@5.2.16) (2020-04-30)
|
## [5.2.16](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.15...@react-navigation/stack@5.2.16) (2020-04-30)
|
||||||
|
|
||||||
**Note:** Version bump only for package @react-navigation/stack
|
**Note:** Version bump only for package @react-navigation/stack
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/stack",
|
"name": "@react-navigation/stack",
|
||||||
"description": "Stack navigator component for iOS and Android with animated transitions and gestures",
|
"description": "Stack navigator component for iOS and Android with animated transitions and gestures",
|
||||||
"version": "5.2.16",
|
"version": "5.4.2",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -19,11 +19,13 @@
|
|||||||
"homepage": "https://reactnavigation.org/docs/stack-navigator.html",
|
"homepage": "https://reactnavigation.org/docs/stack-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,18 +40,18 @@
|
|||||||
"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-native-community/masked-view": "^0.1.7",
|
"@react-native-community/masked-view": "^0.1.10",
|
||||||
"@react-navigation/native": "^5.2.1",
|
"@react-navigation/native": "^5.5.1",
|
||||||
"@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-gesture-handler": "^1.6.0",
|
"react-native-gesture-handler": "^1.6.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": {
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ export const TransitionIOSSpec: TransitionSpec = {
|
|||||||
damping: 500,
|
damping: 500,
|
||||||
mass: 3,
|
mass: 3,
|
||||||
overshootClamping: true,
|
overshootClamping: true,
|
||||||
restDisplacementThreshold: 0.01,
|
restDisplacementThreshold: 10,
|
||||||
restSpeedThreshold: 0.01,
|
restSpeedThreshold: 10,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export { default as useGestureHandlerRef } from './utils/useGestureHandlerRef';
|
|||||||
export type {
|
export type {
|
||||||
StackNavigationOptions,
|
StackNavigationOptions,
|
||||||
StackNavigationProp,
|
StackNavigationProp,
|
||||||
|
StackScreenProps,
|
||||||
StackHeaderProps,
|
StackHeaderProps,
|
||||||
StackHeaderLeftButtonProps,
|
StackHeaderLeftButtonProps,
|
||||||
StackHeaderTitleProps,
|
StackHeaderTitleProps,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user