Compare commits

..

16 Commits

Author SHA1 Message Date
Satyajit Sahoo
e071a978e6 chore: publish
- @react-navigation/bottom-tabs@5.1.1
 - @react-navigation/compat@5.1.1
 - @react-navigation/core@5.2.1
 - @react-navigation/drawer@5.1.1
 - @react-navigation/material-bottom-tabs@5.1.1
 - @react-navigation/material-top-tabs@5.1.1
 - @react-navigation/native@5.0.9
 - @react-navigation/routers@5.1.0
 - @react-navigation/stack@5.1.1
2020-03-03 11:58:45 +01:00
Satyajit Sahoo
296c836064 fix: ignore back button press if screen isn't focused. closes #7673 2020-03-03 11:34:38 +01:00
Satyajit Sahoo
09f6808d7d feat: make reset bubble up 2020-03-01 02:45:08 +01:00
Satyajit Sahoo
5bb0f405ce fix: fix links for documentation 2020-02-28 17:12:18 +01:00
Satyajit Sahoo
2dfa4f3629 fix: move updating state to useEffect 2020-02-28 17:01:58 +01:00
Satyajit Sahoo
cf41288760 chore: run clean before release 2020-02-26 15:03:57 +01:00
Satyajit Sahoo
3677818f63 chore: publish
- @react-navigation/bottom-tabs@5.1.0
 - @react-navigation/compat@5.1.0
 - @react-navigation/core@5.2.0
 - @react-navigation/drawer@5.1.0
 - @react-navigation/material-bottom-tabs@5.1.0
 - @react-navigation/material-top-tabs@5.1.0
 - @react-navigation/native@5.0.8
 - @react-navigation/routers@5.0.3
 - @react-navigation/stack@5.1.0
2020-02-26 13:57:42 +01:00
Satyajit Sahoo
162410843c feat: add ability add listeners with listeners prop
This adds ability to listen to events from the component where the navigator is defined, even if the screen is not rendered.

```js
<Tabs.Screen
  name="Chat"
  component={Chat}
  options={{ title: 'Chat' }}
  listeners={{
    tabPress: e => console.log('Tab press', e.target),
  }}
/>
```

Closes #6756
2020-02-26 13:02:22 +01:00
Satyajit Sahoo
028c2887c6 refactor: tweak error messages more 2020-02-25 20:58:14 +01:00
Satyajit Sahoo
7a44cda136 refactor: tweak error messages 2020-02-25 17:58:09 +01:00
Satyajit Sahoo
a046db536f chore: publish
- @react-navigation/stack@5.0.9
2020-02-24 14:45:00 +01:00
Satyajit Sahoo
d115787b1c chore: mark yarn script as binary 2020-02-24 14:44:29 +01:00
Michał Osadnik
80a337024a fix: enhance border radius in modals on new iPhones (#6945)
Co-authored-by: Satyajit Sahoo <satyajit.happy@gmail.com>
2020-02-24 14:44:20 +01:00
Satyajit Sahoo
c19da31240 refactor: enable screens only for last screen
This will avoid issues such as https://github.com/react-navigation/react-navigation/issues/6909
2020-02-24 11:37:25 +01:00
Satyajit Sahoo
85e9376302 chore: publish
- @react-navigation/stack@5.0.8
2020-02-21 20:09:06 +01:00
Satyajit Sahoo
a67b49477e fix: fix transparent header on Android 2020-02-21 20:07:38 +01:00
53 changed files with 709 additions and 163 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
yarn-*.js binary

View File

@@ -7,7 +7,11 @@ import {
StatusBar, StatusBar,
I18nManager, I18nManager,
} from 'react-native'; } from 'react-native';
// eslint-disable-next-line import/no-unresolved
import { enableScreens } from 'react-native-screens';
import RNRestart from 'react-native-restart'; import RNRestart from 'react-native-restart';
import { Updates } from 'expo';
import { Asset } from 'expo-asset';
import { MaterialIcons } from '@expo/vector-icons'; import { MaterialIcons } from '@expo/vector-icons';
import { import {
Provider as PaperProvider, Provider as PaperProvider,
@@ -17,7 +21,6 @@ import {
List, List,
Divider, Divider,
} from 'react-native-paper'; } from 'react-native-paper';
import { Asset } from 'expo-asset';
import { import {
InitialState, InitialState,
useLinking, useLinking,
@@ -49,10 +52,11 @@ import DynamicTabs from './Screens/DynamicTabs';
import AuthFlow from './Screens/AuthFlow'; import AuthFlow from './Screens/AuthFlow';
import CompatAPI from './Screens/CompatAPI'; import CompatAPI from './Screens/CompatAPI';
import SettingsItem from './Shared/SettingsItem'; import SettingsItem from './Shared/SettingsItem';
import { Updates } from 'expo';
YellowBox.ignoreWarnings(['Require cycle:', 'Warning: Async Storage']); YellowBox.ignoreWarnings(['Require cycle:', 'Warning: Async Storage']);
enableScreens();
type RootDrawerParamList = { type RootDrawerParamList = {
Root: undefined; Root: undefined;
Another: undefined; Another: undefined;

View File

@@ -20,6 +20,7 @@
"lint": "eslint --ext '.js,.ts,.tsx' .", "lint": "eslint --ext '.js,.ts,.tsx' .",
"typescript": "tsc --noEmit", "typescript": "tsc --noEmit",
"test": "jest", "test": "jest",
"prerelease": "lerna run clean",
"release": "lerna publish", "release": "lerna publish",
"example": "yarn --cwd example" "example": "yarn --cwd example"
}, },

View File

@@ -3,6 +3,25 @@
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.1](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.1.0...@react-navigation/bottom-tabs@5.1.1) (2020-03-03)
**Note:** Version bump only for package @react-navigation/bottom-tabs
# [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.7...@react-navigation/bottom-tabs@5.1.0) (2020-02-26)
### Features
* add ability add listeners with listeners prop ([1624108](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/162410843c4f175ae107756de1c3af04d1d47aa7)), closes [#6756](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/issues/6756)
## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.6...@react-navigation/bottom-tabs@5.0.7) (2020-02-21) ## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.0.6...@react-navigation/bottom-tabs@5.0.7) (2020-02-21)
**Note:** Version bump only for package @react-navigation/bottom-tabs **Note:** Version bump only for package @react-navigation/bottom-tabs

View File

@@ -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.0.7", "version": "5.1.1",
"keywords": [ "keywords": [
"react-native-component", "react-native-component",
"react-component", "react-component",
@@ -35,7 +35,7 @@
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.9.3", "@react-native-community/bob": "^0.9.3",
"@react-navigation/native": "^5.0.7", "@react-navigation/native": "^5.0.9",
"@types/color": "^3.0.1", "@types/color": "^3.0.1",
"@types/react": "^16.9.19", "@types/react": "^16.9.19",
"@types/react-native": "^0.60.30", "@types/react-native": "^0.60.30",

View File

@@ -48,6 +48,8 @@ function BottomTabNavigator({
} }
export default createNavigatorFactory< export default createNavigatorFactory<
TabNavigationState,
BottomTabNavigationOptions, BottomTabNavigationOptions,
BottomTabNavigationEventMap,
typeof BottomTabNavigator typeof BottomTabNavigator
>(BottomTabNavigator); >(BottomTabNavigator);

View File

@@ -3,6 +3,25 @@
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.1](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.0...@react-navigation/compat@5.1.1) (2020-03-03)
**Note:** Version bump only for package @react-navigation/compat
# [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.0.7...@react-navigation/compat@5.1.0) (2020-02-26)
### Features
* add ability add listeners with listeners prop ([1624108](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/commit/162410843c4f175ae107756de1c3af04d1d47aa7)), closes [#6756](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/issues/6756)
## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.0.6...@react-navigation/compat@5.0.7) (2020-02-21) ## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.0.6...@react-navigation/compat@5.0.7) (2020-02-21)
**Note:** Version bump only for package @react-navigation/compat **Note:** Version bump only for package @react-navigation/compat

View File

@@ -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.0.7", "version": "5.1.1",
"license": "MIT", "license": "MIT",
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/compat", "repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/compat",
"bugs": { "bugs": {
@@ -26,7 +26,7 @@
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.9.3", "@react-native-community/bob": "^0.9.3",
"@react-navigation/native": "^5.0.7", "@react-navigation/native": "^5.0.9",
"@types/react": "^16.9.19", "@types/react": "^16.9.19",
"react": "~16.9.0", "react": "~16.9.0",
"typescript": "^3.7.5" "typescript": "^3.7.5"

View File

@@ -6,6 +6,7 @@ import {
TypedNavigator, TypedNavigator,
NavigationProp, NavigationProp,
RouteProp, RouteProp,
EventMapBase,
} from '@react-navigation/native'; } from '@react-navigation/native';
import CompatScreen from './CompatScreen'; import CompatScreen from './CompatScreen';
import ScreenPropsContext from './ScreenPropsContext'; import ScreenPropsContext from './ScreenPropsContext';
@@ -15,7 +16,9 @@ import { CompatScreenType, CompatRouteConfig } from './types';
export default function createCompatNavigatorFactory< export default function createCompatNavigatorFactory<
CreateNavigator extends () => TypedNavigator< CreateNavigator extends () => TypedNavigator<
ParamListBase, ParamListBase,
NavigationState,
{}, {},
EventMapBase,
React.ComponentType<any> React.ComponentType<any>
> >
>(createNavigator: CreateNavigator) { >(createNavigator: CreateNavigator) {

View File

@@ -22,5 +22,7 @@ function SwitchNavigator(props: Props) {
} }
export default createCompatNavigatorFactory( export default createCompatNavigatorFactory(
createNavigatorFactory<{}, typeof SwitchNavigator>(SwitchNavigator) createNavigatorFactory<TabNavigationState, {}, {}, typeof SwitchNavigator>(
SwitchNavigator
)
); );

View File

@@ -3,6 +3,29 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.2.1](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.2.0...@react-navigation/core@5.2.1) (2020-03-03)
### Bug Fixes
* fix links for documentation ([5bb0f40](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/5bb0f405ceb5755d39a0b5b1f2e4ecee0da051bc))
* move updating state to useEffect ([2dfa4f3](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/2dfa4f36293a2acb718814f6b2fa79d7c7ddf09c))
# [5.2.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.1.6...@react-navigation/core@5.2.0) (2020-02-26)
### Features
* add ability add listeners with listeners prop ([1624108](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/162410843c4f175ae107756de1c3af04d1d47aa7)), closes [#6756](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/6756)
## [5.1.6](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.1.5...@react-navigation/core@5.1.6) (2020-02-21) ## [5.1.6](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.1.5...@react-navigation/core@5.1.6) (2020-02-21)

View File

@@ -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.1.6", "version": "5.2.1",
"keywords": [ "keywords": [
"react", "react",
"react-native", "react-native",
@@ -29,7 +29,7 @@
"clean": "del lib" "clean": "del lib"
}, },
"dependencies": { "dependencies": {
"@react-navigation/routers": "^5.0.2", "@react-navigation/routers": "^5.1.0",
"escape-string-regexp": "^2.0.0", "escape-string-regexp": "^2.0.0",
"query-string": "^6.10.1", "query-string": "^6.10.1",
"react-is": "^16.12.0", "react-is": "^16.12.0",

View File

@@ -21,10 +21,10 @@ import useSyncState from './useSyncState';
type State = NavigationState | PartialState<NavigationState> | undefined; type State = NavigationState | PartialState<NavigationState> | undefined;
const MISSING_CONTEXT_ERROR = const MISSING_CONTEXT_ERROR =
"We couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'? See https://reactnavigation.org/docs/en/getting-started.html for setup instructions."; "Couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'? See https://reactnavigation.org/docs/getting-started for setup instructions.";
const NOT_INITIALIZED_ERROR = const NOT_INITIALIZED_ERROR =
"The 'navigation' object hasn't been initialized yet. This might happen if you don't have a navigator mounted, or if the navigator hasn't finished mounting. See https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html#handling-initialization for more details."; "The 'navigation' object hasn't been initialized yet. This might happen if you don't have a navigator mounted, or if the navigator hasn't finished mounting. See https://reactnavigation.org/docs/navigating-without-navigation-prop#handling-initialization for more details.";
export const NavigationStateContext = React.createContext<{ export const NavigationStateContext = React.createContext<{
isDefault?: true; isDefault?: true;
@@ -238,7 +238,7 @@ const BaseNavigationContainer = React.forwardRef(
hasWarnedForSerialization = true; hasWarnedForSerialization = true;
console.warn( console.warn(
"We found non-serializable values in the navigation state, which can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params. If you need to use components with callbacks in your options, you can use 'navigation.setOptions' instead. See https://reactnavigation.org/docs/en/troubleshooting.html#i-get-the-warning-we-found-non-serializable-values-in-the-navigation-state for more details." "Non-serializable values were found in the navigation state, which can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params. If you need to use components with callbacks in your options, you can use 'navigation.setOptions' instead. See https://reactnavigation.org/docs/troubleshooting#i-get-the-warning-non-serializable-values-were-found-in-the-navigation-state for more details."
); );
} }
} }

View File

@@ -4,7 +4,7 @@ type Props = {
children: React.ReactNode; children: React.ReactNode;
}; };
const MULTIPLE_NAVIGATOR_ERROR = `Another navigator is already registered for this container. You likely have multiple navigators under a single "NavigationContainer" or "Screen". Make sure each navigator is under a separate "Screen" container. See https://reactnavigation.org/docs/en/nesting-navigators.html for a guide on nesting.`; const MULTIPLE_NAVIGATOR_ERROR = `Another navigator is already registered for this container. You likely have multiple navigators under a single "NavigationContainer" or "Screen". Make sure each navigator is under a separate "Screen" container. See https://reactnavigation.org/docs/nesting-navigators for a guide on nesting.`;
export const SingleNavigatorContext = React.createContext< export const SingleNavigatorContext = React.createContext<
| { | {

View File

@@ -10,10 +10,14 @@ import NavigationContext from './NavigationContext';
import NavigationRouteContext from './NavigationRouteContext'; import NavigationRouteContext from './NavigationRouteContext';
import StaticContainer from './StaticContainer'; import StaticContainer from './StaticContainer';
import EnsureSingleNavigator from './EnsureSingleNavigator'; import EnsureSingleNavigator from './EnsureSingleNavigator';
import { NavigationProp, RouteConfig } from './types'; import { NavigationProp, RouteConfig, EventMapBase } from './types';
type Props<State extends NavigationState, ScreenOptions extends object> = { type Props<
screen: RouteConfig<ParamListBase, string, ScreenOptions>; State extends NavigationState,
ScreenOptions extends object,
EventMap extends EventMapBase
> = {
screen: RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>;
navigation: NavigationProp<ParamListBase, string, State, ScreenOptions>; navigation: NavigationProp<ParamListBase, string, State, ScreenOptions>;
route: Route<string> & { route: Route<string> & {
state?: NavigationState | PartialState<NavigationState>; state?: NavigationState | PartialState<NavigationState>;
@@ -28,14 +32,15 @@ type Props<State extends NavigationState, ScreenOptions extends object> = {
*/ */
export default function SceneView< export default function SceneView<
State extends NavigationState, State extends NavigationState,
ScreenOptions extends object ScreenOptions extends object,
EventMap extends EventMapBase
>({ >({
screen, screen,
route, route,
navigation, navigation,
getState, getState,
setState, setState,
}: Props<State, ScreenOptions>) { }: Props<State, ScreenOptions, EventMap>) {
const navigatorKeyRef = React.useRef<string | undefined>(); const navigatorKeyRef = React.useRef<string | undefined>();
const getKey = React.useCallback(() => navigatorKeyRef.current, []); const getKey = React.useCallback(() => navigatorKeyRef.current, []);

View File

@@ -1,5 +1,5 @@
import { ParamListBase } from '@react-navigation/routers'; import { ParamListBase, NavigationState } from '@react-navigation/routers';
import { RouteConfig } from './types'; import { RouteConfig, EventMapBase } from './types';
/** /**
* Empty component used for specifying route configuration. * Empty component used for specifying route configuration.
@@ -7,8 +7,10 @@ import { RouteConfig } from './types';
export default function Screen< export default function Screen<
ParamList extends ParamListBase, ParamList extends ParamListBase,
RouteName extends keyof ParamList, RouteName extends keyof ParamList,
ScreenOptions extends object State extends NavigationState,
>(_: RouteConfig<ParamList, RouteName, ScreenOptions>) { ScreenOptions extends object,
EventMap extends EventMapBase
>(_: RouteConfig<ParamList, RouteName, State, ScreenOptions, EventMap>) {
/* istanbul ignore next */ /* istanbul ignore next */
return null; return null;
} }

View File

@@ -28,7 +28,7 @@ it('throws when getState is accessed without a container', () => {
const element = <Test />; const element = <Test />;
expect(() => render(element).update(element)).toThrowError( expect(() => render(element).update(element)).toThrowError(
"We couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'?" "Couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'?"
); );
}); });
@@ -47,7 +47,7 @@ it('throws when setState is accessed without a container', () => {
const element = <Test />; const element = <Test />;
expect(() => render(element).update(element)).toThrowError( expect(() => render(element).update(element)).toThrowError(
"We couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'?" "Couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'?"
); );
}); });

View File

@@ -372,7 +372,7 @@ it("doesn't update state if action wasn't handled", () => {
expect(onStateChange).toBeCalledTimes(0); expect(onStateChange).toBeCalledTimes(0);
expect(spy.mock.calls[0][0]).toMatch( expect(spy.mock.calls[0][0]).toMatch(
"The action 'INVALID' with payload 'undefined' was not handled by any navigator." "The action 'INVALID' was not handled by any navigator."
); );
spy.mockRestore(); spy.mockRestore();
@@ -1085,7 +1085,7 @@ it('throws descriptive error for invalid screen component', () => {
); );
expect(() => render(element).update(element)).toThrowError( expect(() => render(element).update(element)).toThrowError(
"Got an invalid value for 'component' prop for the screen 'foo'. It must be a a valid React Component." "Got an invalid value for 'component' prop for the screen 'foo'. It must be a valid React Component."
); );
}); });

View File

@@ -362,7 +362,7 @@ it('fires blur event when a route is removed with a delay', async () => {
expect(blurCallback).toBeCalledTimes(1); expect(blurCallback).toBeCalledTimes(1);
}); });
it('fires custom events', () => { it('fires custom events added with addListener', () => {
const eventName = 'someSuperCoolEvent'; const eventName = 'someSuperCoolEvent';
const TestNavigator = React.forwardRef((props: any, ref: any): any => { const TestNavigator = React.forwardRef((props: any, ref: any): any => {
@@ -409,10 +409,13 @@ it('fires custom events', () => {
expect(secondCallback).toBeCalledTimes(0); expect(secondCallback).toBeCalledTimes(0);
expect(thirdCallback).toBeCalledTimes(0); expect(thirdCallback).toBeCalledTimes(0);
const target =
ref.current.state.routes[ref.current.state.routes.length - 1].key;
act(() => { act(() => {
ref.current.navigation.emit({ ref.current.navigation.emit({
type: eventName, type: eventName,
target: ref.current.state.routes[ref.current.state.routes.length - 1].key, target,
data: 42, data: 42,
}); });
}); });
@@ -422,6 +425,7 @@ it('fires custom events', () => {
expect(thirdCallback).toBeCalledTimes(1); expect(thirdCallback).toBeCalledTimes(1);
expect(thirdCallback.mock.calls[0][0].type).toBe('someSuperCoolEvent'); expect(thirdCallback.mock.calls[0][0].type).toBe('someSuperCoolEvent');
expect(thirdCallback.mock.calls[0][0].data).toBe(42); 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].defaultPrevented).toBe(undefined);
expect(thirdCallback.mock.calls[0][0].preventDefault).toBe(undefined); expect(thirdCallback.mock.calls[0][0].preventDefault).toBe(undefined);
@@ -429,11 +433,197 @@ it('fires custom events', () => {
ref.current.navigation.emit({ type: eventName }); ref.current.navigation.emit({ type: eventName });
}); });
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(firstCallback).toBeCalledTimes(1);
expect(secondCallback).toBeCalledTimes(1); expect(secondCallback).toBeCalledTimes(1);
expect(thirdCallback).toBeCalledTimes(2); expect(thirdCallback).toBeCalledTimes(2);
}); });
it("doesn't call same listener multiple times with addListener", () => {
const eventName = 'someSuperCoolEvent';
const TestNavigator = React.forwardRef((props: any, ref: any): any => {
const { state, navigation, descriptors } = useNavigationBuilder(
MockRouter,
props
);
React.useImperativeHandle(ref, () => ({ navigation, state }), [
navigation,
state,
]);
return state.routes.map(route => descriptors[route.key].render());
});
const callback = jest.fn();
const Test = ({ navigation }: any) => {
React.useEffect(() => navigation.addListener(eventName, callback), [
navigation,
]);
return null;
};
const ref = React.createRef<any>();
const element = (
<BaseNavigationContainer>
<TestNavigator ref={ref}>
<Screen name="first" component={Test} />
<Screen name="second" component={Test} />
<Screen name="third" component={Test} />
</TestNavigator>
</BaseNavigationContainer>
);
render(element);
expect(callback).toBeCalledTimes(0);
act(() => {
ref.current.navigation.emit({ type: eventName });
});
expect(callback).toBeCalledTimes(1);
});
it('fires custom events added with 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={{ someSuperCoolEvent: firstCallback }}
component={jest.fn()}
/>
<Screen
name="second"
listeners={{ someSuperCoolEvent: secondCallback }}
component={jest.fn()}
/>
<Screen
name="third"
listeners={{ someSuperCoolEvent: thirdCallback }}
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(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);
});
it("doesn't call same listener multiple times with listeners", () => {
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 callback = jest.fn();
const ref = React.createRef<any>();
const element = (
<BaseNavigationContainer>
<TestNavigator ref={ref}>
<Screen
name="first"
listeners={{ someSuperCoolEvent: callback }}
component={jest.fn()}
/>
<Screen
name="second"
listeners={{ someSuperCoolEvent: callback }}
component={jest.fn()}
/>
<Screen
name="third"
listeners={{ someSuperCoolEvent: callback }}
component={jest.fn()}
/>
</TestNavigator>
</BaseNavigationContainer>
);
render(element);
expect(callback).toBeCalledTimes(0);
act(() => {
ref.current.navigation.emit({ type: eventName });
});
expect(callback).toBeCalledTimes(1);
});
it('has option to prevent default', () => { it('has option to prevent default', () => {
expect.assertions(5); expect.assertions(5);

View File

@@ -112,7 +112,7 @@ it('throws if called outside a navigation context', () => {
const Test = () => { const Test = () => {
// eslint-disable-next-line react-hooks/rules-of-hooks // eslint-disable-next-line react-hooks/rules-of-hooks
expect(() => useNavigation()).toThrow( expect(() => useNavigation()).toThrow(
"We couldn't find a navigation object. Is your component inside a screen in a navigator?" "Couldn't find a navigation object. Is your component inside a screen in a navigator?"
); );
return null; return null;

View File

@@ -374,7 +374,7 @@ it('logs error if no navigator handled the action', () => {
render(element).update(element); render(element).update(element);
expect(spy.mock.calls[0][0]).toMatch( expect(spy.mock.calls[0][0]).toMatch(
"The action 'UNKNOWN' with payload 'undefined' was not handled by any navigator." "The action 'UNKNOWN' was not handled by any navigator."
); );
spy.mockRestore(); spy.mockRestore();

View File

@@ -1,7 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import { ParamListBase } from '@react-navigation/routers'; import { ParamListBase, NavigationState } from '@react-navigation/routers';
import Screen from './Screen'; import Screen from './Screen';
import { TypedNavigator } from './types'; import { TypedNavigator, EventMapBase } from './types';
/** /**
* Higher order component to create a `Navigator` and `Screen` pair. * Higher order component to create a `Navigator` and `Screen` pair.
@@ -11,17 +11,21 @@ import { TypedNavigator } from './types';
* @returns Factory method to create a `Navigator` and `Screen` pair. * @returns Factory method to create a `Navigator` and `Screen` pair.
*/ */
export default function createNavigatorFactory< export default function createNavigatorFactory<
State extends NavigationState,
ScreenOptions extends object, ScreenOptions extends object,
EventMap extends EventMapBase,
NavigatorComponent extends React.ComponentType<any> NavigatorComponent extends React.ComponentType<any>
>(Navigator: NavigatorComponent) { >(Navigator: NavigatorComponent) {
return function<ParamList extends ParamListBase>(): TypedNavigator< return function<ParamList extends ParamListBase>(): TypedNavigator<
ParamList, ParamList,
State,
ScreenOptions, ScreenOptions,
EventMap,
typeof Navigator typeof Navigator
> { > {
if (arguments[0] !== undefined) { if (arguments[0] !== undefined) {
throw new Error( throw new Error(
"Creating a navigator doesn't take an argument. Maybe you are trying to use React Navigation 4 API with React Navigation 5? See https://reactnavigation.org/docs/en/upgrading-from-4.x.html for migration guide." "Creating a navigator doesn't take an argument. Maybe you are trying to use React Navigation 4 API with React Navigation 5? See https://reactnavigation.org/docs/upgrading-from-4.x for migration guide."
); );
} }

View File

@@ -48,6 +48,7 @@ export type EventArg<
* Type of the event (e.g. `focus`, `blur`) * Type of the event (e.g. `focus`, `blur`)
*/ */
readonly type: EventName; readonly type: EventName;
readonly target?: string;
} & (CanPreventDefault extends true } & (CanPreventDefault extends true
? { ? {
/** /**
@@ -360,7 +361,9 @@ export type Descriptor<
export type RouteConfig< export type RouteConfig<
ParamList extends ParamListBase, ParamList extends ParamListBase,
RouteName extends keyof ParamList, RouteName extends keyof ParamList,
ScreenOptions extends object State extends NavigationState,
ScreenOptions extends object,
EventMap extends EventMapBase
> = { > = {
/** /**
* Route name of this screen. * Route name of this screen.
@@ -377,6 +380,16 @@ export type RouteConfig<
navigation: any; navigation: any;
}) => ScreenOptions); }) => ScreenOptions);
/**
* Event listeners for this screen.
*/
listeners?: Partial<
{
[EventName in keyof (EventMap &
EventMapCore<State>)]: EventListenerCallback<EventMap, EventName>;
}
>;
/** /**
* Initial params object for the route. * Initial params object for the route.
*/ */
@@ -420,7 +433,9 @@ export type NavigationContainerRef =
export type TypedNavigator< export type TypedNavigator<
ParamList extends ParamListBase, ParamList extends ParamListBase,
State extends NavigationState,
ScreenOptions extends object, ScreenOptions extends object,
EventMap extends EventMapBase,
Navigator extends React.ComponentType<any> Navigator extends React.ComponentType<any>
> = { > = {
/** /**
@@ -451,6 +466,6 @@ export type TypedNavigator<
* Component used for specifying route configuration. * Component used for specifying route configuration.
*/ */
Screen: <RouteName extends keyof ParamList>( Screen: <RouteName extends keyof ParamList>(
_: RouteConfig<ParamList, RouteName, ScreenOptions> _: RouteConfig<ParamList, RouteName, State, ScreenOptions, EventMap>
) => null; ) => null;
}; };

View File

@@ -13,11 +13,24 @@ import NavigationBuilderContext, {
} from './NavigationBuilderContext'; } from './NavigationBuilderContext';
import { NavigationEventEmitter } from './useEventEmitter'; import { NavigationEventEmitter } from './useEventEmitter';
import useNavigationCache from './useNavigationCache'; import useNavigationCache from './useNavigationCache';
import { Descriptor, NavigationHelpers, RouteConfig, RouteProp } from './types'; import {
Descriptor,
NavigationHelpers,
RouteConfig,
RouteProp,
EventMapBase,
} from './types';
type Options<State extends NavigationState, ScreenOptions extends object> = { type Options<
State extends NavigationState,
ScreenOptions extends object,
EventMap extends EventMapBase
> = {
state: State; state: State;
screens: Record<string, RouteConfig<ParamListBase, string, ScreenOptions>>; screens: Record<
string,
RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>
>;
navigation: NavigationHelpers<ParamListBase>; navigation: NavigationHelpers<ParamListBase>;
screenOptions?: screenOptions?:
| ScreenOptions | ScreenOptions
@@ -49,7 +62,8 @@ type Options<State extends NavigationState, ScreenOptions extends object> = {
*/ */
export default function useDescriptors< export default function useDescriptors<
State extends NavigationState, State extends NavigationState,
ScreenOptions extends object ScreenOptions extends object,
EventMap extends EventMapBase
>({ >({
state, state,
screens, screens,
@@ -64,7 +78,7 @@ export default function useDescriptors<
onRouteFocus, onRouteFocus,
router, router,
emitter, emitter,
}: Options<State, ScreenOptions>) { }: Options<State, ScreenOptions, EventMap>) {
const [options, setOptions] = React.useState<Record<string, object>>({}); const [options, setOptions] = React.useState<Record<string, object>>({});
const { trackAction } = React.useContext(NavigationBuilderContext); const { trackAction } = React.useContext(NavigationBuilderContext);
@@ -133,6 +147,7 @@ export default function useDescriptors<
: screen.options({ : screen.options({
// @ts-ignore // @ts-ignore
route, route,
// @ts-ignore
navigation, navigation,
})), })),
// The options set via `navigation.setOptions` // The options set via `navigation.setOptions`

View File

@@ -5,12 +5,20 @@ export type NavigationEventEmitter = EventEmitter<Record<string, any>> & {
create: (target: string) => EventConsumer<Record<string, any>>; create: (target: string) => EventConsumer<Record<string, any>>;
}; };
type Listeners = ((data: any) => void)[]; type Listeners = ((e: any) => void)[];
/** /**
* Hook to manage the event system used by the navigator to notify screens of various events. * Hook to manage the event system used by the navigator to notify screens of various events.
*/ */
export default function useEventEmitter(): NavigationEventEmitter { export default function useEventEmitter(
listen?: (e: any) => void
): NavigationEventEmitter {
const listenRef = React.useRef(listen);
React.useEffect(() => {
listenRef.current = listen;
});
const listeners = React.useRef<Record<string, Record<string, Listeners>>>({}); const listeners = React.useRef<Record<string, Record<string, Listeners>>>({});
const create = React.useCallback((target: string) => { const create = React.useCallback((target: string) => {
@@ -60,7 +68,9 @@ export default function useEventEmitter(): NavigationEventEmitter {
const callbacks = const callbacks =
target !== undefined target !== undefined
? items[target] && items[target].slice() ? items[target] && items[target].slice()
: ([] as Listeners).concat(...Object.keys(items).map(t => items[t])); : ([] as Listeners)
.concat(...Object.keys(items).map(t => items[t]))
.filter((cb, i, self) => self.lastIndexOf(cb) === i);
const event: EventArg<any, any, any> = { const event: EventArg<any, any, any> = {
get type() { get type() {
@@ -68,8 +78,18 @@ export default function useEventEmitter(): NavigationEventEmitter {
}, },
}; };
if (target !== undefined) {
Object.defineProperty(event, 'target', {
enumerable: true,
get() {
return target;
},
});
}
if (data !== undefined) { if (data !== undefined) {
Object.defineProperty(event, 'data', { Object.defineProperty(event, 'data', {
enumerable: true,
get() { get() {
return data; return data;
}, },
@@ -81,11 +101,13 @@ export default function useEventEmitter(): NavigationEventEmitter {
Object.defineProperties(event, { Object.defineProperties(event, {
defaultPrevented: { defaultPrevented: {
enumerable: true,
get() { get() {
return defaultPrevented; return defaultPrevented;
}, },
}, },
preventDefault: { preventDefault: {
enumerable: true,
value() { value() {
defaultPrevented = true; defaultPrevented = true;
}, },
@@ -93,6 +115,8 @@ export default function useEventEmitter(): NavigationEventEmitter {
}); });
} }
listenRef.current?.(event);
callbacks?.forEach(cb => cb(event)); callbacks?.forEach(cb => cb(event));
return event as any; return event as any;

View File

@@ -46,7 +46,7 @@ export default function useFocusEffect(effect: EffectCallback) {
' fetchData();\n' + ' fetchData();\n' +
' }, [someId])\n' + ' }, [someId])\n' +
'};\n\n' + '};\n\n' +
'See usage guide: https://reactnavigation.org/docs/en/use-focus-effect.html'; 'See usage guide: https://reactnavigation.org/docs/use-focus-effect';
} else { } else {
message += ` You returned: '${JSON.stringify(destroy)}'`; message += ` You returned: '${JSON.stringify(destroy)}'`;
} }

View File

@@ -15,7 +15,7 @@ export default function useNavigation<
if (navigation === undefined) { if (navigation === undefined) {
throw new Error( throw new Error(
"We couldn't find a navigation object. Is your component inside a screen in a navigator?" "Couldn't find a navigation object. Is your component inside a screen in a navigator?"
); );
} }

View File

@@ -27,6 +27,7 @@ import {
DefaultNavigatorOptions, DefaultNavigatorOptions,
RouteConfig, RouteConfig,
PrivateValueStore, PrivateValueStore,
EventMapBase,
} from './types'; } from './types';
import useStateGetters from './useStateGetters'; import useStateGetters from './useStateGetters';
import useOnGetState from './useOnGetState'; import useOnGetState from './useOnGetState';
@@ -55,18 +56,28 @@ const isArrayEqual = (a: any[], b: any[]) =>
* *
* @param children React Elements to extract the config from. * @param children React Elements to extract the config from.
*/ */
const getRouteConfigsFromChildren = <ScreenOptions extends object>( const getRouteConfigsFromChildren = <
State extends NavigationState,
ScreenOptions extends object,
EventMap extends EventMapBase
>(
children: React.ReactNode children: React.ReactNode
) => { ) => {
const configs = React.Children.toArray(children).reduce< const configs = React.Children.toArray(children).reduce<
RouteConfig<ParamListBase, string, ScreenOptions>[] RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>[]
>((acc, child) => { >((acc, child) => {
if (React.isValidElement(child)) { if (React.isValidElement(child)) {
if (child.type === Screen) { if (child.type === Screen) {
// We can only extract the config from `Screen` elements // We can only extract the config from `Screen` elements
// If something else was rendered, it's probably a bug // If something else was rendered, it's probably a bug
acc.push( acc.push(
child.props as RouteConfig<ParamListBase, string, ScreenOptions> child.props as RouteConfig<
ParamListBase,
string,
State,
ScreenOptions,
EventMap
>
); );
return acc; return acc;
} }
@@ -75,7 +86,9 @@ const getRouteConfigsFromChildren = <ScreenOptions extends object>(
// When we encounter a fragment, we need to dive into its children to extract the configs // When we encounter a fragment, we need to dive into its children to extract the configs
// This is handy to conditionally define a group of screens // This is handy to conditionally define a group of screens
acc.push( acc.push(
...getRouteConfigsFromChildren<ScreenOptions>(child.props.children) ...getRouteConfigsFromChildren<State, ScreenOptions, EventMap>(
child.props.children
)
); );
return acc; return acc;
} }
@@ -116,7 +129,7 @@ const getRouteConfigsFromChildren = <ScreenOptions extends object>(
if (component !== undefined && !isValidElementType(component)) { if (component !== undefined && !isValidElementType(component)) {
throw new Error( throw new Error(
`Got an invalid value for 'component' prop for the screen '${name}'. It must be a a valid React Component.` `Got an invalid value for 'component' prop for the screen '${name}'. It must be a valid React Component.`
); );
} }
@@ -177,9 +190,17 @@ export default function useNavigationBuilder<
}) })
); );
const routeConfigs = getRouteConfigsFromChildren<ScreenOptions>(children); const routeConfigs = getRouteConfigsFromChildren<
State,
ScreenOptions,
EventMap
>(children);
const screens = routeConfigs.reduce< const screens = routeConfigs.reduce<
Record<string, RouteConfig<ParamListBase, string, ScreenOptions>> Record<
string,
RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>
>
>((acc, config) => { >((acc, config) => {
if (config.name in acc) { if (config.name in acc) {
throw new Error( throw new Error(
@@ -312,12 +333,14 @@ export default function useNavigationBuilder<
: state; : state;
} }
if (state !== nextState) { const shouldUpdate = state !== nextState;
// If the state needs to be updated, we'll schedule an update with React
// setState in render seems hacky, but that's how React docs implement getDerivedPropsFromState React.useEffect(() => {
// https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-getderivedstatefromprops if (shouldUpdate) {
setState(nextState); // If the state needs to be updated, we'll schedule an update with React
} setState(nextState);
}
}, [nextState, setState, shouldUpdate]);
// The up-to-date state will come in next render, but we don't need to wait for it // 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 // We can't use the outdated state since the screens have changed, which will cause error due to mismatched config
@@ -344,7 +367,35 @@ export default function useNavigationBuilder<
: (initializedStateRef.current as State); : (initializedStateRef.current as State);
}, [getCurrentState, isStateInitialized]); }, [getCurrentState, isStateInitialized]);
const emitter = useEventEmitter(); const emitter = useEventEmitter(e => {
let routeNames = [];
if (e.target) {
const name = state.routes.find(route => route.key === e.target)?.name;
if (name) {
routeNames.push(name);
}
} else {
routeNames.push(...Object.keys(screens));
}
const listeners = ([] as (((e: any) => void) | undefined)[])
.concat(
...routeNames.map(name => {
const { listeners } = screens[name];
return listeners
? Object.keys(listeners)
.filter(type => type === e.type)
.map(type => listeners[type])
: undefined;
})
)
.filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
listeners.forEach(listener => listener?.(e));
});
useFocusEvents({ state, emitter }); useFocusEvents({ state, emitter });
@@ -400,7 +451,7 @@ export default function useNavigationBuilder<
getStateForRoute, getStateForRoute,
}); });
const descriptors = useDescriptors<State, ScreenOptions>({ const descriptors = useDescriptors<State, ScreenOptions, EventMap>({
state, state,
screens, screens,
navigation, navigation,

View File

@@ -36,18 +36,45 @@ export default function useNavigationHelpers<
const parentNavigationHelpers = React.useContext(NavigationContext); const parentNavigationHelpers = React.useContext(NavigationContext);
return React.useMemo(() => { return React.useMemo(() => {
const dispatch = (action: Action | ((state: State) => Action)) => { const dispatch = (op: Action | ((state: State) => Action)) => {
const payload = const action = typeof op === 'function' ? op(getState()) : op;
typeof action === 'function' ? action(getState()) : action;
const handled = onAction(payload); const handled = onAction(action);
if (!handled && process.env.NODE_ENV !== 'production') { if (!handled && process.env.NODE_ENV !== 'production') {
console.error( const payload: Record<string, any> | undefined = action.payload;
`The action '${payload.type}' with payload '${JSON.stringify(
payload.payload let message = `The action '${action.type}'${
)}' was not handled by any navigator. If you are trying to navigate to a screen, check if the screen exists in your navigator. If you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/en/nesting-navigators.html#navigating-to-a-screen-in-a-nested-navigator.` payload ? ` with payload ${JSON.stringify(action.payload)}` : ''
); } was not handled by any navigator.`;
switch (action.type) {
case 'NAVIGATE':
case 'PUSH':
case 'REPLACE':
case 'JUMP_TO':
if (payload?.name) {
message += `\n\nDo you have a screen named '${payload.name}'?\n\nIf you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator.`;
} else {
message += `\n\nYou need to pass the name of the screen to navigate to.\n\nSee https://reactnavigation.org/docs/navigation-actions for usage.`;
}
break;
case 'GO_BACK':
case 'POP':
case 'POP_TO_TOP':
message += `\n\nIs there any screen to go back to?`;
break;
case 'OPEN_DRAWER':
case 'CLOSE_DRAWER':
case 'TOGGLE_DRAWER':
message += `\n\nIs your screen inside a Drawer navigator?`;
break;
}
message += `\n\nThis is a development-only warning and won't be shown in production.`;
console.error(message);
} }
}; };

View File

@@ -15,7 +15,7 @@ export default function useRoute<
if (route === undefined) { if (route === undefined) {
throw new Error( throw new Error(
"We couldn't find a route object. Is your component inside a screen in a navigator?" "Couldn't find a route object. Is your component inside a screen in a navigator?"
); );
} }

View File

@@ -3,6 +3,25 @@
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.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.1.0...@react-navigation/drawer@5.1.1) (2020-03-03)
**Note:** Version bump only for package @react-navigation/drawer
# [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.7...@react-navigation/drawer@5.1.0) (2020-02-26)
### Features
* add ability add listeners with listeners prop ([1624108](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/162410843c4f175ae107756de1c3af04d1d47aa7)), closes [#6756](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/6756)
## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.6...@react-navigation/drawer@5.0.7) (2020-02-21) ## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.0.6...@react-navigation/drawer@5.0.7) (2020-02-21)
**Note:** Version bump only for package @react-navigation/drawer **Note:** Version bump only for package @react-navigation/drawer

View File

@@ -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.0.7", "version": "5.1.1",
"keywords": [ "keywords": [
"react-native-component", "react-native-component",
"react-component", "react-component",
@@ -40,7 +40,7 @@
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.9.3", "@react-native-community/bob": "^0.9.3",
"@react-navigation/native": "^5.0.7", "@react-navigation/native": "^5.0.9",
"@types/react": "^16.9.19", "@types/react": "^16.9.19",
"@types/react-native": "^0.60.30", "@types/react-native": "^0.60.30",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",

View File

@@ -49,6 +49,8 @@ function DrawerNavigator({
} }
export default createNavigatorFactory< export default createNavigatorFactory<
DrawerNavigationState,
DrawerNavigationOptions, DrawerNavigationOptions,
DrawerNavigationEventMap,
typeof DrawerNavigator typeof DrawerNavigator
>(DrawerNavigator); >(DrawerNavigator);

View File

@@ -3,6 +3,25 @@
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.1](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.0...@react-navigation/material-bottom-tabs@5.1.1) (2020-03-03)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
# [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.7...@react-navigation/material-bottom-tabs@5.1.0) (2020-02-26)
### Features
* add ability add listeners with listeners prop ([1624108](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/commit/162410843c4f175ae107756de1c3af04d1d47aa7)), closes [#6756](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/issues/6756)
## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.6...@react-navigation/material-bottom-tabs@5.0.7) (2020-02-21) ## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.0.6...@react-navigation/material-bottom-tabs@5.0.7) (2020-02-21)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs **Note:** Version bump only for package @react-navigation/material-bottom-tabs

View File

@@ -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.0.7", "version": "5.1.1",
"keywords": [ "keywords": [
"react-native-component", "react-native-component",
"react-component", "react-component",
@@ -36,7 +36,7 @@
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.9.3", "@react-native-community/bob": "^0.9.3",
"@react-navigation/native": "^5.0.7", "@react-navigation/native": "^5.0.9",
"@types/react": "^16.9.19", "@types/react": "^16.9.19",
"@types/react-native": "^0.60.30", "@types/react-native": "^0.60.30",
"@types/react-native-vector-icons": "^6.4.5", "@types/react-native-vector-icons": "^6.4.5",

View File

@@ -49,6 +49,8 @@ function MaterialBottomTabNavigator({
} }
export default createNavigatorFactory< export default createNavigatorFactory<
TabNavigationState,
MaterialBottomTabNavigationOptions, MaterialBottomTabNavigationOptions,
MaterialBottomTabNavigationEventMap,
typeof MaterialBottomTabNavigator typeof MaterialBottomTabNavigator
>(MaterialBottomTabNavigator); >(MaterialBottomTabNavigator);

View File

@@ -3,6 +3,25 @@
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.1](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.0...@react-navigation/material-top-tabs@5.1.1) (2020-03-03)
**Note:** Version bump only for package @react-navigation/material-top-tabs
# [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.0.7...@react-navigation/material-top-tabs@5.1.0) (2020-02-26)
### Features
* add ability add listeners with listeners prop ([1624108](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/commit/162410843c4f175ae107756de1c3af04d1d47aa7)), closes [#6756](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/issues/6756)
## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.0.6...@react-navigation/material-top-tabs@5.0.7) (2020-02-21) ## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.0.6...@react-navigation/material-top-tabs@5.0.7) (2020-02-21)
**Note:** Version bump only for package @react-navigation/material-top-tabs **Note:** Version bump only for package @react-navigation/material-top-tabs

View File

@@ -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.0.7", "version": "5.1.1",
"keywords": [ "keywords": [
"react-native-component", "react-native-component",
"react-component", "react-component",
@@ -39,7 +39,7 @@
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.9.3", "@react-native-community/bob": "^0.9.3",
"@react-navigation/native": "^5.0.7", "@react-navigation/native": "^5.0.9",
"@types/react": "^16.9.19", "@types/react": "^16.9.19",
"@types/react-native": "^0.60.30", "@types/react-native": "^0.60.30",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",

View File

@@ -48,6 +48,8 @@ function MaterialTopTabNavigator({
} }
export default createNavigatorFactory< export default createNavigatorFactory<
TabNavigationState,
MaterialTopTabNavigationOptions, MaterialTopTabNavigationOptions,
MaterialTopTabNavigationEventMap,
typeof MaterialTopTabNavigator typeof MaterialTopTabNavigator
>(MaterialTopTabNavigator); >(MaterialTopTabNavigator);

View File

@@ -3,6 +3,22 @@
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.0.9](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.0.8...@react-navigation/native@5.0.9) (2020-03-03)
**Note:** Version bump only for package @react-navigation/native
## [5.0.8](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.0.7...@react-navigation/native@5.0.8) (2020-02-26)
**Note:** Version bump only for package @react-navigation/native
## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.0.6...@react-navigation/native@5.0.7) (2020-02-21) ## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.0.6...@react-navigation/native@5.0.7) (2020-02-21)
**Note:** Version bump only for package @react-navigation/native **Note:** Version bump only for package @react-navigation/native

View File

@@ -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.0.7", "version": "5.0.9",
"keywords": [ "keywords": [
"react-native", "react-native",
"react-navigation", "react-navigation",
@@ -31,7 +31,7 @@
"clean": "del lib" "clean": "del lib"
}, },
"dependencies": { "dependencies": {
"@react-navigation/core": "^5.1.6" "@react-navigation/core": "^5.2.1"
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.9.3", "@react-native-community/bob": "^0.9.3",

View File

@@ -3,6 +3,30 @@
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.0](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.0.3...@react-navigation/routers@5.1.0) (2020-03-03)
### Bug Fixes
* fix links for documentation ([5bb0f40](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/commit/5bb0f405ceb5755d39a0b5b1f2e4ecee0da051bc))
### Features
* make reset bubble up ([09f6808](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/commit/09f6808d7d43c70b2c502151f9f20fad03972886))
## [5.0.3](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.0.2...@react-navigation/routers@5.0.3) (2020-02-26)
**Note:** Version bump only for package @react-navigation/routers
## [5.0.2](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.0.1...@react-navigation/routers@5.0.2) (2020-02-21) ## [5.0.2](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.0.1...@react-navigation/routers@5.0.2) (2020-02-21)

View File

@@ -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.0.2", "version": "5.1.0",
"keywords": [ "keywords": [
"react", "react",
"react-native", "react-native",

View File

@@ -29,8 +29,28 @@ const BaseRouter = {
}; };
} }
case 'RESET': case 'RESET': {
return action.payload as PartialState<State>; const nextState = action.payload as State | PartialState<State>;
if (nextState.stale === false) {
if (
state.routeNames.length !== nextState.routeNames.length ||
nextState.routeNames.some(name => !state.routeNames.includes(name))
) {
return null;
}
}
if (
nextState.routes.some(
(route: { name: string }) => !state.routeNames.includes(route.name)
)
) {
return null;
}
return nextState;
}
default: default:
return null; return null;

View File

@@ -41,11 +41,11 @@ export function navigate(...args: any): Action {
if (typeof args[0] === 'string') { if (typeof args[0] === 'string') {
return { type: 'NAVIGATE', payload: { name: args[0], params: args[1] } }; return { type: 'NAVIGATE', payload: { name: args[0], params: args[1] } };
} else { } else {
const payload = args[0]; const payload = args[0] || {};
if (!payload.hasOwnProperty('key') && !payload.hasOwnProperty('name')) { if (!payload.hasOwnProperty('key') && !payload.hasOwnProperty('name')) {
throw new Error( throw new Error(
'You need to specify name or key when calling navigate with an object as the argument. See https://reactnavigation.org/docs/navigation-actions.html#navigate for usage.' 'You need to specify name or key when calling navigate with an object as the argument. See https://reactnavigation.org/docs/navigation-actions#navigate for usage.'
); );
} }

View File

@@ -83,3 +83,34 @@ it('resets state to new state with RESET', () => {
expect(result).toEqual({ index: 0, routes }); expect(result).toEqual({ index: 0, routes });
}); });
it("doesn't handle RESET if routes don't match routeNames", () => {
const routes = [
{ key: 'bar', name: 'bar', params: { fruit: 'orange' } },
{ key: 'baz', name: 'baz' },
{ key: 'qux', name: 'quz' },
];
const result = BaseRouter.getStateForAction(
STATE,
CommonActions.reset({
index: 0,
routes,
})
);
expect(result).toEqual(null);
});
it("doesn't handle RESET if routeNames don't match", () => {
const result = BaseRouter.getStateForAction(
STATE,
CommonActions.reset({
...STATE,
// @ts-ignore
routeNames: ['ten'],
})
);
expect(result).toEqual(null);
});

View File

@@ -3,6 +3,50 @@
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.1](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.1.0...@react-navigation/stack@5.1.1) (2020-03-03)
### Bug Fixes
* ignore back button press if screen isn't focused. closes [#7673](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7673) ([296c836](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/296c836064447e055a88e43cfbbf5f9de93838f0))
# [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.0.9...@react-navigation/stack@5.1.0) (2020-02-26)
### Features
* add ability add listeners with listeners prop ([1624108](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/162410843c4f175ae107756de1c3af04d1d47aa7)), closes [#6756](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/6756)
## [5.0.9](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.0.8...@react-navigation/stack@5.0.9) (2020-02-24)
### Bug Fixes
* enhance border radius in modals on new iPhones ([#6945](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/6945)) ([80a3370](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/80a337024abc53537ff4a63916cea38bb4f374bf))
## [5.0.8](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.0.7...@react-navigation/stack@5.0.8) (2020-02-21)
### Bug Fixes
* fix transparent header on Android ([a67b494](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/a67b49477eb500c81fedcd73bbd8102901a95170))
## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.0.6...@react-navigation/stack@5.0.7) (2020-02-21) ## [5.0.7](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.0.6...@react-navigation/stack@5.0.7) (2020-02-21)

View File

@@ -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.0.7", "version": "5.1.1",
"keywords": [ "keywords": [
"react-native-component", "react-native-component",
"react-component", "react-component",
@@ -40,7 +40,7 @@
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.9.3", "@react-native-community/bob": "^0.9.3",
"@react-native-community/masked-view": "^0.1.6", "@react-native-community/masked-view": "^0.1.6",
"@react-navigation/native": "^5.0.7", "@react-navigation/native": "^5.0.9",
"@types/color": "^3.0.1", "@types/color": "^3.0.1",
"@types/react": "^16.9.19", "@types/react": "^16.9.19",
"@types/react-native": "^0.60.30", "@types/react-native": "^0.60.30",

View File

@@ -1,4 +1,5 @@
import { Animated } from 'react-native'; import { Animated } from 'react-native';
import { isIphoneX } from 'react-native-iphone-x-helper';
import conditional from '../utils/conditional'; import conditional from '../utils/conditional';
import { import {
StackCardInterpolationProps, StackCardInterpolationProps,
@@ -152,8 +153,8 @@ export function forModalPresentationIOS({
? 0 ? 0
: index === 0 : index === 0
? progress.interpolate({ ? progress.interpolate({
inputRange: [0, 1, 2], inputRange: [0, 1, 1.0001, 2],
outputRange: [0, 0, 10], outputRange: [0, 0, isIphoneX() ? 38 : 0, 10],
}) })
: 10; : 10;

View File

@@ -74,6 +74,8 @@ function StackNavigator({
} }
export default createNavigatorFactory< export default createNavigatorFactory<
StackNavigationState,
StackNavigationOptions, StackNavigationOptions,
StackNavigationEventMap,
typeof StackNavigator typeof StackNavigator
>(StackNavigator); >(StackNavigator);

View File

@@ -43,7 +43,7 @@ export default React.memo(function Header(props: StackHeaderProps) {
const goBack = React.useCallback( const goBack = React.useCallback(
debounce(() => { debounce(() => {
if (navigation.canGoBack()) { if (navigation.isFocused() && navigation.canGoBack()) {
navigation.dispatch({ navigation.dispatch({
...StackActions.pop(), ...StackActions.pop(),
source: scene.route.key, source: scene.route.key,

View File

@@ -139,7 +139,7 @@ export default function HeaderContainer({
style={ style={
// Avoid positioning the focused header absolutely // Avoid positioning the focused header absolutely
// Otherwise accessibility tools don't seem to be able to find it // Otherwise accessibility tools don't seem to be able to find it
(mode === 'float' || options.headerTransparent) && !isFocused (mode === 'float' && !isFocused) || options.headerTransparent
? styles.header ? styles.header
: null : null
} }

View File

@@ -37,14 +37,6 @@ type GestureValues = {
[key: string]: Animated.Value; [key: string]: Animated.Value;
}; };
// @ts-ignore
const maybeExpoVersion = global.Expo?.Constants.manifest.sdkVersion.split(
'.'
)[0];
const isInsufficientExpoVersion = maybeExpoVersion
? Number(maybeExpoVersion) <= 36
: maybeExpoVersion === 'UNVERSIONED';
type Props = { type Props = {
mode: StackCardMode; mode: StackCardMode;
insets: EdgeInsets; insets: EdgeInsets;
@@ -82,37 +74,27 @@ type State = {
}; };
const EPSILON = 0.01; const EPSILON = 0.01;
const FAR_FAR_AWAY = 9000;
const dimensions = Dimensions.get('window'); const dimensions = Dimensions.get('window');
const layout = { width: dimensions.width, height: dimensions.height }; const layout = { width: dimensions.width, height: dimensions.height };
const MaybeScreenContainer = ({ const MaybeScreenContainer = ({
enabled, enabled,
style,
...rest ...rest
}: ViewProps & { }: ViewProps & {
enabled: boolean; enabled: boolean;
children: React.ReactNode; children: React.ReactNode;
}) => { }) => {
if (enabled && screensEnabled()) { if (enabled && screensEnabled()) {
return <ScreenContainer style={style} {...rest} />; return <ScreenContainer {...rest} />;
} }
return ( return <View {...rest} />;
<View
collapsable={!enabled}
removeClippedSubviews={Platform.OS !== 'ios' && enabled}
style={[style, { overflow: 'hidden' }]}
{...rest}
/>
);
}; };
const MaybeScreen = ({ const MaybeScreen = ({
enabled, enabled,
active, active,
style,
...rest ...rest
}: ViewProps & { }: ViewProps & {
enabled: boolean; enabled: boolean;
@@ -121,39 +103,10 @@ const MaybeScreen = ({
}) => { }) => {
if (enabled && screensEnabled()) { if (enabled && screensEnabled()) {
// @ts-ignore // @ts-ignore
return <Screen active={active} style={style} {...rest} />; return <Screen active={active} {...rest} />;
} }
return ( return <View {...rest} />;
<Animated.View
style={[
style,
{
overflow: 'hidden',
// Position the screen offscreen to take advantage of offscreen perf optimization
// https://facebook.github.io/react-native/docs/view#removeclippedsubviews
// This can be useful if screens is not enabled
// It's buggy on iOS, so we don't enable it there
top:
enabled && typeof active === 'number' && !active ? FAR_FAR_AWAY : 0,
transform: [
{
// If the `active` prop is animated node, we can't use the `left` property due to native driver
// So we use `translateY` instead
translateY:
enabled && typeof active !== 'number'
? active.interpolate({
inputRange: [0, 1],
outputRange: [FAR_FAR_AWAY, 0],
})
: 0,
},
],
},
]}
{...rest}
/>
);
}; };
const FALLBACK_DESCRIPTOR = Object.freeze({ options: {} }); const FALLBACK_DESCRIPTOR = Object.freeze({ options: {} });
@@ -450,9 +403,7 @@ export default class CardStack extends React.Component<Props, State> {
// Screens is buggy on iOS, so we don't enable it there // Screens is buggy on iOS, so we don't enable it there
// For modals, usually we want the screen underneath to be visible, so also disable it there // For modals, usually we want the screen underneath to be visible, so also disable it there
const isScreensEnabled = const isScreensEnabled = Platform.OS !== 'ios' && mode !== 'modal';
Platform.OS !== 'ios' &&
(isInsufficientExpoVersion ? mode !== 'modal' : true);
return ( return (
<React.Fragment> <React.Fragment>
@@ -466,26 +417,13 @@ export default class CardStack extends React.Component<Props, State> {
const gesture = gestures[route.key]; const gesture = gestures[route.key];
const scene = scenes[index]; const scene = scenes[index];
// Display current screen and a screen beneath. const isScreenActive = scene.progress.next
let isScreenActive: Animated.AnimatedInterpolation | 0 | 1 = ? scene.progress.next.interpolate({
index >= self.length - 2 ? 1 : 0; inputRange: [0, 1 - EPSILON, 1],
outputRange: [1, 1, 0],
if (isInsufficientExpoVersion) { extrapolate: 'clamp',
isScreenActive = })
index === self.length - 1 : 1;
? 1
: Platform.OS === 'android'
? scene.progress.next
? scene.progress.next.interpolate({
inputRange: [0, 1 - EPSILON, 1],
outputRange: [1, 1, 0],
extrapolate: 'clamp',
})
: 1
: index === self.length - 2
? 1
: 0;
}
const { const {
safeAreaInsets, safeAreaInsets,