Compare commits

..

32 Commits

Author SHA1 Message Date
Satyajit Sahoo
49f658fbc0 chore: publish
- @react-navigation/bottom-tabs@5.2.6
 - @react-navigation/compat@5.1.8
 - @react-navigation/core@5.3.3
 - @react-navigation/drawer@5.4.1
 - @react-navigation/material-bottom-tabs@5.1.8
 - @react-navigation/material-top-tabs@5.1.8
 - @react-navigation/native@5.1.5
 - @react-navigation/routers@5.3.0
 - @react-navigation/stack@5.2.10
2020-04-08 12:17:31 +02:00
Satyajit Sahoo
cb2f157a56 fix: don't hide content from accessibility with permanent drawer
closes #7976
2020-04-08 12:17:09 +02:00
Juang, Yi-Lin
c4acdaa703 docs: fix typo (#7865) 2020-04-08 11:35:45 +02:00
Satyajit Sahoo
f1a8bceba5 fix: make color of shadow element same as card color in stack 2020-04-07 23:34:55 +02:00
Ruben Grimm
44081172d4 fix: use 1 as default in compatibility pop action 2020-04-07 23:33:38 +02:00
Satyajit Sahoo
de5d985f3b chore: upgrade depenendecies 2020-04-07 15:44:58 +02:00
Satyajit Sahoo
b71de6cc79 fix: mark type exports for all packages 2020-04-07 11:22:47 +02:00
raajnadar
303f0b78a5 fix: separate normal exports and type exports 2020-04-07 11:17:06 +02:00
Satyajit Sahoo
ce3994c82c fix: switch order of focus and blur events. closes #7963 2020-04-07 11:07:16 +02:00
Satyajit Sahoo
ba1f405129 feat: make replace bubble up 2020-04-07 00:02:54 +02:00
Satyajit Sahoo
d4fd906915 fix: workaround warning about setState in another component in render 2020-04-06 23:58:25 +02:00
Vinícius Fraga Modesto
b7fa90bf8d docs: fixes typo (#7923)
This PR fixes a typo in activeBackgroundColor's description
2020-03-31 17:56:26 +02:00
Satyajit Sahoo
9556aa9eff chore: publish
- @react-navigation/bottom-tabs@5.2.5
 - @react-navigation/compat@5.1.7
 - @react-navigation/core@5.3.2
 - @react-navigation/drawer@5.4.0
 - @react-navigation/material-bottom-tabs@5.1.7
 - @react-navigation/material-top-tabs@5.1.7
 - @react-navigation/native@5.1.4
 - @react-navigation/routers@5.2.1
 - @react-navigation/stack@5.2.9
2020-03-30 22:22:25 +02:00
Satyajit Sahoo
9a8fea8f2c fix: when comparing changed routes, only check keys 2020-03-30 22:20:16 +02:00
Satyajit Sahoo
9973db86f0 chore: use non-secure nanoid to be able to run in RN 2020-03-30 22:04:53 +02:00
max
8432e5ab25 fix: dismiss keyboard on screen change for android 2020-03-30 21:50:52 +02:00
Satyajit Sahoo
9bb5cfded3 refactor: replace shortid with nanoid. closes #7858 2020-03-30 21:42:58 +02:00
Satyajit Sahoo
4ac40b5c5d chore: update typescript and babel 2020-03-30 21:42:58 +02:00
Wojciech Lewicki
cd47915861 fix: handle no path property and undefined query params (#7911) 2020-03-30 17:11:33 +02:00
Andrius Janauskas
d649fbc669 fix: finish stack animation on CANCELLED event (#7898)
fixes #7897
2020-03-30 14:36:04 +02:00
Satyajit Sahoo
105da6ab2f fix: disable only swipe gesture on safari 2020-03-30 13:56:30 +02:00
Rajendran Nadar
ac7f972e92 feat: add swipeEnabled option to disable swipe gesture in drawer (#7834) 2020-03-30 13:51:32 +02:00
Satyajit Sahoo
babb5027f9 chore: publish
- @react-navigation/stack@5.2.8
2020-03-27 15:01:32 +01:00
Satyajit Sahoo
78d7a66b2b chore: remove detox dep coz we don't use it 2020-03-27 14:54:54 +01:00
osdnk
a248c453ba chore: publish
- @react-navigation/stack@5.2.7
2020-03-26 17:07:40 +01:00
Wojciech Stanisz
e097df880a fix: add pointerEvents=box-none to overlay View (#7871) 2020-03-26 13:38:30 +01:00
Satyajit Sahoo
856449b200 chore: publish
- @react-navigation/bottom-tabs@5.2.4
 - @react-navigation/compat@5.1.6
 - @react-navigation/core@5.3.1
 - @react-navigation/drawer@5.3.4
 - @react-navigation/material-bottom-tabs@5.1.6
 - @react-navigation/material-top-tabs@5.1.6
 - @react-navigation/native@5.1.3
 - @react-navigation/stack@5.2.6
2020-03-23 17:07:43 +01:00
Steven Bell
d94e43c3c8 fix: add info about android launchMode in useLinking error 2020-03-23 16:39:18 +01:00
Satyajit Sahoo
3096de6286 fix: only call listeners for focused screen for global events 2020-03-23 13:43:43 +01:00
Satyajit Sahoo
1c001424b5 fix: don't emit events for screens that don't exist anymore 2020-03-23 13:03:33 +01:00
Satyajit Sahoo
0f2368965c chore: publish
- @react-navigation/stack@5.2.5
2020-03-23 11:42:01 +01:00
Satyajit Sahoo
61f16d3f25 fix: fix swipe gestures requiring a lot of velocity to dismiss 2020-03-23 11:40:37 +01:00
62 changed files with 3440 additions and 3470 deletions

View File

@@ -7,7 +7,7 @@
"slug": "react-navigation-example",
"description": "Demo app to showcase various functionality of React Navigation",
"privacy": "public",
"sdkVersion": "36.0.0",
"sdkVersion": "37.0.0",
"platforms": [
"ios",
"android",

View File

@@ -1,4 +1,4 @@
/* eslint-disable jest/no-jasmine-globals, import/no-commonjs */
/* eslint-disable import/no-commonjs */
const detox = require('detox');
const config = require('../../package.json').detox;

View File

@@ -12,31 +12,30 @@
},
"dependencies": {
"@expo/vector-icons": "^10.0.0",
"@react-native-community/masked-view": "0.1.7",
"@types/react-native-restart": "^0.0.0",
"@react-native-community/masked-view": "^0.1.7",
"color": "^3.1.2",
"expo": "^36.0.2",
"expo-asset": "~8.0.0",
"expo-blur": "^8.0.0",
"expo": "^37.0.0",
"expo-asset": "~8.1.3",
"expo-blur": "~8.1.0",
"react": "~16.9.0",
"react-dom": "~16.9.0",
"react-native": "~0.61.5",
"react-native-gesture-handler": "^1.6.0",
"react-native-paper": "^3.6.0",
"react-native-paper": "^3.7.0",
"react-native-reanimated": "^1.7.0",
"react-native-restart": "^0.0.14",
"react-native-safe-area-context": "^0.7.3",
"react-native-screens": "^2.3.0",
"react-native-tab-view": "2.13.0",
"react-native-unimodules": "^0.7.0",
"react-native-tab-view": "2.14.0",
"react-native-unimodules": "~0.8.1",
"react-native-web": "^0.11.7"
},
"devDependencies": {
"@expo/webpack-config": "^0.11.7",
"@expo/webpack-config": "^0.11.19",
"@types/react": "^16.9.23",
"@types/react-native": "^0.61.22",
"babel-preset-expo": "^8.0.0",
"expo-cli": "^3.13.8",
"typescript": "^3.7.5"
"@types/react-native": "^0.60.22",
"babel-preset-expo": "^8.1.0",
"expo-cli": "^3.17.18",
"typescript": "^3.8.3"
}
}

View File

@@ -116,7 +116,7 @@ const THEME_PERSISTENCE_KEY = 'THEME_TYPE';
Asset.loadAsync(StackAssets);
export default function App() {
const containerRef = React.useRef<NavigationContainerRef>();
const containerRef = React.useRef<NavigationContainerRef>(null);
// To test deep linking on, run the following in the Terminal:
// Android: adb shell am start -a android.intent.action.VIEW -d "exp://127.0.0.1:19000/--/simple-stack"

View File

@@ -18,7 +18,7 @@
"author": "Satyajit Sahoo <satyajit.happy@gmail.com> (https://github.com/satya164/), Michał Osadnik <micosa97@gmail.com> (https://github.com/osdnk/)",
"scripts": {
"lint": "eslint --ext '.js,.ts,.tsx' .",
"typescript": "tsc --noEmit",
"typescript": "tsc --noEmit --composite false",
"test": "jest",
"prerelease": "lerna run clean",
"release": "lerna publish",
@@ -26,25 +26,25 @@
},
"devDependencies": {
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-proposal-optional-chaining": "^7.8.3",
"@babel/preset-env": "^7.8.7",
"@babel/preset-flow": "^7.8.3",
"@babel/preset-react": "^7.8.3",
"@babel/preset-typescript": "^7.8.3",
"@babel/runtime": "^7.8.7",
"@babel/plugin-proposal-optional-chaining": "^7.9.0",
"@babel/preset-env": "^7.9.0",
"@babel/preset-flow": "^7.9.0",
"@babel/preset-react": "^7.9.4",
"@babel/preset-typescript": "^7.9.0",
"@babel/runtime": "^7.9.2",
"@commitlint/config-conventional": "^8.3.4",
"@types/jest": "^25.1.4",
"@types/jest": "^25.2.1",
"babel-jest": "^25.2.6",
"codecov": "^3.6.5",
"commitlint": "^8.3.5",
"core-js": "^3.6.4",
"detox": "^16.0.0",
"eslint": "^6.8.0",
"eslint-config-satya164": "^3.1.5",
"eslint-config-satya164": "^3.1.6",
"husky": "^4.2.3",
"jest": "^25.1.0",
"jest": "^25.2.7",
"lerna": "^3.20.2",
"prettier": "^2.0.1",
"typescript": "^3.7.5"
"prettier": "^2.0.4",
"typescript": "^3.8.3"
},
"resolutions": {
"react": "~16.9.0",

View File

@@ -3,6 +3,33 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.2.6](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.5...@react-navigation/bottom-tabs@5.2.6) (2020-04-08)
### Bug Fixes
* mark type exports for all packages ([b71de6c](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/b71de6cc799143f1d0e8a0cfcc34f0a2381f9840))
## [5.2.5](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.4...@react-navigation/bottom-tabs@5.2.5) (2020-03-30)
**Note:** Version bump only for package @react-navigation/bottom-tabs
## [5.2.4](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.3...@react-navigation/bottom-tabs@5.2.4) (2020-03-23)
**Note:** Version bump only for package @react-navigation/bottom-tabs
## [5.2.3](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.2...@react-navigation/bottom-tabs@5.2.3) (2020-03-22)
**Note:** Version bump only for package @react-navigation/bottom-tabs

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/bottom-tabs",
"description": "Bottom tab navigator following iOS design guidelines",
"version": "5.2.3",
"version": "5.2.6",
"keywords": [
"react-native-component",
"react-component",
@@ -35,7 +35,7 @@
},
"devDependencies": {
"@react-native-community/bob": "^0.10.0",
"@react-navigation/native": "^5.1.2",
"@react-navigation/native": "^5.1.5",
"@types/color": "^3.0.1",
"@types/react": "^16.9.23",
"@types/react-native": "^0.61.22",
@@ -44,7 +44,7 @@
"react-native": "~0.61.5",
"react-native-safe-area-context": "^0.7.3",
"react-native-screens": "^2.3.0",
"typescript": "^3.7.5"
"typescript": "^3.8.3"
},
"peerDependencies": {
"@react-navigation/native": "^5.0.5",

View File

@@ -12,7 +12,7 @@ export { default as BottomTabBar } from './views/BottomTabBar';
/**
* Types
*/
export {
export type {
BottomTabNavigationOptions,
BottomTabNavigationProp,
BottomTabBarProps,

View File

@@ -138,7 +138,7 @@ export type BottomTabBarOptions = {
*/
inactiveTintColor?: string;
/**
* Background olor for the active tab.
* Background color for the active tab.
*/
activeBackgroundColor?: string;
/**

View File

@@ -3,6 +3,33 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.1.8](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.7...@react-navigation/compat@5.1.8) (2020-04-08)
### Bug Fixes
* use 1 as default in compatibility pop action ([4408117](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/commit/44081172d440c713ad3543a2d5e1e18ebc8f72a4))
## [5.1.7](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.6...@react-navigation/compat@5.1.7) (2020-03-30)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.6](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.5...@react-navigation/compat@5.1.6) (2020-03-23)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.5](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.4...@react-navigation/compat@5.1.5) (2020-03-22)
**Note:** Version bump only for package @react-navigation/compat

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/compat",
"description": "Compatibility layer to write navigator definitions in static configuration format",
"version": "5.1.5",
"version": "5.1.8",
"license": "MIT",
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/compat",
"bugs": {
@@ -26,10 +26,10 @@
},
"devDependencies": {
"@react-native-community/bob": "^0.10.0",
"@react-navigation/native": "^5.1.2",
"@react-navigation/native": "^5.1.5",
"@types/react": "^16.9.23",
"react": "~16.9.0",
"typescript": "^3.7.5"
"typescript": "^3.8.3"
},
"peerDependencies": {
"@react-navigation/native": "^5.0.5",

View File

@@ -57,7 +57,7 @@ export function push(routeName: string, params?: object, action?: never) {
});
}
export function pop(n: number) {
export function pop(n: number = 1) {
return StackActions.pop(typeof n === 'number' ? { n } : n);
}

View File

@@ -3,6 +3,41 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.3.3](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.3.2...@react-navigation/core@5.3.3) (2020-04-08)
### Bug Fixes
* switch order of focus and blur events. closes [#7963](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/7963) ([ce3994c](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/ce3994c82c28669d5742017eb7627e9adf996933))
* workaround warning about setState in another component in render ([d4fd906](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/d4fd906915cc20d6fb21508384c05a540d8644d8))
## [5.3.2](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.3.1...@react-navigation/core@5.3.2) (2020-03-30)
### Bug Fixes
* handle no path property and undefined query params ([#7911](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/7911)) ([cd47915](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/cd47915861a56cd7eaa9ac79f5139cde56ca95a7))
## [5.3.1](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.3.0...@react-navigation/core@5.3.1) (2020-03-23)
### Bug Fixes
* don't emit events for screens that don't exist anymore ([1c00142](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/1c001424b595b40f9db9343096c833f75353b099))
* only call listeners for focused screen for global events ([3096de6](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/3096de62868a7ed9ed65e529c8ddfa001b9be486))
# [5.3.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.2.3...@react-navigation/core@5.3.0) (2020-03-22)

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/core",
"description": "Core utilities for building navigators",
"version": "5.3.0",
"version": "5.3.3",
"keywords": [
"react",
"react-native",
@@ -29,24 +29,23 @@
"clean": "del lib"
},
"dependencies": {
"@react-navigation/routers": "^5.2.0",
"@react-navigation/routers": "^5.3.0",
"escape-string-regexp": "^2.0.0",
"query-string": "^6.11.1",
"nanoid": "^3.0.2",
"query-string": "^6.12.0",
"react-is": "^16.13.0",
"shortid": "^2.2.15",
"use-subscription": "^1.4.0"
},
"devDependencies": {
"@react-native-community/bob": "^0.10.0",
"@types/react": "^16.9.23",
"@types/react-is": "^16.7.1",
"@types/shortid": "^0.0.29",
"@types/use-subscription": "^1.0.0",
"del-cli": "^3.0.0",
"react": "~16.9.0",
"react-native-testing-library": "^1.12.0",
"react-test-renderer": "~16.9.0",
"typescript": "^3.7.5"
"react-test-renderer": "~16.13.1",
"typescript": "^3.8.3"
},
"peerDependencies": {
"react": "*"

View File

@@ -9,14 +9,15 @@ import {
} from '@react-navigation/routers';
import EnsureSingleNavigator from './EnsureSingleNavigator';
import NavigationBuilderContext from './NavigationBuilderContext';
import { ScheduleUpdateContext } from './useScheduleUpdate';
import useFocusedListeners from './useFocusedListeners';
import useDevTools from './useDevTools';
import useStateGetters from './useStateGetters';
import useEventEmitter from './useEventEmitter';
import useSyncState from './useSyncState';
import isSerializable from './isSerializable';
import { NavigationContainerRef, NavigationContainerProps } from './types';
import useEventEmitter from './useEventEmitter';
import useSyncState from './useSyncState';
type State = NavigationState | PartialState<NavigationState> | undefined;
@@ -102,7 +103,7 @@ const BaseNavigationContainer = React.forwardRef(
independent,
children,
}: NavigationContainerProps,
ref: React.Ref<NavigationContainerRef>
ref?: React.Ref<NavigationContainerRef>
) {
const parent = React.useContext(NavigationStateContext);
@@ -112,7 +113,13 @@ const BaseNavigationContainer = React.forwardRef(
);
}
const [state, getState, setState] = useSyncState<State>(() =>
const [
state,
getState,
setState,
scheduleUpdate,
flushUpdates,
] = useSyncState<State>(() =>
getPartialState(initialState == null ? undefined : initialState)
);
@@ -218,6 +225,11 @@ const BaseNavigationContainer = React.forwardRef(
[addFocusedListener, trackAction, addStateGetter]
);
const scheduleContext = React.useMemo(
() => ({ scheduleUpdate, flushUpdates }),
[scheduleUpdate, flushUpdates]
);
const context = React.useMemo(
() => ({
state,
@@ -263,11 +275,13 @@ const BaseNavigationContainer = React.forwardRef(
}, [onStateChange, trackState, getRootState, emitter, state]);
return (
<NavigationBuilderContext.Provider value={builderContext}>
<NavigationStateContext.Provider value={context}>
<EnsureSingleNavigator>{children}</EnsureSingleNavigator>
</NavigationStateContext.Provider>
</NavigationBuilderContext.Provider>
<ScheduleUpdateContext.Provider value={scheduleContext}>
<NavigationBuilderContext.Provider value={builderContext}>
<NavigationStateContext.Provider value={context}>
<EnsureSingleNavigator>{children}</EnsureSingleNavigator>
</NavigationStateContext.Provider>
</NavigationBuilderContext.Provider>
</ScheduleUpdateContext.Provider>
);
}
);

View File

@@ -521,3 +521,209 @@ it('returns "/" for empty path', () => {
expect(getPathFromState(state, config)).toBe('/');
});
it('parses no path specified', () => {
const path = '/Foo/bar';
const config = {
Foo: {
screens: {
Foe: {},
},
},
Bar: 'bar',
};
const state = {
routes: [
{
name: 'Foo',
state: {
routes: [{ name: 'Bar' }],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});
it('parses no path specified in nested config', () => {
const path = '/Foo/Foe/bar';
const config = {
Foo: {
path: 'foo',
screens: {
Foe: {},
},
},
Bar: 'bar',
};
const state = {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Foe',
state: {
routes: [{ name: 'Bar' }],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});
it('strips undefined query params', () => {
const path = '/bar/sweet/apple/foo/bis/jane?count=10&valid=true';
const config = {
Foo: {
path: 'foo',
screens: {
Foe: {
path: 'foe',
},
},
},
Bar: 'bar/:type/:fruit',
Baz: {
path: 'baz',
screens: {
Bos: 'bos',
Bis: {
path: 'bis/:author',
stringify: {
author: (author: string) =>
author.replace(/^\w/, (c) => c.toLowerCase()),
},
parse: {
author: (author: string) =>
author.replace(/^\w/, (c) => c.toUpperCase()),
count: Number,
valid: Boolean,
},
},
},
},
};
const state = {
routes: [
{
name: 'Bar',
params: { fruit: 'apple', type: 'sweet' },
state: {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Baz',
state: {
routes: [
{
name: 'Bis',
params: {
author: 'Jane',
count: 10,
answer: undefined,
valid: true,
},
},
],
},
},
],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});
it('handles stripping all query params', () => {
const path = '/bar/sweet/apple/foo/bis/jane';
const config = {
Foo: {
path: 'foo',
screens: {
Foe: {
path: 'foe',
},
},
},
Bar: 'bar/:type/:fruit',
Baz: {
path: 'baz',
screens: {
Bos: 'bos',
Bis: {
path: 'bis/:author',
stringify: {
author: (author: string) =>
author.replace(/^\w/, (c) => c.toLowerCase()),
},
parse: {
author: (author: string) =>
author.replace(/^\w/, (c) => c.toUpperCase()),
count: Number,
valid: Boolean,
},
},
},
},
};
const state = {
routes: [
{
name: 'Bar',
params: { fruit: 'apple', type: 'sweet' },
state: {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Baz',
state: {
routes: [
{
name: 'Bis',
params: {
author: 'Jane',
count: undefined,
answer: undefined,
valid: undefined,
},
},
],
},
},
],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});

View File

@@ -626,7 +626,7 @@ it('updates route params with setParams applied to parent', () => {
});
});
it('handles change in route names', () => {
it('handles change in route names', async () => {
const TestNavigator = (props: any): any => {
useNavigationBuilder(MockRouter, props);
return null;
@@ -635,7 +635,7 @@ it('handles change in route names', () => {
const onStateChange = jest.fn();
const root = render(
<BaseNavigationContainer onStateChange={onStateChange}>
<BaseNavigationContainer>
<TestNavigator initialRouteName="bar">
<Screen name="foo" component={jest.fn()} />
<Screen name="bar" component={jest.fn()} />

View File

@@ -262,8 +262,10 @@ it('sets initial options with setOptions', () => {
};
const TestScreen = ({ navigation }: any): any => {
navigation.setOptions({
title: 'Hello world',
React.useEffect(() => {
navigation.setOptions({
title: 'Hello world',
});
});
return 'Test screen';
@@ -315,12 +317,12 @@ it('updates options with setOptions', () => {
};
const TestScreen = ({ navigation }: any): any => {
navigation.setOptions({
title: 'Hello world',
description: 'Something here',
});
React.useEffect(() => {
navigation.setOptions({
title: 'Hello world',
description: 'Something here',
});
const timer = setTimeout(() =>
navigation.setOptions({
title: 'Hello again',

View File

@@ -97,6 +97,69 @@ it('fires focus and blur events in root navigator', () => {
expect(fourthBlurCallback).toBeCalledTimes(0);
});
it('fires focus event after blur', () => {
const TestNavigator = React.forwardRef((props: any, ref: any): any => {
const { state, navigation, descriptors } = useNavigationBuilder(
MockRouter,
props
);
React.useImperativeHandle(ref, () => navigation, [navigation]);
return state.routes.map((route) => descriptors[route.key].render());
});
const callback = jest.fn();
const Test = ({ route, navigation }: any) => {
React.useEffect(
() =>
navigation.addListener('focus', () => callback(route.name, 'focus')),
[navigation, route.name]
);
React.useEffect(
() => navigation.addListener('blur', () => callback(route.name, 'blur')),
[navigation, route.name]
);
return null;
};
const navigation = React.createRef<any>();
const element = (
<BaseNavigationContainer>
<TestNavigator ref={navigation}>
<Screen name="first" component={Test} />
<Screen name="second" component={Test} />
</TestNavigator>
</BaseNavigationContainer>
);
render(element);
expect(callback.mock.calls).toEqual([['first', 'focus']]);
act(() => navigation.current.navigate('second'));
expect(callback.mock.calls).toEqual([
['first', 'focus'],
['first', 'blur'],
['second', 'focus'],
]);
act(() => navigation.current.navigate('first'));
expect(callback.mock.calls).toEqual([
['first', 'focus'],
['first', 'blur'],
['second', 'focus'],
['second', 'blur'],
['first', 'focus'],
]);
});
it('fires focus and blur events in nested navigator', () => {
const TestNavigator = React.forwardRef((props: any, ref: any): any => {
const { state, navigation, descriptors } = useNavigationBuilder(
@@ -565,12 +628,10 @@ it('fires custom events added with listeners prop', () => {
});
expect(firstCallback.mock.calls[0][0].target).toBe(undefined);
expect(secondCallback.mock.calls[0][0].target).toBe(undefined);
expect(thirdCallback.mock.calls[1][0].target).toBe(undefined);
expect(firstCallback).toBeCalledTimes(1);
expect(secondCallback).toBeCalledTimes(1);
expect(thirdCallback).toBeCalledTimes(2);
expect(secondCallback).toBeCalledTimes(0);
expect(thirdCallback).toBeCalledTimes(1);
});
it("doesn't call same listener multiple times with listeners", () => {
@@ -624,6 +685,91 @@ it("doesn't call same listener multiple times with listeners", () => {
expect(callback).toBeCalledTimes(1);
});
it('fires listeners when callback is provided for listeners prop', () => {
const eventName = 'someSuperCoolEvent';
const TestNavigator = React.forwardRef((props: any, ref: any): any => {
const { state, navigation } = useNavigationBuilder(MockRouter, props);
React.useImperativeHandle(ref, () => ({ navigation, state }), [
navigation,
state,
]);
return null;
});
const firstCallback = jest.fn();
const secondCallback = jest.fn();
const thirdCallback = jest.fn();
const ref = React.createRef<any>();
const element = (
<BaseNavigationContainer>
<TestNavigator ref={ref}>
<Screen
name="first"
listeners={({ route, navigation }) => ({
someSuperCoolEvent: (e) => firstCallback(e, route, navigation),
})}
component={jest.fn()}
/>
<Screen
name="second"
listeners={({ route, navigation }) => ({
someSuperCoolEvent: (e) => secondCallback(e, route, navigation),
})}
component={jest.fn()}
/>
<Screen
name="third"
listeners={({ route, navigation }) => ({
someSuperCoolEvent: (e) => thirdCallback(e, route, navigation),
})}
component={jest.fn()}
/>
</TestNavigator>
</BaseNavigationContainer>
);
render(element);
expect(firstCallback).toBeCalledTimes(0);
expect(secondCallback).toBeCalledTimes(0);
expect(thirdCallback).toBeCalledTimes(0);
const target =
ref.current.state.routes[ref.current.state.routes.length - 1].key;
act(() => {
ref.current.navigation.emit({
type: eventName,
target,
data: 42,
});
});
expect(firstCallback).toBeCalledTimes(0);
expect(secondCallback).toBeCalledTimes(0);
expect(thirdCallback).toBeCalledTimes(1);
expect(thirdCallback.mock.calls[0][0].type).toBe('someSuperCoolEvent');
expect(thirdCallback.mock.calls[0][0].data).toBe(42);
expect(thirdCallback.mock.calls[0][0].target).toBe(target);
expect(thirdCallback.mock.calls[0][0].defaultPrevented).toBe(undefined);
expect(thirdCallback.mock.calls[0][0].preventDefault).toBe(undefined);
act(() => {
ref.current.navigation.emit({ type: eventName });
});
expect(firstCallback.mock.calls[0][0].target).toBe(undefined);
expect(firstCallback).toBeCalledTimes(1);
expect(secondCallback).toBeCalledTimes(0);
expect(thirdCallback).toBeCalledTimes(1);
});
it('has option to prevent default', () => {
expect.assertions(5);

View File

@@ -64,6 +64,8 @@ export default function getPathFromState(
};
let currentOptions = options;
let pattern = route.name;
// we keep all the route names that appeared during going deeper in config in case the pattern is resolved to undefined
let nestedRouteNames = '';
while (route.name in currentOptions) {
if (typeof currentOptions[route.name] === 'string') {
@@ -77,11 +79,13 @@ export default function getPathFromState(
}).screens
) {
pattern = (currentOptions[route.name] as { path: string }).path;
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
break;
} else {
// if it is the end of state, we return pattern
if (route.state === undefined) {
pattern = (currentOptions[route.name] as { path: string }).path;
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
break;
} else {
index =
@@ -92,11 +96,13 @@ export default function getPathFromState(
}).screens;
// if there is config for next route name, we go deeper
if (nextRoute.name in deeperConfig) {
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
route = nextRoute as Route<string> & { state?: State };
currentOptions = deeperConfig;
} else {
// if not, there is no sense in going deeper in config
pattern = (currentOptions[route.name] as { path: string }).path;
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
break;
}
}
@@ -104,6 +110,11 @@ export default function getPathFromState(
}
}
if (pattern === undefined) {
// cut the first `/`
pattern = nestedRouteNames.substring(1);
}
// we don't add empty path strings to path
if (pattern !== '') {
const config =
@@ -147,6 +158,12 @@ export default function getPathFromState(
if (route.state) {
path += '/';
} else if (params) {
for (let param in params) {
if (params[param] === 'undefined') {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete params[param];
}
}
const query = queryString.stringify(params);
if (query) {

View File

@@ -410,24 +410,19 @@ export type RouteConfig<
}
);
export type NavigationContainerRef =
| (NavigationHelpers<ParamListBase> &
EventConsumer<{ state: { data: { state: NavigationState } } }> & {
/**
* Reset the navigation state of the root navigator to the provided state.
*
* @param state Navigation state object.
*/
resetRoot(
state?: PartialState<NavigationState> | NavigationState
): void;
/**
* Get the rehydrated navigation state of the navigation tree.
*/
getRootState(): NavigationState;
})
| undefined
| null;
export type NavigationContainerRef = NavigationHelpers<ParamListBase> &
EventConsumer<{ state: { data: { state: NavigationState } } }> & {
/**
* Reset the navigation state of the root navigator to the provided state.
*
* @param state Navigation state object.
*/
resetRoot(state?: PartialState<NavigationState> | NavigationState): void;
/**
* Get the rehydrated navigation state of the navigation tree.
*/
getRootState(): NavigationState;
};
export type TypedNavigator<
ParamList extends ParamListBase,

View File

@@ -48,7 +48,7 @@ export default function useFocusEvents({ state, emitter }: Options) {
emitter.emit({ type: 'focus', target: currentFocusedKey });
}
// We should only dispatch events when the focused key changed and navigator is focused
// We should only emit events when the focused key changed and navigator is focused
// When navigator is not focused, screens inside shouldn't receive focused status either
if (
lastFocusedKey === currentFocusedKey ||
@@ -62,7 +62,7 @@ export default function useFocusEvents({ state, emitter }: Options) {
return;
}
emitter.emit({ type: 'focus', target: currentFocusedKey });
emitter.emit({ type: 'blur', target: lastFocusedKey });
emitter.emit({ type: 'focus', target: currentFocusedKey });
}, [currentFocusedKey, emitter, navigation]);
}

View File

@@ -32,6 +32,7 @@ import {
} from './types';
import useStateGetters from './useStateGetters';
import useOnGetState from './useOnGetState';
import useScheduleUpdate from './useScheduleUpdate';
// This is to make TypeScript compiler happy
// eslint-disable-next-line babel/no-unused-expressions
@@ -308,16 +309,13 @@ export default function useNavigationBuilder<
}
if (
previousRouteRef.current &&
route &&
route.params &&
typeof route.params.screen === 'string' &&
route.params !== previousRouteRef.current.params
typeof route?.params?.screen === 'string' &&
route.params !== previousRouteRef.current?.params
) {
// If the route was updated with new name and/or params, we should navigate there
// The update should be limited to current navigator only, so we call the router manually
const updatedState = router.getStateForAction(
state,
nextState,
CommonActions.navigate(route.params.screen, route.params.params),
{
routeNames,
@@ -331,17 +329,17 @@ export default function useNavigationBuilder<
routeNames,
routeParamList,
})
: state;
: nextState;
}
const shouldUpdate = state !== nextState;
React.useEffect(() => {
useScheduleUpdate(() => {
if (shouldUpdate) {
// If the state needs to be updated, we'll schedule an update with React
// If the state needs to be updated, we'll schedule an update
setState(nextState);
}
}, [nextState, setState, shouldUpdate]);
});
// The up-to-date state will come in next render, but we don't need to wait for it
// We can't use the outdated state since the screens have changed, which will cause error due to mismatched config
@@ -371,36 +369,40 @@ export default function useNavigationBuilder<
const emitter = useEventEmitter((e) => {
let routeNames = [];
let target: Route<string> | undefined;
let route: Route<string> | undefined;
if (e.target) {
target = state.routes.find((route) => route.key === e.target);
route = state.routes.find((route) => route.key === e.target);
if (target?.name) {
routeNames.push(target.name);
if (route?.name) {
routeNames.push(route.name);
}
} else {
routeNames.push(...Object.keys(screens));
route = state.routes[state.index];
routeNames.push(
...Object.keys(screens).filter((name) => route?.name === name)
);
}
if (route == null) {
return;
}
const navigation = descriptors[route.key].navigation;
const listeners = ([] as (((e: any) => void) | undefined)[])
.concat(
...routeNames.map((name) => {
const { listeners } = screens[name];
const map =
typeof listeners === 'function'
? listeners({ route: route as any, navigation })
: listeners;
return listeners
? Object.keys(listeners)
return map
? Object.keys(map)
.filter((type) => type === e.type)
.map((type) => {
if (typeof listeners === 'function') {
const route = target ?? state.routes[state.index];
const navigation = descriptors[route.key].navigation;
return listeners({ route: route as any, navigation })[type];
}
return listeners[type];
})
.map((type) => map?.[type])
: undefined;
})
)

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import shortid from 'shortid';
import { nanoid } from 'nanoid/non-secure';
import { SingleNavigatorContext } from './EnsureSingleNavigator';
/**
@@ -7,7 +7,7 @@ import { SingleNavigatorContext } from './EnsureSingleNavigator';
* This is used to prevent multiple navigators under a single container or screen.
*/
export default function useRegisterNavigator() {
const [key] = React.useState(() => shortid());
const [key] = React.useState(() => nanoid());
const container = React.useContext(SingleNavigatorContext);
if (container === undefined) {

View File

@@ -0,0 +1,32 @@
import * as React from 'react';
const MISSING_CONTEXT_ERROR = "Couldn't find a schedule context.";
export const ScheduleUpdateContext = React.createContext<{
scheduleUpdate: (callback: () => void) => void;
flushUpdates: () => void;
}>({
scheduleUpdate() {
throw new Error(MISSING_CONTEXT_ERROR);
},
flushUpdates() {
throw new Error(MISSING_CONTEXT_ERROR);
},
});
/**
* When screen config changes, we want to update the navigator in the same update phase.
* However, navigation state is in the root component and React won't let us update it from a child.
* This is a workaround for that, the scheduled update is stored in the ref without actually calling setState.
* It lets all subsequent updates access the latest state so it stays correct.
* Then we call setState during after the component updates.
*/
export default function useScheduleUpdate(callback: () => void) {
const { scheduleUpdate, flushUpdates } = React.useContext(
ScheduleUpdateContext
);
scheduleUpdate(callback);
React.useEffect(flushUpdates);
}

View File

@@ -2,8 +2,12 @@ import * as React from 'react';
const UNINTIALIZED_STATE = {};
/**
* This is definitely not compatible with concurrent mode, but we don't have a solution for sync state yet.
*/
export default function useSyncState<T>(initialState?: (() => T) | T) {
const stateRef = React.useRef<T>(UNINTIALIZED_STATE as any);
const isSchedulingRef = React.useRef(false);
if (stateRef.current === UNINTIALIZED_STATE) {
stateRef.current =
@@ -11,7 +15,7 @@ export default function useSyncState<T>(initialState?: (() => T) | T) {
typeof initialState === 'function' ? initialState() : initialState;
}
const [state, setTrackingState] = React.useState(stateRef.current);
const [trackingState, setTrackingState] = React.useState(stateRef.current);
const getState = React.useCallback(() => stateRef.current, []);
@@ -21,8 +25,35 @@ export default function useSyncState<T>(initialState?: (() => T) | T) {
}
stateRef.current = state;
setTrackingState(state);
if (!isSchedulingRef.current) {
setTrackingState(state);
}
}, []);
return [state, getState, setState] as const;
const scheduleUpdate = React.useCallback((callback: () => void) => {
isSchedulingRef.current = true;
try {
callback();
} finally {
isSchedulingRef.current = false;
}
}, []);
const flushUpdates = React.useCallback(() => {
// Make sure that the tracking state is up-to-date.
// We call it unconditionally, but React should skip the update if state is unchanged.
setTrackingState(stateRef.current);
}, []);
// If we're rendering and the tracking state is out of date, update it immediately
// This will make sure that our updates are applied as early as possible.
if (trackingState !== stateRef.current) {
setTrackingState(stateRef.current);
}
const state = stateRef.current;
return [state, getState, setState, scheduleUpdate, flushUpdates] as const;
}

View File

@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.4.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.4.0...@react-navigation/drawer@5.4.1) (2020-04-08)
### Bug Fixes
* don't hide content from accessibility with permanent drawer ([cb2f157](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/cb2f157a561a2ce3f073eb4ccb567532c77bd869)), closes [#7976](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/7976)
* mark type exports for all packages ([b71de6c](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/b71de6cc799143f1d0e8a0cfcc34f0a2381f9840))
# [5.4.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.3.4...@react-navigation/drawer@5.4.0) (2020-03-30)
### Bug Fixes
* disable only swipe gesture on safari ([105da6a](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/105da6ab2fe69847b676c4d4117638212cda1f9a))
### Features
* add swipeEnabled option to disable swipe gesture in drawer ([#7834](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/7834)) ([ac7f972](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/ac7f972e922a82cd32d943356941d100b68bd8b0))
## [5.3.4](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.3.3...@react-navigation/drawer@5.3.4) (2020-03-23)
**Note:** Version bump only for package @react-navigation/drawer
## [5.3.3](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.3.2...@react-navigation/drawer@5.3.3) (2020-03-22)
**Note:** Version bump only for package @react-navigation/drawer

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/drawer",
"description": "Drawer navigator component with animated transitions and gesturess",
"version": "5.3.3",
"version": "5.4.1",
"keywords": [
"react-native-component",
"react-component",
@@ -40,7 +40,7 @@
},
"devDependencies": {
"@react-native-community/bob": "^0.10.0",
"@react-navigation/native": "^5.1.2",
"@react-navigation/native": "^5.1.5",
"@types/react": "^16.9.23",
"@types/react-native": "^0.61.22",
"del-cli": "^3.0.0",
@@ -50,7 +50,7 @@
"react-native-reanimated": "^1.7.0",
"react-native-safe-area-context": "^0.7.3",
"react-native-screens": "^2.3.0",
"typescript": "^3.7.5"
"typescript": "^3.8.3"
},
"peerDependencies": {
"@react-navigation/native": "^5.0.5",

View File

@@ -22,7 +22,7 @@ export { default as useIsDrawerOpen } from './utils/useIsDrawerOpen';
/**
* Types
*/
export {
export type {
DrawerNavigationOptions,
DrawerNavigationProp,
DrawerContentOptions,

View File

@@ -111,9 +111,18 @@ export type DrawerNavigationOptions = {
/**
* Whether you can use gestures to open or close the drawer.
* Setting this to `false` disables swipe gestures as well as tap on overlay to close.
* See `swipeEnabled` to disable only the swipe gesture.
* Defaults to `true`
*/
gestureEnabled?: boolean;
/**
* Whether you can use swipe gestures to open or close the drawer.
* Defaults to `true`
*/
swipeEnabled?: boolean;
/**
* Whether this screen should be unmounted when navigating away from it.
* Defaults to `false`.

View File

@@ -80,6 +80,7 @@ type Props = {
onClose: () => void;
onGestureRef?: (ref: PanGestureHandler | null) => void;
gestureEnabled: boolean;
swipeEnabled: boolean;
drawerPosition: 'left' | 'right';
drawerType: 'front' | 'back' | 'slide' | 'permanent';
keyboardDismissMode: 'none' | 'on-drag';
@@ -100,7 +101,7 @@ type Props = {
* Disables the pan gesture by default on Apple devices in the browser.
* https://stackoverflow.com/a/9039885
*/
function shouldEnableGesture(): boolean {
function shouldEnableSwipeGesture(): boolean {
if (
Platform.OS === 'web' &&
typeof navigator !== 'undefined' &&
@@ -115,11 +116,12 @@ function shouldEnableGesture(): boolean {
return true;
}
export default class DrawerView extends React.PureComponent<Props> {
export default class DrawerView extends React.Component<Props> {
static defaultProps = {
drawerPostion: I18nManager.isRTL ? 'left' : 'right',
drawerType: 'front',
gestureEnabled: shouldEnableGesture(),
gestureEnabled: true,
swipeEnabled: shouldEnableSwipeGesture(),
swipeEdgeWidth: 32,
swipeVelocityThreshold: 500,
keyboardDismissMode: 'on-drag',
@@ -138,16 +140,11 @@ export default class DrawerView extends React.PureComponent<Props> {
open,
drawerPosition,
drawerType,
gestureEnabled,
swipeDistanceThreshold,
swipeVelocityThreshold,
hideStatusBar,
} = this.props;
if (prevProps.gestureEnabled !== gestureEnabled) {
this.isGestureEnabled.setValue(gestureEnabled ? TRUE : FALSE);
}
if (
// If we're not in the middle of a transition, sync the drawer's open state
typeof this.pendingOpenValue !== 'boolean' ||
@@ -223,9 +220,6 @@ export default class DrawerView extends React.PureComponent<Props> {
private isDrawerTypeFront = new Value<Binary>(
this.props.drawerType === 'front' ? TRUE : FALSE
);
private isGestureEnabled = new Value(
this.props.gestureEnabled ? TRUE : FALSE
);
private isOpen = new Value<Binary>(this.props.open ? TRUE : FALSE);
private nextIsOpen = new Value<Binary | -1>(UNSET);
@@ -554,6 +548,7 @@ export default class DrawerView extends React.PureComponent<Props> {
const {
open,
gestureEnabled,
swipeEnabled,
drawerPosition,
drawerType,
swipeEdgeWidth,
@@ -605,7 +600,7 @@ export default class DrawerView extends React.PureComponent<Props> {
onGestureEvent={this.handleGestureEvent}
onHandlerStateChange={this.handleGestureStateChange}
hitSlop={hitSlop}
enabled={drawerType !== 'permanent' && gestureEnabled}
enabled={drawerType !== 'permanent' && gestureEnabled && swipeEnabled}
{...gestureHandlerProps}
>
<Animated.View
@@ -628,23 +623,27 @@ export default class DrawerView extends React.PureComponent<Props> {
]}
>
<View
accessibilityElementsHidden={isOpen}
accessibilityElementsHidden={isOpen && drawerType !== 'permanent'}
importantForAccessibility={
isOpen ? 'no-hide-descendants' : 'auto'
isOpen && drawerType !== 'permanent'
? 'no-hide-descendants'
: 'auto'
}
style={styles.content}
>
{renderSceneContent({ progress })}
</View>
{// Disable overlay if sidebar is permanent
drawerType === 'permanent' ? null : (
<TapGestureHandler
enabled={gestureEnabled}
onHandlerStateChange={this.handleTapStateChange}
>
<Overlay progress={progress} style={overlayStyle} />
</TapGestureHandler>
)}
{
// Disable overlay if sidebar is permanent
drawerType === 'permanent' ? null : (
<TapGestureHandler
enabled={gestureEnabled}
onHandlerStateChange={this.handleTapStateChange}
>
<Overlay progress={progress} style={overlayStyle} />
</TapGestureHandler>
)
}
</Animated.View>
{drawerType === 'permanent' ? null : (
<Animated.Code
@@ -659,7 +658,7 @@ export default class DrawerView extends React.PureComponent<Props> {
/>
)}
<Animated.View
accessibilityViewIsModal={isOpen}
accessibilityViewIsModal={isOpen && drawerType !== 'permanent'}
removeClippedSubviews={Platform.OS !== 'ios'}
onLayout={this.handleDrawerLayout}
style={[

View File

@@ -200,7 +200,7 @@ export default function DrawerView({
};
const activeKey = state.routes[state.index].key;
const { gestureEnabled } = descriptors[activeKey].options;
const { gestureEnabled, swipeEnabled } = descriptors[activeKey].options;
return (
<GestureHandlerWrapper style={styles.content}>
@@ -210,6 +210,7 @@ export default function DrawerView({
<Drawer
open={isDrawerOpen}
gestureEnabled={gestureEnabled}
swipeEnabled={swipeEnabled}
onOpen={handleDrawerOpen}
onClose={handleDrawerClose}
onGestureRef={(ref) => {

View File

@@ -3,6 +3,33 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.1.8](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.7...@react-navigation/material-bottom-tabs@5.1.8) (2020-04-08)
### Bug Fixes
* mark type exports for all packages ([b71de6c](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/commit/b71de6cc799143f1d0e8a0cfcc34f0a2381f9840))
## [5.1.7](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.6...@react-navigation/material-bottom-tabs@5.1.7) (2020-03-30)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.1.6](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.5...@react-navigation/material-bottom-tabs@5.1.6) (2020-03-23)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.1.5](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.4...@react-navigation/material-bottom-tabs@5.1.5) (2020-03-22)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/material-bottom-tabs",
"description": "Integration for bottom navigation component from react-native-paper",
"version": "5.1.5",
"version": "5.1.8",
"keywords": [
"react-native-component",
"react-component",
@@ -36,16 +36,16 @@
},
"devDependencies": {
"@react-native-community/bob": "^0.10.0",
"@react-navigation/native": "^5.1.2",
"@react-navigation/native": "^5.1.5",
"@types/react": "^16.9.23",
"@types/react-native": "^0.61.22",
"@types/react-native-vector-icons": "^6.4.5",
"del-cli": "^3.0.0",
"react": "~16.9.0",
"react-native": "~0.61.5",
"react-native-paper": "^3.6.0",
"react-native-paper": "^3.7.0",
"react-native-vector-icons": "^6.6.0",
"typescript": "^3.7.5"
"typescript": "^3.8.3"
},
"peerDependencies": {
"@react-navigation/native": "^5.0.5",

View File

@@ -11,7 +11,7 @@ export { default as MaterialBottomTabView } from './views/MaterialBottomTabView'
/**
* Types
*/
export {
export type {
MaterialBottomTabNavigationOptions,
MaterialBottomTabNavigationProp,
} from './types';

View File

@@ -3,6 +3,33 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.1.8](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.7...@react-navigation/material-top-tabs@5.1.8) (2020-04-08)
### Bug Fixes
* mark type exports for all packages ([b71de6c](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/commit/b71de6cc799143f1d0e8a0cfcc34f0a2381f9840))
## [5.1.7](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.6...@react-navigation/material-top-tabs@5.1.7) (2020-03-30)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.1.6](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.5...@react-navigation/material-top-tabs@5.1.6) (2020-03-23)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.1.5](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.4...@react-navigation/material-top-tabs@5.1.5) (2020-03-22)
**Note:** Version bump only for package @react-navigation/material-top-tabs

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/material-top-tabs",
"description": "Integration for the animated tab view component from react-native-tab-view",
"version": "5.1.5",
"version": "5.1.8",
"keywords": [
"react-native-component",
"react-component",
@@ -39,7 +39,7 @@
},
"devDependencies": {
"@react-native-community/bob": "^0.10.0",
"@react-navigation/native": "^5.1.2",
"@react-navigation/native": "^5.1.5",
"@types/react": "^16.9.23",
"@types/react-native": "^0.61.22",
"del-cli": "^3.0.0",
@@ -47,8 +47,8 @@
"react-native": "~0.61.5",
"react-native-gesture-handler": "^1.6.0",
"react-native-reanimated": "^1.7.0",
"react-native-tab-view": "^2.13.0",
"typescript": "^3.7.5"
"react-native-tab-view": "^2.14.0",
"typescript": "^3.8.3"
},
"peerDependencies": {
"@react-navigation/native": "^5.0.5",

View File

@@ -12,7 +12,7 @@ export { default as MaterialTopTabBar } from './views/MaterialTopTabBar';
/**
* Types
*/
export {
export type {
MaterialTopTabNavigationOptions,
MaterialTopTabNavigationProp,
MaterialTopTabBarProps,

View File

@@ -3,6 +3,33 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.1.5](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.4...@react-navigation/native@5.1.5) (2020-04-08)
**Note:** Version bump only for package @react-navigation/native
## [5.1.4](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.3...@react-navigation/native@5.1.4) (2020-03-30)
**Note:** Version bump only for package @react-navigation/native
## [5.1.3](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.2...@react-navigation/native@5.1.3) (2020-03-23)
### Bug Fixes
* add info about android launchMode in useLinking error ([d94e43c](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/d94e43c3c8625b209a5c883b8cb560496d07fda7))
## [5.1.2](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.1...@react-navigation/native@5.1.2) (2020-03-22)
**Note:** Version bump only for package @react-navigation/native

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/native",
"description": "React Native integration for React Navigation",
"version": "5.1.2",
"version": "5.1.5",
"keywords": [
"react-native",
"react-navigation",
@@ -31,7 +31,7 @@
"clean": "del lib"
},
"dependencies": {
"@react-navigation/core": "^5.3.0"
"@react-navigation/core": "^5.3.3"
},
"devDependencies": {
"@react-native-community/bob": "^0.10.0",
@@ -41,7 +41,7 @@
"react": "~16.9.0",
"react-native": "~0.61.5",
"react-native-testing-library": "^1.12.0",
"typescript": "^3.7.5"
"typescript": "^3.8.3"
},
"peerDependencies": {
"react": "*",

View File

@@ -26,7 +26,7 @@ type Props = NavigationContainerProps & {
*/
const NavigationContainer = React.forwardRef(function NavigationContainer(
{ theme = DefaultTheme, ...rest }: Props,
ref: React.Ref<NavigationContainerRef>
ref?: React.Ref<NavigationContainerRef | null>
) {
const refContainer = React.useRef<NavigationContainerRef>(null);

View File

@@ -41,7 +41,7 @@ export type LinkingOptions = {
*/
getStateFromPath?: typeof getStateFromPathDefault;
/**
* Custom function to conver the state object to a valid URL (advanced).
* Custom function to convert the state object to a valid URL (advanced).
*/
getPathFromState?: typeof getPathFromStateDefault;
};

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { Linking } from 'react-native';
import { Linking, Platform } from 'react-native';
import {
getActionFromState,
getStateFromPath as getStateFromPathDefault,
@@ -20,7 +20,10 @@ export default function useLinking(
React.useEffect(() => {
if (isUsingLinking) {
throw new Error(
"Looks like you are using 'useLinking' in multiple components. This is likely an error since deep links should only be handled in one place to avoid conflicts."
"Looks like you are using 'useLinking' in multiple components. This is likely an error since deep links should only be handled in one place to avoid conflicts." +
(Platform.OS === 'android'
? "\n\nIf you're not using it in multiple components, ensure that you have set 'android:launchMode=singleTask' in the '<activity />' section of the 'AndroidManifest.xml' file to avoid launching multiple activities which run multiple instances of the root component."
: '')
);
} else {
isUsingLinking = true;

View File

@@ -40,7 +40,7 @@ export default function useLinking(
React.useEffect(() => {
if (isUsingLinking) {
throw new Error(
"Looks like you are using 'useLinking' in multiple components. This is likely an error since URL integration should only be handled in one place to avoid conflicts."
"Looks like you are using 'useLinking' in multiple components. This is likely an error since URL integration should only be handled in one place to avoid conflicts. Also ensure that you set your android activity launchMode to single task in your AndroiManifest.xml file."
);
} else {
isUsingLinking = true;

View File

@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.3.0](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.2.1...@react-navigation/routers@5.3.0) (2020-04-08)
### Bug Fixes
* separate normal exports and type exports ([303f0b7](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/commit/303f0b78a5ab717b2d606cd9c8a22f3dae051f0f))
### Features
* make replace bubble up ([ba1f405](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/commit/ba1f4051299ad86001592b8d3601c16fece159df))
## [5.2.1](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.2.0...@react-navigation/routers@5.2.1) (2020-03-30)
**Note:** Version bump only for package @react-navigation/routers
# [5.2.0](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.1.1...@react-navigation/routers@5.2.0) (2020-03-22)

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/routers",
"description": "Routers to help build custom navigators",
"version": "5.2.0",
"version": "5.3.0",
"keywords": [
"react",
"react-native",
@@ -30,12 +30,12 @@
"clean": "del lib"
},
"dependencies": {
"shortid": "^2.2.15"
"nanoid": "^3.0.2"
},
"devDependencies": {
"@react-native-community/bob": "^0.10.0",
"del-cli": "^3.0.0",
"typescript": "^3.7.5"
"typescript": "^3.8.3"
},
"@react-native-community/bob": {
"source": "src",

View File

@@ -1,4 +1,4 @@
import shortid from 'shortid';
import { nanoid } from 'nanoid/non-secure';
import { CommonNavigationAction, NavigationState, PartialState } from './types';
/**
@@ -55,9 +55,7 @@ const BaseRouter = {
return {
...nextState,
routes: nextState.routes.map((route) =>
route.key
? route
: { ...route, key: `${route.name}-${shortid()}` }
route.key ? route : { ...route, key: `${route.name}-${nanoid()}` }
),
};
}

View File

@@ -1,4 +1,4 @@
import shortid from 'shortid';
import { nanoid } from 'nanoid/non-secure';
import {
PartialState,
CommonNavigationAction,
@@ -115,7 +115,7 @@ export default function DrawerRouter(
...state,
stale: false,
type: 'drawer',
key: `drawer-${shortid()}`,
key: `drawer-${nanoid()}`,
};
},
@@ -136,7 +136,7 @@ export default function DrawerRouter(
return {
...state,
type: 'drawer',
key: `drawer-${shortid()}`,
key: `drawer-${nanoid()}`,
};
},

View File

@@ -1,4 +1,4 @@
import shortid from 'shortid';
import { nanoid } from 'nanoid/non-secure';
import BaseRouter from './BaseRouter';
import {
NavigationState,
@@ -113,12 +113,12 @@ export default function StackRouter(options: StackRouterOptions) {
return {
stale: false,
type: 'stack',
key: `stack-${shortid()}`,
key: `stack-${nanoid()}`,
index: 0,
routeNames,
routes: [
{
key: `${initialRouteName}-${shortid()}`,
key: `${initialRouteName}-${nanoid()}`,
name: initialRouteName,
params: routeParamList[initialRouteName],
},
@@ -139,7 +139,7 @@ export default function StackRouter(options: StackRouterOptions) {
(route) =>
({
...route,
key: route.key || `${route.name}-${shortid()}`,
key: route.key || `${route.name}-${nanoid()}`,
params:
routeParamList[route.name] !== undefined
? {
@@ -157,7 +157,7 @@ export default function StackRouter(options: StackRouterOptions) {
: routeNames[0];
routes.push({
key: `${initialRouteName}-${shortid()}`,
key: `${initialRouteName}-${nanoid()}`,
name: initialRouteName,
params: routeParamList[initialRouteName],
});
@@ -166,7 +166,7 @@ export default function StackRouter(options: StackRouterOptions) {
return {
stale: false,
type: 'stack',
key: `stack-${shortid()}`,
key: `stack-${nanoid()}`,
index: routes.length - 1,
routeNames,
routes,
@@ -186,7 +186,7 @@ export default function StackRouter(options: StackRouterOptions) {
: routeNames[0];
routes.push({
key: `${initialRouteName}-${shortid()}`,
key: `${initialRouteName}-${nanoid()}`,
name: initialRouteName,
params: routeParamList[initialRouteName],
});
@@ -219,9 +219,10 @@ export default function StackRouter(options: StackRouterOptions) {
switch (action.type) {
case 'REPLACE': {
const index = action.source
? state.routes.findIndex((r) => r.key === action.source)
: state.index;
const index =
action.target === state.key && action.source
? state.routes.findIndex((r) => r.key === action.source)
: state.index;
if (index === -1) {
return null;
@@ -238,7 +239,7 @@ export default function StackRouter(options: StackRouterOptions) {
routes: state.routes.map((route, i) =>
i === index
? {
key: key !== undefined ? key : `${name}-${shortid()}`,
key: key !== undefined ? key : `${name}-${nanoid()}`,
name,
params:
routeParamList[name] !== undefined
@@ -263,7 +264,7 @@ export default function StackRouter(options: StackRouterOptions) {
{
key:
action.payload.key === undefined
? `${action.payload.name}-${shortid()}`
? `${action.payload.name}-${nanoid()}`
: action.payload.key,
name: action.payload.name,
params:

View File

@@ -1,4 +1,4 @@
import shortid from 'shortid';
import { nanoid } from 'nanoid/non-secure';
import BaseRouter from './BaseRouter';
import {
NavigationState,
@@ -126,7 +126,7 @@ export default function TabRouter({
const routes = routeNames.map((name) => ({
name,
key: `${name}-${shortid()}`,
key: `${name}-${nanoid()}`,
params: routeParamList[name],
}));
@@ -135,7 +135,7 @@ export default function TabRouter({
return {
stale: false,
type: 'tab',
key: `tab-${shortid()}`,
key: `tab-${nanoid()}`,
index,
routeNames,
history,
@@ -161,7 +161,7 @@ export default function TabRouter({
key:
route && route.name === name && route.key
? route.key
: `${name}-${shortid()}`,
: `${name}-${nanoid()}`,
params:
routeParamList[name] !== undefined
? {
@@ -195,7 +195,7 @@ export default function TabRouter({
return {
stale: false,
type: 'tab',
key: `tab-${shortid()}`,
key: `tab-${nanoid()}`,
index,
routeNames,
history,
@@ -208,7 +208,7 @@ export default function TabRouter({
(name) =>
state.routes.find((r) => r.name === name) || {
name,
key: `${name}-${shortid()}`,
key: `${name}-${nanoid()}`,
params: routeParamList[name],
}
);

View File

@@ -1,7 +1,7 @@
import BaseRouter from '../BaseRouter';
import * as CommonActions from '../CommonActions';
jest.mock('shortid', () => () => 'test');
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
const STATE = {
stale: false as const,

View File

@@ -5,7 +5,7 @@ import {
DrawerNavigationState,
} from '..';
jest.mock('shortid', () => () => 'test');
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
it('gets initial state from route names and params with initialRouteName', () => {
const router = DrawerRouter({ initialRouteName: 'baz' });

View File

@@ -1,6 +1,6 @@
import { CommonActions, StackRouter, StackActions } from '..';
jest.mock('shortid', () => () => 'test');
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
it('gets initial state from route names and params with initialRouteName', () => {
const router = StackRouter({ initialRouteName: 'baz' });
@@ -720,7 +720,7 @@ it('replaces focused screen with replace', () => {
});
});
it('replaces source screen with replace', () => {
it('replaces active screen with replace', () => {
const router = StackRouter({});
const options = {
routeNames: ['foo', 'bar', 'baz', 'qux'],
@@ -754,8 +754,8 @@ it('replaces source screen with replace', () => {
index: 1,
routes: [
{ key: 'foo', name: 'foo' },
{ key: 'bar', name: 'bar', params: { fruit: 'orange' } },
{ key: 'qux-test', name: 'qux', params: { answer: 42 } },
{ key: 'baz', name: 'baz' },
],
routeNames: ['foo', 'bar', 'baz', 'qux'],
});
@@ -785,6 +785,7 @@ it("doesn't handle replace if source key isn't present", () => {
{
...StackActions.replace('qux', { answer: 42 }),
source: 'magic',
target: 'root',
},
options
)

View File

@@ -1,6 +1,6 @@
import { CommonActions, TabRouter, TabActions, TabNavigationState } from '..';
jest.mock('shortid', () => () => 'test');
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
it('gets initial state from route names and params with initialRouteName', () => {
const router = TabRouter({ initialRouteName: 'baz' });

View File

@@ -4,27 +4,27 @@ export { CommonActions };
export { default as BaseRouter } from './BaseRouter';
export {
default as StackRouter,
StackActions,
export { default as StackRouter, StackActions } from './StackRouter';
export type {
StackActionHelpers,
StackActionType,
StackRouterOptions,
StackNavigationState,
} from './StackRouter';
export {
default as TabRouter,
TabActions,
export { default as TabRouter, TabActions } from './TabRouter';
export type {
TabActionHelpers,
TabActionType,
TabRouterOptions,
TabNavigationState,
} from './TabRouter';
export {
default as DrawerRouter,
DrawerActions,
export { default as DrawerRouter, DrawerActions } from './DrawerRouter';
export type {
DrawerActionHelpers,
DrawerActionType,
DrawerRouterOptions,

View File

@@ -3,6 +3,61 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.2.10](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.9...@react-navigation/stack@5.2.10) (2020-04-08)
### Bug Fixes
* make color of shadow element same as card color in stack ([f1a8bce](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/f1a8bceba5b736e9f59862a8ae819342209a46f2))
* mark type exports for all packages ([b71de6c](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/b71de6cc799143f1d0e8a0cfcc34f0a2381f9840))
## [5.2.9](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.8...@react-navigation/stack@5.2.9) (2020-03-30)
### Bug Fixes
* dismiss keyboard on screen change for android ([8432e5a](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/8432e5ab25f041af8538ea7fb35e97cfcf1f983e))
* finish stack animation on CANCELLED event ([#7898](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7898)) ([d649fbc](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/d649fbc6691871f0348076bce185d11a183c02cf)), closes [#7897](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7897)
* when comparing changed routes, only check keys ([9a8fea8](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/9a8fea8f2c1bdabfc5dd87e5c3ff4e7b97aef47d))
## [5.2.7](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.6...@react-navigation/stack@5.2.7) (2020-03-26)
### Bug Fixes
* add pointerEvents=box-none to overlay View ([#7871](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7871)) ([e097df8](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/e097df880adab984aae29f847003d2548cfbdce5))
## [5.2.6](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.5...@react-navigation/stack@5.2.6) (2020-03-23)
**Note:** Version bump only for package @react-navigation/stack
## [5.2.5](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.4...@react-navigation/stack@5.2.5) (2020-03-23)
### Bug Fixes
* fix swipe gestures requiring a lot of velocity to dismiss ([61f16d3](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/61f16d3f25cbbcc00d699dd09c5f4abde9b17d5a))
## [5.2.4](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.3...@react-navigation/stack@5.2.4) (2020-03-22)

View File

@@ -1,7 +1,7 @@
{
"name": "@react-navigation/stack",
"description": "Stack navigator component for iOS and Android with animated transitions and gestures",
"version": "5.2.4",
"version": "5.2.10",
"keywords": [
"react-native-component",
"react-component",
@@ -40,7 +40,7 @@
"devDependencies": {
"@react-native-community/bob": "^0.10.0",
"@react-native-community/masked-view": "^0.1.7",
"@react-navigation/native": "^5.1.2",
"@react-navigation/native": "^5.1.5",
"@types/color": "^3.0.1",
"@types/react": "^16.9.23",
"@types/react-native": "^0.61.22",
@@ -50,7 +50,7 @@
"react-native-gesture-handler": "^1.6.0",
"react-native-safe-area-context": "^0.7.3",
"react-native-screens": "^2.3.0",
"typescript": "^3.7.5"
"typescript": "^3.8.3"
},
"peerDependencies": {
"@react-native-community/masked-view": ">= 0.1.0",

View File

@@ -48,7 +48,7 @@ export { default as useGestureHandlerRef } from './utils/useGestureHandlerRef';
/**
* Types
*/
export {
export type {
StackNavigationOptions,
StackNavigationProp,
StackHeaderProps,

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { TextInput } from 'react-native';
import { TextInput, Platform, Keyboard } from 'react-native';
type Props = {
enabled: boolean;
@@ -56,7 +56,9 @@ export default class KeyboardManager extends React.Component<Props> {
const input = this.previouslyFocusedTextInput;
if (input) {
if (Platform.OS === 'android') {
Keyboard.dismiss();
} else if (input) {
TextInput.State.blurTextInput(input);
}

View File

@@ -246,11 +246,21 @@ export default class Card extends React.Component<Props> {
this.handleStartInteraction();
onGestureBegin?.();
break;
case GestureState.CANCELLED:
case GestureState.CANCELLED: {
this.isSwiping.setValue(FALSE);
this.handleEndInteraction();
const velocity =
gestureDirection === 'vertical' ||
gestureDirection === 'vertical-inverted'
? nativeEvent.velocityY
: nativeEvent.velocityX;
this.animate({ closing: this.props.closing, velocity });
onGestureCanceled?.();
break;
}
case GestureState.END: {
this.isSwiping.setValue(FALSE);
@@ -272,8 +282,7 @@ export default class Card extends React.Component<Props> {
}
const closing =
(translation + velocity) *
gestureVelocityImpact *
(translation + velocity * gestureVelocityImpact) *
getInvertedMultiplier(gestureDirection) >
distance / 2
? velocity !== 0 || translation !== 0
@@ -481,7 +490,7 @@ export default class Card extends React.Component<Props> {
<CardAnimationContext.Provider value={animationContext}>
<View pointerEvents="box-none" {...rest}>
{overlayEnabled ? (
<View style={StyleSheet.absoluteFill}>
<View pointerEvents="box-none" style={StyleSheet.absoluteFill}>
{overlay({ style: overlayStyle })}
</View>
) : null}
@@ -508,6 +517,7 @@ export default class Card extends React.Component<Props> {
: gestureDirection === 'vertical'
? [styles.shadowVertical, styles.shadowTop]
: [styles.shadowVertical, styles.shadowBottom],
{ backgroundColor },
shadowStyle,
]}
pointerEvents="none"
@@ -544,7 +554,6 @@ const styles = StyleSheet.create({
},
shadow: {
position: 'absolute',
backgroundColor: '#fff',
shadowRadius: 5,
shadowColor: '#000',
shadowOpacity: 0.3,

View File

@@ -46,31 +46,63 @@ type State = {
const GestureHandlerWrapper = GestureHandlerRootView ?? View;
/**
* Compare two arrays with primitive values as the content.
* We need to make sure that both values and order match.
*/
const isArrayEqual = (a: any[], b: any[]) =>
a.length === b.length && a.every((it, index) => it === b[index]);
export default class StackView extends React.Component<Props, State> {
static getDerivedStateFromProps(
props: Readonly<Props>,
state: Readonly<State>
) {
// If there was no change in routes, we don't need to compute anything
if (props.state.routes === state.previousRoutes && state.routes.length) {
if (props.descriptors !== state.previousDescriptors) {
const descriptors = state.routes.reduce<StackDescriptorMap>(
(acc, route) => {
acc[route.key] =
props.descriptors[route.key] || state.descriptors[route.key];
if (
(props.state.routes === state.previousRoutes ||
isArrayEqual(
props.state.routes.map((r) => r.key),
state.previousRoutes.map((r) => r.key)
)) &&
state.routes.length
) {
let routes = state.routes;
let previousRoutes = state.previousRoutes;
let descriptors = props.descriptors;
let previousDescriptors = state.previousDescriptors;
if (props.descriptors !== state.previousDescriptors) {
descriptors = state.routes.reduce<StackDescriptorMap>((acc, route) => {
acc[route.key] =
props.descriptors[route.key] || state.descriptors[route.key];
return acc;
}, {});
previousDescriptors = props.descriptors;
}
if (props.state.routes !== state.previousRoutes) {
// if any route objects have changed, we should update them
const map = props.state.routes.reduce<Record<string, Route<string>>>(
(acc, route) => {
acc[route.key] = route;
return acc;
},
{}
);
return {
previousDescriptors: props.descriptors,
descriptors,
};
routes = state.routes.map((route) => map[route.key] || route);
previousRoutes = props.state.routes;
}
return null;
return {
routes,
previousRoutes,
descriptors,
previousDescriptors,
};
}
// Here we determine which routes were added or removed to animate them

5665
yarn.lock

File diff suppressed because it is too large Load Diff