mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-14 17:42:29 +08:00
Compare commits
54 Commits
@react-nav
...
@react-nav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f274058b90 | ||
|
|
976178d098 | ||
|
|
493956ef71 | ||
|
|
699ea0cc50 | ||
|
|
a63f9da8c1 | ||
|
|
cceaa6780d | ||
|
|
4b8155386b | ||
|
|
1a757fc30a | ||
|
|
7b353a4aea | ||
|
|
3728390b60 | ||
|
|
a8342aaf3d | ||
|
|
860adbfd8b | ||
|
|
38d680833e | ||
|
|
cae115fc17 | ||
|
|
87b51476d0 | ||
|
|
b1b211855f | ||
|
|
60fe0dbb0a | ||
|
|
bb294b16f9 | ||
|
|
4ca2d2d22b | ||
|
|
35747a6066 | ||
|
|
bae4019995 | ||
|
|
d3a9639060 | ||
|
|
d88cbcb52d | ||
|
|
dc7e876b6f | ||
|
|
1e215614d8 | ||
|
|
dd87fa49a4 | ||
|
|
09f0ebbb0f | ||
|
|
9633c4d35f | ||
|
|
28fac3e0b9 | ||
|
|
a8b8c27174 | ||
|
|
b19f76bfff | ||
|
|
365a2ad28c | ||
|
|
b26b90706f | ||
|
|
47f28558d6 | ||
|
|
26074a28f7 | ||
|
|
6fe1d70c6c | ||
|
|
77fa6fb683 | ||
|
|
2ad61a6735 | ||
|
|
c9a5d45324 | ||
|
|
3c874191ff | ||
|
|
2317633652 | ||
|
|
74d368eb4d | ||
|
|
d617ab82f9 | ||
|
|
f5fd0e5be4 | ||
|
|
7bef138e3d | ||
|
|
1406eb83ed | ||
|
|
3e069b718d | ||
|
|
7754eb450f | ||
|
|
95b2599877 | ||
|
|
efcfa7121f | ||
|
|
a8e27ef448 | ||
|
|
946d2923d7 | ||
|
|
794339eeed | ||
|
|
53141a6436 |
@@ -8,6 +8,12 @@ executors:
|
|||||||
environment:
|
environment:
|
||||||
YARN_CACHE_FOLDER: "~/.cache/yarn"
|
YARN_CACHE_FOLDER: "~/.cache/yarn"
|
||||||
|
|
||||||
|
playwright:
|
||||||
|
docker:
|
||||||
|
- image: mcr.microsoft.com/playwright:bionic
|
||||||
|
environment:
|
||||||
|
NODE_ENV: development
|
||||||
|
|
||||||
commands:
|
commands:
|
||||||
attach_project:
|
attach_project:
|
||||||
steps:
|
steps:
|
||||||
@@ -61,18 +67,9 @@ jobs:
|
|||||||
destination: coverage
|
destination: coverage
|
||||||
|
|
||||||
integration-tests:
|
integration-tests:
|
||||||
executor: default
|
executor: playwright
|
||||||
steps:
|
steps:
|
||||||
- attach_project
|
- attach_project
|
||||||
- run:
|
|
||||||
name: Install Headless Chrome dependencies
|
|
||||||
command: |
|
|
||||||
sudo apt-get install -yq \
|
|
||||||
gconf-service libasound2 libatk1.0-0 libatk-bridge2.0-0 libc6 libcairo2 libcups2 libdbus-1-3 \
|
|
||||||
libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 \
|
|
||||||
libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 \
|
|
||||||
libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates \
|
|
||||||
fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
|
|
||||||
- run:
|
- run:
|
||||||
name: Build example for web
|
name: Build example for web
|
||||||
command: yarn example expo build:web --no-pwa
|
command: yarn example expo build:web --no-pwa
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -14,7 +14,7 @@ contact_links:
|
|||||||
about: Ask and answer questions using the react-navigation label.
|
about: Ask and answer questions using the react-navigation label.
|
||||||
- name: Reactiflux
|
- name: Reactiflux
|
||||||
url: https://www.reactiflux.com/
|
url: https://www.reactiflux.com/
|
||||||
about: Chat with other community members in the react-navigation channel.
|
about: Chat with other community members in the help-react-native channel.
|
||||||
- name: Write an RFC
|
- name: Write an RFC
|
||||||
url: https://github.com/react-navigation/rfcs
|
url: https://github.com/react-navigation/rfcs
|
||||||
about: Write a RFC if you have ideas for how to implement a feature request.
|
about: Write a RFC if you have ideas for how to implement a feature request.
|
||||||
|
|||||||
48
.github/workflows/check-repro.yml
vendored
Normal file
48
.github/workflows/check-repro.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
name: Check for repro
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened, edited]
|
||||||
|
issue_comment:
|
||||||
|
types: [created, edited]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-repro:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/github-script@v3
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
script: |
|
||||||
|
const user = context.payload.sender.login;
|
||||||
|
const body = context.payload.comment
|
||||||
|
? context.payload.comment.body
|
||||||
|
: context.payload.issue.body;
|
||||||
|
|
||||||
|
const regex = new RegExp(
|
||||||
|
`https?:\\/\\/((github\\.com\\/${user}\\/[^/]+\\/?[\\s\\n]+)|(snack\\.expo\\.io\\/.+))`,
|
||||||
|
'gm'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!regex.test(body)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await github.issues.addLabels({
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
labels: ['repro provided'],
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await github.issues.removeLabel({
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
name: 'needs repro',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
if (!/Label does not exist/.test(error.message)) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
2
.github/workflows/expo-preview.yml
vendored
2
.github/workflows/expo-preview.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
|||||||
run: echo "::set-output name=path::@react-navigation/react-navigation-example?release-channel=pr-${{ github.event.number }}"
|
run: echo "::set-output name=path::@react-navigation/react-navigation-example?release-channel=pr-${{ github.event.number }}"
|
||||||
|
|
||||||
- name: Comment on PR
|
- name: Comment on PR
|
||||||
uses: actions/github-script@v2
|
uses: actions/github-script@v3
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
script: |
|
script: |
|
||||||
|
|||||||
45
.github/workflows/first-pull-request.yml
vendored
Normal file
45
.github/workflows/first-pull-request.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
name: First pull request
|
||||||
|
on: pull_request
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
welcome:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/github-script@v3
|
||||||
|
with:
|
||||||
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
script: |
|
||||||
|
// Get a list of all issues created by the PR opener
|
||||||
|
// See: https://octokit.github.io/rest.js/#pagination
|
||||||
|
const creator = context.payload.sender.login;
|
||||||
|
const opts = github.issues.listForRepo.endpoint.merge({
|
||||||
|
...context.issue,
|
||||||
|
creator,
|
||||||
|
state: 'all'
|
||||||
|
});
|
||||||
|
|
||||||
|
const issues = await github.paginate(opts);
|
||||||
|
|
||||||
|
for (const issue of issues) {
|
||||||
|
if (issue.number === context.issue.number) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (issue.pull_request) {
|
||||||
|
return ;// Creator is already a contributor.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await github.issues.addLabels({
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
labels: ['first pull request'],
|
||||||
|
});
|
||||||
|
|
||||||
|
await github.issues.createComment({
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
body: "Hey ${creator}! Thanks for opening the pull request. If you haven't already, make sure to read our [contribution guidelines](https://github.com/react-navigation/react-navigation/blob/main/CONTRIBUTING.md)."
|
||||||
|
});
|
||||||
18
.github/workflows/triage.yml
vendored
18
.github/workflows/triage.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event.label.name == 'needs more info'
|
if: github.event.label.name == 'needs more info'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v2
|
- uses: actions/github-script@v3
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
script: |
|
script: |
|
||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event.label.name == 'needs repro'
|
if: github.event.label.name == 'needs repro'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v2
|
- uses: actions/github-script@v3
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
script: |
|
script: |
|
||||||
@@ -38,7 +38,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event.label.name == 'question'
|
if: github.event.label.name == 'question'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v2
|
- uses: actions/github-script@v3
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
script: |
|
script: |
|
||||||
@@ -46,14 +46,14 @@ jobs:
|
|||||||
issue_number: context.issue.number,
|
issue_number: context.issue.number,
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
body: "Hey! Thanks for opening the issue. The issue tracker is intended for only tracking bug reports. This helps us prioritize fixing bugs in the library. Seems you have a usage question or an issue unrelated to this library. Please ask the question on [StackOverflow](https://stackoverflow.com/questions/tagged/react-navigation) instead using the `react-navigation` label. You can also chat with other community members on [Reactiflux Discord server](https://www.reactiflux.com/) in the `#react-navigation` channel.\n\nIf you believe that this is actually a bug in the library, please open a new issue and fill the issue template with relevant information."
|
body: "Hey! Thanks for opening the issue. The issue tracker is intended for only tracking bug reports. This helps us prioritize fixing bugs in the library. Seems you have a usage question or an issue unrelated to this library. Please ask the question on [StackOverflow](https://stackoverflow.com/questions/tagged/react-navigation) instead using the `react-navigation` label. You can also chat with other community members on [Reactiflux Discord server](https://www.reactiflux.com/) in the `#help-react-native` channel.\n\nIf you believe that this is actually a bug in the library, please open a new issue and fill the issue template with relevant information."
|
||||||
})
|
})
|
||||||
|
|
||||||
feature-request:
|
feature-request:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event.label.name == 'feature-request'
|
if: github.event.label.name == 'feature-request'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v2
|
- uses: actions/github-script@v3
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
script: |
|
script: |
|
||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event.label.name == 'library:react-native-screens'
|
if: github.event.label.name == 'library:react-native-screens'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v2
|
- uses: actions/github-script@v3
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
script: |
|
script: |
|
||||||
@@ -83,7 +83,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event.label.name == 'library:react-native-reanimated'
|
if: github.event.label.name == 'library:react-native-reanimated'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v2
|
- uses: actions/github-script@v3
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
script: |
|
script: |
|
||||||
@@ -98,7 +98,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event.label.name == 'library:react-native-gesture-handler'
|
if: github.event.label.name == 'library:react-native-gesture-handler'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v2
|
- uses: actions/github-script@v3
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
script: |
|
script: |
|
||||||
@@ -113,7 +113,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event.label.name == 'library:react-native-safe-area-context'
|
if: github.event.label.name == 'library:react-native-safe-area-context'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v2
|
- uses: actions/github-script@v3
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
script: |
|
script: |
|
||||||
|
|||||||
@@ -8,12 +8,15 @@ 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');
|
||||||
|
|
||||||
|
// List all packages under `packages/`
|
||||||
const workspaces = fs
|
const workspaces = fs
|
||||||
// List all packages under `packages/`
|
|
||||||
.readdirSync(packages)
|
.readdirSync(packages)
|
||||||
// Ignore hidden files such as .DS_Store
|
.map((p) => path.join(packages, p))
|
||||||
.filter((p) => !p.startsWith('.'))
|
.filter(
|
||||||
.map((p) => path.join(packages, p));
|
(p) =>
|
||||||
|
fs.statSync(p).isDirectory() &&
|
||||||
|
fs.existsSync(path.join(p, 'package.json'))
|
||||||
|
);
|
||||||
|
|
||||||
// Get the list of dependencies for all packages in the monorepo
|
// Get the list of dependencies for all packages in the monorepo
|
||||||
const modules = ['@expo/vector-icons']
|
const modules = ['@expo/vector-icons']
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
"mock-require-assets": "^0.0.1",
|
"mock-require-assets": "^0.0.1",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
"nodemon": "^2.0.6",
|
"nodemon": "^2.0.6",
|
||||||
"playwright": "^0.14.0",
|
"playwright": "^1.9.1",
|
||||||
"serve": "^11.3.0",
|
"serve": "^11.3.0",
|
||||||
"typescript": "^4.0.3"
|
"typescript": "^4.0.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
Linking,
|
Linking,
|
||||||
LogBox,
|
LogBox,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import { enableScreens } from 'react-native-screens';
|
import { enableScreens } from 'react-native-screens';
|
||||||
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||||
import {
|
import {
|
||||||
@@ -291,6 +292,7 @@ export default function App() {
|
|||||||
{() => (
|
{() => (
|
||||||
<Drawer.Navigator
|
<Drawer.Navigator
|
||||||
drawerType={isLargeScreen ? 'permanent' : undefined}
|
drawerType={isLargeScreen ? 'permanent' : undefined}
|
||||||
|
screenOptions={{ headerShown: true }}
|
||||||
>
|
>
|
||||||
<Drawer.Screen
|
<Drawer.Screen
|
||||||
name="Examples"
|
name="Examples"
|
||||||
@@ -305,38 +307,42 @@ export default function App() {
|
|||||||
<ScrollView
|
<ScrollView
|
||||||
style={{ backgroundColor: theme.colors.background }}
|
style={{ backgroundColor: theme.colors.background }}
|
||||||
>
|
>
|
||||||
<SettingsItem
|
<SafeAreaView edges={['right', 'bottom', 'left']}>
|
||||||
label="Right to left"
|
<SettingsItem
|
||||||
value={I18nManager.isRTL}
|
label="Right to left"
|
||||||
onValueChange={() => {
|
value={I18nManager.isRTL}
|
||||||
I18nManager.forceRTL(!I18nManager.isRTL);
|
onValueChange={() => {
|
||||||
restartApp();
|
I18nManager.forceRTL(!I18nManager.isRTL);
|
||||||
}}
|
restartApp();
|
||||||
/>
|
}}
|
||||||
<Divider />
|
/>
|
||||||
<SettingsItem
|
<Divider />
|
||||||
label="Dark theme"
|
<SettingsItem
|
||||||
value={theme.dark}
|
label="Dark theme"
|
||||||
onValueChange={() => {
|
value={theme.dark}
|
||||||
AsyncStorage.setItem(
|
onValueChange={() => {
|
||||||
THEME_PERSISTENCE_KEY,
|
AsyncStorage.setItem(
|
||||||
theme.dark ? 'light' : 'dark'
|
THEME_PERSISTENCE_KEY,
|
||||||
);
|
theme.dark ? 'light' : 'dark'
|
||||||
|
);
|
||||||
|
|
||||||
setTheme((t) => (t.dark ? DefaultTheme : DarkTheme));
|
setTheme((t) =>
|
||||||
}}
|
t.dark ? DefaultTheme : DarkTheme
|
||||||
/>
|
);
|
||||||
<Divider />
|
}}
|
||||||
{(Object.keys(SCREENS) as (keyof typeof SCREENS)[]).map(
|
/>
|
||||||
(name) => (
|
<Divider />
|
||||||
<List.Item
|
{(Object.keys(SCREENS) as (keyof typeof SCREENS)[]).map(
|
||||||
key={name}
|
(name) => (
|
||||||
testID={name}
|
<List.Item
|
||||||
title={SCREENS[name].title}
|
key={name}
|
||||||
onPress={() => navigation.navigate(name)}
|
testID={name}
|
||||||
/>
|
title={SCREENS[name].title}
|
||||||
)
|
onPress={() => navigation.navigate(name)}
|
||||||
)}
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</SafeAreaView>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
)}
|
)}
|
||||||
</Drawer.Screen>
|
</Drawer.Screen>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"version": "independent",
|
"version": "independent",
|
||||||
"command": {
|
"command": {
|
||||||
"publish": {
|
"publish": {
|
||||||
"allowBranch": "main",
|
"allowBranch": "5.x",
|
||||||
"conventionalCommits": true,
|
"conventionalCommits": true,
|
||||||
"createRelease": "github",
|
"createRelease": "github",
|
||||||
"message": "chore: publish",
|
"message": "chore: publish",
|
||||||
|
|||||||
@@ -3,6 +3,86 @@
|
|||||||
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.11.9](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.11.8...@react-navigation/bottom-tabs@5.11.9) (2021-04-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* check for screens enabled in ScreenContainer ([493956e](https://github.com/react-navigation/react-navigation/commit/493956ef717a03bd8c3533a2949434e83718c5e4))
|
||||||
|
* don't pass accessibilityState to link. closes [#9418](https://github.com/react-navigation/react-navigation/issues/9418) ([699ea0c](https://github.com/react-navigation/react-navigation/commit/699ea0cc5052f190acc7ce8bc0328bb052d7cf26))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.11.8](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.11.7...@react-navigation/bottom-tabs@5.11.8) (2021-02-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.11.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.11.6...@react-navigation/bottom-tabs@5.11.7) (2021-01-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix drawer screen content not being interactable on Android ([87b5147](https://github.com/react-navigation/react-navigation/commit/87b51476d0bce8f2dae793416c2976da30a1a5f7))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.11.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.11.5...@react-navigation/bottom-tabs@5.11.6) (2021-01-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix pointerEvents in ResourceSavingScene ([60fe0db](https://github.com/react-navigation/react-navigation/commit/60fe0dbb0ae443fdb21016d368c919b933cb64e7)), closes [#9241](https://github.com/react-navigation/react-navigation/issues/9241) [#9242](https://github.com/react-navigation/react-navigation/issues/9242)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.11.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.11.4...@react-navigation/bottom-tabs@5.11.5) (2021-01-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.11.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.11.3...@react-navigation/bottom-tabs@5.11.4) (2021-01-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix drawer and bottom tabs not being visible on web. closes [#9225](https://github.com/react-navigation/react-navigation/issues/9225) ([d88cbcb](https://github.com/react-navigation/react-navigation/commit/d88cbcb52d46de26edaa9ce6bfb06badb1b1de64))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.11.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.11.2...@react-navigation/bottom-tabs@5.11.3) (2021-01-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* enable detachInactiveScreens by default on web for better a11y ([dd87fa4](https://github.com/react-navigation/react-navigation/commit/dd87fa49a43ad8db105a62418243339e4150fadf))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.11.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.11.1...@react-navigation/bottom-tabs@5.11.2) (2020-11-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.11.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.11.0...@react-navigation/bottom-tabs@5.11.1) (2020-11-10)
|
## [5.11.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.11.0...@react-navigation/bottom-tabs@5.11.1) (2020-11-10)
|
||||||
|
|
||||||
**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.11.1",
|
"version": "5.11.9",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -40,8 +40,7 @@
|
|||||||
"react-native-iphone-x-helper": "^1.3.0"
|
"react-native-iphone-x-helper": "^1.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
"@react-navigation/native": "^5.9.4",
|
||||||
"@react-navigation/native": "^5.8.9",
|
|
||||||
"@testing-library/react-native": "^7.1.0",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/color": "^3.0.1",
|
"@types/color": "^3.0.1",
|
||||||
"@types/react": "^16.9.53",
|
"@types/react": "^16.9.53",
|
||||||
@@ -49,6 +48,7 @@
|
|||||||
"del-cli": "^3.0.1",
|
"del-cli": "^3.0.1",
|
||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
"react-native": "~0.63.2",
|
"react-native": "~0.63.2",
|
||||||
|
"react-native-builder-bob": "^0.17.0",
|
||||||
"react-native-safe-area-context": "3.1.4",
|
"react-native-safe-area-context": "3.1.4",
|
||||||
"react-native-screens": "~2.10.1",
|
"react-native-screens": "~2.10.1",
|
||||||
"typescript": "^4.0.3"
|
"typescript": "^4.0.3"
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
"react-native-safe-area-context": ">= 0.6.0",
|
"react-native-safe-area-context": ">= 0.6.0",
|
||||||
"react-native-screens": ">= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0"
|
"react-native-screens": ">= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0"
|
||||||
},
|
},
|
||||||
"@react-native-community/bob": {
|
"react-native-builder-bob": {
|
||||||
"source": "src",
|
"source": "src",
|
||||||
"output": "lib",
|
"output": "lib",
|
||||||
"targets": [
|
"targets": [
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ export default function BottomTabBarItem({
|
|||||||
onPress,
|
onPress,
|
||||||
to,
|
to,
|
||||||
accessibilityRole,
|
accessibilityRole,
|
||||||
|
accessibilityState,
|
||||||
...rest
|
...rest
|
||||||
}: BottomTabBarButtonProps) => {
|
}: BottomTabBarButtonProps) => {
|
||||||
if (Platform.OS === 'web' && to) {
|
if (Platform.OS === 'web' && to) {
|
||||||
@@ -162,6 +163,7 @@ export default function BottomTabBarItem({
|
|||||||
<TouchableWithoutFeedback
|
<TouchableWithoutFeedback
|
||||||
{...rest}
|
{...rest}
|
||||||
accessibilityRole={accessibilityRole}
|
accessibilityRole={accessibilityRole}
|
||||||
|
accessibilityState={accessibilityState}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
>
|
>
|
||||||
<View style={style}>{children}</View>
|
<View style={style}>{children}</View>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
import { ScreenContainer } from 'react-native-screens';
|
import { ScreenContainer, screensEnabled } from 'react-native-screens';
|
||||||
|
|
||||||
import SafeAreaProviderCompat, {
|
import SafeAreaProviderCompat, {
|
||||||
initialSafeAreaInsets,
|
initialSafeAreaInsets,
|
||||||
@@ -138,55 +138,54 @@ export default class BottomTabView extends React.Component<Props, State> {
|
|||||||
} = this.props;
|
} = this.props;
|
||||||
const { routes } = state;
|
const { routes } = state;
|
||||||
const { loaded, tabBarHeight } = this.state;
|
const { loaded, tabBarHeight } = this.state;
|
||||||
|
const isScreensEnabled = screensEnabled?.() && detachInactiveScreens;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavigationHelpersContext.Provider value={navigation}>
|
<NavigationHelpersContext.Provider value={navigation}>
|
||||||
<SafeAreaProviderCompat>
|
<SafeAreaProviderCompat>
|
||||||
<View style={styles.container}>
|
<ScreenContainer
|
||||||
<ScreenContainer
|
// @ts-ignore
|
||||||
// @ts-ignore
|
enabled={isScreensEnabled}
|
||||||
enabled={detachInactiveScreens}
|
style={styles.container}
|
||||||
style={styles.pages}
|
>
|
||||||
>
|
{routes.map((route, index) => {
|
||||||
{routes.map((route, index) => {
|
const descriptor = descriptors[route.key];
|
||||||
const descriptor = descriptors[route.key];
|
const { unmountOnBlur } = descriptor.options;
|
||||||
const { unmountOnBlur } = descriptor.options;
|
const isFocused = state.index === index;
|
||||||
const isFocused = state.index === index;
|
|
||||||
|
|
||||||
if (unmountOnBlur && !isFocused) {
|
if (unmountOnBlur && !isFocused) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lazy && !loaded.includes(route.key) && !isFocused) {
|
if (lazy && !loaded.includes(route.key) && !isFocused) {
|
||||||
// Don't render a screen if we've never navigated to it
|
// Don't render a screen if we've never navigated to it
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResourceSavingScene
|
<ResourceSavingScene
|
||||||
key={route.key}
|
key={route.key}
|
||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
isVisible={isFocused}
|
isVisible={isFocused}
|
||||||
enabled={detachInactiveScreens}
|
enabled={isScreensEnabled}
|
||||||
|
>
|
||||||
|
<SceneContent
|
||||||
|
isFocused={isFocused}
|
||||||
|
style={sceneContainerStyle}
|
||||||
>
|
>
|
||||||
<SceneContent
|
<BottomTabBarHeightContext.Provider value={tabBarHeight}>
|
||||||
isFocused={isFocused}
|
{descriptor.render()}
|
||||||
style={sceneContainerStyle}
|
</BottomTabBarHeightContext.Provider>
|
||||||
>
|
</SceneContent>
|
||||||
<BottomTabBarHeightContext.Provider value={tabBarHeight}>
|
</ResourceSavingScene>
|
||||||
{descriptor.render()}
|
);
|
||||||
</BottomTabBarHeightContext.Provider>
|
})}
|
||||||
</SceneContent>
|
</ScreenContainer>
|
||||||
</ResourceSavingScene>
|
<BottomTabBarHeightCallbackContext.Provider
|
||||||
);
|
value={this.handleTabBarHeightChange}
|
||||||
})}
|
>
|
||||||
</ScreenContainer>
|
{this.renderTabBar()}
|
||||||
<BottomTabBarHeightCallbackContext.Provider
|
</BottomTabBarHeightCallbackContext.Provider>
|
||||||
value={this.handleTabBarHeightChange}
|
|
||||||
>
|
|
||||||
{this.renderTabBar()}
|
|
||||||
</BottomTabBarHeightCallbackContext.Provider>
|
|
||||||
</View>
|
|
||||||
</SafeAreaProviderCompat>
|
</SafeAreaProviderCompat>
|
||||||
</NavigationHelpersContext.Provider>
|
</NavigationHelpersContext.Provider>
|
||||||
);
|
);
|
||||||
@@ -198,9 +197,6 @@ const styles = StyleSheet.create({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
},
|
},
|
||||||
pages: {
|
|
||||||
flex: 1,
|
|
||||||
},
|
|
||||||
content: {
|
content: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,36 +16,56 @@ type Props = {
|
|||||||
|
|
||||||
const FAR_FAR_AWAY = 30000; // this should be big enough to move the whole view out of its container
|
const FAR_FAR_AWAY = 30000; // this should be big enough to move the whole view out of its container
|
||||||
|
|
||||||
export default class ResourceSavingScene extends React.Component<Props> {
|
export default function ResourceSavingScene({
|
||||||
render() {
|
isVisible,
|
||||||
// react-native-screens is buggy on web
|
children,
|
||||||
if (screensEnabled?.() && Platform.OS !== 'web') {
|
style,
|
||||||
const { isVisible, ...rest } = this.props;
|
...rest
|
||||||
|
}: Props) {
|
||||||
if (shouldUseActivityState) {
|
// react-native-screens is buggy on web
|
||||||
return (
|
if (screensEnabled?.() && Platform.OS !== 'web') {
|
||||||
// @ts-expect-error: there was an `active` prop and no `activityState` in older version and stackPresentation was required
|
if (shouldUseActivityState) {
|
||||||
<Screen activityState={isVisible ? 2 : 0} {...rest} />
|
return (
|
||||||
);
|
// @ts-expect-error: there was an `active` prop and no `activityState` in older version and stackPresentation was required
|
||||||
} else {
|
<Screen activityState={isVisible ? 2 : 0} style={style} {...rest}>
|
||||||
return (
|
{children}
|
||||||
// @ts-expect-error: there was an `active` prop and no `activityState` in older version and stackPresentation was required
|
</Screen>
|
||||||
<Screen active={isVisible ? 1 : 0} {...rest} />
|
);
|
||||||
);
|
} else {
|
||||||
}
|
return (
|
||||||
|
// @ts-expect-error: there was an `active` prop and no `activityState` in older version and stackPresentation was required
|
||||||
|
<Screen active={isVisible ? 1 : 0} style={style} {...rest}>
|
||||||
|
{children}
|
||||||
|
</Screen>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const { isVisible, children, style, ...rest } = this.props;
|
if (Platform.OS === 'web') {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
|
// @ts-expect-error: hidden exists on web, but not in React Native
|
||||||
|
hidden={!isVisible}
|
||||||
style={[
|
style={[
|
||||||
|
{ display: isVisible ? 'flex' : 'none' },
|
||||||
styles.container,
|
styles.container,
|
||||||
Platform.OS === 'web'
|
|
||||||
? { display: isVisible ? 'flex' : 'none' }
|
|
||||||
: null,
|
|
||||||
style,
|
style,
|
||||||
]}
|
]}
|
||||||
|
pointerEvents={isVisible ? 'auto' : 'none'}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[styles.container, style]}
|
||||||
|
// box-none doesn't seem to work properly on Android
|
||||||
|
pointerEvents={isVisible ? 'auto' : 'none'}
|
||||||
|
>
|
||||||
|
<View
|
||||||
collapsable={false}
|
collapsable={false}
|
||||||
removeClippedSubviews={
|
removeClippedSubviews={
|
||||||
// On iOS, set removeClippedSubviews to true only when not focused
|
// On iOS, set removeClippedSubviews to true only when not focused
|
||||||
@@ -53,14 +73,12 @@ export default class ResourceSavingScene extends React.Component<Props> {
|
|||||||
Platform.OS === 'ios' ? !isVisible : true
|
Platform.OS === 'ios' ? !isVisible : true
|
||||||
}
|
}
|
||||||
pointerEvents={isVisible ? 'auto' : 'none'}
|
pointerEvents={isVisible ? 'auto' : 'none'}
|
||||||
{...rest}
|
style={isVisible ? styles.attached : styles.detached}
|
||||||
>
|
>
|
||||||
<View style={isVisible ? styles.attached : styles.detached}>
|
{children}
|
||||||
{children}
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
</View>
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
|||||||
@@ -3,6 +3,54 @@
|
|||||||
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.3.15](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.14...@react-navigation/compat@5.3.15) (2021-04-04)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.14](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.13...@react-navigation/compat@5.3.14) (2021-02-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.13](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.12...@react-navigation/compat@5.3.13) (2021-01-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.12](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.11...@react-navigation/compat@5.3.12) (2021-01-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.11](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.10...@react-navigation/compat@5.3.11) (2021-01-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.10](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.9...@react-navigation/compat@5.3.10) (2020-11-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.3.9](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.8...@react-navigation/compat@5.3.9) (2020-11-10)
|
## [5.3.9](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.8...@react-navigation/compat@5.3.9) (2020-11-10)
|
||||||
|
|
||||||
**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.3.9",
|
"version": "5.3.15",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -31,17 +31,17 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
"@react-navigation/native": "^5.9.4",
|
||||||
"@react-navigation/native": "^5.8.9",
|
|
||||||
"@types/react": "^16.9.53",
|
"@types/react": "^16.9.53",
|
||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
|
"react-native-builder-bob": "^0.17.0",
|
||||||
"typescript": "^4.0.3"
|
"typescript": "^4.0.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@react-navigation/native": "^5.0.5",
|
"@react-navigation/native": "^5.0.5",
|
||||||
"react": "*"
|
"react": "*"
|
||||||
},
|
},
|
||||||
"@react-native-community/bob": {
|
"react-native-builder-bob": {
|
||||||
"source": "src",
|
"source": "src",
|
||||||
"output": "lib",
|
"output": "lib",
|
||||||
"targets": [
|
"targets": [
|
||||||
|
|||||||
@@ -3,6 +3,60 @@
|
|||||||
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.15.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.15.2...@react-navigation/core@5.15.3) (2021-04-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* properly resolve initialRouteNames ([976178d](https://github.com/react-navigation/react-navigation/commit/976178d0986a90697931ab9cc2c297eb7938e28b))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.15.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.15.1...@react-navigation/core@5.15.2) (2021-02-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.15.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.15.0...@react-navigation/core@5.15.1) (2021-01-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.15.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.14.4...@react-navigation/core@5.15.0) (2021-01-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* print an error when passing a second argument to useFocusEffect ([2317633](https://github.com/react-navigation/react-navigation/commit/23176336528f98924d19f321d41cb70f13300edd))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add a way to specify an unique ID for screens ([b19f76b](https://github.com/react-navigation/react-navigation/commit/b19f76bfffe623759e67d925bfd067c753a453bf))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.14.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.14.3...@react-navigation/core@5.14.4) (2020-11-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix incorrect state change events in independent nested container ([95b2599](https://github.com/react-navigation/react-navigation/commit/95b2599877f5ceedf753e399e0586bb4af54cb87)), closes [#9080](https://github.com/react-navigation/react-navigation/issues/9080)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.14.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.14.2...@react-navigation/core@5.14.3) (2020-11-10)
|
## [5.14.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.14.2...@react-navigation/core@5.14.3) (2020-11-10)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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.14.3",
|
"version": "5.15.3",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react",
|
"react",
|
||||||
"react-native",
|
"react-native",
|
||||||
@@ -35,26 +35,26 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-navigation/routers": "^5.6.2",
|
"@react-navigation/routers": "^5.7.2",
|
||||||
"escape-string-regexp": "^4.0.0",
|
"escape-string-regexp": "^4.0.0",
|
||||||
"nanoid": "^3.1.15",
|
"nanoid": "^3.1.15",
|
||||||
"query-string": "^6.13.6",
|
"query-string": "^6.13.6",
|
||||||
"react-is": "^16.13.0"
|
"react-is": "^16.13.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
|
||||||
"@testing-library/react-native": "^7.1.0",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/react": "^16.9.53",
|
"@types/react": "^16.9.53",
|
||||||
"@types/react-is": "^16.7.1",
|
"@types/react-is": "^16.7.1",
|
||||||
"del-cli": "^3.0.1",
|
"del-cli": "^3.0.1",
|
||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
|
"react-native-builder-bob": "^0.17.0",
|
||||||
"react-test-renderer": "~16.13.1",
|
"react-test-renderer": "~16.13.1",
|
||||||
"typescript": "^4.0.3"
|
"typescript": "^4.0.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "*"
|
"react": "*"
|
||||||
},
|
},
|
||||||
"@react-native-community/bob": {
|
"react-native-builder-bob": {
|
||||||
"source": "src",
|
"source": "src",
|
||||||
"output": "lib",
|
"output": "lib",
|
||||||
"targets": [
|
"targets": [
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ import {
|
|||||||
NavigationAction,
|
NavigationAction,
|
||||||
} from '@react-navigation/routers';
|
} from '@react-navigation/routers';
|
||||||
import EnsureSingleNavigator from './EnsureSingleNavigator';
|
import EnsureSingleNavigator from './EnsureSingleNavigator';
|
||||||
|
import UnhandledActionContext from './UnhandledActionContext';
|
||||||
import NavigationBuilderContext from './NavigationBuilderContext';
|
import NavigationBuilderContext from './NavigationBuilderContext';
|
||||||
import NavigationStateContext from './NavigationStateContext';
|
import NavigationStateContext from './NavigationStateContext';
|
||||||
import UnhandledActionContext from './UnhandledActionContext';
|
import NavigationRouteContext from './NavigationRouteContext';
|
||||||
|
import NavigationContext from './NavigationContext';
|
||||||
import { ScheduleUpdateContext } from './useScheduleUpdate';
|
import { ScheduleUpdateContext } from './useScheduleUpdate';
|
||||||
import useChildListeners from './useChildListeners';
|
import useChildListeners from './useChildListeners';
|
||||||
import useKeyedChildListeners from './useKeyedChildListeners';
|
import useKeyedChildListeners from './useKeyedChildListeners';
|
||||||
@@ -397,7 +399,7 @@ const BaseNavigationContainer = React.forwardRef(
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
let element = (
|
||||||
<ScheduleUpdateContext.Provider value={scheduleContext}>
|
<ScheduleUpdateContext.Provider value={scheduleContext}>
|
||||||
<NavigationBuilderContext.Provider value={builderContext}>
|
<NavigationBuilderContext.Provider value={builderContext}>
|
||||||
<NavigationStateContext.Provider value={context}>
|
<NavigationStateContext.Provider value={context}>
|
||||||
@@ -410,6 +412,19 @@ const BaseNavigationContainer = React.forwardRef(
|
|||||||
</NavigationBuilderContext.Provider>
|
</NavigationBuilderContext.Provider>
|
||||||
</ScheduleUpdateContext.Provider>
|
</ScheduleUpdateContext.Provider>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (independent) {
|
||||||
|
// We need to clear any existing contexts for nested independent container to work correctly
|
||||||
|
element = (
|
||||||
|
<NavigationRouteContext.Provider value={undefined}>
|
||||||
|
<NavigationContext.Provider value={undefined}>
|
||||||
|
{element}
|
||||||
|
</NavigationContext.Provider>
|
||||||
|
</NavigationRouteContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -757,3 +757,67 @@ it('invokes the unhandled action listener with the unhandled action', () => {
|
|||||||
type: 'NAVIGATE',
|
type: 'NAVIGATE',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('works with state change events in independent nested container', () => {
|
||||||
|
const TestNavigator = (props: any) => {
|
||||||
|
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{state.routes.map((route) => descriptors[route.key].render())}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ref = React.createRef<NavigationContainerRef>();
|
||||||
|
|
||||||
|
const onStateChange = jest.fn();
|
||||||
|
|
||||||
|
render(
|
||||||
|
<BaseNavigationContainer>
|
||||||
|
<TestNavigator>
|
||||||
|
<Screen name="foo">
|
||||||
|
{() => (
|
||||||
|
<BaseNavigationContainer
|
||||||
|
independent
|
||||||
|
ref={ref}
|
||||||
|
onStateChange={onStateChange}
|
||||||
|
>
|
||||||
|
<TestNavigator>
|
||||||
|
<Screen name="qux">{() => null}</Screen>
|
||||||
|
<Screen name="lex">{() => null}</Screen>
|
||||||
|
</TestNavigator>
|
||||||
|
</BaseNavigationContainer>
|
||||||
|
)}
|
||||||
|
</Screen>
|
||||||
|
<Screen name="bar">{() => null}</Screen>
|
||||||
|
</TestNavigator>
|
||||||
|
</BaseNavigationContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => ref.current?.navigate('lex'));
|
||||||
|
|
||||||
|
expect(onStateChange).toBeCalledWith({
|
||||||
|
index: 1,
|
||||||
|
key: '15',
|
||||||
|
routeNames: ['qux', 'lex'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'qux', name: 'qux' },
|
||||||
|
{ key: 'lex', name: 'lex' },
|
||||||
|
],
|
||||||
|
stale: false,
|
||||||
|
type: 'test',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(ref.current?.getRootState()).toEqual({
|
||||||
|
index: 1,
|
||||||
|
key: '15',
|
||||||
|
routeNames: ['qux', 'lex'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'qux', name: 'qux' },
|
||||||
|
{ key: 'lex', name: 'lex' },
|
||||||
|
],
|
||||||
|
stale: false,
|
||||||
|
type: 'test',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -2821,3 +2821,117 @@ it("throws when using 'initialRouteName' or 'screens' with legacy config", () =>
|
|||||||
})
|
})
|
||||||
).toThrow('Found invalid keys in the configuration object.');
|
).toThrow('Found invalid keys in the configuration object.');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('correctly applies initialRouteName for config with similar route names', () => {
|
||||||
|
const path = '/weekly-earnings';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
screens: {
|
||||||
|
RootTabs: {
|
||||||
|
screens: {
|
||||||
|
HomeTab: {
|
||||||
|
screens: {
|
||||||
|
Home: '',
|
||||||
|
WeeklyEarnings: 'weekly-earnings',
|
||||||
|
EventDetails: 'event-details/:eventId',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
EarningsTab: {
|
||||||
|
initialRouteName: 'Earnings',
|
||||||
|
path: 'earnings',
|
||||||
|
screens: {
|
||||||
|
Earnings: '',
|
||||||
|
WeeklyEarnings: 'weekly-earnings',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'RootTabs',
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'HomeTab',
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'WeeklyEarnings',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getStateFromPath(path, config)).toEqual(state);
|
||||||
|
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||||
|
state
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly applies initialRouteName for config with similar route names v2', () => {
|
||||||
|
const path = '/earnings/weekly-earnings';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
screens: {
|
||||||
|
RootTabs: {
|
||||||
|
screens: {
|
||||||
|
HomeTab: {
|
||||||
|
initialRouteName: 'Home',
|
||||||
|
screens: {
|
||||||
|
Home: '',
|
||||||
|
WeeklyEarnings: 'weekly-earnings',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
EarningsTab: {
|
||||||
|
initialRouteName: 'Earnings',
|
||||||
|
path: 'earnings',
|
||||||
|
screens: {
|
||||||
|
Earnings: '',
|
||||||
|
WeeklyEarnings: 'weekly-earnings',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'RootTabs',
|
||||||
|
state: {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'EarningsTab',
|
||||||
|
state: {
|
||||||
|
index: 1,
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'Earnings',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'WeeklyEarnings',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getStateFromPath(path, config)).toEqual(state);
|
||||||
|
expect(getStateFromPath(getPathFromState(state, config), config)).toEqual(
|
||||||
|
state
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
@@ -239,3 +239,135 @@ it('runs cleanup when component is unmounted', () => {
|
|||||||
expect(focusEffect).toBeCalledTimes(1);
|
expect(focusEffect).toBeCalledTimes(1);
|
||||||
expect(focusEffectCleanup).toBeCalledTimes(1);
|
expect(focusEffectCleanup).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('prints error when a dependency array is passed', () => {
|
||||||
|
const TestNavigator = (props: any): any => {
|
||||||
|
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||||
|
|
||||||
|
return descriptors[state.routes[state.index].key].render();
|
||||||
|
};
|
||||||
|
|
||||||
|
const Test = () => {
|
||||||
|
// @ts-ignore
|
||||||
|
useFocusEffect(() => {}, []);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = () => (
|
||||||
|
<BaseNavigationContainer>
|
||||||
|
<TestNavigator>
|
||||||
|
<Screen name="test" component={Test} />
|
||||||
|
</TestNavigator>
|
||||||
|
</BaseNavigationContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
const spy = jest.spyOn(console, 'error').mockImplementation();
|
||||||
|
|
||||||
|
render(<App />);
|
||||||
|
|
||||||
|
expect(spy.mock.calls[0][0]).toMatch(
|
||||||
|
"You passed a second argument to 'useFocusEffect', but it only accepts one argument."
|
||||||
|
);
|
||||||
|
|
||||||
|
spy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prints error when the effect returns a value', () => {
|
||||||
|
const TestNavigator = (props: any): any => {
|
||||||
|
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||||
|
|
||||||
|
return descriptors[state.routes[state.index].key].render();
|
||||||
|
};
|
||||||
|
|
||||||
|
const Test = () => {
|
||||||
|
// @ts-ignore
|
||||||
|
useFocusEffect(() => 42);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = () => (
|
||||||
|
<BaseNavigationContainer>
|
||||||
|
<TestNavigator>
|
||||||
|
<Screen name="test" component={Test} />
|
||||||
|
</TestNavigator>
|
||||||
|
</BaseNavigationContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
const spy = jest.spyOn(console, 'error').mockImplementation();
|
||||||
|
|
||||||
|
render(<App />);
|
||||||
|
|
||||||
|
expect(spy.mock.calls[0][0]).toMatch(
|
||||||
|
"An effect function must not return anything besides a function, which is used for clean-up. You returned '42'."
|
||||||
|
);
|
||||||
|
|
||||||
|
spy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prints error when the effect returns null', () => {
|
||||||
|
const TestNavigator = (props: any): any => {
|
||||||
|
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||||
|
|
||||||
|
return descriptors[state.routes[state.index].key].render();
|
||||||
|
};
|
||||||
|
|
||||||
|
const Test = () => {
|
||||||
|
// @ts-ignore
|
||||||
|
useFocusEffect(() => null);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = () => (
|
||||||
|
<BaseNavigationContainer>
|
||||||
|
<TestNavigator>
|
||||||
|
<Screen name="test" component={Test} />
|
||||||
|
</TestNavigator>
|
||||||
|
</BaseNavigationContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
const spy = jest.spyOn(console, 'error').mockImplementation();
|
||||||
|
|
||||||
|
render(<App />);
|
||||||
|
|
||||||
|
expect(spy.mock.calls[0][0]).toMatch(
|
||||||
|
"An effect function must not return anything besides a function, which is used for clean-up. You returned 'null'. If your effect does not require clean-up, return 'undefined' (or nothing)."
|
||||||
|
);
|
||||||
|
|
||||||
|
spy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prints error when the effect is an async function', () => {
|
||||||
|
const TestNavigator = (props: any): any => {
|
||||||
|
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||||
|
|
||||||
|
return descriptors[state.routes[state.index].key].render();
|
||||||
|
};
|
||||||
|
|
||||||
|
const Test = () => {
|
||||||
|
// @ts-ignore
|
||||||
|
useFocusEffect(async () => {});
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = () => (
|
||||||
|
<BaseNavigationContainer>
|
||||||
|
<TestNavigator>
|
||||||
|
<Screen name="test" component={Test} />
|
||||||
|
</TestNavigator>
|
||||||
|
</BaseNavigationContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
const spy = jest.spyOn(console, 'error').mockImplementation();
|
||||||
|
|
||||||
|
render(<App />);
|
||||||
|
|
||||||
|
expect(spy.mock.calls[0][0]).toMatch(
|
||||||
|
"An effect function must not return anything besides a function, which is used for clean-up.\n\nIt looks like you wrote 'useFocusEffect(async () => ...)' or returned a Promise."
|
||||||
|
);
|
||||||
|
|
||||||
|
spy.mockRestore();
|
||||||
|
});
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ type RouteConfig = {
|
|||||||
|
|
||||||
type InitialRouteConfig = {
|
type InitialRouteConfig = {
|
||||||
initialRouteName: string;
|
initialRouteName: string;
|
||||||
connectedRoutes: string[];
|
parentScreens: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type ResultState = PartialState<NavigationState> & {
|
type ResultState = PartialState<NavigationState> & {
|
||||||
@@ -70,7 +70,7 @@ export default function getStateFromPath(
|
|||||||
if (compatOptions?.initialRouteName) {
|
if (compatOptions?.initialRouteName) {
|
||||||
initialRoutes.push({
|
initialRoutes.push({
|
||||||
initialRouteName: compatOptions.initialRouteName,
|
initialRouteName: compatOptions.initialRouteName,
|
||||||
connectedRoutes: Object.keys(compatOptions.screens),
|
parentScreens: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +115,8 @@ export default function getStateFromPath(
|
|||||||
key,
|
key,
|
||||||
screens as PathConfigMap,
|
screens as PathConfigMap,
|
||||||
[],
|
[],
|
||||||
initialRoutes
|
initialRoutes,
|
||||||
|
[]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -368,12 +369,15 @@ const createNormalizedConfigs = (
|
|||||||
routeConfig: PathConfigMap,
|
routeConfig: PathConfigMap,
|
||||||
routeNames: string[] = [],
|
routeNames: string[] = [],
|
||||||
initials: InitialRouteConfig[],
|
initials: InitialRouteConfig[],
|
||||||
|
parentScreens: string[],
|
||||||
parentPattern?: string
|
parentPattern?: string
|
||||||
): RouteConfig[] => {
|
): RouteConfig[] => {
|
||||||
const configs: RouteConfig[] = [];
|
const configs: RouteConfig[] = [];
|
||||||
|
|
||||||
routeNames.push(screen);
|
routeNames.push(screen);
|
||||||
|
|
||||||
|
parentScreens.push(screen);
|
||||||
|
|
||||||
const config = routeConfig[screen];
|
const config = routeConfig[screen];
|
||||||
|
|
||||||
if (typeof config === 'string') {
|
if (typeof config === 'string') {
|
||||||
@@ -423,7 +427,7 @@ const createNormalizedConfigs = (
|
|||||||
if (config.initialRouteName) {
|
if (config.initialRouteName) {
|
||||||
initials.push({
|
initials.push({
|
||||||
initialRouteName: config.initialRouteName,
|
initialRouteName: config.initialRouteName,
|
||||||
connectedRoutes: Object.keys(config.screens),
|
parentScreens,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,6 +438,7 @@ const createNormalizedConfigs = (
|
|||||||
config.screens as PathConfigMap,
|
config.screens as PathConfigMap,
|
||||||
routeNames,
|
routeNames,
|
||||||
initials,
|
initials,
|
||||||
|
[...parentScreens],
|
||||||
pattern ?? parentPattern
|
pattern ?? parentPattern
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -506,13 +511,23 @@ const findParseConfigForRoute = (
|
|||||||
// Try to find an initial route connected with the one passed
|
// Try to find an initial route connected with the one passed
|
||||||
const findInitialRoute = (
|
const findInitialRoute = (
|
||||||
routeName: string,
|
routeName: string,
|
||||||
|
parentScreens: 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 (parentScreens.length === config.parentScreens.length) {
|
||||||
return config.initialRouteName === routeName
|
let sameParents = true;
|
||||||
? undefined
|
for (let i = 0; i < parentScreens.length; i++) {
|
||||||
: config.initialRouteName;
|
if (parentScreens[i].localeCompare(config.parentScreens[i]) !== 0) {
|
||||||
|
sameParents = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sameParents) {
|
||||||
|
return routeName !== config.initialRouteName
|
||||||
|
? config.initialRouteName
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -556,7 +571,11 @@ const createNestedStateObject = (
|
|||||||
) => {
|
) => {
|
||||||
let state: InitialState;
|
let state: InitialState;
|
||||||
let route = routes.shift() as ParsedRoute;
|
let route = routes.shift() as ParsedRoute;
|
||||||
let initialRoute = findInitialRoute(route.name, initialRoutes);
|
const parentScreens: string[] = [];
|
||||||
|
|
||||||
|
let initialRoute = findInitialRoute(route.name, parentScreens, initialRoutes);
|
||||||
|
|
||||||
|
parentScreens.push(route.name);
|
||||||
|
|
||||||
state = createStateObject(initialRoute, route, routes.length === 0);
|
state = createStateObject(initialRoute, route, routes.length === 0);
|
||||||
|
|
||||||
@@ -564,7 +583,7 @@ const createNestedStateObject = (
|
|||||||
let nestedState = state;
|
let nestedState = state;
|
||||||
|
|
||||||
while ((route = routes.shift() as ParsedRoute)) {
|
while ((route = routes.shift() as ParsedRoute)) {
|
||||||
initialRoute = findInitialRoute(route.name, initialRoutes);
|
initialRoute = findInitialRoute(route.name, parentScreens, initialRoutes);
|
||||||
|
|
||||||
const nestedStateIndex =
|
const nestedStateIndex =
|
||||||
nestedState.index || nestedState.routes.length - 1;
|
nestedState.index || nestedState.routes.length - 1;
|
||||||
@@ -579,6 +598,8 @@ const createNestedStateObject = (
|
|||||||
nestedState = nestedState.routes[nestedStateIndex]
|
nestedState = nestedState.routes[nestedStateIndex]
|
||||||
.state as InitialState;
|
.state as InitialState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parentScreens.push(route.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -388,6 +388,14 @@ export type RouteConfig<
|
|||||||
navigation: any;
|
navigation: any;
|
||||||
}) => ScreenListeners<State, EventMap>);
|
}) => ScreenListeners<State, EventMap>);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to return an unique ID for this screen.
|
||||||
|
* Receives an object with the route params.
|
||||||
|
* For a given screen name, there will always be only one screen corresponding to an ID.
|
||||||
|
* If `undefined` is returned, it acts same as no `getId` being specified.
|
||||||
|
*/
|
||||||
|
getId?: ({ params }: { params: ParamList[RouteName] }) => string | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initial params object for the route.
|
* Initial params object for the route.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -13,6 +13,20 @@ type EffectCallback = () => undefined | void | (() => void);
|
|||||||
export default function useFocusEffect(effect: EffectCallback) {
|
export default function useFocusEffect(effect: EffectCallback) {
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
|
|
||||||
|
if (arguments[1] !== undefined) {
|
||||||
|
const message =
|
||||||
|
"You passed a second argument to 'useFocusEffect', but it only accepts one argument. " +
|
||||||
|
"If you want to pass a dependency array, you can use 'React.useCallback':\n\n" +
|
||||||
|
'useFocusEffect(\n' +
|
||||||
|
' React.useCallback(() => {\n' +
|
||||||
|
' // Your code here\n' +
|
||||||
|
' }, [depA, depB])\n' +
|
||||||
|
');\n\n' +
|
||||||
|
'See usage guide: https://reactnavigation.org/docs/use-focus-effect';
|
||||||
|
|
||||||
|
console.error(message);
|
||||||
|
}
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
let isFocused = false;
|
let isFocused = false;
|
||||||
let cleanup: undefined | void | (() => void);
|
let cleanup: undefined | void | (() => void);
|
||||||
@@ -45,10 +59,10 @@ export default function useFocusEffect(effect: EffectCallback) {
|
|||||||
' }\n\n' +
|
' }\n\n' +
|
||||||
' fetchData();\n' +
|
' fetchData();\n' +
|
||||||
' }, [someId])\n' +
|
' }, [someId])\n' +
|
||||||
'};\n\n' +
|
');\n\n' +
|
||||||
'See usage guide: https://reactnavigation.org/docs/use-focus-effect';
|
'See usage guide: https://reactnavigation.org/docs/use-focus-effect';
|
||||||
} else {
|
} else {
|
||||||
message += ` You returned: '${JSON.stringify(destroy)}'`;
|
message += ` You returned '${JSON.stringify(destroy)}'.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(message);
|
console.error(message);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
ParamListBase,
|
ParamListBase,
|
||||||
Router,
|
Router,
|
||||||
RouterFactory,
|
RouterFactory,
|
||||||
|
RouterConfigOptions,
|
||||||
PartialState,
|
PartialState,
|
||||||
NavigationAction,
|
NavigationAction,
|
||||||
Route,
|
Route,
|
||||||
@@ -257,6 +258,15 @@ export default function useNavigationBuilder<
|
|||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
const routeGetIdList = routeNames.reduce<
|
||||||
|
RouterConfigOptions['routeGetIdList']
|
||||||
|
>(
|
||||||
|
(acc, curr) =>
|
||||||
|
Object.assign(acc, {
|
||||||
|
[curr]: screens[curr].getId,
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
if (!routeNames.length) {
|
if (!routeNames.length) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -297,6 +307,7 @@ export default function useNavigationBuilder<
|
|||||||
router.getInitialState({
|
router.getInitialState({
|
||||||
routeNames,
|
routeNames,
|
||||||
routeParamList,
|
routeParamList,
|
||||||
|
routeGetIdList,
|
||||||
}),
|
}),
|
||||||
true,
|
true,
|
||||||
];
|
];
|
||||||
@@ -307,6 +318,7 @@ export default function useNavigationBuilder<
|
|||||||
{
|
{
|
||||||
routeNames,
|
routeNames,
|
||||||
routeParamList,
|
routeParamList,
|
||||||
|
routeGetIdList,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
false,
|
false,
|
||||||
@@ -336,6 +348,7 @@ export default function useNavigationBuilder<
|
|||||||
nextState = router.getStateForRouteNamesChange(state, {
|
nextState = router.getStateForRouteNamesChange(state, {
|
||||||
routeNames,
|
routeNames,
|
||||||
routeParamList,
|
routeParamList,
|
||||||
|
routeGetIdList,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,6 +385,7 @@ export default function useNavigationBuilder<
|
|||||||
? router.getStateForAction(nextState, action, {
|
? router.getStateForAction(nextState, action, {
|
||||||
routeNames,
|
routeNames,
|
||||||
routeParamList,
|
routeParamList,
|
||||||
|
routeGetIdList,
|
||||||
})
|
})
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
@@ -380,6 +394,7 @@ export default function useNavigationBuilder<
|
|||||||
? router.getRehydratedState(updatedState, {
|
? router.getRehydratedState(updatedState, {
|
||||||
routeNames,
|
routeNames,
|
||||||
routeParamList,
|
routeParamList,
|
||||||
|
routeGetIdList,
|
||||||
})
|
})
|
||||||
: nextState;
|
: nextState;
|
||||||
}
|
}
|
||||||
@@ -501,6 +516,7 @@ export default function useNavigationBuilder<
|
|||||||
routerConfigOptions: {
|
routerConfigOptions: {
|
||||||
routeNames,
|
routeNames,
|
||||||
routeParamList,
|
routeParamList,
|
||||||
|
routeGetIdList,
|
||||||
},
|
},
|
||||||
emitter,
|
emitter,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ export default function useNavigationHelpers<
|
|||||||
router.getStateForAction(state, CommonActions.goBack() as Action, {
|
router.getStateForAction(state, CommonActions.goBack() as Action, {
|
||||||
routeNames: state.routeNames,
|
routeNames: state.routeNames,
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
}) !== null ||
|
}) !== null ||
|
||||||
parentNavigationHelpers?.canGoBack() ||
|
parentNavigationHelpers?.canGoBack() ||
|
||||||
false
|
false
|
||||||
|
|||||||
@@ -3,6 +3,46 @@
|
|||||||
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.22](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.21...@react-navigation/devtools@5.1.22) (2021-04-04)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/devtools
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.21](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.20...@react-navigation/devtools@5.1.21) (2021-02-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/devtools
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.20](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.19...@react-navigation/devtools@5.1.20) (2021-01-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/devtools
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.19](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.18...@react-navigation/devtools@5.1.19) (2021-01-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/devtools
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.1.18](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.17...@react-navigation/devtools@5.1.18) (2020-11-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/devtools
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.1.17](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.16...@react-navigation/devtools@5.1.17) (2020-11-10)
|
## [5.1.17](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.16...@react-navigation/devtools@5.1.17) (2020-11-10)
|
||||||
|
|
||||||
**Note:** Version bump only for package @react-navigation/devtools
|
**Note:** Version bump only for package @react-navigation/devtools
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/devtools",
|
"name": "@react-navigation/devtools",
|
||||||
"description": "Developer tools for React Navigation",
|
"description": "Developer tools for React Navigation",
|
||||||
"version": "5.1.17",
|
"version": "5.1.22",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react",
|
"react",
|
||||||
"react-native",
|
"react-native",
|
||||||
@@ -36,22 +36,22 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-navigation/core": "^5.14.3",
|
"@react-navigation/core": "^5.15.3",
|
||||||
"deep-equal": "^2.0.4"
|
"deep-equal": "^2.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
|
||||||
"@testing-library/react-native": "^7.1.0",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/deep-equal": "^1.0.1",
|
"@types/deep-equal": "^1.0.1",
|
||||||
"@types/react": "^16.9.53",
|
"@types/react": "^16.9.53",
|
||||||
"del-cli": "^3.0.1",
|
"del-cli": "^3.0.1",
|
||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
|
"react-native-builder-bob": "^0.17.0",
|
||||||
"typescript": "^4.0.3"
|
"typescript": "^4.0.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "*"
|
"react": "*"
|
||||||
},
|
},
|
||||||
"@react-native-community/bob": {
|
"react-native-builder-bob": {
|
||||||
"source": "src",
|
"source": "src",
|
||||||
"output": "lib",
|
"output": "lib",
|
||||||
"targets": [
|
"targets": [
|
||||||
|
|||||||
@@ -3,6 +3,104 @@
|
|||||||
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.12.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.12.4...@react-navigation/drawer@5.12.5) (2021-04-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* check for screens enabled in ScreenContainer ([493956e](https://github.com/react-navigation/react-navigation/commit/493956ef717a03bd8c3533a2949434e83718c5e4))
|
||||||
|
* don't handle back button with permanent drawer ([a63f9da](https://github.com/react-navigation/react-navigation/commit/a63f9da8c1efe5d34567517ac2653608c6bbdeba))
|
||||||
|
* don't pass accessibilityState to link. closes [#9418](https://github.com/react-navigation/react-navigation/issues/9418) ([699ea0c](https://github.com/react-navigation/react-navigation/commit/699ea0cc5052f190acc7ce8bc0328bb052d7cf26))
|
||||||
|
* only handle back button in drawer when focused ([cceaa67](https://github.com/react-navigation/react-navigation/commit/cceaa6780d588b2a2ffa3a2039f65f9e60a33bf9))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.12.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.12.3...@react-navigation/drawer@5.12.4) (2021-02-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.12.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.12.2...@react-navigation/drawer@5.12.3) (2021-01-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix drawer screen content not being interactable on Android ([87b5147](https://github.com/react-navigation/react-navigation/commit/87b51476d0bce8f2dae793416c2976da30a1a5f7))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.12.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.12.1...@react-navigation/drawer@5.12.2) (2021-01-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix pointerEvents in ResourceSavingScene ([60fe0db](https://github.com/react-navigation/react-navigation/commit/60fe0dbb0ae443fdb21016d368c919b933cb64e7)), closes [#9241](https://github.com/react-navigation/react-navigation/issues/9241) [#9242](https://github.com/react-navigation/react-navigation/issues/9242)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.12.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.12.0...@react-navigation/drawer@5.12.1) (2021-01-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.12.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.11.5...@react-navigation/drawer@5.12.0) (2021-01-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix drawer and bottom tabs not being visible on web. closes [#9225](https://github.com/react-navigation/react-navigation/issues/9225) ([d88cbcb](https://github.com/react-navigation/react-navigation/commit/d88cbcb52d46de26edaa9ce6bfb06badb1b1de64))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add pressColor and pressOpacity props to drawerItem ([#8834](https://github.com/react-navigation/react-navigation/issues/8834)) ([bae4019](https://github.com/react-navigation/react-navigation/commit/bae4019995062c682f0213c121b7927ab8006c1e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.11.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.11.4...@react-navigation/drawer@5.11.5) (2021-01-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* enable detachInactiveScreens by default on web for better a11y ([dd87fa4](https://github.com/react-navigation/react-navigation/commit/dd87fa49a43ad8db105a62418243339e4150fadf))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.11.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.11.3...@react-navigation/drawer@5.11.4) (2020-11-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/drawer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.11.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.11.2...@react-navigation/drawer@5.11.3) (2020-11-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* hide drawer's header by default ([794339e](https://github.com/react-navigation/react-navigation/commit/794339eeed7c0d3b0e8b1752e494fbb4608ddfad))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.11.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.11.1...@react-navigation/drawer@5.11.2) (2020-11-10)
|
## [5.11.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.11.1...@react-navigation/drawer@5.11.2) (2020-11-10)
|
||||||
|
|
||||||
**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.11.2",
|
"version": "5.12.5",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -45,14 +45,14 @@
|
|||||||
"react-native-iphone-x-helper": "^1.3.0"
|
"react-native-iphone-x-helper": "^1.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
"@react-navigation/native": "^5.9.4",
|
||||||
"@react-navigation/native": "^5.8.9",
|
|
||||||
"@testing-library/react-native": "^7.1.0",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/react": "^16.9.53",
|
"@types/react": "^16.9.53",
|
||||||
"@types/react-native": "^0.63.30",
|
"@types/react-native": "^0.63.30",
|
||||||
"del-cli": "^3.0.1",
|
"del-cli": "^3.0.1",
|
||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
"react-native": "~0.63.2",
|
"react-native": "~0.63.2",
|
||||||
|
"react-native-builder-bob": "^0.17.0",
|
||||||
"react-native-gesture-handler": "~1.7.0",
|
"react-native-gesture-handler": "~1.7.0",
|
||||||
"react-native-reanimated": "~1.13.0",
|
"react-native-reanimated": "~1.13.0",
|
||||||
"react-native-safe-area-context": "3.1.4",
|
"react-native-safe-area-context": "3.1.4",
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
"react-native-safe-area-context": ">= 0.6.0",
|
"react-native-safe-area-context": ">= 0.6.0",
|
||||||
"react-native-screens": ">= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0"
|
"react-native-screens": ">= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0"
|
||||||
},
|
},
|
||||||
"@react-native-community/bob": {
|
"react-native-builder-bob": {
|
||||||
"source": "src",
|
"source": "src",
|
||||||
"output": "lib",
|
"output": "lib",
|
||||||
"targets": [
|
"targets": [
|
||||||
|
|||||||
@@ -116,12 +116,6 @@ export default class DrawerView extends React.Component<Props> {
|
|||||||
statusBarAnimation: 'slide',
|
statusBarAnimation: 'slide',
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
if (Platform.OS === 'web') {
|
|
||||||
document?.body?.addEventListener?.('keyup', this.handleEscape);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps: Props) {
|
componentDidUpdate(prevProps: Props) {
|
||||||
const {
|
const {
|
||||||
open,
|
open,
|
||||||
@@ -172,22 +166,8 @@ export default class DrawerView extends React.Component<Props> {
|
|||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.toggleStatusBar(false);
|
this.toggleStatusBar(false);
|
||||||
this.handleEndInteraction();
|
this.handleEndInteraction();
|
||||||
|
|
||||||
if (Platform.OS === 'web') {
|
|
||||||
document?.body?.removeEventListener?.('keyup', this.handleEscape);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleEscape = (e: KeyboardEvent) => {
|
|
||||||
const { open, onClose } = this.props;
|
|
||||||
|
|
||||||
if (e.key === 'Escape') {
|
|
||||||
if (open) {
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private handleEndInteraction = () => {
|
private handleEndInteraction = () => {
|
||||||
if (this.interactionHandle !== undefined) {
|
if (this.interactionHandle !== undefined) {
|
||||||
InteractionManager.clearInteractionHandle(this.interactionHandle);
|
InteractionManager.clearInteractionHandle(this.interactionHandle);
|
||||||
|
|||||||
@@ -56,6 +56,20 @@ type Props = {
|
|||||||
* Background color for item when its inactive.
|
* Background color for item when its inactive.
|
||||||
*/
|
*/
|
||||||
inactiveBackgroundColor?: string;
|
inactiveBackgroundColor?: string;
|
||||||
|
/**
|
||||||
|
* Color of the touchable effect on press.
|
||||||
|
* Only supported on Android.
|
||||||
|
*
|
||||||
|
* @platform android
|
||||||
|
*/
|
||||||
|
pressColor?: string;
|
||||||
|
/**
|
||||||
|
* Opacity of the touchable effect on press.
|
||||||
|
* Only supported on iOS.
|
||||||
|
*
|
||||||
|
* @platform ios
|
||||||
|
*/
|
||||||
|
pressOpacity?: string;
|
||||||
/**
|
/**
|
||||||
* Style object for the label element.
|
* Style object for the label element.
|
||||||
*/
|
*/
|
||||||
@@ -72,6 +86,7 @@ const Touchable = ({
|
|||||||
onPress,
|
onPress,
|
||||||
to,
|
to,
|
||||||
accessibilityRole,
|
accessibilityRole,
|
||||||
|
accessibilityState,
|
||||||
delayPressIn,
|
delayPressIn,
|
||||||
...rest
|
...rest
|
||||||
}: TouchableWithoutFeedbackProps & {
|
}: TouchableWithoutFeedbackProps & {
|
||||||
@@ -105,6 +120,7 @@ const Touchable = ({
|
|||||||
<TouchableItem
|
<TouchableItem
|
||||||
{...rest}
|
{...rest}
|
||||||
accessibilityRole={accessibilityRole}
|
accessibilityRole={accessibilityRole}
|
||||||
|
accessibilityState={accessibilityState}
|
||||||
delayPressIn={delayPressIn}
|
delayPressIn={delayPressIn}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
>
|
>
|
||||||
@@ -132,6 +148,8 @@ export default function DrawerItem(props: Props) {
|
|||||||
inactiveBackgroundColor = 'transparent',
|
inactiveBackgroundColor = 'transparent',
|
||||||
style,
|
style,
|
||||||
onPress,
|
onPress,
|
||||||
|
pressColor,
|
||||||
|
pressOpacity,
|
||||||
...rest
|
...rest
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@@ -159,6 +177,8 @@ export default function DrawerItem(props: Props) {
|
|||||||
accessibilityState={{ selected: focused }}
|
accessibilityState={{ selected: focused }}
|
||||||
// @ts-expect-error: keep for compatibility with older React Native versions
|
// @ts-expect-error: keep for compatibility with older React Native versions
|
||||||
accessibilityStates={focused ? ['selected'] : []}
|
accessibilityStates={focused ? ['selected'] : []}
|
||||||
|
pressColor={pressColor}
|
||||||
|
pressOpacity={pressOpacity}
|
||||||
to={to}
|
to={to}
|
||||||
>
|
>
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ import {
|
|||||||
I18nManager,
|
I18nManager,
|
||||||
Platform,
|
Platform,
|
||||||
BackHandler,
|
BackHandler,
|
||||||
NativeEventSubscription,
|
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { ScreenContainer } from 'react-native-screens';
|
import { ScreenContainer, screensEnabled } from 'react-native-screens';
|
||||||
import {
|
import {
|
||||||
NavigationHelpersContext,
|
NavigationHelpersContext,
|
||||||
NavigationContext,
|
NavigationContext,
|
||||||
@@ -112,29 +111,48 @@ export default function DrawerView({
|
|||||||
}, [navigation, state.key]);
|
}, [navigation, state.key]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (isDrawerOpen) {
|
if (!isDrawerOpen || drawerType === 'permanent') {
|
||||||
navigation.emit({ type: 'drawerOpen' });
|
return;
|
||||||
} else {
|
|
||||||
navigation.emit({ type: 'drawerClose' });
|
|
||||||
}
|
|
||||||
}, [isDrawerOpen, navigation]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
let subscription: NativeEventSubscription | undefined;
|
|
||||||
|
|
||||||
if (isDrawerOpen) {
|
|
||||||
// We only add the subscription when drawer opens
|
|
||||||
// This way we can make sure that the subscription is added as late as possible
|
|
||||||
// This will make sure that our handler will run first when back button is pressed
|
|
||||||
subscription = BackHandler.addEventListener('hardwareBackPress', () => {
|
|
||||||
handleDrawerClose();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => subscription?.remove();
|
const handleClose = () => {
|
||||||
}, [handleDrawerClose, isDrawerOpen, navigation, state.key]);
|
// We shouldn't handle the back button if the parent screen isn't focused
|
||||||
|
// This will avoid the drawer overriding event listeners from a focused screen
|
||||||
|
if (!navigation.isFocused()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDrawerClose();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEscape = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
handleClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// We only add the listeners when drawer opens
|
||||||
|
// This way we can make sure that the listener is added as late as possible
|
||||||
|
// This will make sure that our handler will run first when back button is pressed
|
||||||
|
const subscription = BackHandler.addEventListener(
|
||||||
|
'hardwareBackPress',
|
||||||
|
handleClose
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Platform.OS === 'web') {
|
||||||
|
document?.body?.addEventListener?.('keyup', handleEscape);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
subscription.remove();
|
||||||
|
|
||||||
|
if (Platform.OS === 'web') {
|
||||||
|
document?.body?.removeEventListener?.('keyup', handleEscape);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [drawerType, handleDrawerClose, isDrawerOpen, navigation]);
|
||||||
|
|
||||||
const focusedRouteKey = state.routes[state.index].key;
|
const focusedRouteKey = state.routes[state.index].key;
|
||||||
|
|
||||||
@@ -157,9 +175,11 @@ export default function DrawerView({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderContent = () => {
|
const renderContent = () => {
|
||||||
|
const isScreensEnabled = screensEnabled?.() && detachInactiveScreens;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<ScreenContainer enabled={detachInactiveScreens} style={styles.content}>
|
<ScreenContainer enabled={isScreensEnabled} style={styles.content}>
|
||||||
{state.routes.map((route, index) => {
|
{state.routes.map((route, index) => {
|
||||||
const descriptor = descriptors[route.key];
|
const descriptor = descriptors[route.key];
|
||||||
const { unmountOnBlur } = descriptor.options;
|
const { unmountOnBlur } = descriptor.options;
|
||||||
@@ -176,7 +196,7 @@ export default function DrawerView({
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
header = (props: DrawerHeaderProps) => <Header {...props} />,
|
header = (props: DrawerHeaderProps) => <Header {...props} />,
|
||||||
headerShown = true,
|
headerShown = false,
|
||||||
} = descriptor.options;
|
} = descriptor.options;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -184,7 +204,7 @@ export default function DrawerView({
|
|||||||
key={route.key}
|
key={route.key}
|
||||||
style={[StyleSheet.absoluteFill, { opacity: isFocused ? 1 : 0 }]}
|
style={[StyleSheet.absoluteFill, { opacity: isFocused ? 1 : 0 }]}
|
||||||
isVisible={isFocused}
|
isVisible={isFocused}
|
||||||
enabled={detachInactiveScreens}
|
enabled={isScreensEnabled}
|
||||||
>
|
>
|
||||||
{headerShown ? (
|
{headerShown ? (
|
||||||
<NavigationContext.Provider value={descriptor.navigation}>
|
<NavigationContext.Provider value={descriptor.navigation}>
|
||||||
|
|||||||
@@ -16,36 +16,56 @@ type Props = {
|
|||||||
|
|
||||||
const FAR_FAR_AWAY = 30000; // this should be big enough to move the whole view out of its container
|
const FAR_FAR_AWAY = 30000; // this should be big enough to move the whole view out of its container
|
||||||
|
|
||||||
export default class ResourceSavingScene extends React.Component<Props> {
|
export default function ResourceSavingScene({
|
||||||
render() {
|
isVisible,
|
||||||
// react-native-screens is buggy on web
|
children,
|
||||||
if (screensEnabled?.() && Platform.OS !== 'web') {
|
style,
|
||||||
const { isVisible, ...rest } = this.props;
|
...rest
|
||||||
|
}: Props) {
|
||||||
if (shouldUseActivityState) {
|
// react-native-screens is buggy on web
|
||||||
return (
|
if (screensEnabled?.() && Platform.OS !== 'web') {
|
||||||
// @ts-expect-error: there was an `active` prop and no `activityState` in older version and stackPresentation was required
|
if (shouldUseActivityState) {
|
||||||
<Screen activityState={isVisible ? 2 : 0} {...rest} />
|
return (
|
||||||
);
|
// @ts-expect-error: there was an `active` prop and no `activityState` in older version and stackPresentation was required
|
||||||
} else {
|
<Screen activityState={isVisible ? 2 : 0} style={style} {...rest}>
|
||||||
return (
|
{children}
|
||||||
// @ts-expect-error: there was an `active` prop and no `activityState` in older version and stackPresentation was required
|
</Screen>
|
||||||
<Screen active={isVisible ? 1 : 0} {...rest} />
|
);
|
||||||
);
|
} else {
|
||||||
}
|
return (
|
||||||
|
// @ts-expect-error: there was an `active` prop and no `activityState` in older version and stackPresentation was required
|
||||||
|
<Screen active={isVisible ? 1 : 0} style={style} {...rest}>
|
||||||
|
{children}
|
||||||
|
</Screen>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const { isVisible, children, style, ...rest } = this.props;
|
if (Platform.OS === 'web') {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
|
// @ts-expect-error: hidden exists on web, but not in React Native
|
||||||
|
hidden={!isVisible}
|
||||||
style={[
|
style={[
|
||||||
|
{ display: isVisible ? 'flex' : 'none' },
|
||||||
styles.container,
|
styles.container,
|
||||||
Platform.OS === 'web'
|
|
||||||
? { display: isVisible ? 'flex' : 'none' }
|
|
||||||
: { overflow: 'hidden' },
|
|
||||||
style,
|
style,
|
||||||
]}
|
]}
|
||||||
|
pointerEvents={isVisible ? 'auto' : 'none'}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[styles.container, style]}
|
||||||
|
// box-none doesn't seem to work properly on Android
|
||||||
|
pointerEvents={isVisible ? 'auto' : 'none'}
|
||||||
|
>
|
||||||
|
<View
|
||||||
collapsable={false}
|
collapsable={false}
|
||||||
removeClippedSubviews={
|
removeClippedSubviews={
|
||||||
// On iOS, set removeClippedSubviews to true only when not focused
|
// On iOS, set removeClippedSubviews to true only when not focused
|
||||||
@@ -53,14 +73,12 @@ export default class ResourceSavingScene extends React.Component<Props> {
|
|||||||
Platform.OS === 'ios' ? !isVisible : true
|
Platform.OS === 'ios' ? !isVisible : true
|
||||||
}
|
}
|
||||||
pointerEvents={isVisible ? 'auto' : 'none'}
|
pointerEvents={isVisible ? 'auto' : 'none'}
|
||||||
{...rest}
|
style={isVisible ? styles.attached : styles.detached}
|
||||||
>
|
>
|
||||||
<View style={isVisible ? styles.attached : styles.detached}>
|
{children}
|
||||||
{children}
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
</View>
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import { BaseButton } from 'react-native-gesture-handler';
|
|||||||
const AnimatedBaseButton = Animated.createAnimatedComponent(BaseButton);
|
const AnimatedBaseButton = Animated.createAnimatedComponent(BaseButton);
|
||||||
|
|
||||||
type Props = React.ComponentProps<typeof BaseButton> & {
|
type Props = React.ComponentProps<typeof BaseButton> & {
|
||||||
activeOpacity: number;
|
pressOpacity: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const useNativeDriver = Platform.OS !== 'web';
|
const useNativeDriver = Platform.OS !== 'web';
|
||||||
|
|
||||||
export default class TouchableItem extends React.Component<Props> {
|
export default class TouchableItem extends React.Component<Props> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
activeOpacity: 0.3,
|
pressOpacity: 0.3,
|
||||||
borderless: true,
|
borderless: true,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
};
|
};
|
||||||
@@ -27,7 +27,7 @@ export default class TouchableItem extends React.Component<Props> {
|
|||||||
overshootClamping: true,
|
overshootClamping: true,
|
||||||
restDisplacementThreshold: 0.01,
|
restDisplacementThreshold: 0.01,
|
||||||
restSpeedThreshold: 0.01,
|
restSpeedThreshold: 0.01,
|
||||||
toValue: active ? this.props.activeOpacity : 1,
|
toValue: active ? this.props.pressOpacity : 1,
|
||||||
useNativeDriver,
|
useNativeDriver,
|
||||||
}).start();
|
}).start();
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,60 @@
|
|||||||
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.3.15](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.14...@react-navigation/material-bottom-tabs@5.3.15) (2021-04-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* don't pass accessibilityState to link. closes [#9418](https://github.com/react-navigation/react-navigation/issues/9418) ([699ea0c](https://github.com/react-navigation/react-navigation/commit/699ea0cc5052f190acc7ce8bc0328bb052d7cf26))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.14](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.13...@react-navigation/material-bottom-tabs@5.3.14) (2021-02-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.13](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.12...@react-navigation/material-bottom-tabs@5.3.13) (2021-01-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.12](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.11...@react-navigation/material-bottom-tabs@5.3.12) (2021-01-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.11](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.10...@react-navigation/material-bottom-tabs@5.3.11) (2021-01-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* handle fallback for MaterialCommunityIcons better ([26074a2](https://github.com/react-navigation/react-navigation/commit/26074a28f768ba01743e2ca3b3cb9873a04c9d9c))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.10](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.9...@react-navigation/material-bottom-tabs@5.3.10) (2020-11-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.3.9](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.8...@react-navigation/material-bottom-tabs@5.3.9) (2020-11-10)
|
## [5.3.9](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.8...@react-navigation/material-bottom-tabs@5.3.9) (2020-11-10)
|
||||||
|
|
||||||
**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.3.9",
|
"version": "5.3.15",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -41,8 +41,7 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
"@react-navigation/native": "^5.9.4",
|
||||||
"@react-navigation/native": "^5.8.9",
|
|
||||||
"@testing-library/react-native": "^7.1.0",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/react": "^16.9.53",
|
"@types/react": "^16.9.53",
|
||||||
"@types/react-native": "^0.63.30",
|
"@types/react-native": "^0.63.30",
|
||||||
@@ -50,6 +49,7 @@
|
|||||||
"del-cli": "^3.0.1",
|
"del-cli": "^3.0.1",
|
||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
"react-native": "~0.63.2",
|
"react-native": "~0.63.2",
|
||||||
|
"react-native-builder-bob": "^0.17.0",
|
||||||
"react-native-paper": "^4.2.0",
|
"react-native-paper": "^4.2.0",
|
||||||
"react-native-vector-icons": "^7.0.0",
|
"react-native-vector-icons": "^7.0.0",
|
||||||
"typescript": "^4.0.3"
|
"typescript": "^4.0.3"
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
"react-native-paper": ">= 3.0.0",
|
"react-native-paper": ">= 3.0.0",
|
||||||
"react-native-vector-icons": ">= 6.0.0"
|
"react-native-vector-icons": ">= 6.0.0"
|
||||||
},
|
},
|
||||||
"@react-native-community/bob": {
|
"react-native-builder-bob": {
|
||||||
"source": "src",
|
"source": "src",
|
||||||
"output": "lib",
|
"output": "lib",
|
||||||
"targets": [
|
"targets": [
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { StyleSheet, Platform } from 'react-native';
|
import { Text, StyleSheet, Platform } from 'react-native';
|
||||||
import { BottomNavigation, DefaultTheme, DarkTheme } from 'react-native-paper';
|
import { BottomNavigation, DefaultTheme, DarkTheme } from 'react-native-paper';
|
||||||
import {
|
import {
|
||||||
NavigationHelpersContext,
|
NavigationHelpersContext,
|
||||||
@@ -28,44 +28,48 @@ type Scene = { route: { key: string } };
|
|||||||
|
|
||||||
// Optionally require vector-icons referenced from react-native-paper:
|
// Optionally require vector-icons referenced from react-native-paper:
|
||||||
// https://github.com/callstack/react-native-paper/blob/4b26429c49053eaa4c3e0fae208639e01093fa87/src/components/MaterialCommunityIcon.tsx#L14
|
// https://github.com/callstack/react-native-paper/blob/4b26429c49053eaa4c3e0fae208639e01093fa87/src/components/MaterialCommunityIcon.tsx#L14
|
||||||
let MaterialCommunityIcons: any;
|
let MaterialCommunityIcons: React.ComponentType<React.ComponentProps<
|
||||||
|
typeof import('react-native-vector-icons/MaterialCommunityIcons').default
|
||||||
|
>>;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Optionally require vector-icons
|
// Optionally require vector-icons
|
||||||
MaterialCommunityIcons = require('react-native-vector-icons/MaterialCommunityIcons')
|
MaterialCommunityIcons = require('react-native-vector-icons/MaterialCommunityIcons')
|
||||||
.default;
|
.default;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// @ts-expect-error
|
let isErrorLogged = false;
|
||||||
if (global.__expo?.Icon?.MaterialCommunityIcons) {
|
|
||||||
// Snack doesn't properly bundle vector icons from sub-path
|
|
||||||
// Use icons from the __expo global if available
|
|
||||||
// @ts-expect-error
|
|
||||||
MaterialCommunityIcons = global.__expo.Icon.MaterialCommunityIcons;
|
|
||||||
} else {
|
|
||||||
let isErrorLogged = false;
|
|
||||||
|
|
||||||
// Fallback component for icons
|
// Fallback component for icons
|
||||||
MaterialCommunityIcons = () => {
|
MaterialCommunityIcons = ({
|
||||||
if (!isErrorLogged) {
|
name,
|
||||||
if (
|
color,
|
||||||
!/(Cannot find module|Module not found|Cannot resolve module)/.test(
|
size,
|
||||||
e.message
|
selectionColor: _,
|
||||||
)
|
...rest
|
||||||
) {
|
}) => {
|
||||||
console.error(e);
|
if (!isErrorLogged) {
|
||||||
}
|
if (
|
||||||
|
!/(Cannot find module|Module not found|Cannot resolve module)/.test(
|
||||||
console.warn(
|
e.message
|
||||||
`Tried to use the icon '${name}' in a component from '@react-navigation/material-bottom-tabs', but 'react-native-vector-icons' could not be loaded.`,
|
)
|
||||||
`To remove this warning, try installing 'react-native-vector-icons' or use another method.`
|
) {
|
||||||
);
|
console.error(e);
|
||||||
|
|
||||||
isErrorLogged = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
console.warn(
|
||||||
};
|
`Tried to use the icon '${name}' in a component from '@react-navigation/material-bottom-tabs', but 'react-native-vector-icons/MaterialCommunityIcons' could not be loaded.`,
|
||||||
}
|
`To remove this warning, try installing 'react-native-vector-icons' or use another method to specify icon: https://reactnavigation.org/docs/material-bottom-tab-navigator/#tabbaricon.`
|
||||||
|
);
|
||||||
|
|
||||||
|
isErrorLogged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text {...rest} style={[styles.icon, { color, fontSize: size }]}>
|
||||||
|
□
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function MaterialBottomTabViewInner({
|
function MaterialBottomTabViewInner({
|
||||||
@@ -107,10 +111,13 @@ function MaterialBottomTabViewInner({
|
|||||||
? ({
|
? ({
|
||||||
onPress,
|
onPress,
|
||||||
route,
|
route,
|
||||||
accessibilityRole: _0,
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
borderless: _1,
|
accessibilityRole,
|
||||||
centered: _2,
|
accessibilityState,
|
||||||
rippleColor: _3,
|
borderless,
|
||||||
|
centered,
|
||||||
|
rippleColor,
|
||||||
|
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||||||
style,
|
style,
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -3,6 +3,54 @@
|
|||||||
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.3.15](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.14...@react-navigation/material-top-tabs@5.3.15) (2021-04-04)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.14](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.13...@react-navigation/material-top-tabs@5.3.14) (2021-02-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.13](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.12...@react-navigation/material-top-tabs@5.3.13) (2021-01-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.12](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.11...@react-navigation/material-top-tabs@5.3.12) (2021-01-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.11](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.10...@react-navigation/material-top-tabs@5.3.11) (2021-01-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.3.10](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.9...@react-navigation/material-top-tabs@5.3.10) (2020-11-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.3.9](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.8...@react-navigation/material-top-tabs@5.3.9) (2020-11-10)
|
## [5.3.9](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.8...@react-navigation/material-top-tabs@5.3.9) (2020-11-10)
|
||||||
|
|
||||||
**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.3.9",
|
"version": "5.3.15",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -44,14 +44,14 @@
|
|||||||
"color": "^3.1.3"
|
"color": "^3.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
"@react-navigation/native": "^5.9.4",
|
||||||
"@react-navigation/native": "^5.8.9",
|
|
||||||
"@testing-library/react-native": "^7.1.0",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/react": "^16.9.53",
|
"@types/react": "^16.9.53",
|
||||||
"@types/react-native": "^0.63.30",
|
"@types/react-native": "^0.63.30",
|
||||||
"del-cli": "^3.0.1",
|
"del-cli": "^3.0.1",
|
||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
"react-native": "~0.63.2",
|
"react-native": "~0.63.2",
|
||||||
|
"react-native-builder-bob": "^0.17.0",
|
||||||
"react-native-gesture-handler": "~1.7.0",
|
"react-native-gesture-handler": "~1.7.0",
|
||||||
"react-native-reanimated": "~1.13.0",
|
"react-native-reanimated": "~1.13.0",
|
||||||
"react-native-tab-view": "^2.15.2",
|
"react-native-tab-view": "^2.15.2",
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
"react-native-reanimated": ">= 1.0.0",
|
"react-native-reanimated": ">= 1.0.0",
|
||||||
"react-native-tab-view": ">= 2.0.0"
|
"react-native-tab-view": ">= 2.0.0"
|
||||||
},
|
},
|
||||||
"@react-native-community/bob": {
|
"react-native-builder-bob": {
|
||||||
"source": "src",
|
"source": "src",
|
||||||
"output": "lib",
|
"output": "lib",
|
||||||
"targets": [
|
"targets": [
|
||||||
|
|||||||
@@ -3,6 +3,68 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [5.9.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.9.3...@react-navigation/native@5.9.4) (2021-04-04)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/native
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.9.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.9.2...@react-navigation/native@5.9.3) (2021-02-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* address breaking change in react-native for Linking ([a8342aa](https://github.com/react-navigation/react-navigation/commit/a8342aaf3d1ba8fb29faa91c7b63ed25f11745e5))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.9.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.9.1...@react-navigation/native@5.9.2) (2021-01-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* normalize prefix when parsing. fixes [#9081](https://github.com/react-navigation/react-navigation/issues/9081) ([4ca2d2d](https://github.com/react-navigation/react-navigation/commit/4ca2d2d22bc9eccf87451b15c823174d98cbd0a2))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.9.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.9.0...@react-navigation/native@5.9.1) (2021-01-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/native
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.9.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.10...@react-navigation/native@5.9.0) (2021-01-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* support sync getInitialURL in native useLinking ([b26b907](https://github.com/react-navigation/react-navigation/commit/b26b90706fe0a0d914d4a868df1310d2dc3a7623))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* expose getActionForState in linking ([c9a5d45](https://github.com/react-navigation/react-navigation/commit/c9a5d4532406c6bfdac0c675a3fe4db5430e9a55))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.8.10](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.9...@react-navigation/native@5.8.10) (2020-11-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/native
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.8.9](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.8...@react-navigation/native@5.8.9) (2020-11-10)
|
## [5.8.9](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.8...@react-navigation/native@5.8.9) (2020-11-10)
|
||||||
|
|
||||||
**Note:** Version bump only for package @react-navigation/native
|
**Note:** Version bump only for package @react-navigation/native
|
||||||
|
|||||||
@@ -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.8.9",
|
"version": "5.9.4",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native",
|
"react-native",
|
||||||
"react-navigation",
|
"react-navigation",
|
||||||
@@ -37,12 +37,11 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-navigation/core": "^5.14.3",
|
"@react-navigation/core": "^5.15.3",
|
||||||
"escape-string-regexp": "^4.0.0",
|
"escape-string-regexp": "^4.0.0",
|
||||||
"nanoid": "^3.1.15"
|
"nanoid": "^3.1.15"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
|
||||||
"@testing-library/react-native": "^7.1.0",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/react": "^16.9.53",
|
"@types/react": "^16.9.53",
|
||||||
"@types/react-dom": "^16.9.8",
|
"@types/react-dom": "^16.9.8",
|
||||||
@@ -51,13 +50,14 @@
|
|||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
"react-native": "~0.63.2",
|
"react-native": "~0.63.2",
|
||||||
|
"react-native-builder-bob": "^0.17.0",
|
||||||
"typescript": "^4.0.3"
|
"typescript": "^4.0.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "*",
|
"react": "*",
|
||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
},
|
},
|
||||||
"@react-native-community/bob": {
|
"react-native-builder-bob": {
|
||||||
"source": "src",
|
"source": "src",
|
||||||
"output": "lib",
|
"output": "lib",
|
||||||
"targets": [
|
"targets": [
|
||||||
|
|||||||
@@ -5,4 +5,6 @@ const LinkingContext = React.createContext<{
|
|||||||
options: LinkingOptions | undefined;
|
options: LinkingOptions | undefined;
|
||||||
}>({ options: undefined });
|
}>({ options: undefined });
|
||||||
|
|
||||||
|
LinkingContext.displayName = 'LinkingContext';
|
||||||
|
|
||||||
export default LinkingContext;
|
export default LinkingContext;
|
||||||
|
|||||||
318
packages/native/src/__tests__/extractPathFromURL.test.tsx
Normal file
318
packages/native/src/__tests__/extractPathFromURL.test.tsx
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
import extractPathFromURL from '../extractPathFromURL';
|
||||||
|
|
||||||
|
it('extracts path from URL with protocol', () => {
|
||||||
|
expect(extractPathFromURL(['scheme://'], 'scheme://some/path')).toBe(
|
||||||
|
'some/path'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme://'], 'scheme:some/path')).toBe(
|
||||||
|
'some/path'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme://'], 'scheme:///some/path')).toBe(
|
||||||
|
'some/path'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme:///'], 'scheme:some/path')).toBe(
|
||||||
|
'some/path'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme:'], 'scheme:some/path')).toBe('some/path');
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme:'], 'scheme://some/path')).toBe(
|
||||||
|
'some/path'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme:'], 'scheme:///some/path')).toBe(
|
||||||
|
'some/path'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('extracts path from URL with protocol and host', () => {
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme://example.com'],
|
||||||
|
'scheme://example.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(['scheme://example.com'], 'scheme:example.com/some/path')
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme://example.com'],
|
||||||
|
'scheme:///example.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:///example.com'],
|
||||||
|
'scheme:example.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(['scheme:example.com'], 'scheme:example.com/some/path')
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(['scheme:example.com'], 'scheme://example.com/some/path')
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:example.com'],
|
||||||
|
'scheme:///example.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('extracts path from URL with protocol and host with wildcard', () => {
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme://*.example.com'],
|
||||||
|
'scheme://test.example.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme://*.example.com'],
|
||||||
|
'scheme:test.example.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme://*.example.com'],
|
||||||
|
'scheme:///test.example.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:///*.example.com'],
|
||||||
|
'scheme:test.example.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:*.example.com'],
|
||||||
|
'scheme:test.example.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:*.example.com'],
|
||||||
|
'scheme://test.example.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:*.example.com'],
|
||||||
|
'scheme:///test.example.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('extracts path from URL with protocol, host and path', () => {
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme://example.com/test'],
|
||||||
|
'scheme://example.com/test/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(['scheme://example.com'], 'scheme:example.com/some/path')
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme://example.com/test'],
|
||||||
|
'scheme:///example.com/test/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:///example.com/test'],
|
||||||
|
'scheme:example.com/test/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:example.com/test'],
|
||||||
|
'scheme:example.com/test/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:example.com/test'],
|
||||||
|
'scheme://example.com/test/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:example.com/test'],
|
||||||
|
'scheme:///example.com/test/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:example.com/test'],
|
||||||
|
'scheme:///example.com//test/some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:example.com/test'],
|
||||||
|
'scheme:///example.com/test//some/path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:example.com/test'],
|
||||||
|
'scheme:///example.com/test/some//path'
|
||||||
|
)
|
||||||
|
).toBe('/some/path');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined for non-matching protocol', () => {
|
||||||
|
expect(extractPathFromURL(['scheme://'], 'foo://some/path')).toBe(undefined);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme://'], 'foo:some/path')).toBe(undefined);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme://'], 'foo:///some/path')).toBe(undefined);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme:///'], 'foo:some/path')).toBe(undefined);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme:'], 'foo:some/path')).toBe(undefined);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme:'], 'foo://some/path')).toBe(undefined);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme:'], 'foo:///some/path')).toBe(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined for non-matching path', () => {
|
||||||
|
expect(extractPathFromURL(['scheme://foo'], 'scheme://some/path')).toBe(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme://foo'], 'scheme:some/path')).toBe(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme://foo'], 'scheme:///some/path')).toBe(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme:///foo'], 'scheme:some/path')).toBe(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme:foo'], 'scheme:some/path')).toBe(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme:foo'], 'scheme://some/path')).toBe(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(extractPathFromURL(['scheme:foo'], 'scheme:///some/path')).toBe(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined for non-matching host', () => {
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(['scheme://example.com'], 'scheme://foo.com/some/path')
|
||||||
|
).toBe(undefined);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(['scheme://example.com'], 'scheme:foo.com/some/path')
|
||||||
|
).toBe(undefined);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(['scheme://example.com'], 'scheme:///foo.com/some/path')
|
||||||
|
).toBe(undefined);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(['scheme:///example.com'], 'scheme:foo.com/some/path')
|
||||||
|
).toBe(undefined);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(['scheme:example.com'], 'scheme:foo.com/some/path')
|
||||||
|
).toBe(undefined);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(['scheme:example.com'], 'scheme://foo.com/some/path')
|
||||||
|
).toBe(undefined);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(['scheme:example.com'], 'scheme:///foo.com/some/path')
|
||||||
|
).toBe(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined for non-matching host with wildcard', () => {
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme://*.example.com'],
|
||||||
|
'scheme://test.foo.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe(undefined);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme://*.example.com'],
|
||||||
|
'scheme:test.foo.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe(undefined);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme://*.example.com'],
|
||||||
|
'scheme:///test.foo.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe(undefined);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:///*.example.com'],
|
||||||
|
'scheme:test.foo.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe(undefined);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:*.example.com'],
|
||||||
|
'scheme:test.foo.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe(undefined);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:*.example.com'],
|
||||||
|
'scheme://test.foo.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe(undefined);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
extractPathFromURL(
|
||||||
|
['scheme:*.example.com'],
|
||||||
|
'scheme:///test.foo.com/some/path'
|
||||||
|
)
|
||||||
|
).toBe(undefined);
|
||||||
|
});
|
||||||
26
packages/native/src/extractPathFromURL.tsx
Normal file
26
packages/native/src/extractPathFromURL.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import escapeStringRegexp from 'escape-string-regexp';
|
||||||
|
|
||||||
|
export default function extractPathFromURL(prefixes: string[], url: string) {
|
||||||
|
for (const prefix of prefixes) {
|
||||||
|
const protocol = prefix.match(/^[^:]+:/)?.[0] ?? '';
|
||||||
|
const host = prefix
|
||||||
|
.replace(new RegExp(`^${escapeStringRegexp(protocol)}`), '')
|
||||||
|
.replace(/\/+/g, '/') // Replace multiple slash (//) with single ones
|
||||||
|
.replace(/^\//, ''); // Remove extra leading slash
|
||||||
|
|
||||||
|
const prefixRegex = new RegExp(
|
||||||
|
`^${escapeStringRegexp(protocol)}(/)*${host
|
||||||
|
.split('.')
|
||||||
|
.map((it) => (it === '*' ? '[^/]+' : escapeStringRegexp(it)))
|
||||||
|
.join('\\.')}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const normalizedURL = url.replace(/\/+/g, '/');
|
||||||
|
|
||||||
|
if (prefixRegex.test(normalizedURL)) {
|
||||||
|
return normalizedURL.replace(prefixRegex, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
@@ -4,4 +4,6 @@ import type { Theme } from '../types';
|
|||||||
|
|
||||||
const ThemeContext = React.createContext<Theme>(DefaultTheme);
|
const ThemeContext = React.createContext<Theme>(DefaultTheme);
|
||||||
|
|
||||||
|
ThemeContext.displayName = 'ThemeContext';
|
||||||
|
|
||||||
export default ThemeContext;
|
export default ThemeContext;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type {
|
import type {
|
||||||
getStateFromPath as getStateFromPathDefault,
|
getStateFromPath as getStateFromPathDefault,
|
||||||
getPathFromState as getPathFromStateDefault,
|
getPathFromState as getPathFromStateDefault,
|
||||||
|
getActionFromState as getActionFromStateDefault,
|
||||||
PathConfigMap,
|
PathConfigMap,
|
||||||
Route,
|
Route,
|
||||||
} from '@react-navigation/core';
|
} from '@react-navigation/core';
|
||||||
@@ -66,7 +67,11 @@ export type LinkingOptions = {
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
getInitialURL?: () => Promise<string | null | undefined>;
|
getInitialURL?: () =>
|
||||||
|
| string
|
||||||
|
| null
|
||||||
|
| undefined
|
||||||
|
| Promise<string | null | undefined>;
|
||||||
/**
|
/**
|
||||||
* Custom function to get subscribe to URL updates.
|
* Custom function to get subscribe to URL updates.
|
||||||
* Uses `Linking.addEventListener('url', callback)` by default.
|
* Uses `Linking.addEventListener('url', callback)` by default.
|
||||||
@@ -90,11 +95,18 @@ export type LinkingOptions = {
|
|||||||
) => undefined | void | (() => void);
|
) => undefined | void | (() => void);
|
||||||
/**
|
/**
|
||||||
* Custom function to parse the URL to a valid navigation state (advanced).
|
* Custom function to parse the URL to a valid navigation state (advanced).
|
||||||
|
* This state object will be passed as `initialState` for initial URL,
|
||||||
|
* and converted to an action object to `dispatch` for subsequent URLs.
|
||||||
*/
|
*/
|
||||||
getStateFromPath?: typeof getStateFromPathDefault;
|
getStateFromPath?: typeof getStateFromPathDefault;
|
||||||
|
/**
|
||||||
|
* Custom function to convert the state object to an action to dispatch (advanced).
|
||||||
|
* By default, the state is converted to a `NAVIGATE` action.
|
||||||
|
*/
|
||||||
|
getActionFromState?: typeof getActionFromStateDefault;
|
||||||
/**
|
/**
|
||||||
* Custom function to convert the state object to a valid URL (advanced).
|
* Custom function to convert the state object to a valid URL (advanced).
|
||||||
* Only applicable on Web.
|
* Used for creating links for navigation, primarily useful on Web.
|
||||||
*/
|
*/
|
||||||
getPathFromState?: typeof getPathFromStateDefault;
|
getPathFromState?: typeof getPathFromStateDefault;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Linking, Platform } from 'react-native';
|
import { Linking, Platform } from 'react-native';
|
||||||
import {
|
import {
|
||||||
getActionFromState,
|
getActionFromState as getActionFromStateDefault,
|
||||||
getStateFromPath as getStateFromPathDefault,
|
getStateFromPath as getStateFromPathDefault,
|
||||||
NavigationContainerRef,
|
NavigationContainerRef,
|
||||||
} from '@react-navigation/core';
|
} from '@react-navigation/core';
|
||||||
|
import extractPathFromURL from './extractPathFromURL';
|
||||||
import type { LinkingOptions } from './types';
|
import type { LinkingOptions } from './types';
|
||||||
import escapeStringRegexp from 'escape-string-regexp';
|
|
||||||
|
type ResultState = ReturnType<typeof getStateFromPathDefault>;
|
||||||
|
|
||||||
let isUsingLinking = false;
|
let isUsingLinking = false;
|
||||||
|
|
||||||
@@ -28,11 +30,21 @@ export default function useLinking(
|
|||||||
subscribe = (listener) => {
|
subscribe = (listener) => {
|
||||||
const callback = ({ url }: { url: string }) => listener(url);
|
const callback = ({ url }: { url: string }) => listener(url);
|
||||||
|
|
||||||
Linking.addEventListener('url', callback);
|
const subscription = Linking.addEventListener('url', callback) as
|
||||||
|
| { remove(): void }
|
||||||
|
| undefined;
|
||||||
|
|
||||||
return () => Linking.removeEventListener('url', callback);
|
return () => {
|
||||||
|
// https://github.com/facebook/react-native/commit/6d1aca806cee86ad76de771ed3a1cc62982ebcd7
|
||||||
|
if (subscription?.remove) {
|
||||||
|
subscription.remove();
|
||||||
|
} else {
|
||||||
|
Linking.removeEventListener('url', callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
},
|
},
|
||||||
getStateFromPath = getStateFromPathDefault,
|
getStateFromPath = getStateFromPathDefault,
|
||||||
|
getActionFromState = getActionFromStateDefault,
|
||||||
}: LinkingOptions
|
}: LinkingOptions
|
||||||
) {
|
) {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@@ -66,6 +78,7 @@ export default function useLinking(
|
|||||||
const configRef = React.useRef(config);
|
const configRef = React.useRef(config);
|
||||||
const getInitialURLRef = React.useRef(getInitialURL);
|
const getInitialURLRef = React.useRef(getInitialURL);
|
||||||
const getStateFromPathRef = React.useRef(getStateFromPath);
|
const getStateFromPathRef = React.useRef(getStateFromPath);
|
||||||
|
const getActionFromStateRef = React.useRef(getActionFromState);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
enabledRef.current = enabled;
|
enabledRef.current = enabled;
|
||||||
@@ -73,48 +86,53 @@ export default function useLinking(
|
|||||||
configRef.current = config;
|
configRef.current = config;
|
||||||
getInitialURLRef.current = getInitialURL;
|
getInitialURLRef.current = getInitialURL;
|
||||||
getStateFromPathRef.current = getStateFromPath;
|
getStateFromPathRef.current = getStateFromPath;
|
||||||
}, [config, enabled, prefixes, getInitialURL, getStateFromPath]);
|
getActionFromStateRef.current = getActionFromState;
|
||||||
|
});
|
||||||
|
|
||||||
const extractPathFromURL = React.useCallback((url: string) => {
|
const getInitialState = React.useCallback(() => {
|
||||||
for (const prefix of prefixesRef.current) {
|
let state: ResultState | undefined;
|
||||||
const protocol = prefix.match(/^[^:]+:\/\//)?.[0] ?? '';
|
|
||||||
const host = prefix.replace(protocol, '');
|
if (enabledRef.current) {
|
||||||
const prefixRegex = new RegExp(
|
const url = getInitialURLRef.current();
|
||||||
`^${escapeStringRegexp(protocol)}${host
|
|
||||||
.split('.')
|
if (url != null && typeof url !== 'string') {
|
||||||
.map((it) => (it === '*' ? '[^/]+' : escapeStringRegexp(it)))
|
return url.then((url) => {
|
||||||
.join('\\.')}`
|
const path = url
|
||||||
);
|
? extractPathFromURL(prefixesRef.current, url)
|
||||||
if (prefixRegex.test(url)) {
|
: null;
|
||||||
return url.replace(prefixRegex, '');
|
|
||||||
|
return path
|
||||||
|
? getStateFromPathRef.current(path, configRef.current)
|
||||||
|
: undefined;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const path = url ? extractPathFromURL(prefixesRef.current, url) : null;
|
||||||
|
|
||||||
|
state = path
|
||||||
|
? getStateFromPathRef.current(path, configRef.current)
|
||||||
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
const thenable = {
|
||||||
|
then(onfulfilled?: (state: ResultState | undefined) => void) {
|
||||||
|
return Promise.resolve(onfulfilled ? onfulfilled(state) : state);
|
||||||
|
},
|
||||||
|
catch() {
|
||||||
|
return thenable;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return thenable as PromiseLike<ResultState | undefined>;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getInitialState = React.useCallback(async () => {
|
|
||||||
if (!enabledRef.current) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = await getInitialURLRef.current();
|
|
||||||
const path = url ? extractPathFromURL(url) : null;
|
|
||||||
|
|
||||||
if (path) {
|
|
||||||
return getStateFromPathRef.current(path, configRef.current);
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}, [extractPathFromURL]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const listener = (url: string) => {
|
const listener = (url: string) => {
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = extractPathFromURL(url);
|
const path = extractPathFromURL(prefixesRef.current, url);
|
||||||
const navigation = ref.current;
|
const navigation = ref.current;
|
||||||
|
|
||||||
if (navigation && path) {
|
if (navigation && path) {
|
||||||
@@ -134,7 +152,10 @@ export default function useLinking(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const action = getActionFromState(state, configRef.current);
|
const action = getActionFromStateRef.current(
|
||||||
|
state,
|
||||||
|
configRef.current
|
||||||
|
);
|
||||||
|
|
||||||
if (action !== undefined) {
|
if (action !== undefined) {
|
||||||
try {
|
try {
|
||||||
@@ -154,7 +175,7 @@ export default function useLinking(
|
|||||||
};
|
};
|
||||||
|
|
||||||
return subscribe(listener);
|
return subscribe(listener);
|
||||||
}, [enabled, ref, subscribe, extractPathFromURL]);
|
}, [enabled, ref, subscribe]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getInitialState,
|
getInitialState,
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import * as React from 'react';
|
|||||||
import {
|
import {
|
||||||
getStateFromPath as getStateFromPathDefault,
|
getStateFromPath as getStateFromPathDefault,
|
||||||
getPathFromState as getPathFromStateDefault,
|
getPathFromState as getPathFromStateDefault,
|
||||||
|
getActionFromState as getActionFromStateDefault,
|
||||||
NavigationContainerRef,
|
NavigationContainerRef,
|
||||||
NavigationState,
|
NavigationState,
|
||||||
getActionFromState,
|
|
||||||
} from '@react-navigation/core';
|
} from '@react-navigation/core';
|
||||||
import { nanoid } from 'nanoid/non-secure';
|
import { nanoid } from 'nanoid/non-secure';
|
||||||
import ServerContext from './ServerContext';
|
import ServerContext from './ServerContext';
|
||||||
@@ -134,7 +134,7 @@ const createMemoryHistory = () => {
|
|||||||
// - There's history to go back, `history.go` is called, and `popstate` fires
|
// - There's history to go back, `history.go` is called, and `popstate` fires
|
||||||
// - `history.go` is called multiple times, we need to resolve on respective `popstate`
|
// - `history.go` is called multiple times, we need to resolve on respective `popstate`
|
||||||
// - No history to go back, but `history.go` was called, browser has no API to detect it
|
// - No history to go back, but `history.go` was called, browser has no API to detect it
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const done = (interrupted?: boolean) => {
|
const done = (interrupted?: boolean) => {
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
|
|
||||||
@@ -293,6 +293,7 @@ export default function useLinking(
|
|||||||
config,
|
config,
|
||||||
getStateFromPath = getStateFromPathDefault,
|
getStateFromPath = getStateFromPathDefault,
|
||||||
getPathFromState = getPathFromStateDefault,
|
getPathFromState = getPathFromStateDefault,
|
||||||
|
getActionFromState = getActionFromStateDefault,
|
||||||
}: LinkingOptions
|
}: LinkingOptions
|
||||||
) {
|
) {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@@ -323,14 +324,16 @@ export default function useLinking(
|
|||||||
const enabledRef = React.useRef(enabled);
|
const enabledRef = React.useRef(enabled);
|
||||||
const configRef = React.useRef(config);
|
const configRef = React.useRef(config);
|
||||||
const getStateFromPathRef = React.useRef(getStateFromPath);
|
const getStateFromPathRef = React.useRef(getStateFromPath);
|
||||||
|
const getActionFromStateRef = React.useRef(getActionFromState);
|
||||||
const getPathFromStateRef = React.useRef(getPathFromState);
|
const getPathFromStateRef = React.useRef(getPathFromState);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
enabledRef.current = enabled;
|
enabledRef.current = enabled;
|
||||||
configRef.current = config;
|
configRef.current = config;
|
||||||
getStateFromPathRef.current = getStateFromPath;
|
getStateFromPathRef.current = getStateFromPath;
|
||||||
|
getActionFromStateRef.current = getActionFromState;
|
||||||
getPathFromStateRef.current = getPathFromState;
|
getPathFromStateRef.current = getPathFromState;
|
||||||
}, [config, enabled, getPathFromState, getStateFromPath]);
|
});
|
||||||
|
|
||||||
const server = React.useContext(ServerContext);
|
const server = React.useContext(ServerContext);
|
||||||
|
|
||||||
@@ -349,7 +352,6 @@ export default function useLinking(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make it a thenable to keep consistent with the native impl
|
|
||||||
const thenable = {
|
const thenable = {
|
||||||
then(onfulfilled?: (state: ResultState | undefined) => void) {
|
then(onfulfilled?: (state: ResultState | undefined) => void) {
|
||||||
return Promise.resolve(onfulfilled ? onfulfilled(value) : value);
|
return Promise.resolve(onfulfilled ? onfulfilled(value) : value);
|
||||||
@@ -412,7 +414,10 @@ export default function useLinking(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (index > previousIndex) {
|
if (index > previousIndex) {
|
||||||
const action = getActionFromState(state, configRef.current);
|
const action = getActionFromStateRef.current(
|
||||||
|
state,
|
||||||
|
configRef.current
|
||||||
|
);
|
||||||
|
|
||||||
if (action !== undefined) {
|
if (action !== undefined) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -3,6 +3,45 @@
|
|||||||
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.7.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@5.7.1...@react-navigation/routers@5.7.2) (2021-02-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix getId being called for incorrect routes. closes [#9343](https://github.com/react-navigation/react-navigation/issues/9343) ([3728390](https://github.com/react-navigation/react-navigation/commit/3728390b60814ba414bd15cc5b7e5b51baa1f026))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.7.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@5.7.0...@react-navigation/routers@5.7.1) (2021-01-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix StackRouter incorrectly handling invalid route if key is present ([d3a9639](https://github.com/react-navigation/react-navigation/commit/d3a9639060631b06551daf0eac191ec1a442e298))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.7.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@5.6.2...@react-navigation/routers@5.7.0) (2021-01-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* consider openByDefault prop when rehydrating drawer state ([#9099](https://github.com/react-navigation/react-navigation/issues/9099)) ([2ad61a6](https://github.com/react-navigation/react-navigation/commit/2ad61a67357242fc4663ecad62ab311facbaf1be))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add a new backBehavior: firstRoute for TabRouter ([3c87419](https://github.com/react-navigation/react-navigation/commit/3c874191ffbd24b953ded5b62f606c4cc47e5651))
|
||||||
|
* add a way to specify an unique ID for screens ([b19f76b](https://github.com/react-navigation/react-navigation/commit/b19f76bfffe623759e67d925bfd067c753a453bf))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.6.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@5.6.1...@react-navigation/routers@5.6.2) (2020-11-09)
|
## [5.6.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@5.6.1...@react-navigation/routers@5.6.2) (2020-11-09)
|
||||||
|
|
||||||
**Note:** Version bump only for package @react-navigation/routers
|
**Note:** Version bump only for package @react-navigation/routers
|
||||||
|
|||||||
@@ -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.6.2",
|
"version": "5.7.2",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react",
|
"react",
|
||||||
"react-native",
|
"react-native",
|
||||||
@@ -39,11 +39,11 @@
|
|||||||
"nanoid": "^3.1.15"
|
"nanoid": "^3.1.15"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
|
||||||
"del-cli": "^3.0.1",
|
"del-cli": "^3.0.1",
|
||||||
|
"react-native-builder-bob": "^0.17.0",
|
||||||
"typescript": "^4.0.3"
|
"typescript": "^4.0.3"
|
||||||
},
|
},
|
||||||
"@react-native-community/bob": {
|
"react-native-builder-bob": {
|
||||||
"source": "src",
|
"source": "src",
|
||||||
"output": "lib",
|
"output": "lib",
|
||||||
"targets": [
|
"targets": [
|
||||||
|
|||||||
@@ -120,8 +120,12 @@ export default function DrawerRouter({
|
|||||||
|
|
||||||
type: 'drawer',
|
type: 'drawer',
|
||||||
|
|
||||||
getInitialState({ routeNames, routeParamList }) {
|
getInitialState({ routeNames, routeParamList, routeGetIdList }) {
|
||||||
let state = router.getInitialState({ routeNames, routeParamList });
|
let state = router.getInitialState({
|
||||||
|
routeNames,
|
||||||
|
routeParamList,
|
||||||
|
routeGetIdList,
|
||||||
|
});
|
||||||
|
|
||||||
if (openByDefault) {
|
if (openByDefault) {
|
||||||
state = openDrawer(state);
|
state = openDrawer(state);
|
||||||
@@ -135,7 +139,10 @@ export default function DrawerRouter({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
getRehydratedState(partialState, { routeNames, routeParamList }) {
|
getRehydratedState(
|
||||||
|
partialState,
|
||||||
|
{ routeNames, routeParamList, routeGetIdList }
|
||||||
|
) {
|
||||||
if (partialState.stale === false) {
|
if (partialState.stale === false) {
|
||||||
return partialState;
|
return partialState;
|
||||||
}
|
}
|
||||||
@@ -143,9 +150,10 @@ export default function DrawerRouter({
|
|||||||
let state = router.getRehydratedState(partialState, {
|
let state = router.getRehydratedState(partialState, {
|
||||||
routeNames,
|
routeNames,
|
||||||
routeParamList,
|
routeParamList,
|
||||||
|
routeGetIdList,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isDrawerOpen(partialState)) {
|
if (partialState.history ? isDrawerOpen(partialState) : openByDefault) {
|
||||||
state = openDrawer(state);
|
state = openDrawer(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -258,6 +258,9 @@ export default function StackRouter(options: StackRouterOptions) {
|
|||||||
|
|
||||||
case 'PUSH':
|
case 'PUSH':
|
||||||
if (state.routeNames.includes(action.payload.name)) {
|
if (state.routeNames.includes(action.payload.name)) {
|
||||||
|
const getId = options.routeGetIdList[action.payload.name];
|
||||||
|
const id = getId?.({ params: action.payload.params });
|
||||||
|
|
||||||
const route =
|
const route =
|
||||||
action.payload.name && action.payload.key
|
action.payload.name && action.payload.key
|
||||||
? state.routes.find(
|
? state.routes.find(
|
||||||
@@ -265,34 +268,34 @@ export default function StackRouter(options: StackRouterOptions) {
|
|||||||
route.name === action.payload.name &&
|
route.name === action.payload.name &&
|
||||||
route.key === action.payload.key
|
route.key === action.payload.key
|
||||||
)
|
)
|
||||||
|
: id
|
||||||
|
? state.routes.find(
|
||||||
|
(route) =>
|
||||||
|
route.name === action.payload.name &&
|
||||||
|
id === getId?.({ params: route.params })
|
||||||
|
)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
let routes: Route<string>[];
|
let routes: Route<string>[];
|
||||||
|
|
||||||
if (route) {
|
if (route) {
|
||||||
routes = state.routes.filter((r) => r.key !== route.key);
|
routes = state.routes.filter((r) => r.key !== route.key);
|
||||||
routes.push(
|
routes.push({
|
||||||
action.payload.params
|
...route,
|
||||||
? {
|
params:
|
||||||
...route,
|
action.payload.params !== undefined
|
||||||
params:
|
? {
|
||||||
action.payload.params !== undefined
|
...route.params,
|
||||||
? {
|
...action.payload.params,
|
||||||
...route.params,
|
}
|
||||||
...action.payload.params,
|
: route.params,
|
||||||
}
|
});
|
||||||
: route.params,
|
|
||||||
}
|
|
||||||
: route
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
routes = [
|
routes = [
|
||||||
...state.routes,
|
...state.routes,
|
||||||
{
|
{
|
||||||
key:
|
key:
|
||||||
action.payload.key === undefined
|
action.payload.key ?? `${action.payload.name}-${nanoid()}`,
|
||||||
? `${action.payload.name}-${nanoid()}`
|
|
||||||
: action.payload.key,
|
|
||||||
name: action.payload.name,
|
name: action.payload.name,
|
||||||
params:
|
params:
|
||||||
routeParamList[action.payload.name] !== undefined
|
routeParamList[action.payload.name] !== undefined
|
||||||
@@ -348,14 +351,31 @@ export default function StackRouter(options: StackRouterOptions) {
|
|||||||
|
|
||||||
case 'NAVIGATE':
|
case 'NAVIGATE':
|
||||||
if (
|
if (
|
||||||
action.payload.key ||
|
action.payload.name !== undefined &&
|
||||||
(action.payload.name &&
|
!state.routeNames.includes(action.payload.name)
|
||||||
state.routeNames.includes(action.payload.name))
|
|
||||||
) {
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.payload.key || action.payload.name) {
|
||||||
// If the route already exists, navigate to that
|
// If the route already exists, navigate to that
|
||||||
let index = -1;
|
let index = -1;
|
||||||
|
|
||||||
if (
|
const getId =
|
||||||
|
// `getId` and `key` can't be used together
|
||||||
|
action.payload.key === undefined &&
|
||||||
|
action.payload.name !== undefined
|
||||||
|
? options.routeGetIdList[action.payload.name]
|
||||||
|
: undefined;
|
||||||
|
const id = getId?.({ params: action.payload.params });
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
index = state.routes.findIndex(
|
||||||
|
(route) =>
|
||||||
|
route.name === action.payload.name &&
|
||||||
|
id === getId?.({ params: route.params })
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
(state.routes[state.index].name === action.payload.name &&
|
(state.routes[state.index].name === action.payload.name &&
|
||||||
action.payload.key === undefined) ||
|
action.payload.key === undefined) ||
|
||||||
state.routes[state.index].key === action.payload.key
|
state.routes[state.index].key === action.payload.key
|
||||||
@@ -383,18 +403,27 @@ export default function StackRouter(options: StackRouterOptions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (index === -1 && action.payload.name !== undefined) {
|
if (index === -1 && action.payload.name !== undefined) {
|
||||||
return router.getStateForAction(
|
const routes = [
|
||||||
state,
|
...state.routes,
|
||||||
{
|
{
|
||||||
type: 'PUSH',
|
key:
|
||||||
payload: {
|
action.payload.key ?? `${action.payload.name}-${nanoid()}`,
|
||||||
key: action.payload.key,
|
name: action.payload.name,
|
||||||
name: action.payload.name,
|
params:
|
||||||
params: action.payload.params,
|
routeParamList[action.payload.name] !== undefined
|
||||||
},
|
? {
|
||||||
|
...routeParamList[action.payload.name],
|
||||||
|
...action.payload.params,
|
||||||
|
}
|
||||||
|
: action.payload.params,
|
||||||
},
|
},
|
||||||
options
|
];
|
||||||
);
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
routes,
|
||||||
|
index: routes.length - 1,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const route = state.routes[index];
|
const route = state.routes[index];
|
||||||
|
|||||||
@@ -17,7 +17,12 @@ export type TabActionType = {
|
|||||||
target?: string;
|
target?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BackBehavior = 'initialRoute' | 'order' | 'history' | 'none';
|
export type BackBehavior =
|
||||||
|
| 'initialRoute'
|
||||||
|
| 'firstRoute'
|
||||||
|
| 'history'
|
||||||
|
| 'order'
|
||||||
|
| 'none';
|
||||||
|
|
||||||
export type TabRouterOptions = DefaultRouterOptions & {
|
export type TabRouterOptions = DefaultRouterOptions & {
|
||||||
backBehavior?: BackBehavior;
|
backBehavior?: BackBehavior;
|
||||||
@@ -74,13 +79,21 @@ const getRouteHistory = (
|
|||||||
history.unshift({ type: TYPE_ROUTE, key: routes[i - 1].key });
|
history.unshift({ type: TYPE_ROUTE, key: routes[i - 1].key });
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'firstRoute':
|
||||||
|
if (index !== 0) {
|
||||||
|
history.unshift({
|
||||||
|
type: TYPE_ROUTE,
|
||||||
|
key: routes[0].key,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'initialRoute':
|
case 'initialRoute':
|
||||||
initialRouteIndex = routes.findIndex(
|
initialRouteIndex = routes.findIndex(
|
||||||
(route) => route.name === initialRouteName
|
(route) => route.name === initialRouteName
|
||||||
);
|
);
|
||||||
initialRouteIndex = initialRouteIndex === -1 ? 0 : initialRouteIndex;
|
initialRouteIndex = initialRouteIndex === -1 ? 0 : initialRouteIndex;
|
||||||
|
|
||||||
if (initialRouteIndex !== index) {
|
if (index !== initialRouteIndex) {
|
||||||
history.unshift({
|
history.unshift({
|
||||||
type: TYPE_ROUTE,
|
type: TYPE_ROUTE,
|
||||||
key: routes[initialRouteIndex].key,
|
key: routes[initialRouteIndex].key,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
DrawerActions,
|
DrawerActions,
|
||||||
DrawerNavigationState,
|
DrawerNavigationState,
|
||||||
ParamListBase,
|
ParamListBase,
|
||||||
|
RouterConfigOptions,
|
||||||
} from '..';
|
} from '..';
|
||||||
|
|
||||||
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
|
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
|
||||||
@@ -18,6 +19,7 @@ it('gets initial state from route names and params with initialRouteName', () =>
|
|||||||
baz: { answer: 42 },
|
baz: { answer: 42 },
|
||||||
qux: { name: 'Jane' },
|
qux: { name: 'Jane' },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
})
|
})
|
||||||
).toEqual({
|
).toEqual({
|
||||||
index: 1,
|
index: 1,
|
||||||
@@ -44,6 +46,7 @@ it('gets initial state from route names and params without initialRouteName', ()
|
|||||||
baz: { answer: 42 },
|
baz: { answer: 42 },
|
||||||
qux: { name: 'Jane' },
|
qux: { name: 'Jane' },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
})
|
})
|
||||||
).toEqual({
|
).toEqual({
|
||||||
index: 0,
|
index: 0,
|
||||||
@@ -60,15 +63,43 @@ it('gets initial state from route names and params without initialRouteName', ()
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('gets initial state from route names and params with openByDefault', () => {
|
||||||
|
const router = DrawerRouter({ openByDefault: true });
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getInitialState({
|
||||||
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
|
routeParamList: {
|
||||||
|
baz: { answer: 42 },
|
||||||
|
qux: { name: 'Jane' },
|
||||||
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
|
})
|
||||||
|
).toEqual({
|
||||||
|
index: 0,
|
||||||
|
key: 'drawer-test',
|
||||||
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar-test', name: 'bar' },
|
||||||
|
{ key: 'baz-test', name: 'baz', params: { answer: 42 } },
|
||||||
|
{ key: 'qux-test', name: 'qux', params: { name: 'Jane' } },
|
||||||
|
],
|
||||||
|
history: [{ type: 'route', key: 'bar-test' }, { type: 'drawer' }],
|
||||||
|
stale: false,
|
||||||
|
type: 'drawer',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('gets rehydrated state from partial state', () => {
|
it('gets rehydrated state from partial state', () => {
|
||||||
const router = DrawerRouter({});
|
const router = DrawerRouter({});
|
||||||
|
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['bar', 'baz', 'qux'],
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
routeParamList: {
|
routeParamList: {
|
||||||
baz: { answer: 42 },
|
baz: { answer: 42 },
|
||||||
qux: { name: 'Jane' },
|
qux: { name: 'Jane' },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -218,15 +249,87 @@ it("doesn't rehydrate state if it's not stale", () => {
|
|||||||
router.getRehydratedState(state, {
|
router.getRehydratedState(state, {
|
||||||
routeNames: [],
|
routeNames: [],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
})
|
})
|
||||||
).toBe(state);
|
).toBe(state);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('respects openByDefault when rehydrating', () => {
|
||||||
|
const router = DrawerRouter({ openByDefault: true });
|
||||||
|
|
||||||
|
const options: RouterConfigOptions = {
|
||||||
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
|
routeParamList: {
|
||||||
|
baz: { answer: 42 },
|
||||||
|
qux: { name: 'Jane' },
|
||||||
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getRehydratedState(
|
||||||
|
{
|
||||||
|
index: 0,
|
||||||
|
key: 'drawer-test',
|
||||||
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar-test', name: 'bar' },
|
||||||
|
{ key: 'baz-test', name: 'baz', params: { answer: 42 } },
|
||||||
|
{ key: 'qux-test', name: 'qux', params: { name: 'Jane' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
index: 0,
|
||||||
|
key: 'drawer-test',
|
||||||
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar-test', name: 'bar' },
|
||||||
|
{ key: 'baz-test', name: 'baz', params: { answer: 42 } },
|
||||||
|
{ key: 'qux-test', name: 'qux', params: { name: 'Jane' } },
|
||||||
|
],
|
||||||
|
history: [{ key: 'bar-test', type: 'route' }, { type: 'drawer' }],
|
||||||
|
stale: false,
|
||||||
|
type: 'drawer',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getRehydratedState(
|
||||||
|
{
|
||||||
|
index: 0,
|
||||||
|
key: 'drawer-test',
|
||||||
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar-test', name: 'bar' },
|
||||||
|
{ key: 'baz-test', name: 'baz', params: { answer: 42 } },
|
||||||
|
{ key: 'qux-test', name: 'qux', params: { name: 'Jane' } },
|
||||||
|
],
|
||||||
|
history: [{ type: 'route', key: 'bar-test' }],
|
||||||
|
},
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
index: 0,
|
||||||
|
key: 'drawer-test',
|
||||||
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar-test', name: 'bar' },
|
||||||
|
{ key: 'baz-test', name: 'baz', params: { answer: 42 } },
|
||||||
|
{ key: 'qux-test', name: 'qux', params: { name: 'Jane' } },
|
||||||
|
],
|
||||||
|
history: [{ type: 'route', key: 'bar-test' }],
|
||||||
|
stale: false,
|
||||||
|
type: 'drawer',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('handles navigate action', () => {
|
it('handles navigate action', () => {
|
||||||
const router = DrawerRouter({});
|
const router = DrawerRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['baz', 'bar'],
|
routeNames: ['baz', 'bar'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -265,9 +368,10 @@ it('handles navigate action', () => {
|
|||||||
|
|
||||||
it('handles navigate action with open drawer', () => {
|
it('handles navigate action with open drawer', () => {
|
||||||
const router = DrawerRouter({});
|
const router = DrawerRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['baz', 'bar'],
|
routeNames: ['baz', 'bar'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -306,9 +410,10 @@ it('handles navigate action with open drawer', () => {
|
|||||||
|
|
||||||
it('handles open drawer action', () => {
|
it('handles open drawer action', () => {
|
||||||
const router = DrawerRouter({});
|
const router = DrawerRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['baz', 'bar'],
|
routeNames: ['baz', 'bar'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -361,9 +466,10 @@ it('handles open drawer action', () => {
|
|||||||
|
|
||||||
it('handles close drawer action', () => {
|
it('handles close drawer action', () => {
|
||||||
const router = DrawerRouter({});
|
const router = DrawerRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['baz', 'bar'],
|
routeNames: ['baz', 'bar'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -419,9 +525,10 @@ it('handles close drawer action', () => {
|
|||||||
|
|
||||||
it('handles toggle drawer action', () => {
|
it('handles toggle drawer action', () => {
|
||||||
const router = DrawerRouter({});
|
const router = DrawerRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['baz', 'bar'],
|
routeNames: ['baz', 'bar'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import { CommonActions, StackRouter, StackActions } from '..';
|
import {
|
||||||
|
CommonActions,
|
||||||
|
StackRouter,
|
||||||
|
StackActions,
|
||||||
|
RouterConfigOptions,
|
||||||
|
} from '..';
|
||||||
|
|
||||||
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
|
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
|
||||||
|
|
||||||
@@ -12,6 +17,7 @@ it('gets initial state from route names and params with initialRouteName', () =>
|
|||||||
baz: { answer: 42 },
|
baz: { answer: 42 },
|
||||||
qux: { name: 'Jane' },
|
qux: { name: 'Jane' },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
})
|
})
|
||||||
).toEqual({
|
).toEqual({
|
||||||
index: 0,
|
index: 0,
|
||||||
@@ -33,6 +39,7 @@ it('gets initial state from route names and params without initialRouteName', ()
|
|||||||
baz: { answer: 42 },
|
baz: { answer: 42 },
|
||||||
qux: { name: 'Jane' },
|
qux: { name: 'Jane' },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
})
|
})
|
||||||
).toEqual({
|
).toEqual({
|
||||||
index: 0,
|
index: 0,
|
||||||
@@ -47,12 +54,13 @@ it('gets initial state from route names and params without initialRouteName', ()
|
|||||||
it('gets rehydrated state from partial state', () => {
|
it('gets rehydrated state from partial state', () => {
|
||||||
const router = StackRouter({});
|
const router = StackRouter({});
|
||||||
|
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['bar', 'baz', 'qux'],
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
routeParamList: {
|
routeParamList: {
|
||||||
baz: { answer: 42 },
|
baz: { answer: 42 },
|
||||||
qux: { name: 'Jane' },
|
qux: { name: 'Jane' },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -136,6 +144,7 @@ it("doesn't rehydrate state if it's not stale", () => {
|
|||||||
router.getRehydratedState(state, {
|
router.getRehydratedState(state, {
|
||||||
routeNames: [],
|
routeNames: [],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
})
|
})
|
||||||
).toBe(state);
|
).toBe(state);
|
||||||
});
|
});
|
||||||
@@ -163,6 +172,7 @@ it('gets state on route names change', () => {
|
|||||||
qux: { name: 'John' },
|
qux: { name: 'John' },
|
||||||
fiz: { fruit: 'apple' },
|
fiz: { fruit: 'apple' },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).toEqual({
|
).toEqual({
|
||||||
@@ -195,6 +205,7 @@ it('gets state on route names change', () => {
|
|||||||
routeParamList: {
|
routeParamList: {
|
||||||
baz: { name: 'John' },
|
baz: { name: 'John' },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).toEqual({
|
).toEqual({
|
||||||
@@ -228,6 +239,7 @@ it('gets state on route names change with initialRouteName', () => {
|
|||||||
routeParamList: {
|
routeParamList: {
|
||||||
baz: { name: 'John' },
|
baz: { name: 'John' },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).toEqual({
|
).toEqual({
|
||||||
@@ -242,9 +254,10 @@ it('gets state on route names change with initialRouteName', () => {
|
|||||||
|
|
||||||
it('handles navigate action', () => {
|
it('handles navigate action', () => {
|
||||||
const router = StackRouter({});
|
const router = StackRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['baz', 'bar', 'qux'],
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -427,11 +440,230 @@ it('handles navigate action', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles go back action', () => {
|
it("doesn't navigate to nonexistent screen", () => {
|
||||||
const router = StackRouter({});
|
const router = StackRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['baz', 'bar', 'qux'],
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'baz', name: 'baz' },
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
CommonActions.navigate('far', { answer: 42 }),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toBe(null);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'baz', name: 'baz' },
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
CommonActions.navigate({
|
||||||
|
name: 'far',
|
||||||
|
key: 'test',
|
||||||
|
params: { answer: 42 },
|
||||||
|
}),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ensures unique ID for navigate', () => {
|
||||||
|
const router = StackRouter({});
|
||||||
|
const options: RouterConfigOptions = {
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routeParamList: {},
|
||||||
|
routeGetIdList: {
|
||||||
|
bar: ({ params }) => params?.foo,
|
||||||
|
qux: ({ params }) => params?.fux,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 0,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [{ key: 'bar', name: 'bar' }],
|
||||||
|
},
|
||||||
|
CommonActions.navigate('bar', { foo: 'a' }),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
CommonActions.navigate('bar', { foo: 'a' }),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
CommonActions.navigate('bar', { foo: 'b' }),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 2,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
|
||||||
|
{ key: 'bar-test', name: 'bar', params: { foo: 'b' } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
CommonActions.navigate({
|
||||||
|
key: 'test',
|
||||||
|
name: 'bar',
|
||||||
|
params: { foo: 'a' },
|
||||||
|
}),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 2,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
|
||||||
|
{ key: 'test', name: 'bar', params: { foo: 'a' } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ensure unique ID is only per route name for navigate', () => {
|
||||||
|
const router = StackRouter({});
|
||||||
|
const options: RouterConfigOptions = {
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routeParamList: {},
|
||||||
|
routeGetIdList: {
|
||||||
|
baz: ({ params }) => params?.foo,
|
||||||
|
bar: ({ params }) => params?.foo,
|
||||||
|
qux: ({ params }) => params?.test,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'qux-test', name: 'qux', params: { test: 'a' } },
|
||||||
|
{ key: 'baz-test', name: 'baz', params: { foo: 'a' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
CommonActions.navigate('bar', { foo: 'a' }),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 2,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'qux-test', name: 'qux', params: { test: 'a' } },
|
||||||
|
{ key: 'baz-test', name: 'baz', params: { foo: 'a' } },
|
||||||
|
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles go back action', () => {
|
||||||
|
const router = StackRouter({});
|
||||||
|
const options: RouterConfigOptions = {
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -477,9 +709,10 @@ it('handles go back action', () => {
|
|||||||
|
|
||||||
it('handles pop action', () => {
|
it('handles pop action', () => {
|
||||||
const router = StackRouter({});
|
const router = StackRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['baz', 'bar', 'qux'],
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -650,9 +883,10 @@ it('handles pop action', () => {
|
|||||||
|
|
||||||
it('handles pop to top action', () => {
|
it('handles pop to top action', () => {
|
||||||
const router = StackRouter({});
|
const router = StackRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['baz', 'bar', 'qux'],
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -684,9 +918,10 @@ it('handles pop to top action', () => {
|
|||||||
|
|
||||||
it('replaces focused screen with replace', () => {
|
it('replaces focused screen with replace', () => {
|
||||||
const router = StackRouter({});
|
const router = StackRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -722,9 +957,10 @@ it('replaces focused screen with replace', () => {
|
|||||||
|
|
||||||
it('replaces active screen with replace', () => {
|
it('replaces active screen with replace', () => {
|
||||||
const router = StackRouter({});
|
const router = StackRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -763,9 +999,10 @@ it('replaces active screen with replace', () => {
|
|||||||
|
|
||||||
it("doesn't handle replace if source key isn't present", () => {
|
it("doesn't handle replace if source key isn't present", () => {
|
||||||
const router = StackRouter({});
|
const router = StackRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -794,9 +1031,10 @@ it("doesn't handle replace if source key isn't present", () => {
|
|||||||
|
|
||||||
it("doesn't handle replace if screen to replace with isn't present", () => {
|
it("doesn't handle replace if screen to replace with isn't present", () => {
|
||||||
const router = StackRouter({});
|
const router = StackRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -824,11 +1062,12 @@ it("doesn't handle replace if screen to replace with isn't present", () => {
|
|||||||
|
|
||||||
it('handles push action', () => {
|
it('handles push action', () => {
|
||||||
const router = StackRouter({});
|
const router = StackRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['baz', 'bar', 'qux'],
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
routeParamList: {
|
routeParamList: {
|
||||||
baz: { foo: 21 },
|
baz: { foo: 21 },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -895,6 +1134,152 @@ it('handles push action', () => {
|
|||||||
options
|
options
|
||||||
)
|
)
|
||||||
).toBe(null);
|
).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("doesn't push nonexistent screen", () => {
|
||||||
|
const router = StackRouter({});
|
||||||
|
const options: RouterConfigOptions = {
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'baz', name: 'baz' },
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
StackActions.push('far', { answer: 42 }),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toBe(null);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'baz', name: 'baz' },
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'PUSH',
|
||||||
|
payload: {
|
||||||
|
name: 'far',
|
||||||
|
key: 'test',
|
||||||
|
params: { answer: 42 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ensures unique ID for push', () => {
|
||||||
|
const router = StackRouter({});
|
||||||
|
const options: RouterConfigOptions = {
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routeParamList: {},
|
||||||
|
routeGetIdList: {
|
||||||
|
bar: ({ params }) => params?.foo,
|
||||||
|
qux: ({ params }) => params?.fux,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 0,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [{ key: 'bar', name: 'bar' }],
|
||||||
|
},
|
||||||
|
StackActions.push('bar', { foo: 'a' }),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
StackActions.push('bar', { foo: 'a' }),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
StackActions.push('bar', { foo: 'b' }),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 2,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
|
||||||
|
{ key: 'bar-test', name: 'bar', params: { foo: 'b' } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
router.getStateForAction(
|
router.getStateForAction(
|
||||||
@@ -964,57 +1349,54 @@ it('handles push action', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('changes index on focus change', () => {
|
it('ensure unique ID is only per route name for push', () => {
|
||||||
const router = StackRouter({});
|
const router = StackRouter({});
|
||||||
|
const options: RouterConfigOptions = {
|
||||||
expect(
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
router.getStateForRouteFocus(
|
routeParamList: {},
|
||||||
{
|
routeGetIdList: {
|
||||||
index: 2,
|
baz: ({ params }) => params?.foo,
|
||||||
key: 'stack-test',
|
bar: ({ params }) => params?.foo,
|
||||||
routeNames: ['bar', 'baz', 'qux'],
|
qux: ({ params }) => params?.test,
|
||||||
routes: [
|
},
|
||||||
{ key: 'bar-0', name: 'bar' },
|
|
||||||
{ key: 'baz-0', name: 'baz' },
|
|
||||||
{ key: 'qux-0', name: 'qux' },
|
|
||||||
],
|
|
||||||
stale: false,
|
|
||||||
type: 'stack',
|
|
||||||
},
|
|
||||||
'baz-0'
|
|
||||||
)
|
|
||||||
).toEqual({
|
|
||||||
index: 1,
|
|
||||||
key: 'stack-test',
|
|
||||||
routeNames: ['bar', 'baz', 'qux'],
|
|
||||||
routes: [
|
|
||||||
{ key: 'bar-0', name: 'bar' },
|
|
||||||
{ key: 'baz-0', name: 'baz' },
|
|
||||||
],
|
|
||||||
stale: false,
|
|
||||||
type: 'stack',
|
|
||||||
});
|
|
||||||
|
|
||||||
const state = {
|
|
||||||
index: 0,
|
|
||||||
key: 'stack-test',
|
|
||||||
routeNames: ['bar', 'baz', 'qux'],
|
|
||||||
routes: [
|
|
||||||
{ key: 'bar-0', name: 'bar' },
|
|
||||||
{ key: 'baz-0', name: 'baz' },
|
|
||||||
],
|
|
||||||
stale: false as const,
|
|
||||||
type: 'stack' as const,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(router.getStateForRouteFocus(state, 'qux-0')).toEqual(state);
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'qux-test', name: 'qux', params: { test: 'a' } },
|
||||||
|
{ key: 'baz-test', name: 'baz', params: { foo: 'a' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
StackActions.push('bar', { foo: 'a' }),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 2,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'qux-test', name: 'qux', params: { test: 'a' } },
|
||||||
|
{ key: 'baz-test', name: 'baz', params: { foo: 'a' } },
|
||||||
|
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
|
||||||
|
],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('merges params on navigate to an existing screen', () => {
|
it('merges params on navigate to an existing screen', () => {
|
||||||
const router = StackRouter({});
|
const router = StackRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['baz', 'bar', 'qux'],
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -1077,11 +1459,12 @@ it('merges params on navigate to an existing screen', () => {
|
|||||||
|
|
||||||
it("doesn't merge params on navigate to an existing screen if merge: false", () => {
|
it("doesn't merge params on navigate to an existing screen if merge: false", () => {
|
||||||
const router = StackRouter({});
|
const router = StackRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['baz', 'bar', 'qux'],
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
routeParamList: {
|
routeParamList: {
|
||||||
baz: { foo: 12 },
|
baz: { foo: 12 },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
TabActions,
|
TabActions,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
ParamListBase,
|
ParamListBase,
|
||||||
|
RouterConfigOptions,
|
||||||
} from '..';
|
} from '..';
|
||||||
|
|
||||||
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
|
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
|
||||||
@@ -18,6 +19,7 @@ it('gets initial state from route names and params with initialRouteName', () =>
|
|||||||
baz: { answer: 42 },
|
baz: { answer: 42 },
|
||||||
qux: { name: 'Jane' },
|
qux: { name: 'Jane' },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
})
|
})
|
||||||
).toEqual({
|
).toEqual({
|
||||||
index: 1,
|
index: 1,
|
||||||
@@ -44,6 +46,7 @@ it('gets initial state from route names and params without initialRouteName', ()
|
|||||||
baz: { answer: 42 },
|
baz: { answer: 42 },
|
||||||
qux: { name: 'Jane' },
|
qux: { name: 'Jane' },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
})
|
})
|
||||||
).toEqual({
|
).toEqual({
|
||||||
index: 0,
|
index: 0,
|
||||||
@@ -63,12 +66,13 @@ it('gets initial state from route names and params without initialRouteName', ()
|
|||||||
it('gets rehydrated state from partial state', () => {
|
it('gets rehydrated state from partial state', () => {
|
||||||
const router = TabRouter({});
|
const router = TabRouter({});
|
||||||
|
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['bar', 'baz', 'qux'],
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
routeParamList: {
|
routeParamList: {
|
||||||
baz: { answer: 42 },
|
baz: { answer: 42 },
|
||||||
qux: { name: 'Jane' },
|
qux: { name: 'Jane' },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -241,6 +245,7 @@ it("doesn't rehydrate state if it's not stale", () => {
|
|||||||
router.getRehydratedState(state, {
|
router.getRehydratedState(state, {
|
||||||
routeNames: [],
|
routeNames: [],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
})
|
})
|
||||||
).toBe(state);
|
).toBe(state);
|
||||||
});
|
});
|
||||||
@@ -248,9 +253,10 @@ it("doesn't rehydrate state if it's not stale", () => {
|
|||||||
it('restores correct history on rehydrating with backBehavior: order', () => {
|
it('restores correct history on rehydrating with backBehavior: order', () => {
|
||||||
const router = TabRouter({ backBehavior: 'order' });
|
const router = TabRouter({ backBehavior: 'order' });
|
||||||
|
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -289,9 +295,10 @@ it('restores correct history on rehydrating with backBehavior: order', () => {
|
|||||||
it('restores correct history on rehydrating with backBehavior: history', () => {
|
it('restores correct history on rehydrating with backBehavior: history', () => {
|
||||||
const router = TabRouter({ backBehavior: 'history' });
|
const router = TabRouter({ backBehavior: 'history' });
|
||||||
|
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -323,12 +330,16 @@ it('restores correct history on rehydrating with backBehavior: history', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('restores correct history on rehydrating with backBehavior: initialRoute', () => {
|
it('restores correct history on rehydrating with backBehavior: firstRoute', () => {
|
||||||
const router = TabRouter({ backBehavior: 'initialRoute' });
|
const router = TabRouter({
|
||||||
|
backBehavior: 'firstRoute',
|
||||||
|
initialRouteName: 'bar',
|
||||||
|
});
|
||||||
|
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -363,12 +374,57 @@ it('restores correct history on rehydrating with backBehavior: initialRoute', ()
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('restores correct history on rehydrating with backBehavior: initialRoute', () => {
|
||||||
|
const router = TabRouter({
|
||||||
|
backBehavior: 'initialRoute',
|
||||||
|
initialRouteName: 'bar',
|
||||||
|
});
|
||||||
|
|
||||||
|
const options: RouterConfigOptions = {
|
||||||
|
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||||
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getRehydratedState(
|
||||||
|
{
|
||||||
|
index: 2,
|
||||||
|
routes: [
|
||||||
|
{ key: 'foo-0', name: 'foo' },
|
||||||
|
{ key: 'bar-0', name: 'bar' },
|
||||||
|
{ key: 'baz-0', name: 'baz' },
|
||||||
|
{ key: 'qux-0', name: 'qux' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
key: 'tab-test',
|
||||||
|
index: 2,
|
||||||
|
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'foo-0', name: 'foo' },
|
||||||
|
{ key: 'bar-0', name: 'bar' },
|
||||||
|
{ key: 'baz-0', name: 'baz' },
|
||||||
|
{ key: 'qux-0', name: 'qux' },
|
||||||
|
],
|
||||||
|
history: [
|
||||||
|
{ key: 'bar-0', type: 'route' },
|
||||||
|
{ key: 'baz-0', type: 'route' },
|
||||||
|
],
|
||||||
|
stale: false,
|
||||||
|
type: 'tab',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('restores correct history on rehydrating with backBehavior: none', () => {
|
it('restores correct history on rehydrating with backBehavior: none', () => {
|
||||||
const router = TabRouter({ backBehavior: 'none' });
|
const router = TabRouter({ backBehavior: 'none' });
|
||||||
|
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
routeNames: ['foo', 'bar', 'baz', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -424,6 +480,7 @@ it('gets state on route names change', () => {
|
|||||||
qux: { name: 'John' },
|
qux: { name: 'John' },
|
||||||
fiz: { fruit: 'apple' },
|
fiz: { fruit: 'apple' },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).toEqual({
|
).toEqual({
|
||||||
@@ -458,6 +515,7 @@ it('gets state on route names change', () => {
|
|||||||
{
|
{
|
||||||
routeNames: ['foo', 'fiz'],
|
routeNames: ['foo', 'fiz'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).toEqual({
|
).toEqual({
|
||||||
@@ -498,6 +556,7 @@ it('preserves focused route on route names change', () => {
|
|||||||
qux: { name: 'John' },
|
qux: { name: 'John' },
|
||||||
fiz: { fruit: 'apple' },
|
fiz: { fruit: 'apple' },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).toEqual({
|
).toEqual({
|
||||||
@@ -540,6 +599,7 @@ it('falls back to first route if route is removed on route names change', () =>
|
|||||||
qux: { name: 'John' },
|
qux: { name: 'John' },
|
||||||
fiz: { fruit: 'apple' },
|
fiz: { fruit: 'apple' },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).toEqual({
|
).toEqual({
|
||||||
@@ -559,9 +619,10 @@ it('falls back to first route if route is removed on route names change', () =>
|
|||||||
|
|
||||||
it('handles navigate action', () => {
|
it('handles navigate action', () => {
|
||||||
const router = TabRouter({});
|
const router = TabRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['bar', 'baz'],
|
routeNames: ['bar', 'baz'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -647,11 +708,63 @@ it('handles navigate action', () => {
|
|||||||
).toBe(null);
|
).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("doesn't navigate to nonexistent screen", () => {
|
||||||
|
const router = TabRouter({});
|
||||||
|
const options: RouterConfigOptions = {
|
||||||
|
routeNames: ['baz', 'bar'],
|
||||||
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'tab',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'baz', name: 'baz' },
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
],
|
||||||
|
history: [{ type: 'route', key: 'bar' }],
|
||||||
|
},
|
||||||
|
CommonActions.navigate('foo', { answer: 42 }),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toBe(null);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'tab',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'baz', name: 'baz' },
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
],
|
||||||
|
history: [{ type: 'route', key: 'bar' }],
|
||||||
|
},
|
||||||
|
CommonActions.navigate({
|
||||||
|
name: 'foo',
|
||||||
|
key: 'test',
|
||||||
|
params: { answer: 42 },
|
||||||
|
}),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
it('handles jump to action', () => {
|
it('handles jump to action', () => {
|
||||||
const router = TabRouter({});
|
const router = TabRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['bar', 'baz'],
|
routeNames: ['bar', 'baz'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -688,11 +801,40 @@ it('handles jump to action', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("doesn't jump to nonexistent screen", () => {
|
||||||
|
const router = TabRouter({});
|
||||||
|
const options: RouterConfigOptions = {
|
||||||
|
routeNames: ['baz', 'bar'],
|
||||||
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'tab',
|
||||||
|
key: 'root',
|
||||||
|
index: 1,
|
||||||
|
routeNames: ['baz', 'bar'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'baz', name: 'baz' },
|
||||||
|
{ key: 'bar', name: 'bar' },
|
||||||
|
],
|
||||||
|
history: [{ type: 'route', key: 'bar' }],
|
||||||
|
},
|
||||||
|
TabActions.jumpTo('foo', { answer: 42 }),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
it('handles back action with backBehavior: history', () => {
|
it('handles back action with backBehavior: history', () => {
|
||||||
const router = TabRouter({ backBehavior: 'history' });
|
const router = TabRouter({ backBehavior: 'history' });
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['bar', 'baz', 'qux'],
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
let state = router.getInitialState(options);
|
let state = router.getInitialState(options);
|
||||||
@@ -776,9 +918,10 @@ it('handles back action with backBehavior: history', () => {
|
|||||||
|
|
||||||
it('handles back action with backBehavior: order', () => {
|
it('handles back action with backBehavior: order', () => {
|
||||||
const router = TabRouter({ backBehavior: 'order' });
|
const router = TabRouter({ backBehavior: 'order' });
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['bar', 'baz', 'qux'],
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
let state = router.getInitialState(options);
|
let state = router.getInitialState(options);
|
||||||
@@ -847,9 +990,10 @@ it('handles back action with backBehavior: order', () => {
|
|||||||
|
|
||||||
it('handles back action with backBehavior: initialRoute', () => {
|
it('handles back action with backBehavior: initialRoute', () => {
|
||||||
const router = TabRouter({ backBehavior: 'initialRoute' });
|
const router = TabRouter({ backBehavior: 'initialRoute' });
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['bar', 'baz', 'qux'],
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
let state = router.getInitialState(options);
|
let state = router.getInitialState(options);
|
||||||
@@ -919,9 +1063,10 @@ it('handles back action with backBehavior: initialRoute and initialRouteName', (
|
|||||||
initialRouteName: 'baz',
|
initialRouteName: 'baz',
|
||||||
});
|
});
|
||||||
|
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['bar', 'baz', 'qux'],
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
let state = router.getInitialState(options);
|
let state = router.getInitialState(options);
|
||||||
@@ -987,9 +1132,10 @@ it('handles back action with backBehavior: initialRoute and initialRouteName', (
|
|||||||
|
|
||||||
it('handles back action with backBehavior: none', () => {
|
it('handles back action with backBehavior: none', () => {
|
||||||
const router = TabRouter({ backBehavior: 'none' });
|
const router = TabRouter({ backBehavior: 'none' });
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['bar', 'baz', 'qux'],
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
let state = router.getInitialState(options);
|
let state = router.getInitialState(options);
|
||||||
@@ -1007,9 +1153,10 @@ it('handles back action with backBehavior: none', () => {
|
|||||||
|
|
||||||
it('updates route key history on navigate and jump to', () => {
|
it('updates route key history on navigate and jump to', () => {
|
||||||
const router = TabRouter({ backBehavior: 'history' });
|
const router = TabRouter({ backBehavior: 'history' });
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['bar', 'baz', 'qux'],
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
let state: TabNavigationState<ParamListBase> = {
|
let state: TabNavigationState<ParamListBase> = {
|
||||||
@@ -1110,9 +1257,10 @@ it('updates route key history on focus change', () => {
|
|||||||
|
|
||||||
it('merges params on navigate to an existing screen', () => {
|
it('merges params on navigate to an existing screen', () => {
|
||||||
const router = TabRouter({});
|
const router = TabRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['baz', 'bar', 'qux'],
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -1193,6 +1341,7 @@ it("doesn't merge params on navigate to an existing screen if merge: false", ()
|
|||||||
routeParamList: {
|
routeParamList: {
|
||||||
qux: { color: 'indigo' },
|
qux: { color: 'indigo' },
|
||||||
},
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -1235,6 +1384,17 @@ it("doesn't merge params on navigate to an existing screen if merge: false", ()
|
|||||||
{ type: 'route', key: 'bar' },
|
{ type: 'route', key: 'bar' },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('merges params on navigate to an existing screen if merge: true', () => {
|
||||||
|
const router = TabRouter({});
|
||||||
|
const options: RouterConfigOptions = {
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routeParamList: {
|
||||||
|
qux: { color: 'indigo' },
|
||||||
|
},
|
||||||
|
routeGetIdList: {},
|
||||||
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
router.getStateForAction(
|
router.getStateForAction(
|
||||||
@@ -1323,9 +1483,10 @@ it("doesn't merge params on navigate to an existing screen if merge: false", ()
|
|||||||
|
|
||||||
it('merges params on jump to an existing screen', () => {
|
it('merges params on jump to an existing screen', () => {
|
||||||
const router = TabRouter({});
|
const router = TabRouter({});
|
||||||
const options = {
|
const options: RouterConfigOptions = {
|
||||||
routeNames: ['baz', 'bar', 'qux'],
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
|
routeGetIdList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
|||||||
@@ -132,6 +132,11 @@ export type RouterFactory<
|
|||||||
export type RouterConfigOptions = {
|
export type RouterConfigOptions = {
|
||||||
routeNames: string[];
|
routeNames: string[];
|
||||||
routeParamList: ParamListBase;
|
routeParamList: ParamListBase;
|
||||||
|
routeGetIdList: Record<
|
||||||
|
string,
|
||||||
|
| ((options: { params?: Record<string, any> }) => string | undefined)
|
||||||
|
| undefined
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Router<
|
export type Router<
|
||||||
|
|||||||
@@ -3,6 +3,90 @@
|
|||||||
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.14.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.14.3...@react-navigation/stack@5.14.4) (2021-04-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* check for screens enabled in ScreenContainer ([493956e](https://github.com/react-navigation/react-navigation/commit/493956ef717a03bd8c3533a2949434e83718c5e4))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.14.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.14.2...@react-navigation/stack@5.14.3) (2021-02-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/stack
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.14.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.14.1...@react-navigation/stack@5.14.2) (2021-01-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix transparent modal on web ([38d6808](https://github.com/react-navigation/react-navigation/commit/38d680833e31e62736da19f79328aec553ced814))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.14.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.14.0...@react-navigation/stack@5.14.1) (2021-01-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/stack
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.14.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.13.0...@react-navigation/stack@5.14.0) (2021-01-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add pressColor and pressOpacity props to drawerItem ([#8834](https://github.com/react-navigation/react-navigation/issues/8834)) ([bae4019](https://github.com/react-navigation/react-navigation/commit/bae4019995062c682f0213c121b7927ab8006c1e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.13.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.12.8...@react-navigation/stack@5.13.0) (2021-01-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* enable detachInactiveScreens by default on web for better a11y ([dd87fa4](https://github.com/react-navigation/react-navigation/commit/dd87fa49a43ad8db105a62418243339e4150fadf))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* export TransitionPreset for custom TransitionPresets ([#9173](https://github.com/react-navigation/react-navigation/issues/9173)) ([9633c4d](https://github.com/react-navigation/react-navigation/commit/9633c4d35fe2f9cb4f37a7629872e436a4528238))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.12.8](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.12.7...@react-navigation/stack@5.12.8) (2020-11-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* force dismiss keyboard if there was no gesture ([3e069b7](https://github.com/react-navigation/react-navigation/commit/3e069b718d60f5381957f2d3838ee04ee9384779)), closes [#9078](https://github.com/react-navigation/react-navigation/issues/9078)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.12.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.12.6...@react-navigation/stack@5.12.7) (2020-11-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/stack
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [5.12.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.12.5...@react-navigation/stack@5.12.6) (2020-11-10)
|
## [5.12.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.12.5...@react-navigation/stack@5.12.6) (2020-11-10)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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.12.6",
|
"version": "5.14.4",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -44,9 +44,8 @@
|
|||||||
"react-native-iphone-x-helper": "^1.3.0"
|
"react-native-iphone-x-helper": "^1.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
|
||||||
"@react-native-community/masked-view": "^0.1.10",
|
"@react-native-community/masked-view": "^0.1.10",
|
||||||
"@react-navigation/native": "^5.8.9",
|
"@react-navigation/native": "^5.9.4",
|
||||||
"@testing-library/react-native": "^7.1.0",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/color": "^3.0.1",
|
"@types/color": "^3.0.1",
|
||||||
"@types/react": "^16.9.53",
|
"@types/react": "^16.9.53",
|
||||||
@@ -54,6 +53,7 @@
|
|||||||
"del-cli": "^3.0.1",
|
"del-cli": "^3.0.1",
|
||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
"react-native": "~0.63.2",
|
"react-native": "~0.63.2",
|
||||||
|
"react-native-builder-bob": "^0.17.0",
|
||||||
"react-native-gesture-handler": "~1.7.0",
|
"react-native-gesture-handler": "~1.7.0",
|
||||||
"react-native-safe-area-context": "3.1.4",
|
"react-native-safe-area-context": "3.1.4",
|
||||||
"react-native-screens": "~2.10.1",
|
"react-native-screens": "~2.10.1",
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
"react-native-safe-area-context": ">= 0.6.0",
|
"react-native-safe-area-context": ">= 0.6.0",
|
||||||
"react-native-screens": ">= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0"
|
"react-native-screens": ">= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0"
|
||||||
},
|
},
|
||||||
"@react-native-community/bob": {
|
"react-native-builder-bob": {
|
||||||
"source": "src",
|
"source": "src",
|
||||||
"output": "lib",
|
"output": "lib",
|
||||||
"targets": [
|
"targets": [
|
||||||
|
|||||||
@@ -61,4 +61,5 @@ export type {
|
|||||||
StackHeaderInterpolatedStyle,
|
StackHeaderInterpolatedStyle,
|
||||||
StackHeaderInterpolationProps,
|
StackHeaderInterpolationProps,
|
||||||
StackHeaderStyleInterpolator,
|
StackHeaderStyleInterpolator,
|
||||||
|
TransitionPreset,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { BaseButton } from 'react-native-gesture-handler';
|
|||||||
const AnimatedBaseButton = Animated.createAnimatedComponent(BaseButton);
|
const AnimatedBaseButton = Animated.createAnimatedComponent(BaseButton);
|
||||||
|
|
||||||
type Props = React.ComponentProps<typeof BaseButton> & {
|
type Props = React.ComponentProps<typeof BaseButton> & {
|
||||||
activeOpacity: number;
|
pressOpacity: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const useNativeDriver = Platform.OS !== 'web';
|
const useNativeDriver = Platform.OS !== 'web';
|
||||||
@@ -27,7 +27,7 @@ export default class BorderlessButton extends React.Component<Props> {
|
|||||||
overshootClamping: true,
|
overshootClamping: true,
|
||||||
restDisplacementThreshold: 0.01,
|
restDisplacementThreshold: 0.01,
|
||||||
restSpeedThreshold: 0.01,
|
restSpeedThreshold: 0.01,
|
||||||
toValue: active ? this.props.activeOpacity : 1,
|
toValue: active ? this.props.pressOpacity : 1,
|
||||||
useNativeDriver,
|
useNativeDriver,
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { TextInput, Platform, Keyboard } from 'react-native';
|
import { TextInput, Keyboard, HostComponent } from 'react-native';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
children: (props: {
|
children: (props: {
|
||||||
onPageChangeStart: () => void;
|
onPageChangeStart: () => void;
|
||||||
onPageChangeConfirm: () => void;
|
onPageChangeConfirm: (force: boolean) => void;
|
||||||
onPageChangeCancel: () => void;
|
onPageChangeCancel: () => void;
|
||||||
}) => React.ReactNode;
|
}) => React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type InputRef = React.ElementRef<HostComponent<unknown>> | undefined;
|
||||||
|
|
||||||
export default class KeyboardManager extends React.Component<Props> {
|
export default class KeyboardManager extends React.Component<Props> {
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.clearKeyboardTimeout();
|
this.clearKeyboardTimeout();
|
||||||
@@ -17,7 +19,7 @@ export default class KeyboardManager extends React.Component<Props> {
|
|||||||
|
|
||||||
// Numeric id of the previously focused text input
|
// Numeric id of the previously focused text input
|
||||||
// When a gesture didn't change the tab, we can restore the focused input with this
|
// When a gesture didn't change the tab, we can restore the focused input with this
|
||||||
private previouslyFocusedTextInput: any | null = null;
|
private previouslyFocusedTextInput: InputRef = undefined;
|
||||||
private startTimestamp: number = 0;
|
private startTimestamp: number = 0;
|
||||||
private keyboardTimeout: any;
|
private keyboardTimeout: any;
|
||||||
|
|
||||||
@@ -35,7 +37,8 @@ export default class KeyboardManager extends React.Component<Props> {
|
|||||||
|
|
||||||
this.clearKeyboardTimeout();
|
this.clearKeyboardTimeout();
|
||||||
|
|
||||||
const input: any = TextInput.State.currentlyFocusedInput
|
// @ts-expect-error: blurTextInput accepts both number and ref, but types say only ref
|
||||||
|
const input: InputRef = TextInput.State.currentlyFocusedInput
|
||||||
? TextInput.State.currentlyFocusedInput()
|
? TextInput.State.currentlyFocusedInput()
|
||||||
: TextInput.State.currentlyFocusedField();
|
: TextInput.State.currentlyFocusedField();
|
||||||
|
|
||||||
@@ -49,25 +52,30 @@ export default class KeyboardManager extends React.Component<Props> {
|
|||||||
this.startTimestamp = Date.now();
|
this.startTimestamp = Date.now();
|
||||||
};
|
};
|
||||||
|
|
||||||
private handlePageChangeConfirm = () => {
|
private handlePageChangeConfirm = (force: boolean) => {
|
||||||
if (!this.props.enabled) {
|
if (!this.props.enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clearKeyboardTimeout();
|
this.clearKeyboardTimeout();
|
||||||
|
|
||||||
const input = this.previouslyFocusedTextInput;
|
if (force) {
|
||||||
|
// Always dismiss input, even if we don't have a ref to it
|
||||||
|
// We might not have the ref if onPageChangeStart was never called
|
||||||
|
// This can happen if page change was not from a gesture
|
||||||
|
Keyboard.dismiss();
|
||||||
|
} else {
|
||||||
|
const input = this.previouslyFocusedTextInput;
|
||||||
|
|
||||||
if (input) {
|
if (input) {
|
||||||
if (Platform.OS === 'android') {
|
// Dismiss the keyboard only if an input was a focused before
|
||||||
Keyboard.dismiss();
|
// This makes sure we don't dismiss input on going back and focusing an input
|
||||||
} else {
|
|
||||||
TextInput.State.blurTextInput(input);
|
TextInput.State.blurTextInput(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup the ID on successful page change
|
// Cleanup the ID on successful page change
|
||||||
this.previouslyFocusedTextInput = null;
|
this.previouslyFocusedTextInput = undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
private handlePageChangeCancel = () => {
|
private handlePageChangeCancel = () => {
|
||||||
@@ -91,11 +99,11 @@ export default class KeyboardManager extends React.Component<Props> {
|
|||||||
if (Date.now() - this.startTimestamp < 100) {
|
if (Date.now() - this.startTimestamp < 100) {
|
||||||
this.keyboardTimeout = setTimeout(() => {
|
this.keyboardTimeout = setTimeout(() => {
|
||||||
TextInput.State.focusTextInput(input);
|
TextInput.State.focusTextInput(input);
|
||||||
this.previouslyFocusedTextInput = null;
|
this.previouslyFocusedTextInput = undefined;
|
||||||
}, 100);
|
}, 100);
|
||||||
} else {
|
} else {
|
||||||
TextInput.State.focusTextInput(input);
|
TextInput.State.focusTextInput(input);
|
||||||
this.previouslyFocusedTextInput = null;
|
this.previouslyFocusedTextInput = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ type Props = ViewProps & {
|
|||||||
gestureDirection: GestureDirection;
|
gestureDirection: GestureDirection;
|
||||||
onOpen: () => void;
|
onOpen: () => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onTransitionStart?: (props: { closing: boolean }) => void;
|
onTransition?: (props: { closing: boolean; gesture: boolean }) => void;
|
||||||
onGestureBegin?: () => void;
|
onGestureBegin?: () => void;
|
||||||
onGestureCanceled?: () => void;
|
onGestureCanceled?: () => void;
|
||||||
onGestureEnd?: () => void;
|
onGestureEnd?: () => void;
|
||||||
@@ -178,7 +178,7 @@ export default class Card extends React.Component<Props> {
|
|||||||
transitionSpec,
|
transitionSpec,
|
||||||
onOpen,
|
onOpen,
|
||||||
onClose,
|
onClose,
|
||||||
onTransitionStart,
|
onTransition,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const toValue = this.getAnimateToValue({
|
const toValue = this.getAnimateToValue({
|
||||||
@@ -198,7 +198,7 @@ export default class Card extends React.Component<Props> {
|
|||||||
|
|
||||||
clearTimeout(this.pendingGestureCallback);
|
clearTimeout(this.pendingGestureCallback);
|
||||||
|
|
||||||
onTransitionStart?.({ closing });
|
onTransition?.({ closing, gesture: velocity !== undefined });
|
||||||
animation(gesture, {
|
animation(gesture, {
|
||||||
...spec.config,
|
...spec.config,
|
||||||
velocity,
|
velocity,
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ type Props = TransitionPreset & {
|
|||||||
) => void;
|
) => void;
|
||||||
onTransitionEnd?: (props: { route: Route<string> }, closing: boolean) => void;
|
onTransitionEnd?: (props: { route: Route<string> }, closing: boolean) => void;
|
||||||
onPageChangeStart?: () => void;
|
onPageChangeStart?: () => void;
|
||||||
onPageChangeConfirm?: () => void;
|
onPageChangeConfirm?: (force: boolean) => void;
|
||||||
onPageChangeCancel?: () => void;
|
onPageChangeCancel?: () => void;
|
||||||
onGestureStart?: (props: { route: Route<string> }) => void;
|
onGestureStart?: (props: { route: Route<string> }) => void;
|
||||||
onGestureEnd?: (props: { route: Route<string> }) => void;
|
onGestureEnd?: (props: { route: Route<string> }) => void;
|
||||||
@@ -116,42 +116,58 @@ function CardContainer({
|
|||||||
scene,
|
scene,
|
||||||
transitionSpec,
|
transitionSpec,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
React.useEffect(() => {
|
|
||||||
onPageChangeConfirm?.();
|
|
||||||
}, [active, onPageChangeConfirm]);
|
|
||||||
|
|
||||||
const handleOpen = () => {
|
const handleOpen = () => {
|
||||||
onTransitionEnd?.({ route: scene.route }, false);
|
const { route } = scene;
|
||||||
onOpenRoute({ route: scene.route });
|
|
||||||
|
onTransitionEnd?.({ route }, false);
|
||||||
|
onOpenRoute({ route });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
onTransitionEnd?.({ route: scene.route }, true);
|
const { route } = scene;
|
||||||
onCloseRoute({ route: scene.route });
|
|
||||||
|
onTransitionEnd?.({ route }, true);
|
||||||
|
onCloseRoute({ route });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGestureBegin = () => {
|
const handleGestureBegin = () => {
|
||||||
|
const { route } = scene;
|
||||||
|
|
||||||
onPageChangeStart?.();
|
onPageChangeStart?.();
|
||||||
onGestureStart?.({ route: scene.route });
|
onGestureStart?.({ route });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGestureCanceled = () => {
|
const handleGestureCanceled = () => {
|
||||||
|
const { route } = scene;
|
||||||
|
|
||||||
onPageChangeCancel?.();
|
onPageChangeCancel?.();
|
||||||
onGestureCancel?.({ route: scene.route });
|
onGestureCancel?.({ route });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGestureEnd = () => {
|
const handleGestureEnd = () => {
|
||||||
onGestureEnd?.({ route: scene.route });
|
const { route } = scene;
|
||||||
|
|
||||||
|
onGestureEnd?.({ route });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTransitionStart = ({ closing }: { closing: boolean }) => {
|
const handleTransition = ({
|
||||||
if (active && closing) {
|
closing,
|
||||||
onPageChangeConfirm?.();
|
gesture,
|
||||||
|
}: {
|
||||||
|
closing: boolean;
|
||||||
|
gesture: boolean;
|
||||||
|
}) => {
|
||||||
|
const { route } = scene;
|
||||||
|
|
||||||
|
if (!gesture) {
|
||||||
|
onPageChangeConfirm?.(true);
|
||||||
|
} else if (active && closing) {
|
||||||
|
onPageChangeConfirm?.(false);
|
||||||
} else {
|
} else {
|
||||||
onPageChangeCancel?.();
|
onPageChangeCancel?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
onTransitionStart?.({ route: scene.route }, closing);
|
onTransitionStart?.({ route }, closing);
|
||||||
};
|
};
|
||||||
|
|
||||||
const insets = {
|
const insets = {
|
||||||
@@ -201,7 +217,7 @@ function CardContainer({
|
|||||||
overlay={cardOverlay}
|
overlay={cardOverlay}
|
||||||
overlayEnabled={cardOverlayEnabled}
|
overlayEnabled={cardOverlayEnabled}
|
||||||
shadowEnabled={cardShadowEnabled}
|
shadowEnabled={cardShadowEnabled}
|
||||||
onTransitionStart={handleTransitionStart}
|
onTransition={handleTransition}
|
||||||
onGestureBegin={handleGestureBegin}
|
onGestureBegin={handleGestureBegin}
|
||||||
onGestureCanceled={handleGestureCanceled}
|
onGestureCanceled={handleGestureCanceled}
|
||||||
onGestureEnd={handleGestureEnd}
|
onGestureEnd={handleGestureEnd}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
StyleSheet,
|
StyleSheet,
|
||||||
LayoutChangeEvent,
|
LayoutChangeEvent,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
|
Platform,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import type { EdgeInsets } from 'react-native-safe-area-context';
|
import type { EdgeInsets } from 'react-native-safe-area-context';
|
||||||
import type {
|
import type {
|
||||||
@@ -11,6 +12,7 @@ import type {
|
|||||||
Route,
|
Route,
|
||||||
StackNavigationState,
|
StackNavigationState,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
import { screensEnabled } from 'react-native-screens';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
MaybeScreenContainer,
|
MaybeScreenContainer,
|
||||||
@@ -65,7 +67,7 @@ type Props = {
|
|||||||
) => void;
|
) => void;
|
||||||
onTransitionEnd: (props: { route: Route<string> }, closing: boolean) => void;
|
onTransitionEnd: (props: { route: Route<string> }, closing: boolean) => void;
|
||||||
onPageChangeStart?: () => void;
|
onPageChangeStart?: () => void;
|
||||||
onPageChangeConfirm?: () => void;
|
onPageChangeConfirm?: (force: boolean) => void;
|
||||||
onPageChangeCancel?: () => void;
|
onPageChangeCancel?: () => void;
|
||||||
onGestureStart?: (props: { route: Route<string> }) => void;
|
onGestureStart?: (props: { route: Route<string> }) => void;
|
||||||
onGestureEnd?: (props: { route: Route<string> }) => void;
|
onGestureEnd?: (props: { route: Route<string> }) => void;
|
||||||
@@ -398,7 +400,7 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
onGestureCancel,
|
onGestureCancel,
|
||||||
// Enable on new versions of `react-native-screens`
|
// Enable on new versions of `react-native-screens`
|
||||||
// On older versions of `react-native-screens`, there's an issue with screens not being responsive to user interaction.
|
// On older versions of `react-native-screens`, there's an issue with screens not being responsive to user interaction.
|
||||||
detachInactiveScreens = shouldUseActivityState,
|
detachInactiveScreens = Platform.OS === 'web' || shouldUseActivityState,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { scenes, layout, gestures, headerHeights } = this.state;
|
const { scenes, layout, gestures, headerHeights } = this.state;
|
||||||
@@ -488,11 +490,13 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
|
const isScreensEnabled = screensEnabled?.() && detachInactiveScreens;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{isFloatHeaderAbsolute ? null : floatingHeader}
|
{isFloatHeaderAbsolute ? null : floatingHeader}
|
||||||
<MaybeScreenContainer
|
<MaybeScreenContainer
|
||||||
enabled={detachInactiveScreens}
|
enabled={isScreensEnabled}
|
||||||
style={styles.container}
|
style={styles.container}
|
||||||
onLayout={this.handleLayout}
|
onLayout={this.handleLayout}
|
||||||
>
|
>
|
||||||
@@ -507,7 +511,7 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
// For the old implementation, it stays the same it was
|
// For the old implementation, it stays the same it was
|
||||||
let isScreenActive: Animated.AnimatedInterpolation | 2 | 1 | 0 = 1;
|
let isScreenActive: Animated.AnimatedInterpolation | 2 | 1 | 0 = 1;
|
||||||
|
|
||||||
if (shouldUseActivityState) {
|
if (shouldUseActivityState || Platform.OS === 'web') {
|
||||||
if (index < self.length - activeScreensLimit - 1) {
|
if (index < self.length - activeScreensLimit - 1) {
|
||||||
// screen should be inactive because it is too deep in the stack
|
// screen should be inactive because it is too deep in the stack
|
||||||
isScreenActive = STATE_INACTIVE;
|
isScreenActive = STATE_INACTIVE;
|
||||||
@@ -612,7 +616,7 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
<MaybeScreen
|
<MaybeScreen
|
||||||
key={route.key}
|
key={route.key}
|
||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
enabled={detachInactiveScreens}
|
enabled={isScreensEnabled}
|
||||||
active={isScreenActive}
|
active={isScreenActive}
|
||||||
pointerEvents="box-none"
|
pointerEvents="box-none"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { BaseButton } from 'react-native-gesture-handler';
|
|||||||
const AnimatedBaseButton = Animated.createAnimatedComponent(BaseButton);
|
const AnimatedBaseButton = Animated.createAnimatedComponent(BaseButton);
|
||||||
|
|
||||||
type Props = React.ComponentProps<typeof BaseButton> & {
|
type Props = React.ComponentProps<typeof BaseButton> & {
|
||||||
activeOpacity: number;
|
pressOpacity: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const useNativeDriver = Platform.OS !== 'web';
|
const useNativeDriver = Platform.OS !== 'web';
|
||||||
@@ -27,7 +27,7 @@ export default class TouchableItem extends React.Component<Props> {
|
|||||||
overshootClamping: true,
|
overshootClamping: true,
|
||||||
restDisplacementThreshold: 0.01,
|
restDisplacementThreshold: 0.01,
|
||||||
restSpeedThreshold: 0.01,
|
restSpeedThreshold: 0.01,
|
||||||
toValue: active ? this.props.activeOpacity : 1,
|
toValue: active ? this.props.pressOpacity : 1,
|
||||||
useNativeDriver,
|
useNativeDriver,
|
||||||
}).start();
|
}).start();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user