mirror of
https://github.com/status-im/react-navigation.git
synced 2025-02-23 16:48:22 +00:00
Fix eslint issues and turn on prettier by default (#1195)
* Automatically generate prop-types from Flow * Remove propTypes usage * Fix flow * Modify some eslint settings * Fix flowtype * Lint tweaks * use prop-types pkg * Run prettier * Fix flow * Fix few lint issues * Make eslint pass * Run lint on tests * Fix flow * Fixes * Alphabetical * Trailing comma: ES5 for website compat, also fix config/paths * Apply eslint --fix only to src now * Fix missing transitionconfig * Update TypeDefinition.js * New stuff * Unstage website and examples * reformat code * Update circle.yml
This commit is contained in:
parent
23e310742c
commit
bbe9caff06
43
.eslintrc
43
.eslintrc
@ -1,13 +1,27 @@
|
||||
{
|
||||
"extends": "airbnb",
|
||||
"extends": [
|
||||
"airbnb",
|
||||
"prettier",
|
||||
"prettier/flowtype",
|
||||
"prettier/react"
|
||||
],
|
||||
"parser": "babel-eslint",
|
||||
"plugins": [
|
||||
"flowtype"
|
||||
"flowtype",
|
||||
"prettier"
|
||||
],
|
||||
"env": {
|
||||
"jasmine": true
|
||||
},
|
||||
"globals": {
|
||||
"ReactClass": true
|
||||
},
|
||||
"rules": {
|
||||
"prettier/prettier": ["error", {
|
||||
"trailingComma": "all",
|
||||
"singleQuote": true
|
||||
}],
|
||||
|
||||
"no-underscore-dangle": 0,
|
||||
"no-use-before-define": 0,
|
||||
"no-unused-expressions": 0,
|
||||
@ -36,11 +50,6 @@
|
||||
2,
|
||||
"boolean"
|
||||
],
|
||||
"flowtype/define-flow-type": 1,
|
||||
"flowtype/generic-spacing": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"flowtype/no-weak-types": 1,
|
||||
"flowtype/require-parameter-type": 2,
|
||||
"flowtype/require-return-type": [
|
||||
@ -51,26 +60,6 @@
|
||||
}
|
||||
],
|
||||
"flowtype/require-valid-file-annotation": 2,
|
||||
"flowtype/semi": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"flowtype/space-after-type-colon": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"flowtype/space-before-generic-bracket": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"flowtype/space-before-type-colon": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"flowtype/union-intersection-spacing": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"flowtype/use-flow-type": 1,
|
||||
"flowtype/valid-syntax": 1
|
||||
},
|
||||
|
@ -17,6 +17,3 @@ deployment:
|
||||
commands:
|
||||
- yarn run build-docs
|
||||
- ./scripts/deploy-website.sh
|
||||
test:
|
||||
pre:
|
||||
- yarn run flow
|
||||
|
@ -27,8 +27,10 @@
|
||||
"start": "node ./node_modules/react-native/local-cli/cli.js start --config ./rn-cli.config.js",
|
||||
"run-playground-ios": "cd examples/NavigationPlayground && react-native run-ios",
|
||||
"run-playground-android": "cd examples/NavigationPlayground && react-native run-android",
|
||||
"test": "jest",
|
||||
"test": "npm run lint && npm run flow && npm run jest",
|
||||
"jest": "jest",
|
||||
"lint": "eslint src",
|
||||
"format": "eslint --fix src",
|
||||
"flow": "flow",
|
||||
"prepublish": "npm run clean && npm run build"
|
||||
},
|
||||
@ -50,12 +52,15 @@
|
||||
"babel-preset-stage-1": "^6.16.0",
|
||||
"eslint": "^3.17.1",
|
||||
"eslint-config-airbnb": "^14.1.0",
|
||||
"eslint-config-prettier": "^1.5.0",
|
||||
"eslint-plugin-flowtype": "^2.30.3",
|
||||
"eslint-plugin-import": "^2.2.0",
|
||||
"eslint-plugin-jsx-a11y": "^4.0.0",
|
||||
"eslint-plugin-prettier": "^2.0.1",
|
||||
"eslint-plugin-react": "^6.10.0",
|
||||
"flow-bin": "^0.40.0",
|
||||
"jest": "^19.0.2",
|
||||
"prettier": "^0.22.0",
|
||||
"react": "16.0.0-alpha.6",
|
||||
"react-native": "^0.43.2",
|
||||
"react-native-vector-icons": "^3.0.0",
|
||||
|
@ -7,10 +7,11 @@ const RESET = namespacedAction('RESET');
|
||||
const SET_PARAMS = namespacedAction('SET_PARAMS');
|
||||
const URI = namespacedAction('URI');
|
||||
|
||||
const createAction = (type: string) => (payload: object = {}) => ({
|
||||
type,
|
||||
...payload,
|
||||
});
|
||||
const createAction = (type: string) =>
|
||||
(payload: Object = {}) => ({
|
||||
type,
|
||||
...payload,
|
||||
});
|
||||
|
||||
const back = createAction(BACK);
|
||||
const init = createAction(INIT);
|
||||
@ -28,19 +29,23 @@ const deprecatedActionMap = {
|
||||
Uri: URI,
|
||||
};
|
||||
|
||||
const mapDeprecatedActionAndWarn = (action: object) => {
|
||||
const mapDeprecatedActionAndWarn = (action: Object) => {
|
||||
const mappedType = deprecatedActionMap[action.type];
|
||||
if (!mappedType) { return action; }
|
||||
if (!mappedType) {
|
||||
return action;
|
||||
}
|
||||
|
||||
console.warn([
|
||||
`The action type '${action.type}' has been renamed to '${mappedType}'.`,
|
||||
`'${action.type}' will continue to work while in beta but will be removed`,
|
||||
'in the first major release. Moving forward, you should use the',
|
||||
'action constants and action creators exported by this library in',
|
||||
"the 'actions' object.",
|
||||
'See https://github.com/react-community/react-navigation/pull/120 for',
|
||||
'more details.',
|
||||
].join(' '));
|
||||
console.warn(
|
||||
[
|
||||
`The action type '${action.type}' has been renamed to '${mappedType}'.`,
|
||||
`'${action.type}' will continue to work while in beta but will be removed`,
|
||||
'in the first major release. Moving forward, you should use the',
|
||||
'action constants and action creators exported by this library in',
|
||||
"the 'actions' object.",
|
||||
'See https://github.com/react-community/react-navigation/pull/120 for',
|
||||
'more details.',
|
||||
].join(' '),
|
||||
);
|
||||
|
||||
return {
|
||||
...action,
|
||||
|
@ -1,12 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
import {
|
||||
BackAndroid,
|
||||
Linking,
|
||||
} from 'react-native';
|
||||
|
||||
export {
|
||||
BackAndroid,
|
||||
Linking,
|
||||
};
|
||||
import { BackAndroid, Linking } from 'react-native';
|
||||
|
||||
export { BackAndroid, Linking };
|
||||
|
@ -2,10 +2,7 @@
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
import type {
|
||||
NavigationRoute,
|
||||
NavigationState,
|
||||
} from './TypeDefinition';
|
||||
import type { NavigationRoute, NavigationState } from './TypeDefinition';
|
||||
|
||||
/**
|
||||
* Utilities to perform atomic operation with navigate state and routes.
|
||||
@ -16,12 +13,11 @@ import type {
|
||||
* ```
|
||||
*/
|
||||
const StateUtils = {
|
||||
|
||||
/**
|
||||
* Gets a route by key. If the route isn't found, returns `null`.
|
||||
*/
|
||||
get(state: NavigationState, key: string): ?NavigationRoute {
|
||||
return state.routes.find(route => route.key === key) || null;
|
||||
return state.routes.find((route: *) => route.key === key) || null;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -29,7 +25,7 @@ const StateUtils = {
|
||||
* routes of the navigation state, or -1 if it is not present.
|
||||
*/
|
||||
indexOf(state: NavigationState, key: string): number {
|
||||
return state.routes.map(route => route.key).indexOf(key);
|
||||
return state.routes.map((route: *) => route.key).indexOf(key);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -37,7 +33,7 @@ const StateUtils = {
|
||||
* routes of the navigation state.
|
||||
*/
|
||||
has(state: NavigationState, key: string): boolean {
|
||||
return !!state.routes.some(route => route.key === key);
|
||||
return !!state.routes.some((route: *) => route.key === key);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -185,7 +181,7 @@ const StateUtils = {
|
||||
const nextIndex: number = index === undefined ? routes.length - 1 : index;
|
||||
|
||||
if (state.routes.length === routes.length && state.index === nextIndex) {
|
||||
const compare = (route, ii) => routes[ii] === route;
|
||||
const compare = (route: *, ii: *) => routes[ii] === route;
|
||||
if (state.routes.every(compare)) {
|
||||
return state;
|
||||
}
|
||||
|
@ -11,7 +11,9 @@ export type HeaderMode = 'float' | 'screen' | 'none';
|
||||
export type HeaderProps = NavigationSceneRendererProps & {
|
||||
mode: HeaderMode,
|
||||
router: NavigationRouter<NavigationState, NavigationAction, NavigationStackScreenOptions>,
|
||||
getScreenDetails: NavigationScene => NavigationScreenDetails<NavigationStackScreenOptions>,
|
||||
getScreenDetails: (
|
||||
NavigationScene,
|
||||
) => NavigationScreenDetails<NavigationStackScreenOptions>,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -75,18 +77,21 @@ export type NavigationRouter<State, Action, Options> = {
|
||||
* an optional previous state. When the action is considered handled but the
|
||||
* state is unchanged, the output state is null.
|
||||
*/
|
||||
getStateForAction: (
|
||||
action: Action,
|
||||
lastState: ?State,
|
||||
) => ?State,
|
||||
getStateForAction: (action: Action, lastState: ?State) => ?State,
|
||||
|
||||
/**
|
||||
* Maps a URI-like string to an action. This can be mapped to a state
|
||||
* using `getStateForAction`.
|
||||
*/
|
||||
getActionForPathAndParams: (path: string, params?: NavigationParams) => ?Action,
|
||||
getActionForPathAndParams: (
|
||||
path: string,
|
||||
params?: NavigationParams,
|
||||
) => ?Action,
|
||||
|
||||
getPathAndParamsForState: (state: State) => {path: string, params?: NavigationParams},
|
||||
getPathAndParamsForState: (state: State) => {
|
||||
path: string,
|
||||
params?: NavigationParams,
|
||||
},
|
||||
|
||||
getComponentForRouteName: (routeName: string) => NavigationComponent,
|
||||
|
||||
@ -100,15 +105,23 @@ export type NavigationRouter<State, Action, Options> = {
|
||||
*
|
||||
* {routeName: 'Foo', key: '123'}
|
||||
*/
|
||||
getScreenOptions: NavigationScreenOptionsGetter<Options, Action>
|
||||
getScreenOptions: NavigationScreenOptionsGetter<Options, Action>,
|
||||
};
|
||||
|
||||
export type NavigationScreenOption<T> =
|
||||
| T
|
||||
| (navigation: NavigationScreenProp<NavigationRoute, NavigationAction>,
|
||||
config: T) => T;
|
||||
| ((
|
||||
navigation: NavigationScreenProp<NavigationRoute, NavigationAction>,
|
||||
config: T,
|
||||
) => T);
|
||||
|
||||
export type Style = { [key: string]: any } | number | false | null | void | Array<Style>;
|
||||
export type Style =
|
||||
| { [key: string]: any }
|
||||
| number
|
||||
| false
|
||||
| null
|
||||
| void
|
||||
| Array<Style>;
|
||||
|
||||
export type NavigationScreenDetails<T> = {
|
||||
options: T,
|
||||
@ -125,12 +138,17 @@ export type NavigationScreenConfigProps = {
|
||||
screenProps: Object,
|
||||
};
|
||||
|
||||
export type NavigationScreenConfig<Options> = Options
|
||||
| NavigationScreenConfigProps & {
|
||||
navigationOptions: NavigationScreenProp<NavigationRoute, NavigationAction>
|
||||
} => Options;
|
||||
export type NavigationScreenConfig<Options> =
|
||||
| Options
|
||||
| (NavigationScreenConfigProps & ((
|
||||
{
|
||||
navigationOptions: NavigationScreenProp<NavigationRoute, NavigationAction>,
|
||||
},
|
||||
) => Options));
|
||||
|
||||
export type NavigationComponent = NavigationScreenComponent<*, *> | NavigationNavigator<*, *, *, *>;
|
||||
export type NavigationComponent =
|
||||
| NavigationScreenComponent<*, *>
|
||||
| NavigationNavigator<*, *, *, *>;
|
||||
|
||||
export type NavigationScreenComponent<T, Options> = ReactClass<T> & {
|
||||
navigationOptions?: NavigationScreenConfig<Options>,
|
||||
@ -192,7 +210,7 @@ export type NavigationStackViewConfig = {
|
||||
headerComponent?: ReactClass<HeaderProps<*>>,
|
||||
cardStyle?: Style,
|
||||
onTransitionStart?: () => void,
|
||||
onTransitionEnd?: () => void
|
||||
onTransitionEnd?: () => void,
|
||||
};
|
||||
|
||||
export type NavigationStackScreenOptions = NavigationScreenOptions & {
|
||||
@ -238,13 +256,13 @@ export type NavigationRouteConfig<T> = T & {
|
||||
path?: string,
|
||||
};
|
||||
|
||||
export type NavigationScreenRouteConfig = NavigationScreenRouteConfig<{
|
||||
// React component or navigator for this route */
|
||||
screen: NavigationComponent,
|
||||
} | {
|
||||
// React component to lazily require and render for this route */
|
||||
getScreen: () => NavigationComponent,
|
||||
}>;
|
||||
export type NavigationScreenRouteConfig =
|
||||
| {
|
||||
screen: NavigationComponent,
|
||||
}
|
||||
| {
|
||||
getScreen: () => NavigationComponent,
|
||||
};
|
||||
|
||||
export type NavigationPathsConfig = {
|
||||
[routeName: string]: string,
|
||||
@ -261,18 +279,31 @@ export type NavigationTabRouterConfig = {
|
||||
};
|
||||
|
||||
export type NavigationTabScreenOptions = NavigationScreenOptions & {
|
||||
tabBarIcon?: React.Element<*>
|
||||
| (options: { tintColor: ?string, focused: boolean }) => ?React.Element<*>,
|
||||
tabBarLabel?: string | React.Element<*>
|
||||
| (options: { tintColor: ?string, focused: boolean }) => ?React.Element<*>,
|
||||
tabBarIcon?:
|
||||
| React.Element<*>
|
||||
| ((
|
||||
options: { tintColor: ?string, focused: boolean },
|
||||
) => ?React.Element<*>),
|
||||
tabBarLabel?:
|
||||
| string
|
||||
| React.Element<*>
|
||||
| ((
|
||||
options: { tintColor: ?string, focused: boolean },
|
||||
) => ?React.Element<*>),
|
||||
tabBarVisible?: boolean,
|
||||
};
|
||||
|
||||
export type NavigationDrawerScreenOptions = NavigationScreenOptions & {
|
||||
drawerIcon?: React.Element<*>
|
||||
| (options: { tintColor: ?string, focused: boolean }) => ?React.Element<*>,
|
||||
drawerLabel?: React.Element<*>
|
||||
| (options: { tintColor: ?string, focused: boolean }) => ?React.Element<*>,
|
||||
drawerIcon?:
|
||||
| React.Element<*>
|
||||
| ((
|
||||
options: { tintColor: ?string, focused: boolean },
|
||||
) => ?React.Element<*>),
|
||||
drawerLabel?:
|
||||
| React.Element<*>
|
||||
| ((
|
||||
options: { tintColor: ?string, focused: boolean },
|
||||
) => ?React.Element<*>),
|
||||
};
|
||||
|
||||
export type NavigationRouteConfigMap = {
|
||||
@ -290,7 +321,11 @@ export type NavigationScreenProp<S, A> = {
|
||||
state: S,
|
||||
dispatch: NavigationDispatch<A>,
|
||||
goBack: (routeKey?: ?string) => boolean,
|
||||
navigate: (routeName: string, params?: NavigationParams, action?: NavigationAction) => boolean,
|
||||
navigate: (
|
||||
routeName: string,
|
||||
params?: NavigationParams,
|
||||
action?: NavigationAction,
|
||||
) => boolean,
|
||||
setParams: (newParams: NavigationParams) => boolean,
|
||||
};
|
||||
|
||||
@ -381,10 +416,10 @@ export type NavigationStyleInterpolator = (
|
||||
export type LayoutEvent = {
|
||||
nativeEvent: {
|
||||
layout: {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
@ -7,31 +7,55 @@ describe('actions', () => {
|
||||
|
||||
it('exports back action and type', () => {
|
||||
expect(NavigationActions.back()).toEqual({ type: NavigationActions.BACK });
|
||||
expect(NavigationActions.back(data)).toEqual({ type: NavigationActions.BACK, ...data });
|
||||
expect(NavigationActions.back(data)).toEqual({
|
||||
type: NavigationActions.BACK,
|
||||
...data,
|
||||
});
|
||||
});
|
||||
|
||||
it('exports init action and type', () => {
|
||||
expect(NavigationActions.init()).toEqual({ type: NavigationActions.INIT });
|
||||
expect(NavigationActions.init(data)).toEqual({ type: NavigationActions.INIT, ...data });
|
||||
expect(NavigationActions.init(data)).toEqual({
|
||||
type: NavigationActions.INIT,
|
||||
...data,
|
||||
});
|
||||
});
|
||||
|
||||
it('exports navigate action and type', () => {
|
||||
expect(NavigationActions.navigate()).toEqual({ type: NavigationActions.NAVIGATE });
|
||||
expect(NavigationActions.navigate(data)).toEqual({ type: NavigationActions.NAVIGATE, ...data });
|
||||
expect(NavigationActions.navigate()).toEqual({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
});
|
||||
expect(NavigationActions.navigate(data)).toEqual({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
...data,
|
||||
});
|
||||
});
|
||||
|
||||
it('exports reset action and type', () => {
|
||||
expect(NavigationActions.reset()).toEqual({ type: NavigationActions.RESET });
|
||||
expect(NavigationActions.reset(data)).toEqual({ type: NavigationActions.RESET, ...data });
|
||||
expect(NavigationActions.reset()).toEqual({
|
||||
type: NavigationActions.RESET,
|
||||
});
|
||||
expect(NavigationActions.reset(data)).toEqual({
|
||||
type: NavigationActions.RESET,
|
||||
...data,
|
||||
});
|
||||
});
|
||||
|
||||
it('exports setParams action and type', () => {
|
||||
expect(NavigationActions.setParams()).toEqual({ type: NavigationActions.SET_PARAMS });
|
||||
expect(NavigationActions.setParams(data)).toEqual({ type: NavigationActions.SET_PARAMS, ...data });
|
||||
expect(NavigationActions.setParams()).toEqual({
|
||||
type: NavigationActions.SET_PARAMS,
|
||||
});
|
||||
expect(NavigationActions.setParams(data)).toEqual({
|
||||
type: NavigationActions.SET_PARAMS,
|
||||
...data,
|
||||
});
|
||||
});
|
||||
|
||||
it('exports uri action and type', () => {
|
||||
expect(NavigationActions.uri()).toEqual({ type: NavigationActions.URI });
|
||||
expect(NavigationActions.uri(data)).toEqual({ type: NavigationActions.URI, ...data });
|
||||
expect(NavigationActions.uri(data)).toEqual({
|
||||
type: NavigationActions.URI,
|
||||
...data,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -8,19 +8,28 @@ describe('StateUtils', () => {
|
||||
// Getters
|
||||
it('gets route', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }] };
|
||||
expect(NavigationStateUtils.get(state, 'a')).toEqual({ key: 'a', routeName });
|
||||
expect(NavigationStateUtils.get(state, 'a')).toEqual({
|
||||
key: 'a',
|
||||
routeName,
|
||||
});
|
||||
expect(NavigationStateUtils.get(state, 'b')).toBe(null);
|
||||
});
|
||||
|
||||
it('gets route index', () => {
|
||||
const state = { index: 1, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const state = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
expect(NavigationStateUtils.indexOf(state, 'a')).toBe(0);
|
||||
expect(NavigationStateUtils.indexOf(state, 'b')).toBe(1);
|
||||
expect(NavigationStateUtils.indexOf(state, 'c')).toBe(-1);
|
||||
});
|
||||
|
||||
it('has a route', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
expect(NavigationStateUtils.has(state, 'b')).toBe(true);
|
||||
expect(NavigationStateUtils.has(state, 'c')).toBe(false);
|
||||
});
|
||||
@ -28,18 +37,27 @@ describe('StateUtils', () => {
|
||||
// Push
|
||||
it('pushes a route', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }] };
|
||||
const newState = { index: 1, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
expect(NavigationStateUtils.push(state, { key: 'b', routeName })).toEqual(newState);
|
||||
const newState = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
expect(NavigationStateUtils.push(state, { key: 'b', routeName })).toEqual(
|
||||
newState,
|
||||
);
|
||||
});
|
||||
|
||||
it('does not push duplicated route', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }] };
|
||||
expect(() => NavigationStateUtils.push(state, { key: 'a', routeName })).toThrow();
|
||||
expect(() =>
|
||||
NavigationStateUtils.push(state, { key: 'a', routeName })).toThrow();
|
||||
});
|
||||
|
||||
// Pop
|
||||
it('pops route', () => {
|
||||
const state = { index: 1, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const state = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
const newState = { index: 0, routes: [{ key: 'a', routeName }] };
|
||||
expect(NavigationStateUtils.pop(state)).toEqual(newState);
|
||||
});
|
||||
@ -51,88 +69,127 @@ describe('StateUtils', () => {
|
||||
|
||||
// Jump
|
||||
it('jumps to new index', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const newState = { index: 1, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
const newState = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
expect(NavigationStateUtils.jumpToIndex(state, 0)).toBe(state);
|
||||
expect(NavigationStateUtils.jumpToIndex(state, 1)).toEqual(newState);
|
||||
});
|
||||
|
||||
it('throws if jumps to invalid index', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
expect(() => NavigationStateUtils.jumpToIndex(state, 2)).toThrow();
|
||||
});
|
||||
|
||||
it('jumps to new key', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const newState = { index: 1, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
const newState = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
expect(NavigationStateUtils.jumpTo(state, 'a')).toBe(state);
|
||||
expect(NavigationStateUtils.jumpTo(state, 'b')).toEqual(newState);
|
||||
});
|
||||
|
||||
it('throws if jumps to invalid key', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
expect(() => NavigationStateUtils.jumpTo(state, 'c')).toThrow();
|
||||
});
|
||||
|
||||
it('move backwards', () => {
|
||||
const state = { index: 1, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const newState = { index: 0, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const state = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
const newState = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
expect(NavigationStateUtils.back(state)).toEqual(newState);
|
||||
expect(NavigationStateUtils.back(newState)).toBe(newState);
|
||||
});
|
||||
|
||||
it('move forwards', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const newState = { index: 1, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
const newState = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
expect(NavigationStateUtils.forward(state)).toEqual(newState);
|
||||
expect(NavigationStateUtils.forward(newState)).toBe(newState);
|
||||
});
|
||||
|
||||
// Replace
|
||||
it('Replaces by key', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const newState = { index: 1, routes: [{ key: 'a', routeName }, { key: 'c', routeName }] };
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
const newState = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'c', routeName }],
|
||||
};
|
||||
expect(
|
||||
NavigationStateUtils.replaceAt(
|
||||
state,
|
||||
'b',
|
||||
{ key: 'c', routeName },
|
||||
)
|
||||
NavigationStateUtils.replaceAt(state, 'b', { key: 'c', routeName }),
|
||||
).toEqual(newState);
|
||||
});
|
||||
|
||||
it('Replaces by index', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const newState = { index: 1, routes: [{ key: 'a', routeName }, { key: 'c', routeName }] };
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
const newState = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'c', routeName }],
|
||||
};
|
||||
expect(
|
||||
NavigationStateUtils.replaceAtIndex(
|
||||
state,
|
||||
1,
|
||||
{ key: 'c', routeName },
|
||||
)
|
||||
NavigationStateUtils.replaceAtIndex(state, 1, { key: 'c', routeName }),
|
||||
).toEqual(newState);
|
||||
});
|
||||
|
||||
it('Returns the state if index matches the route', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
expect(
|
||||
NavigationStateUtils.replaceAtIndex(
|
||||
state,
|
||||
1,
|
||||
state.routes[1],
|
||||
)
|
||||
NavigationStateUtils.replaceAtIndex(state, 1, state.routes[1]),
|
||||
).toEqual(state);
|
||||
});
|
||||
|
||||
// Reset
|
||||
it('Resets routes', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const newState = { index: 1, routes: [{ key: 'x', routeName }, { key: 'y', routeName }] };
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
const newState = {
|
||||
index: 1,
|
||||
routes: [{ key: 'x', routeName }, { key: 'y', routeName }],
|
||||
};
|
||||
expect(
|
||||
NavigationStateUtils.reset(
|
||||
state,
|
||||
[{ key: 'x', routeName }, { key: 'y', routeName }],
|
||||
)
|
||||
NavigationStateUtils.reset(state, [
|
||||
{ key: 'x', routeName },
|
||||
{ key: 'y', routeName },
|
||||
]),
|
||||
).toEqual(newState);
|
||||
|
||||
expect(() => {
|
||||
@ -141,18 +198,28 @@ describe('StateUtils', () => {
|
||||
});
|
||||
|
||||
it('Resets routes with index', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }, { key: 'b', routeName }] };
|
||||
const newState = { index: 0, routes: [{ key: 'x', routeName }, { key: 'y', routeName }] };
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
const newState = {
|
||||
index: 0,
|
||||
routes: [{ key: 'x', routeName }, { key: 'y', routeName }],
|
||||
};
|
||||
expect(
|
||||
NavigationStateUtils.reset(
|
||||
state,
|
||||
[{ key: 'x', routeName }, { key: 'y', routeName }],
|
||||
0,
|
||||
)
|
||||
),
|
||||
).toEqual(newState);
|
||||
|
||||
expect(() => {
|
||||
NavigationStateUtils.reset(state, [{ key: 'x', routeName }, { key: 'y', routeName }], 100);
|
||||
NavigationStateUtils.reset(
|
||||
state,
|
||||
[{ key: 'x', routeName }, { key: 'y', routeName }],
|
||||
100,
|
||||
);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
@ -5,31 +5,46 @@ import addNavigationHelpers from '../addNavigationHelpers';
|
||||
|
||||
describe('addNavigationHelpers', () => {
|
||||
it('handles Back action', () => {
|
||||
const mockedDispatch = jest.fn(() => false).mockImplementationOnce(() => true);
|
||||
expect(addNavigationHelpers({
|
||||
state: { key: 'A', routeName: 'Home' },
|
||||
dispatch: mockedDispatch,
|
||||
}).goBack('A')).toEqual(true);
|
||||
expect(mockedDispatch).toBeCalledWith({ type: NavigationActions.BACK, key: 'A' });
|
||||
const mockedDispatch = jest
|
||||
.fn(() => false)
|
||||
.mockImplementationOnce(() => true);
|
||||
expect(
|
||||
addNavigationHelpers({
|
||||
state: { key: 'A', routeName: 'Home' },
|
||||
dispatch: mockedDispatch,
|
||||
}).goBack('A'),
|
||||
).toEqual(true);
|
||||
expect(mockedDispatch).toBeCalledWith({
|
||||
type: NavigationActions.BACK,
|
||||
key: 'A',
|
||||
});
|
||||
expect(mockedDispatch.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('handles Back action when the key is not defined', () => {
|
||||
const mockedDispatch = jest.fn(() => false).mockImplementationOnce(() => true);
|
||||
expect(addNavigationHelpers({
|
||||
state: { routeName: 'Home' },
|
||||
dispatch: mockedDispatch,
|
||||
}).goBack()).toEqual(true);
|
||||
const mockedDispatch = jest
|
||||
.fn(() => false)
|
||||
.mockImplementationOnce(() => true);
|
||||
expect(
|
||||
addNavigationHelpers({
|
||||
state: { routeName: 'Home' },
|
||||
dispatch: mockedDispatch,
|
||||
}).goBack(),
|
||||
).toEqual(true);
|
||||
expect(mockedDispatch).toBeCalledWith({ type: NavigationActions.BACK });
|
||||
expect(mockedDispatch.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('handles Navigate action', () => {
|
||||
const mockedDispatch = jest.fn(() => false).mockImplementationOnce(() => true);
|
||||
expect(addNavigationHelpers({
|
||||
state: { routeName: 'Home' },
|
||||
dispatch: mockedDispatch,
|
||||
}).navigate('Profile', { name: 'Matt' })).toEqual(true);
|
||||
const mockedDispatch = jest
|
||||
.fn(() => false)
|
||||
.mockImplementationOnce(() => true);
|
||||
expect(
|
||||
addNavigationHelpers({
|
||||
state: { routeName: 'Home' },
|
||||
dispatch: mockedDispatch,
|
||||
}).navigate('Profile', { name: 'Matt' }),
|
||||
).toEqual(true);
|
||||
expect(mockedDispatch).toBeCalledWith({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
params: { name: 'Matt' },
|
||||
@ -39,11 +54,15 @@ describe('addNavigationHelpers', () => {
|
||||
});
|
||||
|
||||
it('handles SetParams action', () => {
|
||||
const mockedDispatch = jest.fn(() => false).mockImplementationOnce(() => true);
|
||||
expect(addNavigationHelpers({
|
||||
state: { key: 'B', routeName: 'Settings' },
|
||||
dispatch: mockedDispatch,
|
||||
}).setParams({ notificationsEnabled: 'yes' })).toEqual(true);
|
||||
const mockedDispatch = jest
|
||||
.fn(() => false)
|
||||
.mockImplementationOnce(() => true);
|
||||
expect(
|
||||
addNavigationHelpers({
|
||||
state: { key: 'B', routeName: 'Settings' },
|
||||
dispatch: mockedDispatch,
|
||||
}).setParams({ notificationsEnabled: 'yes' }),
|
||||
).toEqual(true);
|
||||
expect(mockedDispatch).toBeCalledWith({
|
||||
type: NavigationActions.SET_PARAMS,
|
||||
key: 'B',
|
||||
|
@ -12,30 +12,38 @@ import type {
|
||||
|
||||
import NavigationActions from './NavigationActions';
|
||||
|
||||
export default function<S: *> (navigation: NavigationProp<S, NavigationAction>) {
|
||||
export default function<S: *>(navigation: NavigationProp<S, NavigationAction>) {
|
||||
return {
|
||||
...navigation,
|
||||
goBack: (key?: ?string): boolean => navigation.dispatch(NavigationActions.back({
|
||||
key: key === undefined ? navigation.state.key : key,
|
||||
})),
|
||||
goBack: (key?: ?string): boolean =>
|
||||
navigation.dispatch(
|
||||
NavigationActions.back({
|
||||
key: key === undefined ? navigation.state.key : key,
|
||||
}),
|
||||
),
|
||||
navigate: (
|
||||
routeName: string,
|
||||
params?: NavigationParams,
|
||||
action?: NavigationAction): boolean =>
|
||||
navigation.dispatch(NavigationActions.navigate({
|
||||
action?: NavigationAction,
|
||||
): boolean =>
|
||||
navigation.dispatch(
|
||||
NavigationActions.navigate({
|
||||
routeName,
|
||||
params,
|
||||
action,
|
||||
})),
|
||||
}),
|
||||
),
|
||||
/**
|
||||
* For updating current route params. For example the nav bar title and
|
||||
* buttons are based on the route params.
|
||||
* This means `setParams` can be used to update nav bar for example.
|
||||
*/
|
||||
setParams: (params: NavigationParams): boolean =>
|
||||
navigation.dispatch(NavigationActions.setParams({
|
||||
params,
|
||||
key: navigation.state.key,
|
||||
})),
|
||||
navigation.dispatch(
|
||||
NavigationActions.setParams({
|
||||
params,
|
||||
key: navigation.state.key,
|
||||
}),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
@ -2,10 +2,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import {
|
||||
BackAndroid,
|
||||
Linking,
|
||||
} from './PlatformHelpers';
|
||||
import { BackAndroid, Linking } from './PlatformHelpers';
|
||||
import NavigationActions from './NavigationActions';
|
||||
import addNavigationHelpers from './addNavigationHelpers';
|
||||
|
||||
@ -19,7 +16,11 @@ import type {
|
||||
|
||||
type NavigationContainerProps = {
|
||||
uriPrefix?: string,
|
||||
onNavigationStateChange?: (NavigationState, NavigationState, NavigationAction) => void,
|
||||
onNavigationStateChange?: (
|
||||
NavigationState,
|
||||
NavigationState,
|
||||
NavigationAction,
|
||||
) => void,
|
||||
};
|
||||
|
||||
type Props<T> = NavigationContainerProps & NavigationNavigatorProps<T>;
|
||||
@ -75,7 +76,9 @@ export default function createNavigationContainer<T: *>(
|
||||
}
|
||||
|
||||
const {
|
||||
navigation, screenProps, navigationOptions,
|
||||
navigation,
|
||||
screenProps,
|
||||
navigationOptions,
|
||||
...containerProps
|
||||
} = props;
|
||||
|
||||
@ -84,9 +87,9 @@ export default function createNavigationContainer<T: *>(
|
||||
invariant(
|
||||
keys.length === 0,
|
||||
'This navigator has both navigation and container props, so it is ' +
|
||||
`unclear if it should own its own state. Remove props: "${keys.join(', ')}" ` +
|
||||
'if the navigator should get its state from the navigation prop. If the ' +
|
||||
'navigator should maintain its own state, do not pass a navigation prop.',
|
||||
`unclear if it should own its own state. Remove props: "${keys.join(', ')}" ` +
|
||||
'if the navigator should get its state from the navigation prop. If the ' +
|
||||
'navigator should maintain its own state, do not pass a navigation prop.',
|
||||
);
|
||||
}
|
||||
|
||||
@ -120,8 +123,8 @@ export default function createNavigationContainer<T: *>(
|
||||
action: NavigationAction,
|
||||
) {
|
||||
if (
|
||||
typeof this.props.onNavigationStateChange === 'undefined'
|
||||
&& this._isStateful()
|
||||
typeof this.props.onNavigationStateChange === 'undefined' &&
|
||||
this._isStateful()
|
||||
) {
|
||||
/* eslint-disable no-console */
|
||||
if (console.group) {
|
||||
@ -131,7 +134,11 @@ export default function createNavigationContainer<T: *>(
|
||||
console.log('Last State: ', prevNav);
|
||||
console.groupEnd();
|
||||
} else {
|
||||
console.log('Navigation Dispatch: ', { action, newState: nav, lastState: prevNav });
|
||||
console.log('Navigation Dispatch: ', {
|
||||
action,
|
||||
newState: nav,
|
||||
lastState: prevNav,
|
||||
});
|
||||
}
|
||||
/* eslint-enable no-console */
|
||||
return;
|
||||
@ -151,16 +158,16 @@ export default function createNavigationContainer<T: *>(
|
||||
return;
|
||||
}
|
||||
|
||||
this.subs = BackAndroid.addEventListener(
|
||||
'backPress',
|
||||
() => this.dispatch(NavigationActions.back()),
|
||||
);
|
||||
this.subs = BackAndroid.addEventListener('backPress', () =>
|
||||
this.dispatch(NavigationActions.back()));
|
||||
|
||||
Linking.addEventListener('url', ({ url }: { url: string }) => {
|
||||
this._handleOpenURL(url);
|
||||
});
|
||||
|
||||
Linking.getInitialURL().then((url: string) => url && this._handleOpenURL(url));
|
||||
Linking.getInitialURL().then(
|
||||
(url: string) => url && this._handleOpenURL(url),
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@ -175,7 +182,8 @@ export default function createNavigationContainer<T: *>(
|
||||
}
|
||||
const nav = Component.router.getStateForAction(action, state.nav);
|
||||
if (nav && nav !== state.nav) {
|
||||
this.setState({ nav }, () => this._onNavigationStateChange(state.nav, nav, action));
|
||||
this.setState({ nav }, () =>
|
||||
this._onNavigationStateChange(state.nav, nav, action));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -194,15 +202,9 @@ export default function createNavigationContainer<T: *>(
|
||||
}
|
||||
navigation = this._navigation;
|
||||
}
|
||||
return (
|
||||
<Component
|
||||
{...this.props}
|
||||
navigation={navigation}
|
||||
/>
|
||||
);
|
||||
return <Component {...this.props} navigation={navigation} />;
|
||||
}
|
||||
}
|
||||
|
||||
return NavigationContainer;
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
Dimensions,
|
||||
Platform,
|
||||
} from 'react-native';
|
||||
import { Dimensions, Platform } from 'react-native';
|
||||
|
||||
import createNavigator from './createNavigator';
|
||||
import createNavigationContainer from '../createNavigationContainer';
|
||||
@ -29,7 +26,8 @@ const DefaultDrawerConfig = {
|
||||
* Default drawer width is screen width - header width
|
||||
* https://material.io/guidelines/patterns/navigation-drawer.html
|
||||
*/
|
||||
drawerWidth: Dimensions.get('window').width - (Platform.OS === 'android' ? 56 : 64),
|
||||
drawerWidth: Dimensions.get('window').width -
|
||||
(Platform.OS === 'android' ? 56 : 64),
|
||||
contentComponent: DrawerView.Items,
|
||||
drawerPosition: 'left',
|
||||
};
|
||||
@ -49,31 +47,41 @@ const DrawerNavigator = (
|
||||
} = mergedConfig;
|
||||
|
||||
const contentRouter = TabRouter(routeConfigs, tabsConfig);
|
||||
const drawerRouter = TabRouter({
|
||||
DrawerClose: {
|
||||
screen: createNavigator(contentRouter, routeConfigs, config, NavigatorTypes.DRAWER)(
|
||||
(props: *) => <DrawerScreen {...props} />,
|
||||
),
|
||||
},
|
||||
DrawerOpen: {
|
||||
screen: () => null,
|
||||
},
|
||||
}, {
|
||||
initialRouteName: 'DrawerClose',
|
||||
});
|
||||
|
||||
const navigator = createNavigator(drawerRouter, routeConfigs, config, NavigatorTypes.DRAWER)(
|
||||
(props: *) => (
|
||||
<DrawerView
|
||||
{...props}
|
||||
drawerWidth={drawerWidth}
|
||||
contentComponent={contentComponent}
|
||||
contentOptions={contentOptions}
|
||||
drawerPosition={drawerPosition}
|
||||
/>
|
||||
),
|
||||
const drawerRouter = TabRouter(
|
||||
{
|
||||
DrawerClose: {
|
||||
screen: createNavigator(
|
||||
contentRouter,
|
||||
routeConfigs,
|
||||
config,
|
||||
NavigatorTypes.DRAWER,
|
||||
)((props: *) => <DrawerScreen {...props} />),
|
||||
},
|
||||
DrawerOpen: {
|
||||
screen: () => null,
|
||||
},
|
||||
},
|
||||
{
|
||||
initialRouteName: 'DrawerClose',
|
||||
},
|
||||
);
|
||||
|
||||
const navigator = createNavigator(
|
||||
drawerRouter,
|
||||
routeConfigs,
|
||||
config,
|
||||
NavigatorTypes.DRAWER,
|
||||
)((props: *) => (
|
||||
<DrawerView
|
||||
{...props}
|
||||
drawerWidth={drawerWidth}
|
||||
contentComponent={contentComponent}
|
||||
contentOptions={contentOptions}
|
||||
drawerPosition={drawerPosition}
|
||||
/>
|
||||
));
|
||||
|
||||
return createNavigationContainer(navigator, containerConfig);
|
||||
};
|
||||
|
||||
|
@ -1,10 +1,9 @@
|
||||
/* @flow */
|
||||
|
||||
export type NavigatorType = (
|
||||
'react-navigation/STACK' |
|
||||
'react-navigation/TABS' |
|
||||
'react-navigation/DRAWER'
|
||||
);
|
||||
export type NavigatorType =
|
||||
| 'react-navigation/STACK'
|
||||
| 'react-navigation/TABS'
|
||||
| 'react-navigation/DRAWER';
|
||||
|
||||
const STACK = 'react-navigation/STACK';
|
||||
const TABS = 'react-navigation/TABS';
|
||||
|
@ -42,19 +42,23 @@ export default (
|
||||
};
|
||||
|
||||
const router = StackRouter(routeConfigMap, stackRouterConfig);
|
||||
const navigator = createNavigator(router, routeConfigMap, stackConfig, NavigatorTypes.STACK)(
|
||||
(props: *) => (
|
||||
<CardStackTransitioner
|
||||
{...props}
|
||||
headerComponent={headerComponent}
|
||||
headerMode={headerMode}
|
||||
mode={mode}
|
||||
cardStyle={cardStyle}
|
||||
onTransitionStart={onTransitionStart}
|
||||
onTransitionEnd={onTransitionEnd}
|
||||
/>
|
||||
),
|
||||
);
|
||||
|
||||
const navigator = createNavigator(
|
||||
router,
|
||||
routeConfigMap,
|
||||
stackConfig,
|
||||
NavigatorTypes.STACK,
|
||||
)((props: *) => (
|
||||
<CardStackTransitioner
|
||||
{...props}
|
||||
headerComponent={headerComponent}
|
||||
headerMode={headerMode}
|
||||
mode={mode}
|
||||
cardStyle={cardStyle}
|
||||
onTransitionStart={onTransitionStart}
|
||||
onTransitionEnd={onTransitionEnd}
|
||||
/>
|
||||
));
|
||||
|
||||
return createNavigationContainer(navigator, stackConfig.containerOptions);
|
||||
};
|
||||
|
@ -38,19 +38,23 @@ const TabNavigator = (
|
||||
} = mergedConfig;
|
||||
|
||||
const router = TabRouter(routeConfigs, tabsConfig);
|
||||
const navigator = createNavigator(router, routeConfigs, config, NavigatorTypes.STACK)(
|
||||
(props: *) => (
|
||||
<TabView
|
||||
{...props}
|
||||
tabBarComponent={tabBarComponent}
|
||||
tabBarPosition={tabBarPosition}
|
||||
tabBarOptions={tabBarOptions}
|
||||
swipeEnabled={swipeEnabled}
|
||||
animationEnabled={animationEnabled}
|
||||
lazyLoad={lazyLoad}
|
||||
/>
|
||||
),
|
||||
);
|
||||
|
||||
const navigator = createNavigator(
|
||||
router,
|
||||
routeConfigs,
|
||||
config,
|
||||
NavigatorTypes.STACK,
|
||||
)((props: *) => (
|
||||
<TabView
|
||||
{...props}
|
||||
tabBarComponent={tabBarComponent}
|
||||
tabBarPosition={tabBarPosition}
|
||||
tabBarOptions={tabBarOptions}
|
||||
swipeEnabled={swipeEnabled}
|
||||
animationEnabled={animationEnabled}
|
||||
lazyLoad={lazyLoad}
|
||||
/>
|
||||
));
|
||||
|
||||
return createNavigationContainer(navigator, tabsConfig.containerOptions);
|
||||
};
|
||||
@ -93,7 +97,9 @@ const Presets = {
|
||||
TabNavigator.Presets = {
|
||||
iOSBottomTabs: Presets.iOSBottomTabs,
|
||||
AndroidTopTabs: Presets.AndroidTopTabs,
|
||||
Default: Platform.OS === 'ios' ? Presets.iOSBottomTabs : Presets.AndroidTopTabs,
|
||||
Default: Platform.OS === 'ios'
|
||||
? Presets.iOSBottomTabs
|
||||
: Presets.AndroidTopTabs,
|
||||
};
|
||||
|
||||
export default TabNavigator;
|
||||
|
@ -4,20 +4,22 @@ import React from 'react';
|
||||
|
||||
import type {
|
||||
NavigationRouter,
|
||||
NavigationRoute,
|
||||
NavigationNavigator,
|
||||
NavigationNavigatorProps,
|
||||
NavigationRouteConfigMap,
|
||||
} from '../TypeDefinition';
|
||||
|
||||
import type {
|
||||
NavigatorType,
|
||||
} from './NavigatorTypes';
|
||||
import type { NavigatorType } from './NavigatorTypes';
|
||||
|
||||
/**
|
||||
* Creates a navigator based on a router and a view that renders the screens.
|
||||
*/
|
||||
const createNavigator = (router: NavigationRouter<*, *, *>, routeConfigs: NavigationRouteConfigMap, navigatorConfig: any, navigatorType: NavigatorType) =>
|
||||
const createNavigator = (
|
||||
router: NavigationRouter<*, *, *>,
|
||||
routeConfigs: NavigationRouteConfigMap,
|
||||
navigatorConfig: any,
|
||||
navigatorType: NavigatorType,
|
||||
) =>
|
||||
(View: NavigationNavigator<*, *, *, *>) => {
|
||||
class Navigator extends React.Component {
|
||||
props: NavigationNavigatorProps<*>;
|
||||
@ -29,12 +31,7 @@ const createNavigator = (router: NavigationRouter<*, *, *>, routeConfigs: Naviga
|
||||
static navigatorType = navigatorType;
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
{...this.props}
|
||||
router={router}
|
||||
/>
|
||||
);
|
||||
return <View {...this.props} router={router} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
75
src/react-navigation.js
vendored
75
src/react-navigation.js
vendored
@ -2,33 +2,70 @@
|
||||
* @noflow - get/set properties not yet supported by flow. also `...require(x)` is broken #6560135
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
/* eslint global-require: 0 */
|
||||
|
||||
module.exports = {
|
||||
// Core
|
||||
get createNavigationContainer() { return require('./createNavigationContainer').default; },
|
||||
get StateUtils() { return require('./StateUtils').default; },
|
||||
get addNavigationHelpers() { return require('./addNavigationHelpers').default; },
|
||||
get NavigationActions() { return require('./NavigationActions').default; },
|
||||
get createNavigationContainer() {
|
||||
return require('./createNavigationContainer').default;
|
||||
},
|
||||
get StateUtils() {
|
||||
return require('./StateUtils').default;
|
||||
},
|
||||
get addNavigationHelpers() {
|
||||
return require('./addNavigationHelpers').default;
|
||||
},
|
||||
get NavigationActions() {
|
||||
return require('./NavigationActions').default;
|
||||
},
|
||||
|
||||
// Navigators
|
||||
get createNavigator() { return require('./navigators/createNavigator').default; },
|
||||
get StackNavigator() { return require('./navigators/StackNavigator').default; },
|
||||
get TabNavigator() { return require('./navigators/TabNavigator').default; },
|
||||
get DrawerNavigator() { return require('./navigators/DrawerNavigator').default; },
|
||||
get createNavigator() {
|
||||
return require('./navigators/createNavigator').default;
|
||||
},
|
||||
get StackNavigator() {
|
||||
return require('./navigators/StackNavigator').default;
|
||||
},
|
||||
get TabNavigator() {
|
||||
return require('./navigators/TabNavigator').default;
|
||||
},
|
||||
get DrawerNavigator() {
|
||||
return require('./navigators/DrawerNavigator').default;
|
||||
},
|
||||
|
||||
// Routers
|
||||
get StackRouter() { return require('./routers/StackRouter').default; },
|
||||
get TabRouter() { return require('./routers/TabRouter').default; },
|
||||
get StackRouter() {
|
||||
return require('./routers/StackRouter').default;
|
||||
},
|
||||
get TabRouter() {
|
||||
return require('./routers/TabRouter').default;
|
||||
},
|
||||
|
||||
// Views
|
||||
get Transitioner() { return require('./views/Transitioner').default; },
|
||||
get CardStack() { return require('./views/CardStack').default; },
|
||||
get Card() { return require('./views/Card').default; },
|
||||
get Header() { return require('./views/Header').default; },
|
||||
get HeaderBackButton() { return require('./views/HeaderBackButton').default; },
|
||||
get DrawerView() { return require('./views/Drawer/DrawerView').default; },
|
||||
get TabView() { return require('./views/TabView/TabView').default; },
|
||||
get Transitioner() {
|
||||
return require('./views/Transitioner').default;
|
||||
},
|
||||
get CardStack() {
|
||||
return require('./views/CardStack').default;
|
||||
},
|
||||
get Card() {
|
||||
return require('./views/Card').default;
|
||||
},
|
||||
get Header() {
|
||||
return require('./views/Header').default;
|
||||
},
|
||||
get HeaderBackButton() {
|
||||
return require('./views/HeaderBackButton').default;
|
||||
},
|
||||
get DrawerView() {
|
||||
return require('./views/Drawer/DrawerView').default;
|
||||
},
|
||||
get TabView() {
|
||||
return require('./views/TabView/TabView').default;
|
||||
},
|
||||
|
||||
// HOCs
|
||||
get withNavigation() { return require('./views/withNavigation').default; },
|
||||
get withNavigation() {
|
||||
return require('./views/withNavigation').default;
|
||||
},
|
||||
};
|
||||
|
@ -2,21 +2,38 @@
|
||||
* @noflow - get/set properties not yet supported by flow. also `...require(x)` is broken #6560135
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
/* eslint global-require: 0 */
|
||||
|
||||
module.exports = {
|
||||
// Core
|
||||
get createNavigationContainer() { return require('./createNavigationContainer').default; },
|
||||
get StateUtils() { return require('./StateUtils').default; },
|
||||
get addNavigationHelpers() { return require('./addNavigationHelpers').default; },
|
||||
get NavigationActions() { return require('./NavigationActions').default; },
|
||||
get createNavigationContainer() {
|
||||
return require('./createNavigationContainer').default;
|
||||
},
|
||||
get StateUtils() {
|
||||
return require('./StateUtils').default;
|
||||
},
|
||||
get addNavigationHelpers() {
|
||||
return require('./addNavigationHelpers').default;
|
||||
},
|
||||
get NavigationActions() {
|
||||
return require('./NavigationActions').default;
|
||||
},
|
||||
|
||||
// Navigators
|
||||
get createNavigator() { return require('./navigators/createNavigator').default; },
|
||||
get createNavigator() {
|
||||
return require('./navigators/createNavigator').default;
|
||||
},
|
||||
|
||||
// Routers
|
||||
get StackRouter() { return require('./routers/StackRouter').default; },
|
||||
get TabRouter() { return require('./routers/TabRouter').default; },
|
||||
get StackRouter() {
|
||||
return require('./routers/StackRouter').default;
|
||||
},
|
||||
get TabRouter() {
|
||||
return require('./routers/TabRouter').default;
|
||||
},
|
||||
|
||||
// HOCs
|
||||
get withNavigation() { return require('./views/withNavigation').default; },
|
||||
get withNavigation() {
|
||||
return require('./views/withNavigation').default;
|
||||
},
|
||||
};
|
||||
|
@ -70,12 +70,11 @@ export default (
|
||||
const wildcardRe = pathToRegexp(`${pathPattern}/*`, keys);
|
||||
re = new RegExp(`(?:${re.source})|(?:${wildcardRe.source})`);
|
||||
}
|
||||
/* $FlowFixMe */
|
||||
/* $FlowFixMe */
|
||||
paths[routeName] = { re, keys };
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
getComponentForState(state: NavigationState): NavigationComponent {
|
||||
const activeChildRoute = state.routes[state.index];
|
||||
const { routeName } = activeChildRoute;
|
||||
@ -89,13 +88,19 @@ export default (
|
||||
return getScreenForRouteName(routeConfigs, routeName);
|
||||
},
|
||||
|
||||
getStateForAction(action: NavigationStackAction, state: ?NavigationState) {
|
||||
action = NavigationActions.mapDeprecatedActionAndWarn(action);
|
||||
getStateForAction(
|
||||
passedAction: NavigationStackAction,
|
||||
state: ?NavigationState,
|
||||
) {
|
||||
const action = NavigationActions.mapDeprecatedActionAndWarn(passedAction);
|
||||
|
||||
// Set up the initial state if needed
|
||||
if (!state) {
|
||||
let route = {};
|
||||
if (action.type === NavigationActions.NAVIGATE && (childRouters[action.routeName] !== undefined)) {
|
||||
if (
|
||||
action.type === NavigationActions.NAVIGATE &&
|
||||
childRouters[action.routeName] !== undefined
|
||||
) {
|
||||
return {
|
||||
index: 0,
|
||||
routes: [
|
||||
@ -108,12 +113,16 @@ export default (
|
||||
};
|
||||
}
|
||||
if (initialChildRouter) {
|
||||
route = initialChildRouter.getStateForAction(NavigationActions.navigate({
|
||||
routeName: initialRouteName,
|
||||
params: initialRouteParams,
|
||||
}));
|
||||
route = initialChildRouter.getStateForAction(
|
||||
NavigationActions.navigate({
|
||||
routeName: initialRouteName,
|
||||
params: initialRouteParams,
|
||||
}),
|
||||
);
|
||||
}
|
||||
const params = (route.params || action.params || initialRouteParams) && {
|
||||
const params = (route.params ||
|
||||
action.params ||
|
||||
initialRouteParams) && {
|
||||
...(route.params || {}),
|
||||
...(action.params || {}),
|
||||
...(initialRouteParams || {}),
|
||||
@ -124,6 +133,7 @@ export default (
|
||||
key: 'Init',
|
||||
...(params ? { params } : {}),
|
||||
};
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
state = {
|
||||
index: 0,
|
||||
routes: [route],
|
||||
@ -131,8 +141,10 @@ export default (
|
||||
}
|
||||
|
||||
// Check if a child scene wants to handle the action as long as it is not a reset to the root stack
|
||||
if(action.type !== NavigationActions.RESET || action.key !== null) {
|
||||
const keyIndex = action.key ? StateUtils.indexOf(state, action.key) : -1
|
||||
if (action.type !== NavigationActions.RESET || action.key !== null) {
|
||||
const keyIndex = action.key
|
||||
? StateUtils.indexOf(state, action.key)
|
||||
: -1;
|
||||
const childIndex = keyIndex >= 0 ? keyIndex : state.index;
|
||||
const childRoute = state.routes[childIndex];
|
||||
const childRouter = childRouters[childRoute.routeName];
|
||||
@ -148,11 +160,15 @@ export default (
|
||||
}
|
||||
|
||||
// Handle explicit push navigation action
|
||||
if (action.type === NavigationActions.NAVIGATE && childRouters[action.routeName] !== undefined) {
|
||||
if (
|
||||
action.type === NavigationActions.NAVIGATE &&
|
||||
childRouters[action.routeName] !== undefined
|
||||
) {
|
||||
const childRouter = childRouters[action.routeName];
|
||||
let route;
|
||||
if (childRouter) {
|
||||
const childAction = action.action || NavigationActions.init({ params: action.params });
|
||||
const childAction = action.action ||
|
||||
NavigationActions.init({ params: action.params });
|
||||
route = {
|
||||
params: action.params,
|
||||
...childRouter.getStateForAction(childAction),
|
||||
@ -177,9 +193,14 @@ export default (
|
||||
const childRouter = childRouters[childRouterName];
|
||||
if (childRouter) {
|
||||
// For each child router, start with a blank state
|
||||
const initChildRoute = childRouter.getStateForAction(NavigationActions.init());
|
||||
const initChildRoute = childRouter.getStateForAction(
|
||||
NavigationActions.init(),
|
||||
);
|
||||
// Then check to see if the router handles our navigate action
|
||||
const navigatedChildRoute = childRouter.getStateForAction(action, initChildRoute);
|
||||
const navigatedChildRoute = childRouter.getStateForAction(
|
||||
action,
|
||||
initChildRoute,
|
||||
);
|
||||
let routeToPush = null;
|
||||
if (navigatedChildRoute === null) {
|
||||
// Push the route if the router has 'handled' the action and returned null
|
||||
@ -200,8 +221,10 @@ export default (
|
||||
}
|
||||
|
||||
if (action.type === NavigationActions.SET_PARAMS) {
|
||||
/* $FlowFixMe */
|
||||
const lastRoute = state.routes.find((route: *) => route.key === action.key);
|
||||
const lastRoute = state.routes.find(
|
||||
/* $FlowFixMe */
|
||||
(route: *) => route.key === action.key,
|
||||
);
|
||||
if (lastRoute) {
|
||||
const params = {
|
||||
...lastRoute.params,
|
||||
@ -220,27 +243,29 @@ export default (
|
||||
}
|
||||
|
||||
if (action.type === NavigationActions.RESET) {
|
||||
const resetAction = ((action: any): NavigationResetAction);
|
||||
const resetAction: NavigationResetAction = action;
|
||||
|
||||
return {
|
||||
...state,
|
||||
routes: resetAction.actions.map((action: NavigationNavigateAction, index: number) => {
|
||||
const router = childRouters[action.routeName];
|
||||
if (router) {
|
||||
return {
|
||||
...action,
|
||||
...router.getStateForAction(action),
|
||||
routeName: action.routeName,
|
||||
routes: resetAction.actions.map(
|
||||
(childAction: NavigationNavigateAction, index: number) => {
|
||||
const router = childRouters[childAction.routeName];
|
||||
if (router) {
|
||||
return {
|
||||
...childAction,
|
||||
...router.getStateForAction(childAction),
|
||||
routeName: childAction.routeName,
|
||||
key: `Init${index}`,
|
||||
};
|
||||
}
|
||||
const route = {
|
||||
...childAction,
|
||||
key: `Init${index}`,
|
||||
};
|
||||
}
|
||||
const route = {
|
||||
...action,
|
||||
key: `Init${index}`,
|
||||
};
|
||||
delete route.type;
|
||||
return route;
|
||||
}),
|
||||
delete route.type;
|
||||
return route;
|
||||
},
|
||||
),
|
||||
index: action.index,
|
||||
};
|
||||
}
|
||||
@ -248,8 +273,10 @@ export default (
|
||||
if (action.type === NavigationActions.BACK) {
|
||||
let backRouteIndex = null;
|
||||
if (action.key) {
|
||||
/* $FlowFixMe */
|
||||
const backRoute = state.routes.find((route: *) => route.key === action.key);
|
||||
const backRoute = state.routes.find(
|
||||
/* $FlowFixMe */
|
||||
(route: *) => route.key === action.key,
|
||||
);
|
||||
/* $FlowFixMe */
|
||||
backRouteIndex = state.routes.indexOf(backRoute);
|
||||
}
|
||||
@ -267,7 +294,7 @@ export default (
|
||||
return state;
|
||||
},
|
||||
|
||||
getPathAndParamsForState(state: NavigationState): {path: string, params?: NavigationParams} {
|
||||
getPathAndParamsForState(): { path: string, params?: NavigationParams } {
|
||||
// TODO: implement this!
|
||||
return {
|
||||
path: '',
|
||||
@ -291,9 +318,10 @@ export default (
|
||||
let pathMatch;
|
||||
let pathMatchKeys;
|
||||
|
||||
for (const routeName in paths) {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const [routeName, path] of Object.entries(paths)) {
|
||||
/* $FlowFixMe */
|
||||
const { re, keys } = paths[routeName];
|
||||
const { re, keys } = path;
|
||||
pathMatch = re.exec(pathNameToResolve);
|
||||
if (pathMatch && pathMatch.length) {
|
||||
pathMatchKeys = keys;
|
||||
@ -315,36 +343,41 @@ export default (
|
||||
if (childRouters[matchedRouteName]) {
|
||||
nestedAction = childRouters[matchedRouteName].getActionForPathAndParams(
|
||||
/* $FlowFixMe */
|
||||
pathMatch.slice(pathMatchKeys.length).join('/')
|
||||
pathMatch.slice(pathMatchKeys.length).join('/'),
|
||||
);
|
||||
}
|
||||
|
||||
// reduce the items of the query string. any query params may
|
||||
// be overridden by path params
|
||||
const queryParams = (queryString || '').split('&').reduce((result: *, item: string) => {
|
||||
if (item !== '') {
|
||||
const nextResult = result || {};
|
||||
const [key, value] = item.split('=');
|
||||
nextResult[key] = value;
|
||||
return nextResult;
|
||||
}
|
||||
return result;
|
||||
}, null);
|
||||
const queryParams = (queryString || '').split('&').reduce(
|
||||
(result: *, item: string) => {
|
||||
if (item !== '') {
|
||||
const nextResult = result || {};
|
||||
const [key, value] = item.split('=');
|
||||
nextResult[key] = value;
|
||||
return nextResult;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
null,
|
||||
);
|
||||
|
||||
// reduce the matched pieces of the path into the params
|
||||
// of the route. `params` is null if there are no params.
|
||||
/* $FlowFixMe */
|
||||
const params = pathMatch.slice(1).reduce((result: *, matchResult: *, i: number) => {
|
||||
const key = pathMatchKeys[i];
|
||||
if (key.asterisk || !key) {
|
||||
return result;
|
||||
}
|
||||
const nextResult = result || {};
|
||||
const paramName = key.name;
|
||||
nextResult[paramName] = matchResult;
|
||||
return nextResult;
|
||||
}, queryParams);
|
||||
|
||||
const params = pathMatch.slice(1).reduce(
|
||||
(result: *, matchResult: *, i: number) => {
|
||||
const key = pathMatchKeys[i];
|
||||
if (key.asterisk || !key) {
|
||||
return result;
|
||||
}
|
||||
const nextResult = result || {};
|
||||
const paramName = key.name;
|
||||
nextResult[paramName] = matchResult;
|
||||
return nextResult;
|
||||
},
|
||||
queryParams,
|
||||
);
|
||||
|
||||
return NavigationActions.navigate({
|
||||
routeName: matchedRouteName,
|
||||
@ -353,7 +386,10 @@ export default (
|
||||
});
|
||||
},
|
||||
|
||||
getScreenOptions: createConfigGetter(routeConfigs, stackConfig.navigationOptions),
|
||||
getScreenOptions: createConfigGetter(
|
||||
routeConfigs,
|
||||
stackConfig.navigationOptions,
|
||||
),
|
||||
|
||||
getScreenConfig: getScreenConfigDeprecated,
|
||||
};
|
||||
|
@ -24,7 +24,7 @@ import type {
|
||||
|
||||
export default (
|
||||
routeConfigs: NavigationRouteConfigMap,
|
||||
config: NavigationTabRouterConfig = {}
|
||||
config: NavigationTabRouterConfig = {},
|
||||
): NavigationRouter<*, *, *> => {
|
||||
// Fail fast on invalid route definitions
|
||||
validateRouteConfigMap(routeConfigs);
|
||||
@ -38,7 +38,9 @@ export default (
|
||||
const tabRouters = {};
|
||||
order.forEach((routeName: string) => {
|
||||
const routeConfig = routeConfigs[routeName];
|
||||
paths[routeName] = typeof routeConfig.path === 'string' ? routeConfig.path : routeName;
|
||||
paths[routeName] = typeof routeConfig.path === 'string'
|
||||
? routeConfig.path
|
||||
: routeName;
|
||||
tabRouters[routeName] = null;
|
||||
if (routeConfig.screen && routeConfig.screen.router) {
|
||||
tabRouters[routeName] = routeConfig.screen.router;
|
||||
@ -47,14 +49,14 @@ export default (
|
||||
invariant(
|
||||
initialRouteIndex !== -1,
|
||||
`Invalid initialRouteName '${initialRouteName}' for TabRouter. ` +
|
||||
`Should be one of ${order.map((n: *) => `"${n}"`).join(', ')}`
|
||||
`Should be one of ${order.map((n: *) => `"${n}"`).join(', ')}`,
|
||||
);
|
||||
return {
|
||||
getStateForAction(
|
||||
action: NavigationAction | { action: NavigationAction },
|
||||
inputState?: ?NavigationState
|
||||
inputState?: ?NavigationState,
|
||||
): ?NavigationState {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
action = NavigationActions.mapDeprecatedActionAndWarn(action);
|
||||
|
||||
// Establish a default state
|
||||
@ -63,9 +65,10 @@ export default (
|
||||
const routes = order.map((routeName: string) => {
|
||||
const tabRouter = tabRouters[routeName];
|
||||
if (tabRouter) {
|
||||
const childAction = action.action || NavigationActions.init({
|
||||
const childAction = action.action ||
|
||||
NavigationActions.init({
|
||||
...(action.params ? { params: action.params } : {}),
|
||||
});
|
||||
});
|
||||
return {
|
||||
...tabRouter.getStateForAction(childAction),
|
||||
key: routeName,
|
||||
@ -88,13 +91,16 @@ export default (
|
||||
// Merge any params from the action into all the child routes
|
||||
const { params } = action;
|
||||
if (params) {
|
||||
state.routes = state.routes.map(route => ({
|
||||
...route,
|
||||
params: {
|
||||
...route.params,
|
||||
...params,
|
||||
},
|
||||
}: NavigationRoute));
|
||||
state.routes = state.routes.map(
|
||||
(route: *) =>
|
||||
({
|
||||
...route,
|
||||
params: {
|
||||
...route.params,
|
||||
...params,
|
||||
},
|
||||
}: NavigationRoute),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,10 +128,12 @@ export default (
|
||||
// Handle tab changing. Do this after letting the current tab try to
|
||||
// handle the action, to allow inner tabs to change first
|
||||
let activeTabIndex = state.index;
|
||||
const isBackEligible = action.key == null || action.key === activeTabLastState.key;
|
||||
const isBackEligible = action.key == null ||
|
||||
action.key === activeTabLastState.key;
|
||||
if (
|
||||
action.type === NavigationActions.BACK &&
|
||||
isBackEligible && shouldBackNavigateToInitialRoute
|
||||
isBackEligible &&
|
||||
shouldBackNavigateToInitialRoute
|
||||
) {
|
||||
activeTabIndex = initialRouteIndex;
|
||||
}
|
||||
@ -247,7 +255,7 @@ export default (
|
||||
invariant(
|
||||
routeName,
|
||||
`There is no route defined for index ${state.index}. Check that
|
||||
that you passed in a navigation state with a valid tab/screen index.`
|
||||
that you passed in a navigation state with a valid tab/screen index.`,
|
||||
);
|
||||
const childRouter = tabRouters[routeName];
|
||||
if (childRouter) {
|
||||
@ -286,30 +294,45 @@ export default (
|
||||
* This will return null if there is no action matched
|
||||
*/
|
||||
getActionForPathAndParams(path: string, params: ?NavigationParams) {
|
||||
return order.map((tabId: string) => {
|
||||
const parts = path.split('/');
|
||||
const pathToTest = paths[tabId];
|
||||
if (parts[0] === pathToTest) {
|
||||
const tabRouter = tabRouters[tabId];
|
||||
const action: NavigationNavigateAction = NavigationActions.navigate({
|
||||
routeName: tabId,
|
||||
});
|
||||
if (tabRouter && tabRouter.getActionForPathAndParams) {
|
||||
action.action = tabRouter.getActionForPathAndParams(parts.slice(1).join('/'), params);
|
||||
} else if (params) {
|
||||
action.params = params;
|
||||
return order
|
||||
.map((tabId: string) => {
|
||||
const parts = path.split('/');
|
||||
const pathToTest = paths[tabId];
|
||||
if (parts[0] === pathToTest) {
|
||||
const tabRouter = tabRouters[tabId];
|
||||
const action: NavigationNavigateAction = NavigationActions.navigate(
|
||||
{
|
||||
routeName: tabId,
|
||||
},
|
||||
);
|
||||
if (tabRouter && tabRouter.getActionForPathAndParams) {
|
||||
action.action = tabRouter.getActionForPathAndParams(
|
||||
parts.slice(1).join('/'),
|
||||
params,
|
||||
);
|
||||
} else if (params) {
|
||||
action.params = params;
|
||||
}
|
||||
return action;
|
||||
}
|
||||
return action;
|
||||
}
|
||||
return null;
|
||||
}).find((action: *) => !!action) || order.map((tabId: string) => {
|
||||
const tabRouter = tabRouters[tabId];
|
||||
return tabRouter && tabRouter.getActionForPathAndParams(path, params);
|
||||
}).find((action: *) => !!action) || null;
|
||||
return null;
|
||||
})
|
||||
.find((action: *) => !!action) ||
|
||||
order
|
||||
.map((tabId: string) => {
|
||||
const tabRouter = tabRouters[tabId];
|
||||
return tabRouter &&
|
||||
tabRouter.getActionForPathAndParams(path, params);
|
||||
})
|
||||
.find((action: *) => !!action) ||
|
||||
null;
|
||||
},
|
||||
|
||||
getScreenOptions: createConfigGetter(routeConfigs, config.navigationOptions),
|
||||
|
||||
getScreenOptions: createConfigGetter(
|
||||
routeConfigs,
|
||||
config.navigationOptions,
|
||||
),
|
||||
|
||||
getScreenConfig: getScreenConfigDeprecated,
|
||||
};
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* @flow */
|
||||
/* eslint react/no-multi-comp:0 */
|
||||
|
||||
import React from 'react';
|
||||
|
||||
@ -19,15 +20,23 @@ Object.keys(ROUTERS).forEach((routerName: string) => {
|
||||
describe(`General router features - ${routerName}`, () => {
|
||||
test('title is configurable using navigationOptions and getScreenOptions', () => {
|
||||
class FooView extends React.Component {
|
||||
render() { return <div />; }
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
class BarView extends React.Component {
|
||||
render() { return <div />; }
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
static navigationOptions = { title: 'BarTitle' };
|
||||
}
|
||||
class BazView extends React.Component {
|
||||
render() { return <div />; }
|
||||
static navigationOptions = ({navigation}) => ({ title: `Baz-${navigation.state.params.id}` });
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
static navigationOptions = ({ navigation }: *) => ({
|
||||
title: `Baz-${navigation.state.params.id}`,
|
||||
});
|
||||
}
|
||||
const router = Router({
|
||||
Foo: { screen: FooView },
|
||||
@ -39,9 +48,24 @@ Object.keys(ROUTERS).forEach((routerName: string) => {
|
||||
{ key: 'B', routeName: 'Bar' },
|
||||
{ key: 'A', routeName: 'Baz', params: { id: '123' } },
|
||||
];
|
||||
expect(router.getScreenOptions(addNavigationHelpers({ state: routes[0], dispatch: () => false }), {}).title).toEqual(undefined);
|
||||
expect(router.getScreenOptions(addNavigationHelpers({ state: routes[1], dispatch: () => false }), {}).title).toEqual('BarTitle');
|
||||
expect(router.getScreenOptions(addNavigationHelpers({ state: routes[2], dispatch: () => false }), {}).title).toEqual('Baz-123');
|
||||
expect(
|
||||
router.getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[0], dispatch: () => false }),
|
||||
{},
|
||||
).title,
|
||||
).toEqual(undefined);
|
||||
expect(
|
||||
router.getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[1], dispatch: () => false }),
|
||||
{},
|
||||
).title,
|
||||
).toEqual('BarTitle');
|
||||
expect(
|
||||
router.getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[2], dispatch: () => false }),
|
||||
{},
|
||||
).title,
|
||||
).toEqual('Baz-123');
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -62,9 +86,15 @@ test('Handles no-op actions with tabs within stack router', () => {
|
||||
},
|
||||
});
|
||||
const state1 = TestRouter.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = TestRouter.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Qux' });
|
||||
const state2 = TestRouter.getStateForAction({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Qux',
|
||||
});
|
||||
expect(state1).toEqual(state2);
|
||||
const state3 = TestRouter.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Zap' }, state2);
|
||||
const state3 = TestRouter.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Zap' },
|
||||
state2,
|
||||
);
|
||||
expect(state2).toEqual(state3);
|
||||
});
|
||||
|
||||
@ -90,7 +120,14 @@ test('Handles deep action', () => {
|
||||
],
|
||||
};
|
||||
expect(state1).toEqual(expectedState);
|
||||
const state2 = TestRouter.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Foo', action: { type: NavigationActions.NAVIGATE, routeName: 'Zoo' } }, state1);
|
||||
const state2 = TestRouter.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Foo',
|
||||
action: { type: NavigationActions.NAVIGATE, routeName: 'Zoo' },
|
||||
},
|
||||
state1,
|
||||
);
|
||||
expect(state2 && state2.index).toEqual(1);
|
||||
/* $FlowFixMe */
|
||||
expect(state2 && state2.routes[1].index).toEqual(1);
|
||||
@ -112,8 +149,14 @@ test('Supports lazily-evaluated getScreen', () => {
|
||||
},
|
||||
});
|
||||
const state1 = TestRouter.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = TestRouter.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Qux' });
|
||||
const state2 = TestRouter.getStateForAction({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Qux',
|
||||
});
|
||||
expect(state1).toEqual(state2);
|
||||
const state3 = TestRouter.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Zap' }, state2);
|
||||
const state3 = TestRouter.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Zap' },
|
||||
state2,
|
||||
);
|
||||
expect(state2).toEqual(state3);
|
||||
});
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* @flow */
|
||||
/* eslint no-shadow:0 react/no-multi-comp:0 */
|
||||
|
||||
import React from 'react';
|
||||
|
||||
@ -80,21 +81,25 @@ describe('StackRouter', () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(router.getComponentForState({
|
||||
index: 0,
|
||||
routes: [
|
||||
{ key: 'a', routeName: 'foo' },
|
||||
{ key: 'b', routeName: 'bar' },
|
||||
{ key: 'c', routeName: 'foo' },
|
||||
],
|
||||
})).toBe(FooScreen);
|
||||
expect(router.getComponentForState({
|
||||
index: 1,
|
||||
routes: [
|
||||
{ key: 'a', routeName: 'foo' },
|
||||
{ key: 'b', routeName: 'bar' },
|
||||
],
|
||||
})).toBe(BarScreen);
|
||||
expect(
|
||||
router.getComponentForState({
|
||||
index: 0,
|
||||
routes: [
|
||||
{ key: 'a', routeName: 'foo' },
|
||||
{ key: 'b', routeName: 'bar' },
|
||||
{ key: 'c', routeName: 'foo' },
|
||||
],
|
||||
}),
|
||||
).toBe(FooScreen);
|
||||
expect(
|
||||
router.getComponentForState({
|
||||
index: 1,
|
||||
routes: [
|
||||
{ key: 'a', routeName: 'foo' },
|
||||
{ key: 'b', routeName: 'bar' },
|
||||
],
|
||||
}),
|
||||
).toBe(BarScreen);
|
||||
});
|
||||
|
||||
test('Handles getScreen in getComponentForState', () => {
|
||||
@ -109,27 +114,39 @@ describe('StackRouter', () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(router.getComponentForState({
|
||||
index: 0,
|
||||
routes: [
|
||||
{ key: 'a', routeName: 'foo' },
|
||||
{ key: 'b', routeName: 'bar' },
|
||||
{ key: 'c', routeName: 'foo' },
|
||||
],
|
||||
})).toBe(FooScreen);
|
||||
expect(router.getComponentForState({
|
||||
index: 1,
|
||||
routes: [
|
||||
{ key: 'a', routeName: 'foo' },
|
||||
{ key: 'b', routeName: 'bar' },
|
||||
],
|
||||
})).toBe(BarScreen);
|
||||
expect(
|
||||
router.getComponentForState({
|
||||
index: 0,
|
||||
routes: [
|
||||
{ key: 'a', routeName: 'foo' },
|
||||
{ key: 'b', routeName: 'bar' },
|
||||
{ key: 'c', routeName: 'foo' },
|
||||
],
|
||||
}),
|
||||
).toBe(FooScreen);
|
||||
expect(
|
||||
router.getComponentForState({
|
||||
index: 1,
|
||||
routes: [
|
||||
{ key: 'a', routeName: 'foo' },
|
||||
{ key: 'b', routeName: 'bar' },
|
||||
],
|
||||
}),
|
||||
).toBe(BarScreen);
|
||||
});
|
||||
|
||||
test('Gets the screen for given route', () => {
|
||||
const FooScreen = () => <div />;
|
||||
const BarScreen = class extends React.Component { render() { return <div />; } };
|
||||
const BazScreen = React.createClass({ render() { return <div />; } });
|
||||
const BarScreen = class extends React.Component {
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
};
|
||||
const BazScreen = class extends React.Component {
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
};
|
||||
const router = StackRouter({
|
||||
foo: {
|
||||
screen: FooScreen,
|
||||
@ -149,8 +166,16 @@ describe('StackRouter', () => {
|
||||
|
||||
test('Handles getScreen in getComponent', () => {
|
||||
const FooScreen = () => <div />;
|
||||
const BarScreen = class extends React.Component { render() { return <div />; } };
|
||||
const BazScreen = React.createClass({ render() { return <div />; } });
|
||||
const BarScreen = class extends React.Component {
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
};
|
||||
const BazScreen = class extends React.Component {
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
};
|
||||
const router = StackRouter({
|
||||
foo: {
|
||||
getScreen: () => FooScreen,
|
||||
@ -186,7 +211,9 @@ describe('StackRouter', () => {
|
||||
});
|
||||
|
||||
test('Parses paths with a query', () => {
|
||||
expect(TestStackRouter.getActionForPathAndParams('people/foo?code=test&foo=bar')).toEqual({
|
||||
expect(
|
||||
TestStackRouter.getActionForPathAndParams('people/foo?code=test&foo=bar'),
|
||||
).toEqual({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'person',
|
||||
params: {
|
||||
@ -198,7 +225,9 @@ describe('StackRouter', () => {
|
||||
});
|
||||
|
||||
test('Parses paths with an empty query value', () => {
|
||||
expect(TestStackRouter.getActionForPathAndParams('people/foo?code=&foo=bar')).toEqual({
|
||||
expect(
|
||||
TestStackRouter.getActionForPathAndParams('people/foo?code=&foo=bar'),
|
||||
).toEqual({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'person',
|
||||
params: {
|
||||
@ -288,19 +317,20 @@ describe('StackRouter', () => {
|
||||
Bar.router = StackRouter({
|
||||
baz: { screen: () => <div /> },
|
||||
qux: { screen: () => <div /> },
|
||||
})
|
||||
});
|
||||
const TestRouter = StackRouter({
|
||||
foo: { screen: () => <div /> },
|
||||
bar: { screen: Bar },
|
||||
})
|
||||
});
|
||||
const initState = TestRouter.getStateForAction(NavigationActions.init());
|
||||
expect(initState).toEqual({
|
||||
index: 0,
|
||||
routes: [
|
||||
{ key: 'Init', routeName: 'foo' }
|
||||
]
|
||||
routes: [{ key: 'Init', routeName: 'foo' }],
|
||||
});
|
||||
const pushedState = TestRouter.getStateForAction(NavigationActions.navigate({ routeName: 'qux' }), initState);
|
||||
const pushedState = TestRouter.getStateForAction(
|
||||
NavigationActions.navigate({ routeName: 'qux' }),
|
||||
initState,
|
||||
);
|
||||
// $FlowFixMe
|
||||
expect(pushedState.index).toEqual(1);
|
||||
// $FlowFixMe
|
||||
@ -330,12 +360,22 @@ describe('StackRouter', () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
const state2 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Bar', params: { name: 'Zoom' } }, state);
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
params: { name: 'Zoom' },
|
||||
},
|
||||
state,
|
||||
);
|
||||
expect(state2 && state2.index).toEqual(1);
|
||||
expect(state2 && state2.routes[1].routeName).toEqual('Bar');
|
||||
expect(state2 && state2.routes[1].params).toEqual({ name: 'Zoom' });
|
||||
expect(state2 && state2.routes.length).toEqual(2);
|
||||
const state3 = router.getStateForAction({ type: NavigationActions.BACK }, state2);
|
||||
const state3 = router.getStateForAction(
|
||||
{ type: NavigationActions.BACK },
|
||||
state2,
|
||||
);
|
||||
expect(state3).toEqual({
|
||||
index: 0,
|
||||
routes: [
|
||||
@ -373,12 +413,22 @@ describe('StackRouter', () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
const state2 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Bar', params: { name: 'Zoom' } }, state);
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
params: { name: 'Zoom' },
|
||||
},
|
||||
state,
|
||||
);
|
||||
expect(state2 && state2.index).toEqual(1);
|
||||
expect(state2 && state2.routes[1].routeName).toEqual('Bar');
|
||||
expect(state2 && state2.routes[1].params).toEqual({ name: 'Zoom' });
|
||||
expect(state2 && state2.routes.length).toEqual(2);
|
||||
const state3 = router.getStateForAction({ type: NavigationActions.BACK }, state2);
|
||||
const state3 = router.getStateForAction(
|
||||
{ type: NavigationActions.BACK },
|
||||
state2,
|
||||
);
|
||||
expect(state3).toEqual({
|
||||
index: 0,
|
||||
routes: [
|
||||
@ -402,25 +452,48 @@ describe('StackRouter', () => {
|
||||
},
|
||||
});
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Bar', params: { name: 'Zoom' } }, state);
|
||||
const state3 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Bar', params: { name: 'Foo' } }, state2);
|
||||
const state4 = router.getStateForAction({ type: NavigationActions.BACK, key: 'wrongKey' }, state3);
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
params: { name: 'Zoom' },
|
||||
},
|
||||
state,
|
||||
);
|
||||
const state3 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
params: { name: 'Foo' },
|
||||
},
|
||||
state2,
|
||||
);
|
||||
const state4 = router.getStateForAction(
|
||||
{ type: NavigationActions.BACK, key: 'wrongKey' },
|
||||
state3,
|
||||
);
|
||||
expect(state3).toEqual(state4);
|
||||
const state5 = router.getStateForAction({ type: NavigationActions.BACK, key: state3 && state3.routes[1].key }, state4);
|
||||
const state5 = router.getStateForAction(
|
||||
{ type: NavigationActions.BACK, key: state3 && state3.routes[1].key },
|
||||
state4,
|
||||
);
|
||||
expect(state5).toEqual(state);
|
||||
});
|
||||
|
||||
test('Handle initial route navigation', () => {
|
||||
const FooScreen = () => <div />;
|
||||
const BarScreen = () => <div />;
|
||||
const router = StackRouter({
|
||||
Foo: {
|
||||
screen: FooScreen,
|
||||
const router = StackRouter(
|
||||
{
|
||||
Foo: {
|
||||
screen: FooScreen,
|
||||
},
|
||||
Bar: {
|
||||
screen: BarScreen,
|
||||
},
|
||||
},
|
||||
Bar: {
|
||||
screen: BarScreen,
|
||||
},
|
||||
}, { initialRouteName: 'Bar' });
|
||||
{ initialRouteName: 'Bar' },
|
||||
);
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
expect(state).toEqual({
|
||||
index: 0,
|
||||
@ -435,11 +508,14 @@ describe('StackRouter', () => {
|
||||
|
||||
test('Initial route params appear in nav state', () => {
|
||||
const FooScreen = () => <div />;
|
||||
const router = StackRouter({
|
||||
Foo: {
|
||||
screen: FooScreen,
|
||||
const router = StackRouter(
|
||||
{
|
||||
Foo: {
|
||||
screen: FooScreen,
|
||||
},
|
||||
},
|
||||
}, { initialRouteName: 'Bar', initialRouteParams: { foo: 'bar' } });
|
||||
{ initialRouteName: 'Bar', initialRouteParams: { foo: 'bar' } },
|
||||
);
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
expect(state).toEqual({
|
||||
index: 0,
|
||||
@ -465,30 +541,43 @@ describe('StackRouter', () => {
|
||||
},
|
||||
});
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Bar', params: { bar: '42' } }, state);
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
params: { bar: '42' },
|
||||
},
|
||||
state,
|
||||
);
|
||||
expect(state2).not.toBeNull();
|
||||
expect(state2 && state2.index).toEqual(1);
|
||||
expect(state2 && state2.routes[1].params).toEqual({ bar: '42' });
|
||||
});
|
||||
|
||||
test('Handles the SetParams action', () => {
|
||||
const router = StackRouter({
|
||||
Foo: {
|
||||
screen: () => <div />,
|
||||
const router = StackRouter(
|
||||
{
|
||||
Foo: {
|
||||
screen: () => <div />,
|
||||
},
|
||||
Bar: {
|
||||
screen: () => <div />,
|
||||
},
|
||||
},
|
||||
Bar: {
|
||||
screen: () => <div />,
|
||||
{
|
||||
initialRouteName: 'Bar',
|
||||
initialRouteParams: { name: 'Zoo' },
|
||||
},
|
||||
}, {
|
||||
initialRouteName: 'Bar',
|
||||
initialRouteParams: { name: 'Zoo' },
|
||||
});
|
||||
);
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = router.getStateForAction({
|
||||
type: NavigationActions.SET_PARAMS,
|
||||
params: { name: 'Qux' },
|
||||
key: 'Init',
|
||||
}, state);
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.SET_PARAMS,
|
||||
params: { name: 'Qux' },
|
||||
key: 'Init',
|
||||
},
|
||||
state,
|
||||
);
|
||||
expect(state2 && state2.index).toEqual(0);
|
||||
expect(state2 && state2.routes[0].params).toEqual({ name: 'Qux' });
|
||||
});
|
||||
@ -497,23 +586,26 @@ describe('StackRouter', () => {
|
||||
const ChildNavigator = () => <div />;
|
||||
const GrandChildNavigator = () => <div />;
|
||||
GrandChildNavigator.router = StackRouter({
|
||||
Quux: { screen: () => <div />, },
|
||||
Corge: { screen: () => <div />, },
|
||||
Quux: { screen: () => <div /> },
|
||||
Corge: { screen: () => <div /> },
|
||||
});
|
||||
ChildNavigator.router = TabRouter({
|
||||
Baz: { screen: GrandChildNavigator, },
|
||||
Qux: { screen: () => <div />, },
|
||||
Baz: { screen: GrandChildNavigator },
|
||||
Qux: { screen: () => <div /> },
|
||||
});
|
||||
const router = StackRouter({
|
||||
Foo: { screen: ChildNavigator, },
|
||||
Bar: { screen: () => <div />, },
|
||||
Foo: { screen: ChildNavigator },
|
||||
Bar: { screen: () => <div /> },
|
||||
});
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = router.getStateForAction({
|
||||
type: NavigationActions.SET_PARAMS,
|
||||
params: { name: 'foobar' },
|
||||
key: 'Init',
|
||||
}, state);
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.SET_PARAMS,
|
||||
params: { name: 'foobar' },
|
||||
key: 'Init',
|
||||
},
|
||||
state,
|
||||
);
|
||||
expect(state2 && state2.index).toEqual(0);
|
||||
/* $FlowFixMe */
|
||||
expect(state2 && state2.routes[0].routes[0].routes).toEqual([
|
||||
@ -535,7 +627,21 @@ describe('StackRouter', () => {
|
||||
},
|
||||
});
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = router.getStateForAction({ type: NavigationActions.RESET, actions: [{ type: NavigationActions.NAVIGATE, routeName: 'Foo', params: { bar: '42' } }, { type: NavigationActions.NAVIGATE, routeName: 'Bar' }], index: 1 }, state);
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.RESET,
|
||||
actions: [
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Foo',
|
||||
params: { bar: '42' },
|
||||
},
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Bar' },
|
||||
],
|
||||
index: 1,
|
||||
},
|
||||
state,
|
||||
);
|
||||
expect(state2 && state2.index).toEqual(1);
|
||||
expect(state2 && state2.routes[0].params).toEqual({ bar: '42' });
|
||||
expect(state2 && state2.routes[0].routeName).toEqual('Foo');
|
||||
@ -561,7 +667,14 @@ describe('StackRouter', () => {
|
||||
},
|
||||
});
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = router.getStateForAction({ type: NavigationActions.RESET, actions: [{ type: NavigationActions.NAVIGATE, routeName: 'Foo' }], index: 0 }, state);
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.RESET,
|
||||
actions: [{ type: NavigationActions.NAVIGATE, routeName: 'Foo' }],
|
||||
index: 0,
|
||||
},
|
||||
state,
|
||||
);
|
||||
|
||||
expect(state2 && state2.index).toEqual(0);
|
||||
expect(state2 && state2.routes[0].routeName).toEqual('Foo');
|
||||
@ -588,9 +701,32 @@ describe('StackRouter', () => {
|
||||
},
|
||||
});
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Foo', action: { type: NavigationActions.NAVIGATE, routeName: 'baz' }}, state);
|
||||
const state3 = router.getStateForAction({ type: NavigationActions.RESET, key: 'Init', actions: [{ type: NavigationActions.NAVIGATE, routeName: 'Foo' }], index: 0 }, state2);
|
||||
const state4 = router.getStateForAction({ type: NavigationActions.RESET, key: null, actions: [{ type: NavigationActions.NAVIGATE, routeName: 'Bar' }], index: 0 }, state3);
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Foo',
|
||||
action: { type: NavigationActions.NAVIGATE, routeName: 'baz' },
|
||||
},
|
||||
state,
|
||||
);
|
||||
const state3 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.RESET,
|
||||
key: 'Init',
|
||||
actions: [{ type: NavigationActions.NAVIGATE, routeName: 'Foo' }],
|
||||
index: 0,
|
||||
},
|
||||
state2,
|
||||
);
|
||||
const state4 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.RESET,
|
||||
key: null,
|
||||
actions: [{ type: NavigationActions.NAVIGATE, routeName: 'Bar' }],
|
||||
index: 0,
|
||||
},
|
||||
state3,
|
||||
);
|
||||
|
||||
expect(state4 && state4.index).toEqual(0);
|
||||
expect(state4 && state4.routes[0].routeName).toEqual('Bar');
|
||||
@ -601,11 +737,18 @@ describe('StackRouter', () => {
|
||||
ChildNavigator.router = StackRouter({ Baz: { screen: () => <div /> } });
|
||||
|
||||
const router = StackRouter({
|
||||
Foo: { screen: () => <div />, },
|
||||
Bar: { screen: ChildNavigator, },
|
||||
Foo: { screen: () => <div /> },
|
||||
Bar: { screen: ChildNavigator },
|
||||
});
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Bar', params: { foo: '42' } }, state);
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
params: { foo: '42' },
|
||||
},
|
||||
state,
|
||||
);
|
||||
expect(state2 && state2.routes[1].params).toEqual({ foo: '42' });
|
||||
/* $FlowFixMe */
|
||||
expect(state2 && state2.routes[1].routes).toEqual([
|
||||
@ -625,11 +768,18 @@ describe('StackRouter', () => {
|
||||
});
|
||||
|
||||
const router = StackRouter({
|
||||
Foo: { screen: () => <div />, },
|
||||
Bar: { screen: ChildNavigator, },
|
||||
Foo: { screen: () => <div /> },
|
||||
Bar: { screen: ChildNavigator },
|
||||
});
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Bar', params: { foo: '42' } }, state);
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
params: { foo: '42' },
|
||||
},
|
||||
state,
|
||||
);
|
||||
expect(state2 && state2.routes[1].params).toEqual({ foo: '42' });
|
||||
/* $FlowFixMe */
|
||||
expect(state2 && state2.routes[1].routes).toEqual([
|
||||
@ -647,16 +797,22 @@ describe('StackRouter', () => {
|
||||
});
|
||||
|
||||
test('Handles empty URIs', () => {
|
||||
const router = StackRouter({
|
||||
Foo: {
|
||||
screen: () => <div />,
|
||||
const router = StackRouter(
|
||||
{
|
||||
Foo: {
|
||||
screen: () => <div />,
|
||||
},
|
||||
Bar: {
|
||||
screen: () => <div />,
|
||||
},
|
||||
},
|
||||
Bar: {
|
||||
screen: () => <div />,
|
||||
},
|
||||
}, { initialRouteName: 'Bar' });
|
||||
{ initialRouteName: 'Bar' },
|
||||
);
|
||||
const action = router.getActionForPathAndParams('');
|
||||
expect(action).toEqual({ type: NavigationActions.NAVIGATE, routeName: 'Bar' });
|
||||
expect(action).toEqual({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
});
|
||||
let state = null;
|
||||
if (action) {
|
||||
state = router.getStateForAction(action);
|
||||
@ -677,7 +833,17 @@ describe('StackRouter', () => {
|
||||
/* $FlowFixMe: these are for deprecated action names */
|
||||
const state = router.getStateForAction({ type: 'Init' });
|
||||
/* $FlowFixMe: these are for deprecated action names */
|
||||
const state2 = router.getStateForAction({ type: 'Reset', actions: [{ type: 'Navigate', routeName: 'Foo', params: { bar: '42' } }, { type: 'Navigate', routeName: 'Bar' }], index: 1 }, state);
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: 'Reset',
|
||||
actions: [
|
||||
{ type: 'Navigate', routeName: 'Foo', params: { bar: '42' } },
|
||||
{ type: 'Navigate', routeName: 'Bar' },
|
||||
],
|
||||
index: 1,
|
||||
},
|
||||
state,
|
||||
);
|
||||
expect(state2 && state2.index).toEqual(1);
|
||||
expect(state2 && state2.routes[0].params).toEqual({ bar: '42' });
|
||||
expect(state2 && state2.routes[0].routeName).toEqual('Foo');
|
||||
|
@ -22,18 +22,30 @@ describe('TabRouter', () => {
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const expectedState = {
|
||||
index: 0,
|
||||
routes: [{ key: 'Foo', routeName: 'Foo' }, { key: 'Bar', routeName: 'Bar' }],
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
};
|
||||
expect(state).toEqual(expectedState);
|
||||
const state2 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Bar' }, state);
|
||||
const state2 = router.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Bar' },
|
||||
state,
|
||||
);
|
||||
const expectedState2 = {
|
||||
index: 1,
|
||||
routes: [{ key: 'Foo', routeName: 'Foo' }, { key: 'Bar', routeName: 'Bar' }],
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
};
|
||||
expect(state2).toEqual(expectedState2);
|
||||
expect(router.getComponentForState(expectedState)).toEqual(ScreenA);
|
||||
expect(router.getComponentForState(expectedState2)).toEqual(ScreenB);
|
||||
const state3 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Bar' }, state2);
|
||||
const state3 = router.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Bar' },
|
||||
state2,
|
||||
);
|
||||
expect(state3).toEqual(null);
|
||||
});
|
||||
|
||||
@ -47,27 +59,45 @@ describe('TabRouter', () => {
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const expectedState = {
|
||||
index: 0,
|
||||
routes: [{ key: 'Foo', routeName: 'Foo' }, { key: 'Bar', routeName: 'Bar' }],
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
};
|
||||
expect(state).toEqual(expectedState);
|
||||
const state2 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Bar' }, state);
|
||||
const state2 = router.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Bar' },
|
||||
state,
|
||||
);
|
||||
const expectedState2 = {
|
||||
index: 1,
|
||||
routes: [{ key: 'Foo', routeName: 'Foo' }, { key: 'Bar', routeName: 'Bar' }],
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
};
|
||||
expect(state2).toEqual(expectedState2);
|
||||
expect(router.getComponentForState(expectedState)).toEqual(ScreenA);
|
||||
expect(router.getComponentForState(expectedState2)).toEqual(ScreenB);
|
||||
const state3 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Bar' }, state2);
|
||||
const state3 = router.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Bar' },
|
||||
state2,
|
||||
);
|
||||
expect(state3).toEqual(null);
|
||||
});
|
||||
|
||||
test('Can set the initial tab', () => {
|
||||
const router = TabRouter({ Foo: BareLeafRouteConfig, Bar: BareLeafRouteConfig }, { initialRouteName: 'Bar' });
|
||||
const router = TabRouter(
|
||||
{ Foo: BareLeafRouteConfig, Bar: BareLeafRouteConfig },
|
||||
{ initialRouteName: 'Bar' },
|
||||
);
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
expect(state).toEqual({
|
||||
index: 1,
|
||||
routes: [{ key: 'Foo', routeName: 'Foo' }, { key: 'Bar', routeName: 'Bar' }],
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
@ -89,25 +119,52 @@ describe('TabRouter', () => {
|
||||
});
|
||||
|
||||
test('getStateForAction returns null when navigating to same tab', () => {
|
||||
const router = TabRouter({ Foo: BareLeafRouteConfig, Bar: BareLeafRouteConfig }, { initialRouteName: 'Bar' });
|
||||
const router = TabRouter(
|
||||
{ Foo: BareLeafRouteConfig, Bar: BareLeafRouteConfig },
|
||||
{ initialRouteName: 'Bar' },
|
||||
);
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Bar' }, state);
|
||||
const state2 = router.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Bar' },
|
||||
state,
|
||||
);
|
||||
expect(state2).toEqual(null);
|
||||
});
|
||||
|
||||
test('getStateForAction returns initial navigate', () => {
|
||||
const router = TabRouter({ Foo: BareLeafRouteConfig, Bar: BareLeafRouteConfig });
|
||||
const state = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Foo' });
|
||||
const router = TabRouter({
|
||||
Foo: BareLeafRouteConfig,
|
||||
Bar: BareLeafRouteConfig,
|
||||
});
|
||||
const state = router.getStateForAction({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Foo',
|
||||
});
|
||||
expect(state && state.index).toEqual(0);
|
||||
});
|
||||
|
||||
test('Handles nested tabs and nested actions', () => {
|
||||
const ChildTabNavigator = () => <div />;
|
||||
ChildTabNavigator.router = TabRouter({ Foo: BareLeafRouteConfig, Bar: BareLeafRouteConfig });
|
||||
const router = TabRouter({ Foo: BareLeafRouteConfig, Baz: { screen: ChildTabNavigator }, Boo: BareLeafRouteConfig });
|
||||
ChildTabNavigator.router = TabRouter({
|
||||
Foo: BareLeafRouteConfig,
|
||||
Bar: BareLeafRouteConfig,
|
||||
});
|
||||
const router = TabRouter({
|
||||
Foo: BareLeafRouteConfig,
|
||||
Baz: { screen: ChildTabNavigator },
|
||||
Boo: BareLeafRouteConfig,
|
||||
});
|
||||
const params = { foo: '42' };
|
||||
const action = router.getActionForPathAndParams('Baz/Bar', params);
|
||||
const navAction = { type: NavigationActions.NAVIGATE, routeName: 'Baz', action: { type: NavigationActions.NAVIGATE, routeName: 'Bar', params: { foo: '42' } } };
|
||||
const navAction = {
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Baz',
|
||||
action: {
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
params: { foo: '42' },
|
||||
},
|
||||
};
|
||||
expect(action).toEqual(navAction);
|
||||
const state = router.getStateForAction(navAction);
|
||||
expect(state).toEqual({
|
||||
@ -143,9 +200,19 @@ describe('TabRouter', () => {
|
||||
|
||||
test('Handles passing params to nested tabs', () => {
|
||||
const ChildTabNavigator = () => <div />;
|
||||
ChildTabNavigator.router = TabRouter({ Boo: BareLeafRouteConfig, Bar: BareLeafRouteConfig });
|
||||
const router = TabRouter({ Foo: BareLeafRouteConfig, Baz: { screen: ChildTabNavigator } });
|
||||
const navAction = { type: NavigationActions.NAVIGATE, routeName: 'Baz', params: { foo: '42', bar: '43' } };
|
||||
ChildTabNavigator.router = TabRouter({
|
||||
Boo: BareLeafRouteConfig,
|
||||
Bar: BareLeafRouteConfig,
|
||||
});
|
||||
const router = TabRouter({
|
||||
Foo: BareLeafRouteConfig,
|
||||
Baz: { screen: ChildTabNavigator },
|
||||
});
|
||||
const navAction = {
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Baz',
|
||||
params: { foo: '42', bar: '43' },
|
||||
};
|
||||
let state = router.getStateForAction(navAction);
|
||||
expect(state).toEqual({
|
||||
index: 1,
|
||||
@ -164,8 +231,14 @@ describe('TabRouter', () => {
|
||||
});
|
||||
|
||||
// Ensure that navigating back and forth doesn't overwrite
|
||||
state = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Bar' }, state);
|
||||
state = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Boo' }, state);
|
||||
state = router.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Bar' },
|
||||
state,
|
||||
);
|
||||
state = router.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Boo' },
|
||||
state,
|
||||
);
|
||||
expect(state && state.routes[1]).toEqual({
|
||||
index: 0,
|
||||
key: 'Baz',
|
||||
@ -179,9 +252,19 @@ describe('TabRouter', () => {
|
||||
|
||||
test('Handles initial deep linking into nested tabs', () => {
|
||||
const ChildTabNavigator = () => <div />;
|
||||
ChildTabNavigator.router = TabRouter({ Foo: BareLeafRouteConfig, Bar: BareLeafRouteConfig });
|
||||
const router = TabRouter({ Foo: BareLeafRouteConfig, Baz: { screen: ChildTabNavigator }, Boo: BareLeafRouteConfig });
|
||||
const state = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Bar' });
|
||||
ChildTabNavigator.router = TabRouter({
|
||||
Foo: BareLeafRouteConfig,
|
||||
Bar: BareLeafRouteConfig,
|
||||
});
|
||||
const router = TabRouter({
|
||||
Foo: BareLeafRouteConfig,
|
||||
Baz: { screen: ChildTabNavigator },
|
||||
Boo: BareLeafRouteConfig,
|
||||
});
|
||||
const state = router.getStateForAction({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
});
|
||||
expect(state).toEqual({
|
||||
index: 1,
|
||||
routes: [
|
||||
@ -198,7 +281,10 @@ describe('TabRouter', () => {
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
],
|
||||
});
|
||||
const state2 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Foo' }, state);
|
||||
const state2 = router.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Foo' },
|
||||
state,
|
||||
);
|
||||
expect(state2).toEqual({
|
||||
index: 1,
|
||||
routes: [
|
||||
@ -215,101 +301,147 @@ describe('TabRouter', () => {
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
],
|
||||
});
|
||||
const state3 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Foo' }, state2);
|
||||
const state3 = router.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Foo' },
|
||||
state2,
|
||||
);
|
||||
expect(state3).toEqual(null);
|
||||
});
|
||||
|
||||
test('Handles linking across of deeply nested tabs', () => {
|
||||
const ChildNavigator0 = () => <div />;
|
||||
ChildNavigator0.router = TabRouter({ Boo: BareLeafRouteConfig, Baz: BareLeafRouteConfig });
|
||||
ChildNavigator0.router = TabRouter({
|
||||
Boo: BareLeafRouteConfig,
|
||||
Baz: BareLeafRouteConfig,
|
||||
});
|
||||
const ChildNavigator1 = () => <div />;
|
||||
ChildNavigator1.router = TabRouter({ Zoo: BareLeafRouteConfig, Zap: BareLeafRouteConfig });
|
||||
ChildNavigator1.router = TabRouter({
|
||||
Zoo: BareLeafRouteConfig,
|
||||
Zap: BareLeafRouteConfig,
|
||||
});
|
||||
const MidNavigator = () => <div />;
|
||||
MidNavigator.router = TabRouter({ Foo: { screen: ChildNavigator0 }, Bar: { screen: ChildNavigator1 } });
|
||||
const router = TabRouter({ Foo: { screen: MidNavigator }, Gah: BareLeafRouteConfig });
|
||||
MidNavigator.router = TabRouter({
|
||||
Foo: { screen: ChildNavigator0 },
|
||||
Bar: { screen: ChildNavigator1 },
|
||||
});
|
||||
const router = TabRouter({
|
||||
Foo: { screen: MidNavigator },
|
||||
Gah: BareLeafRouteConfig,
|
||||
});
|
||||
const state = router.getStateForAction(INIT_ACTION);
|
||||
expect(state).toEqual({
|
||||
index: 0,
|
||||
routes: [
|
||||
{ index: 0,
|
||||
{
|
||||
index: 0,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
routes: [
|
||||
{ index: 0,
|
||||
{
|
||||
index: 0,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
routes: [
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Baz', routeName: 'Baz' },
|
||||
] },
|
||||
{ index: 0,
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Baz', routeName: 'Baz' },
|
||||
],
|
||||
},
|
||||
{
|
||||
index: 0,
|
||||
key: 'Bar',
|
||||
routeName: 'Bar',
|
||||
routes: [
|
||||
{ key: 'Zoo', routeName: 'Zoo' },
|
||||
{ key: 'Zap', routeName: 'Zap' },
|
||||
] },
|
||||
] },
|
||||
{ key: 'Zoo', routeName: 'Zoo' },
|
||||
{ key: 'Zap', routeName: 'Zap' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ key: 'Gah', routeName: 'Gah' },
|
||||
],
|
||||
});
|
||||
const state2 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Zap' }, state);
|
||||
const state2 = router.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Zap' },
|
||||
state,
|
||||
);
|
||||
expect(state2).toEqual({
|
||||
index: 0,
|
||||
routes: [
|
||||
{ index: 1,
|
||||
{
|
||||
index: 1,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
routes: [
|
||||
{ index: 0,
|
||||
{
|
||||
index: 0,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
routes: [
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Baz', routeName: 'Baz' },
|
||||
] },
|
||||
{ index: 1,
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Baz', routeName: 'Baz' },
|
||||
],
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
key: 'Bar',
|
||||
routeName: 'Bar',
|
||||
routes: [
|
||||
{ key: 'Zoo', routeName: 'Zoo' },
|
||||
{ key: 'Zap', routeName: 'Zap' },
|
||||
] },
|
||||
] },
|
||||
{ key: 'Zoo', routeName: 'Zoo' },
|
||||
{ key: 'Zap', routeName: 'Zap' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ key: 'Gah', routeName: 'Gah' },
|
||||
],
|
||||
});
|
||||
const state3 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Zap' }, state2);
|
||||
const state3 = router.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Zap' },
|
||||
state2,
|
||||
);
|
||||
expect(state3).toEqual(null);
|
||||
const state4 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Foo', action: { type: NavigationActions.NAVIGATE, routeName: 'Bar', action: { type: NavigationActions.NAVIGATE, routeName: 'Zap' } } });
|
||||
const state4 = router.getStateForAction({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Foo',
|
||||
action: {
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
action: { type: NavigationActions.NAVIGATE, routeName: 'Zap' },
|
||||
},
|
||||
});
|
||||
expect(state4).toEqual({
|
||||
index: 0,
|
||||
routes: [
|
||||
{ index: 1,
|
||||
{
|
||||
index: 1,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
routes: [
|
||||
{ index: 0,
|
||||
{
|
||||
index: 0,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
routes: [
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Baz', routeName: 'Baz' },
|
||||
] },
|
||||
{ index: 1,
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Baz', routeName: 'Baz' },
|
||||
],
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
key: 'Bar',
|
||||
routeName: 'Bar',
|
||||
routes: [
|
||||
{ key: 'Zoo', routeName: 'Zoo' },
|
||||
{ key: 'Zap', routeName: 'Zap' },
|
||||
] },
|
||||
] },
|
||||
{ key: 'Zoo', routeName: 'Zoo' },
|
||||
{ key: 'Zap', routeName: 'Zap' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ key: 'Gah', routeName: 'Gah' },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('Handles path configuration', () => {
|
||||
const ScreenA = () => <div />;
|
||||
const ScreenB = () => <div />;
|
||||
@ -335,7 +467,10 @@ describe('TabRouter', () => {
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const expectedState = {
|
||||
index: 0,
|
||||
routes: [{ key: 'Foo', routeName: 'Foo' }, { key: 'Bar', routeName: 'Bar' }],
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
};
|
||||
expect(state).toEqual(expectedState);
|
||||
const state2 = router.getStateForAction(expectedAction, state);
|
||||
@ -376,7 +511,6 @@ describe('TabRouter', () => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('Gets deep path', () => {
|
||||
const ScreenA = () => <div />;
|
||||
const ScreenB = () => <div />;
|
||||
@ -414,11 +548,17 @@ describe('TabRouter', () => {
|
||||
});
|
||||
|
||||
test('Maps old actions (uses "getStateForAction returns null when navigating to same tab" test)', () => {
|
||||
const router = TabRouter({ Foo: BareLeafRouteConfig, Bar: BareLeafRouteConfig }, { initialRouteName: 'Bar' });
|
||||
const router = TabRouter(
|
||||
{ Foo: BareLeafRouteConfig, Bar: BareLeafRouteConfig },
|
||||
{ initialRouteName: 'Bar' },
|
||||
);
|
||||
/* $FlowFixMe: these are for deprecated action names */
|
||||
const state = router.getStateForAction({ type: 'Init' });
|
||||
/* $FlowFixMe: these are for deprecated action names */
|
||||
const state2 = router.getStateForAction({ type: 'Navigate', routeName: 'Bar' }, state);
|
||||
const state2 = router.getStateForAction(
|
||||
{ type: 'Navigate', routeName: 'Bar' },
|
||||
state,
|
||||
);
|
||||
expect(state2).toEqual(null);
|
||||
});
|
||||
|
||||
@ -435,10 +575,7 @@ describe('TabRouter', () => {
|
||||
|
||||
expect(state0).toEqual({
|
||||
index: 0,
|
||||
routes: [
|
||||
{ key: 'a', routeName: 'a' },
|
||||
{ key: 'b', routeName: 'b' },
|
||||
],
|
||||
routes: [{ key: 'a', routeName: 'a' }, { key: 'b', routeName: 'b' }],
|
||||
});
|
||||
|
||||
const params = { key: 'value' };
|
||||
|
@ -12,7 +12,7 @@ test('should get config for screen', () => {
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
|
||||
class HomeScreen extends Component {
|
||||
static navigationOptions = ({ navigation }) => ({
|
||||
static navigationOptions = ({ navigation }: *) => ({
|
||||
title: `Welcome ${navigation.state.params ? navigation.state.params.user : 'anonymous'}`,
|
||||
headerVisible: true,
|
||||
});
|
||||
@ -34,9 +34,11 @@ test('should get config for screen', () => {
|
||||
}
|
||||
|
||||
class NotificationScreen extends Component {
|
||||
static navigationOptions = ({ navigation }) => ({
|
||||
static navigationOptions = ({ navigation }: *) => ({
|
||||
title: '42',
|
||||
headerVisible: navigation.state.params ? !navigation.state.params.fullscreen : true,
|
||||
headerVisible: navigation.state.params
|
||||
? !navigation.state.params.fullscreen
|
||||
: true,
|
||||
});
|
||||
|
||||
render() {
|
||||
@ -44,16 +46,18 @@ test('should get config for screen', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const getScreenOptions: NavigationScreenOptionsGetter<NavigationStackScreenOptions, *> = createConfigGetter({
|
||||
Home: { screen: HomeScreen },
|
||||
Settings: { screen: SettingsScreen },
|
||||
Notifications: {
|
||||
screen: NotificationScreen,
|
||||
navigationOptions: {
|
||||
title: '10 new notifications',
|
||||
const getScreenOptions: NavigationScreenOptionsGetter<NavigationStackScreenOptions, *> = createConfigGetter(
|
||||
{
|
||||
Home: { screen: HomeScreen },
|
||||
Settings: { screen: SettingsScreen },
|
||||
Notifications: {
|
||||
screen: NotificationScreen,
|
||||
navigationOptions: {
|
||||
title: '10 new notifications',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
const routes = [
|
||||
{ key: 'A', routeName: 'Home' },
|
||||
@ -63,14 +67,54 @@ test('should get config for screen', () => {
|
||||
{ key: 'E', routeName: 'Notifications', params: { fullscreen: true } },
|
||||
];
|
||||
|
||||
expect(getScreenOptions(addNavigationHelpers({ state: routes[0], dispatch: () => false }), {}).title).toEqual('Welcome anonymous');
|
||||
expect(getScreenOptions(addNavigationHelpers({ state: routes[1], dispatch: () => false }), {}).title).toEqual('Welcome jane');
|
||||
expect(getScreenOptions(addNavigationHelpers({ state: routes[0], dispatch: () => false }), {}).headerVisible).toEqual(true);
|
||||
expect(getScreenOptions(addNavigationHelpers({ state: routes[2], dispatch: () => false }), {}).title).toEqual('Settings!!!');
|
||||
expect(getScreenOptions(addNavigationHelpers({ state: routes[2], dispatch: () => false }), {}).headerVisible).toEqual(false);
|
||||
expect(getScreenOptions(addNavigationHelpers({ state: routes[3], dispatch: () => false }), {}).title).toEqual('10 new notifications');
|
||||
expect(getScreenOptions(addNavigationHelpers({ state: routes[3], dispatch: () => false }), {}).headerVisible).toEqual(true);
|
||||
expect(getScreenOptions(addNavigationHelpers({ state: routes[4], dispatch: () => false }), {}).headerVisible).toEqual(false);
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[0], dispatch: () => false }),
|
||||
{},
|
||||
).title,
|
||||
).toEqual('Welcome anonymous');
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[1], dispatch: () => false }),
|
||||
{},
|
||||
).title,
|
||||
).toEqual('Welcome jane');
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[0], dispatch: () => false }),
|
||||
{},
|
||||
).headerVisible,
|
||||
).toEqual(true);
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[2], dispatch: () => false }),
|
||||
{},
|
||||
).title,
|
||||
).toEqual('Settings!!!');
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[2], dispatch: () => false }),
|
||||
{},
|
||||
).headerVisible,
|
||||
).toEqual(false);
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[3], dispatch: () => false }),
|
||||
{},
|
||||
).title,
|
||||
).toEqual('10 new notifications');
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[3], dispatch: () => false }),
|
||||
{},
|
||||
).headerVisible,
|
||||
).toEqual(true);
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[4], dispatch: () => false }),
|
||||
{},
|
||||
).headerVisible,
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
test('should throw if the route does not exist', () => {
|
||||
@ -86,13 +130,15 @@ test('should throw if the route does not exist', () => {
|
||||
Home: { screen: HomeScreen },
|
||||
});
|
||||
|
||||
const routes = [
|
||||
{ key: 'B', routeName: 'Settings' },
|
||||
];
|
||||
const routes = [{ key: 'B', routeName: 'Settings' }];
|
||||
|
||||
expect(
|
||||
() => getScreenOptions(addNavigationHelpers({ state: routes[0], dispatch: () => false }), {}),
|
||||
).toThrowError("There is no route defined for key Settings.\nMust be one of: 'Home'");
|
||||
expect(() =>
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[0], dispatch: () => false }),
|
||||
{},
|
||||
)).toThrowError(
|
||||
"There is no route defined for key Settings.\nMust be one of: 'Home'",
|
||||
);
|
||||
});
|
||||
|
||||
test('should throw if the screen is not defined under the route config', () => {
|
||||
@ -102,11 +148,10 @@ test('should throw if the screen is not defined under the route config', () => {
|
||||
Home: {},
|
||||
});
|
||||
|
||||
const routes = [
|
||||
{ key: 'B', routeName: 'Home' },
|
||||
];
|
||||
const routes = [{ key: 'B', routeName: 'Home' }];
|
||||
|
||||
expect(
|
||||
() => getScreenOptions(addNavigationHelpers({ state: routes[0], dispatch: () => false }))
|
||||
).toThrowError('Route Home must define a screen or a getScreen.');
|
||||
expect(() =>
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[0], dispatch: () => false }),
|
||||
)).toThrowError('Route Home must define a screen or a getScreen.');
|
||||
});
|
||||
|
@ -22,6 +22,8 @@ describe('validateRouteConfigMap', () => {
|
||||
});
|
||||
test('Fails on bad object', () => {
|
||||
const invalidMap = {
|
||||
// @todo fix flow, this should error as no screen/getScreen
|
||||
// provided
|
||||
Home: {
|
||||
foo: 'bar',
|
||||
},
|
||||
|
@ -12,6 +12,7 @@ import type {
|
||||
NavigationScreenProp,
|
||||
NavigationAction,
|
||||
NavigationRoute,
|
||||
NavigationStateRoute,
|
||||
NavigationRouteConfigMap,
|
||||
NavigationScreenConfig,
|
||||
NavigationScreenConfigProps,
|
||||
@ -54,8 +55,7 @@ export default (
|
||||
const { routes, index } = (route: NavigationStateRoute);
|
||||
|
||||
invariant(
|
||||
route.routeName &&
|
||||
typeof route.routeName === 'string',
|
||||
route.routeName && typeof route.routeName === 'string',
|
||||
'Cannot get config because the route does not have a routeName.',
|
||||
);
|
||||
|
||||
@ -66,14 +66,17 @@ export default (
|
||||
if (Component.router) {
|
||||
invariant(
|
||||
route && routes && index != null,
|
||||
`Expect nav state to have routes and index, ${JSON.stringify(route)}`
|
||||
`Expect nav state to have routes and index, ${JSON.stringify(route)}`,
|
||||
);
|
||||
const childRoute = routes[index];
|
||||
const childNavigation = addNavigationHelpers({
|
||||
state: childRoute,
|
||||
dispatch,
|
||||
});
|
||||
outputConfig = Component.router.getScreenOptions(childNavigation, screenProps);
|
||||
outputConfig = Component.router.getScreenOptions(
|
||||
childNavigation,
|
||||
screenProps,
|
||||
);
|
||||
}
|
||||
|
||||
const routeConfig = routeConfigs[route.routeName];
|
||||
@ -83,8 +86,16 @@ export default (
|
||||
|
||||
const configOptions = { navigation, screenProps: screenProps || {} };
|
||||
|
||||
outputConfig = applyConfig(navigatorScreenConfig, outputConfig, configOptions);
|
||||
outputConfig = applyConfig(componentScreenConfig, outputConfig, configOptions);
|
||||
outputConfig = applyConfig(
|
||||
navigatorScreenConfig,
|
||||
outputConfig,
|
||||
configOptions,
|
||||
);
|
||||
outputConfig = applyConfig(
|
||||
componentScreenConfig,
|
||||
outputConfig,
|
||||
configOptions,
|
||||
);
|
||||
outputConfig = applyConfig(routeScreenConfig, outputConfig, configOptions);
|
||||
|
||||
validateScreenOptions(outputConfig, route);
|
||||
|
@ -3,7 +3,8 @@
|
||||
*/
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
export default () => invariant(
|
||||
false,
|
||||
'`getScreenConfig` has been replaced with `getScreenOptions`',
|
||||
);
|
||||
export default () =>
|
||||
invariant(
|
||||
false,
|
||||
'`getScreenConfig` has been replaced with `getScreenOptions`',
|
||||
);
|
||||
|
@ -20,7 +20,9 @@ export default function getScreenForRouteName( // eslint-disable-line consistent
|
||||
invariant(
|
||||
routeConfig,
|
||||
`There is no route defined for key ${routeName}.\n` +
|
||||
`Must be one of: ${Object.keys(routeConfigs).map((a: string) => `'${a}'`).join(',')}`
|
||||
`Must be one of: ${Object.keys(routeConfigs)
|
||||
.map((a: string) => `'${a}'`)
|
||||
.join(',')}`,
|
||||
);
|
||||
|
||||
if (routeConfig.screen) {
|
||||
@ -32,9 +34,9 @@ export default function getScreenForRouteName( // eslint-disable-line consistent
|
||||
invariant(
|
||||
typeof screen === 'function',
|
||||
`The getScreen defined for route '${routeName} didn't return a valid ` +
|
||||
'screen or navigator.\n\n' +
|
||||
'Please pass it like this:\n' +
|
||||
`${routeName}: {\n getScreen: () => require('./MyScreen').default\n}`
|
||||
'screen or navigator.\n\n' +
|
||||
'Please pass it like this:\n' +
|
||||
`${routeName}: {\n getScreen: () => require('./MyScreen').default\n}`,
|
||||
);
|
||||
return screen;
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
/** @flow */
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
import type { NavigationRouteConfigMap } from '../TypeDefinition';
|
||||
|
||||
/**
|
||||
* Make sure the config passed e.g. to StackRouter, TabRouter has
|
||||
* the correct format, and throw a clear error if it doesn't.
|
||||
@ -8,7 +12,7 @@ function validateRouteConfigMap(routeConfigs: NavigationRouteConfigMap) {
|
||||
const routeNames = Object.keys(routeConfigs);
|
||||
invariant(
|
||||
routeNames.length > 0,
|
||||
'Please specify at least one route when configuring a navigator.'
|
||||
'Please specify at least one route when configuring a navigator.',
|
||||
);
|
||||
|
||||
routeNames.forEach((routeName: string) => {
|
||||
@ -17,37 +21,38 @@ function validateRouteConfigMap(routeConfigs: NavigationRouteConfigMap) {
|
||||
invariant(
|
||||
routeConfig.screen || routeConfig.getScreen,
|
||||
`Route '${routeName}' should declare a screen. ` +
|
||||
'For example:\n\n' +
|
||||
'import MyScreen from \'./MyScreen\';\n' +
|
||||
'...\n' +
|
||||
`${routeName}: {\n` +
|
||||
' screen: MyScreen,\n' +
|
||||
'}'
|
||||
'For example:\n\n' +
|
||||
"import MyScreen from './MyScreen';\n" +
|
||||
'...\n' +
|
||||
`${routeName}: {\n` +
|
||||
' screen: MyScreen,\n' +
|
||||
'}',
|
||||
);
|
||||
|
||||
if (routeConfig.screen && routeConfig.getScreen) {
|
||||
invariant(false,
|
||||
invariant(
|
||||
false,
|
||||
`Route '${routeName}' should declare a screen or ` +
|
||||
'a getScreen, not both.'
|
||||
'a getScreen, not both.',
|
||||
);
|
||||
}
|
||||
|
||||
if (routeConfig.screen) {
|
||||
invariant(
|
||||
typeof (routeConfig.screen) === 'function',
|
||||
typeof routeConfig.screen === 'function',
|
||||
`The component for route '${routeName}' must be a ` +
|
||||
'a React component. For example:\n\n' +
|
||||
'import MyScreen from \'./MyScreen\';\n' +
|
||||
'...\n' +
|
||||
`${routeName}: {\n` +
|
||||
' screen: MyScreen,\n' +
|
||||
'}\n\n' +
|
||||
'You can also use a navigator:\n\n' +
|
||||
'import MyNavigator from \'./MyNavigator\';\n' +
|
||||
'...\n' +
|
||||
`${routeName}: {\n` +
|
||||
' screen: MyNavigator,\n' +
|
||||
'}'
|
||||
'a React component. For example:\n\n' +
|
||||
"import MyScreen from './MyScreen';\n" +
|
||||
'...\n' +
|
||||
`${routeName}: {\n` +
|
||||
' screen: MyScreen,\n' +
|
||||
'}\n\n' +
|
||||
'You can also use a navigator:\n\n' +
|
||||
"import MyNavigator from './MyNavigator';\n" +
|
||||
'...\n' +
|
||||
`${routeName}: {\n` +
|
||||
' screen: MyNavigator,\n' +
|
||||
'}',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -1,9 +1,7 @@
|
||||
/* @flow */
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
import type {
|
||||
NavigationRoute,
|
||||
} from '../TypeDefinition';
|
||||
import type { NavigationRoute } from '../TypeDefinition';
|
||||
|
||||
const deprecatedKeys = ['tabBar', 'header'];
|
||||
|
||||
@ -14,45 +12,43 @@ const deprecatedKeys = ['tabBar', 'header'];
|
||||
export default (screenOptions: *, route: NavigationRoute) => {
|
||||
const keys: Array<string> = Object.keys(screenOptions);
|
||||
|
||||
const deprecatedKey = keys.find(
|
||||
(key: *) => deprecatedKeys.includes(key),
|
||||
);
|
||||
const deprecatedKey = keys.find((key: *) => deprecatedKeys.includes(key));
|
||||
|
||||
if (typeof screenOptions.title === 'function') {
|
||||
invariant(
|
||||
false,
|
||||
[
|
||||
`\`title\` cannot be defined as a function in navigation options for \`${route.routeName}\` screen. \n`,
|
||||
'Try replacing the following:',
|
||||
'{',
|
||||
' title: ({ state }) => state...',
|
||||
'}',
|
||||
'',
|
||||
'with:',
|
||||
'({ navigation }) => ({',
|
||||
' title: navigation.state...',
|
||||
'})',
|
||||
].join('\n'),
|
||||
[
|
||||
`\`title\` cannot be defined as a function in navigation options for \`${route.routeName}\` screen. \n`,
|
||||
'Try replacing the following:',
|
||||
'{',
|
||||
' title: ({ state }) => state...',
|
||||
'}',
|
||||
'',
|
||||
'with:',
|
||||
'({ navigation }) => ({',
|
||||
' title: navigation.state...',
|
||||
'})',
|
||||
].join('\n'),
|
||||
);
|
||||
}
|
||||
|
||||
if (deprecatedKey && typeof screenOptions[deprecatedKey] === 'function') {
|
||||
invariant(
|
||||
false,
|
||||
[
|
||||
`\`${deprecatedKey}\` cannot be defined as a function in navigation options for \`${route.routeName}\` screen. \n`,
|
||||
'Try replacing the following:',
|
||||
'{',
|
||||
` ${deprecatedKey}: ({ state }) => ({`,
|
||||
' key: state...',
|
||||
' })',
|
||||
'}',
|
||||
'',
|
||||
'with:',
|
||||
'({ navigation }) => ({',
|
||||
` ${deprecatedKey}Key: navigation.state...`,
|
||||
'})',
|
||||
].join('\n'),
|
||||
[
|
||||
`\`${deprecatedKey}\` cannot be defined as a function in navigation options for \`${route.routeName}\` screen. \n`,
|
||||
'Try replacing the following:',
|
||||
'{',
|
||||
` ${deprecatedKey}: ({ state }) => ({`,
|
||||
' key: state...',
|
||||
' })',
|
||||
'}',
|
||||
'',
|
||||
'with:',
|
||||
'({ navigation }) => ({',
|
||||
` ${deprecatedKey}Key: navigation.state...`,
|
||||
'})',
|
||||
].join('\n'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -65,17 +61,18 @@ export default (screenOptions: *, route: NavigationRoute) => {
|
||||
'Try replacing the following navigation options:',
|
||||
'{',
|
||||
` ${deprecatedKey}: {`,
|
||||
...Object.keys(screenOptions[deprecatedKey]).map((key: string) => (
|
||||
` ${key}: ...,`
|
||||
)),
|
||||
...Object.keys(screenOptions[deprecatedKey]).map(
|
||||
(key: string) => ` ${key}: ...,`,
|
||||
),
|
||||
' },',
|
||||
'}',
|
||||
'\n',
|
||||
'with:',
|
||||
'{',
|
||||
...Object.keys(screenOptions[deprecatedKey]).map((key: string) => (
|
||||
` ${deprecatedKey + key[0].toUpperCase() + key.slice(1)}: ...,`
|
||||
)),
|
||||
...Object.keys(screenOptions[deprecatedKey]).map(
|
||||
(key: string) =>
|
||||
` ${deprecatedKey + key[0].toUpperCase() + key.slice(1)}: ...,`,
|
||||
),
|
||||
'}',
|
||||
].join('\n'),
|
||||
);
|
||||
|
@ -2,17 +2,11 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
Animated,
|
||||
StyleSheet,
|
||||
} from 'react-native';
|
||||
import { Animated, StyleSheet } from 'react-native';
|
||||
|
||||
import createPointerEventsContainer from './PointerEventsContainer';
|
||||
|
||||
import type {
|
||||
NavigationSceneRenderer,
|
||||
NavigationSceneRendererProps,
|
||||
} from '../TypeDefinition';
|
||||
import type { NavigationSceneRendererProps } from '../TypeDefinition';
|
||||
|
||||
type Props = NavigationSceneRendererProps & {
|
||||
children: React.Children<*>,
|
||||
@ -31,7 +25,6 @@ class Card extends React.Component<any, Props, any> {
|
||||
const {
|
||||
children,
|
||||
pointerEvents,
|
||||
scene,
|
||||
style,
|
||||
} = this.props;
|
||||
return (
|
||||
|
@ -1,15 +1,22 @@
|
||||
/* @flow */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Animated, StyleSheet, PanResponder, Platform, View, I18nManager } from 'react-native';
|
||||
|
||||
import clamp from 'clamp';
|
||||
import {
|
||||
Animated,
|
||||
StyleSheet,
|
||||
PanResponder,
|
||||
Platform,
|
||||
View,
|
||||
I18nManager,
|
||||
} from 'react-native';
|
||||
|
||||
import Card from './Card';
|
||||
import NavigationActions from '../NavigationActions';
|
||||
import addNavigationHelpers from '../addNavigationHelpers';
|
||||
import SceneView from './SceneView';
|
||||
|
||||
import clamp from 'clamp';
|
||||
|
||||
import type {
|
||||
NavigationAction,
|
||||
NavigationLayout,
|
||||
@ -77,6 +84,15 @@ const RESPOND_THRESHOLD = 12;
|
||||
*/
|
||||
const GESTURE_RESPONSE_DISTANCE = 35;
|
||||
|
||||
const animatedSubscribeValue = (animatedValue: Animated.Value) => {
|
||||
if (!animatedValue.__isNative) {
|
||||
return;
|
||||
}
|
||||
if (Object.keys(animatedValue._listeners).length === 0) {
|
||||
animatedValue.addListener(emptyFunction);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The ratio between the gesture velocity and the animation velocity. This allows
|
||||
* the velocity of a swipe release to carry on into the new animation.
|
||||
@ -86,7 +102,6 @@ const GESTURE_RESPONSE_DISTANCE = 35;
|
||||
const GESTURE_ANIMATED_VELOCITY_RATIO = -4;
|
||||
|
||||
class CardStack extends Component {
|
||||
|
||||
/**
|
||||
* Used to identify the starting point of the position when the gesture starts, such that it can
|
||||
* be updated according to its relative position. This means that a card can effectively be
|
||||
@ -117,16 +132,17 @@ class CardStack extends Component {
|
||||
if (props.screenProps !== this.props.screenProps) {
|
||||
this._screenDetails = {};
|
||||
}
|
||||
props.scenes.forEach(newScene => {
|
||||
if (this._screenDetails[newScene.key] && this._screenDetails[newScene.key].state !== newScene.route) {
|
||||
props.scenes.forEach((newScene: *) => {
|
||||
if (
|
||||
this._screenDetails[newScene.key] &&
|
||||
this._screenDetails[newScene.key].state !== newScene.route
|
||||
) {
|
||||
this._screenDetails[newScene.key] = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_getScreenDetails = (
|
||||
scene: NavigationScene,
|
||||
): NavigationScreenDetails<*> => {
|
||||
_getScreenDetails = (scene: NavigationScene): NavigationScreenDetails<*> => {
|
||||
const { screenProps, navigation, router } = this.props;
|
||||
let screenDetails = this._screenDetails[scene.key];
|
||||
if (!screenDetails || screenDetails.state !== scene.route) {
|
||||
@ -146,7 +162,7 @@ class CardStack extends Component {
|
||||
|
||||
_renderHeader(
|
||||
scene: NavigationScene,
|
||||
headerMode: HeaderMode
|
||||
headerMode: HeaderMode,
|
||||
): ?React.Element<*> {
|
||||
return (
|
||||
<this.props.headerComponent
|
||||
@ -158,7 +174,8 @@ class CardStack extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
_animatedSubscribe(props) {
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
_animatedSubscribe(props: Props) {
|
||||
// Hack to make this work with native driven animations. We add a single listener
|
||||
// so the JS value of the following animated values gets updated. We rely on
|
||||
// some Animated private APIs and not doing so would require using a bunch of
|
||||
@ -166,32 +183,23 @@ class CardStack extends Component {
|
||||
// when we'd do that with the current structure we have. `stopAnimation` callback
|
||||
// is also broken with native animated values that have no listeners so if we
|
||||
// want to remove this we have to fix this too.
|
||||
this._animatedSubscribeValue(props.layout.width);
|
||||
this._animatedSubscribeValue(props.layout.height);
|
||||
this._animatedSubscribeValue(props.position);
|
||||
}
|
||||
_animatedSubscribeValue(animatedValue) {
|
||||
if (!animatedValue.__isNative) {
|
||||
return;
|
||||
}
|
||||
if (Object.keys(animatedValue._listeners).length === 0) {
|
||||
animatedValue.addListener(emptyFunction);
|
||||
}
|
||||
animatedSubscribeValue(props.layout.width);
|
||||
animatedSubscribeValue(props.layout.height);
|
||||
animatedSubscribeValue(props.position);
|
||||
}
|
||||
|
||||
_reset(resetToIndex: number, velocity: number): void {
|
||||
Animated.timing(this.props.position, {
|
||||
toValue: resetToIndex,
|
||||
duration: ANIMATION_DURATION,
|
||||
useNativeDriver: this.props.position.__isNative,
|
||||
velocity: velocity * GESTURE_ANIMATED_VELOCITY_RATIO,
|
||||
bounciness: 0,
|
||||
})
|
||||
.start();
|
||||
toValue: resetToIndex,
|
||||
duration: ANIMATION_DURATION,
|
||||
useNativeDriver: this.props.position.__isNative,
|
||||
velocity: velocity * GESTURE_ANIMATED_VELOCITY_RATIO,
|
||||
bounciness: 0,
|
||||
}).start();
|
||||
}
|
||||
|
||||
_goBack(backFromIndex: number, velocity: number) {
|
||||
const {navigation, position, scenes} = this.props;
|
||||
const { navigation, position, scenes } = this.props;
|
||||
const toValue = Math.max(backFromIndex - 1, 0);
|
||||
|
||||
// set temporary index for gesture handler to respect until the action is
|
||||
@ -199,21 +207,20 @@ class CardStack extends Component {
|
||||
this._immediateIndex = toValue;
|
||||
|
||||
Animated.timing(position, {
|
||||
toValue,
|
||||
duration: ANIMATION_DURATION,
|
||||
useNativeDriver: position.__isNative,
|
||||
velocity: velocity * GESTURE_ANIMATED_VELOCITY_RATIO,
|
||||
bounciness: 0,
|
||||
})
|
||||
.start(({finished}) => {
|
||||
this._immediateIndex = null;
|
||||
const backFromScene = scenes.find(s => s.index === toValue + 1);
|
||||
if (!this._isResponding && backFromScene) {
|
||||
navigation.dispatch(
|
||||
NavigationActions.back({ key: backFromScene.route.key })
|
||||
);
|
||||
}
|
||||
});
|
||||
toValue,
|
||||
duration: ANIMATION_DURATION,
|
||||
useNativeDriver: position.__isNative,
|
||||
velocity: velocity * GESTURE_ANIMATED_VELOCITY_RATIO,
|
||||
bounciness: 0,
|
||||
}).start(() => {
|
||||
this._immediateIndex = null;
|
||||
const backFromScene = scenes.find((s: *) => s.index === toValue + 1);
|
||||
if (!this._isResponding && backFromScene) {
|
||||
navigation.dispatch(
|
||||
NavigationActions.back({ key: backFromScene.route.key }),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render(): React.Element<*> {
|
||||
@ -222,8 +229,8 @@ class CardStack extends Component {
|
||||
if (headerMode === 'float') {
|
||||
floatingHeader = this._renderHeader(this.props.scene, headerMode);
|
||||
}
|
||||
const {navigation, position, scene, mode, scenes} = this.props;
|
||||
const {index} = navigation.state;
|
||||
const { navigation, position, scene, mode, scenes } = this.props;
|
||||
const { index } = navigation.state;
|
||||
const responder = PanResponder.create({
|
||||
onPanResponderTerminate: () => {
|
||||
this._isResponding = false;
|
||||
@ -237,14 +244,16 @@ class CardStack extends Component {
|
||||
},
|
||||
onMoveShouldSetPanResponder: (
|
||||
event: { nativeEvent: { pageY: number, pageX: number } },
|
||||
gesture: any
|
||||
gesture: any,
|
||||
) => {
|
||||
const layout = this.props.layout;
|
||||
if (index !== scene.index) {
|
||||
return false;
|
||||
}
|
||||
const isVertical = false; // todo: bring back gestures for mode=modal
|
||||
const immediateIndex = this._immediateIndex == null ? index : this._immediateIndex;
|
||||
const immediateIndex = this._immediateIndex == null
|
||||
? index
|
||||
: this._immediateIndex;
|
||||
const currentDragDistance = gesture[isVertical ? 'dy' : 'dx'];
|
||||
const currentDragPosition = event.nativeEvent[
|
||||
isVertical ? 'pageY' : 'pageX'
|
||||
@ -252,7 +261,7 @@ class CardStack extends Component {
|
||||
const axisLength = isVertical
|
||||
? layout.height.__getValue()
|
||||
: layout.width.__getValue();
|
||||
const axisHasBeenMeasured = !! axisLength;
|
||||
const axisHasBeenMeasured = !!axisLength;
|
||||
|
||||
// Measure the distance from the touch to the edge of the screen
|
||||
const screenEdgeDistance = currentDragPosition - currentDragDistance;
|
||||
@ -262,10 +271,13 @@ class CardStack extends Component {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hasDraggedEnough = Math.abs(currentDragDistance) > RESPOND_THRESHOLD;
|
||||
const hasDraggedEnough = Math.abs(currentDragDistance) >
|
||||
RESPOND_THRESHOLD;
|
||||
|
||||
const isOnFirstCard = immediateIndex === 0;
|
||||
const shouldSetResponder = hasDraggedEnough && axisHasBeenMeasured && !isOnFirstCard;
|
||||
const shouldSetResponder = hasDraggedEnough &&
|
||||
axisHasBeenMeasured &&
|
||||
!isOnFirstCard;
|
||||
return shouldSetResponder;
|
||||
},
|
||||
onPanResponderMove: (event: any, gesture: any) => {
|
||||
@ -283,20 +295,20 @@ class CardStack extends Component {
|
||||
const value = clamp(index - 1, currentValue, index);
|
||||
position.setValue(value);
|
||||
},
|
||||
onPanResponderTerminationRequest: (event: any, gesture: any) => {
|
||||
onPanResponderTerminationRequest: () =>
|
||||
// Returning false will prevent other views from becoming responder while
|
||||
// the navigation view is the responder (mid-gesture)
|
||||
return false;
|
||||
},
|
||||
false,
|
||||
onPanResponderRelease: (event: any, gesture: any) => {
|
||||
if (!this._isResponding) {
|
||||
return;
|
||||
}
|
||||
this._isResponding = false;
|
||||
const isVertical = false;
|
||||
const axis = isVertical ? 'dy' : 'dx';
|
||||
const velocity = gesture[isVertical ? 'vy' : 'vx'];
|
||||
const immediateIndex = this._immediateIndex == null ? index : this._immediateIndex;
|
||||
const immediateIndex = this._immediateIndex == null
|
||||
? index
|
||||
: this._immediateIndex;
|
||||
|
||||
// To asyncronously get the current animated value, we need to run stopAnimation:
|
||||
position.stopAnimation((value: number) => {
|
||||
@ -321,21 +333,19 @@ class CardStack extends Component {
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const gesturesEnabled = mode === 'card' && Platform.OS === 'ios';
|
||||
const handlers = gesturesEnabled ? responder.panHandlers : {};
|
||||
|
||||
|
||||
return (
|
||||
<View
|
||||
{...handlers}
|
||||
style={styles.container}>
|
||||
<View {...handlers} style={styles.container}>
|
||||
<View style={styles.scenes}>
|
||||
{scenes.map((scene: NavigationScene) => this._renderCard(scene))}
|
||||
{scenes.map((s: *) => this._renderCard(s))}
|
||||
</View>
|
||||
{floatingHeader}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
_getHeaderMode(): HeaderMode {
|
||||
if (this.props.headerMode) {
|
||||
@ -383,25 +393,32 @@ class CardStack extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
_renderCard = (scene): React.Element<*> => {
|
||||
_renderCard = (scene: NavigationScene): React.Element<*> => {
|
||||
const isModal = this.props.mode === 'modal';
|
||||
|
||||
/* $FlowFixMe */
|
||||
const { screenInterpolator } = TransitionConfigs.getTransitionConfig(this.props.transitionConfig, {}, {}, isModal);
|
||||
const style = screenInterpolator && screenInterpolator({...this.props, scene});
|
||||
const { screenInterpolator } = TransitionConfigs.getTransitionConfig(
|
||||
this.props.transitionConfig,
|
||||
{},
|
||||
{},
|
||||
isModal,
|
||||
);
|
||||
const style = screenInterpolator &&
|
||||
screenInterpolator({ ...this.props, scene });
|
||||
|
||||
const SceneComponent = this.props.router.getComponentForRouteName(
|
||||
scene.route.routeName
|
||||
scene.route.routeName,
|
||||
);
|
||||
|
||||
return (
|
||||
<Card
|
||||
{...this.props}
|
||||
key={`card_${scene.key}`}
|
||||
children={this._renderInnerScene(SceneComponent, scene)}
|
||||
style={[style, this.props.cardStyle]}
|
||||
scene={scene}
|
||||
/>
|
||||
>
|
||||
{this._renderInnerScene(SceneComponent, scene)}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
@ -1,12 +1,8 @@
|
||||
/* @flow */
|
||||
|
||||
import {
|
||||
I18nManager,
|
||||
} from 'react-native';
|
||||
import { I18nManager } from 'react-native';
|
||||
|
||||
import type {
|
||||
NavigationSceneRendererProps,
|
||||
} from '../TypeDefinition';
|
||||
import type { NavigationSceneRendererProps } from '../TypeDefinition';
|
||||
|
||||
/**
|
||||
* Utility that builds the style for the card in the cards stack.
|
||||
@ -38,10 +34,7 @@ function forInitial(props: NavigationSceneRendererProps): Object {
|
||||
const translate = focused ? 0 : 1000000;
|
||||
return {
|
||||
opacity,
|
||||
transform: [
|
||||
{ translateX: translate },
|
||||
{ translateY: translate },
|
||||
],
|
||||
transform: [{ translateX: translate }, { translateY: translate }],
|
||||
};
|
||||
}
|
||||
|
||||
@ -65,10 +58,9 @@ function forHorizontal(props: NavigationSceneRendererProps): Object {
|
||||
// Add ~30px to the interpolated width screens width for horizontal movement. This allows
|
||||
// the screen's shadow to go screen fully offscreen without abruptly dissapearing
|
||||
const width = layout.initWidth + 30;
|
||||
const outputRange = I18nManager.isRTL ?
|
||||
([-width, 0, 10, 10]: Array<number>) :
|
||||
([width, 0, -10, -10]: Array<number>);
|
||||
|
||||
const outputRange = I18nManager.isRTL
|
||||
? ([-width, 0, 10, 10]: Array<number>)
|
||||
: ([width, 0, -10, -10]: Array<number>);
|
||||
|
||||
const opacity = position.interpolate({
|
||||
inputRange,
|
||||
@ -83,10 +75,7 @@ function forHorizontal(props: NavigationSceneRendererProps): Object {
|
||||
|
||||
return {
|
||||
opacity,
|
||||
transform: [
|
||||
{ translateX },
|
||||
{ translateY },
|
||||
],
|
||||
transform: [{ translateX }, { translateY }],
|
||||
};
|
||||
}
|
||||
|
||||
@ -121,10 +110,7 @@ function forVertical(props: NavigationSceneRendererProps): Object {
|
||||
|
||||
return {
|
||||
opacity,
|
||||
transform: [
|
||||
{ translateX },
|
||||
{ translateY },
|
||||
],
|
||||
transform: [{ translateX }, { translateY }],
|
||||
};
|
||||
}
|
||||
|
||||
@ -144,7 +130,6 @@ function forFadeFromBottomAndroid(props: NavigationSceneRendererProps): Object {
|
||||
|
||||
const index = scene.index;
|
||||
const inputRange = [index - 1, index, index + 0.99, index + 1];
|
||||
const height = layout.initHeight;
|
||||
|
||||
const opacity = position.interpolate({
|
||||
inputRange,
|
||||
@ -159,14 +144,11 @@ function forFadeFromBottomAndroid(props: NavigationSceneRendererProps): Object {
|
||||
|
||||
return {
|
||||
opacity,
|
||||
transform: [
|
||||
{ translateX },
|
||||
{ translateY },
|
||||
],
|
||||
transform: [{ translateX }, { translateY }],
|
||||
};
|
||||
}
|
||||
|
||||
function canUseNativeDriver(isVertical: boolean): boolean {
|
||||
function canUseNativeDriver(): boolean {
|
||||
// The native driver can be enabled for this interpolator animating
|
||||
// opacity, translateX, and translateY is supported by the native animation
|
||||
// driver on iOS and Android.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { NativeModules, View } from 'react-native';
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import CardStack from './CardStack';
|
||||
import CardStackStyleInterpolator from './CardStackStyleInterpolator';
|
||||
@ -9,8 +9,7 @@ import Transitioner from './Transitioner';
|
||||
import TransitionConfigs from './TransitionConfigs';
|
||||
import Header from './Header';
|
||||
|
||||
const NativeAnimatedModule = NativeModules &&
|
||||
NativeModules.NativeAnimatedModule;
|
||||
import type { TransitionConfig } from './TransitionConfigs';
|
||||
|
||||
import type {
|
||||
NavigationAction,
|
||||
@ -24,7 +23,8 @@ import type {
|
||||
Style,
|
||||
} from '../TypeDefinition';
|
||||
|
||||
import type { TransitionConfig } from './TransitionConfigs';
|
||||
const NativeAnimatedModule = NativeModules &&
|
||||
NativeModules.NativeAnimatedModule;
|
||||
|
||||
type Props = {
|
||||
screenProps?: {},
|
||||
@ -73,7 +73,7 @@ class CardStackTransitioner extends Component<DefaultProps, Props, void> {
|
||||
// props for the new screen
|
||||
transitionProps: NavigationTransitionProps,
|
||||
// props for the old screen
|
||||
prevTransitionProps: NavigationTransitionProps
|
||||
prevTransitionProps: NavigationTransitionProps,
|
||||
) => {
|
||||
const isModal = this.props.mode === 'modal';
|
||||
// Copy the object so we can assign useNativeDriver below
|
||||
|
@ -1,12 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
} from 'react-native';
|
||||
import { View, Text, Platform, StyleSheet } from 'react-native';
|
||||
|
||||
import TouchableItem from '../TouchableItem';
|
||||
|
||||
@ -16,41 +11,43 @@ import type {
|
||||
NavigationAction,
|
||||
Style,
|
||||
} from '../../TypeDefinition';
|
||||
import type {
|
||||
DrawerScene,
|
||||
} from './DrawerView.js';
|
||||
import type { DrawerScene } from './DrawerView.js';
|
||||
|
||||
type Props = {
|
||||
navigation: NavigationScreenProp<NavigationState, NavigationAction>;
|
||||
activeTintColor?: string;
|
||||
activeBackgroundColor?: string;
|
||||
inactiveTintColor?: string;
|
||||
inactiveBackgroundColor?: string;
|
||||
getLabel: (scene: DrawerScene) => ?(React.Element<*> | string);
|
||||
renderIcon: (scene: DrawerScene) => ?React.Element<*>;
|
||||
style?: Style;
|
||||
labelStyle?: Style;
|
||||
navigation: NavigationScreenProp<NavigationState, NavigationAction>,
|
||||
activeTintColor?: string,
|
||||
activeBackgroundColor?: string,
|
||||
inactiveTintColor?: string,
|
||||
inactiveBackgroundColor?: string,
|
||||
getLabel: (scene: DrawerScene) => ?(React.Element<*> | string),
|
||||
renderIcon: (scene: DrawerScene) => ?React.Element<*>,
|
||||
style?: Style,
|
||||
labelStyle?: Style,
|
||||
};
|
||||
|
||||
/**
|
||||
* Component that renders the navigation list in the drawer.
|
||||
*/
|
||||
const DrawerNavigatorItems = ({
|
||||
navigation,
|
||||
activeTintColor,
|
||||
activeBackgroundColor,
|
||||
inactiveTintColor,
|
||||
inactiveBackgroundColor,
|
||||
getLabel,
|
||||
renderIcon,
|
||||
style,
|
||||
labelStyle,
|
||||
}: Props) => (
|
||||
const DrawerNavigatorItems = (
|
||||
{
|
||||
navigation,
|
||||
activeTintColor,
|
||||
activeBackgroundColor,
|
||||
inactiveTintColor,
|
||||
inactiveBackgroundColor,
|
||||
getLabel,
|
||||
renderIcon,
|
||||
style,
|
||||
labelStyle,
|
||||
}: Props,
|
||||
) => (
|
||||
<View style={[styles.container, style]}>
|
||||
{navigation.state.routes.map((route: *, index: number) => {
|
||||
const focused = navigation.state.index === index;
|
||||
const color = focused ? activeTintColor : inactiveTintColor;
|
||||
const backgroundColor = focused ? activeBackgroundColor : inactiveBackgroundColor;
|
||||
const backgroundColor = focused
|
||||
? activeBackgroundColor
|
||||
: inactiveBackgroundColor;
|
||||
const scene = { route, index, focused, tintColor: color };
|
||||
const icon = renderIcon(scene);
|
||||
const label = getLabel(scene);
|
||||
@ -64,19 +61,18 @@ const DrawerNavigatorItems = ({
|
||||
delayPressIn={0}
|
||||
>
|
||||
<View style={[styles.item, { backgroundColor }]}>
|
||||
{icon ? (
|
||||
<View style={[styles.icon, focused ? null : styles.inactiveIcon]}>
|
||||
{icon}
|
||||
</View>
|
||||
) : null}
|
||||
{icon
|
||||
? <View
|
||||
style={[styles.icon, focused ? null : styles.inactiveIcon]}
|
||||
>
|
||||
{icon}
|
||||
</View>
|
||||
: null}
|
||||
{typeof label === 'string'
|
||||
? (
|
||||
<Text style={[styles.label, { color }, labelStyle]}>
|
||||
? <Text style={[styles.label, { color }, labelStyle]}>
|
||||
{label}
|
||||
</Text>
|
||||
)
|
||||
: label
|
||||
}
|
||||
: label}
|
||||
</View>
|
||||
</TouchableItem>
|
||||
);
|
||||
|
@ -15,11 +15,11 @@ import type {
|
||||
} from '../../TypeDefinition';
|
||||
|
||||
type Props = {
|
||||
screenProps?: {};
|
||||
screenProps?: {},
|
||||
router: NavigationRouter<NavigationState, NavigationAction, NavigationDrawerScreenOptions>,
|
||||
navigation: NavigationScreenProp<NavigationState, NavigationAction>,
|
||||
childNavigationProps: {
|
||||
[key: string]: NavigationScreenProp<NavigationRoute, NavigationAction>;
|
||||
[key: string]: NavigationScreenProp<NavigationRoute, NavigationAction>,
|
||||
},
|
||||
};
|
||||
|
||||
@ -30,7 +30,12 @@ class DrawerScreen extends PureComponent<void, Props, void> {
|
||||
props: Props;
|
||||
|
||||
render() {
|
||||
const { router, navigation, childNavigationProps, screenProps } = this.props;
|
||||
const {
|
||||
router,
|
||||
navigation,
|
||||
childNavigationProps,
|
||||
screenProps,
|
||||
} = this.props;
|
||||
const { routes, index } = navigation.state;
|
||||
const childNavigation = childNavigationProps[routes[index].key];
|
||||
const Content = router.getComponentForRouteName(routes[index].routeName);
|
||||
@ -39,7 +44,10 @@ class DrawerScreen extends PureComponent<void, Props, void> {
|
||||
screenProps={screenProps}
|
||||
component={Content}
|
||||
navigation={childNavigation}
|
||||
navigationOptions={router.getScreenOptions(childNavigation, screenProps)}
|
||||
navigationOptions={router.getScreenOptions(
|
||||
childNavigation,
|
||||
screenProps,
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,11 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import {
|
||||
Platform,
|
||||
StyleSheet,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
import withCachedChildNavigation from '../../withCachedChildNavigation';
|
||||
|
||||
@ -19,9 +15,7 @@ import type {
|
||||
Style,
|
||||
} from '../../TypeDefinition';
|
||||
|
||||
import type {
|
||||
DrawerScene,
|
||||
} from './DrawerView';
|
||||
import type { DrawerScene } from './DrawerView';
|
||||
|
||||
type Navigation = NavigationScreenProp<NavigationRoute, NavigationAction>;
|
||||
|
||||
@ -32,7 +26,7 @@ type Props = {
|
||||
contentComponent: ReactClass<*>,
|
||||
contentOptions?: {},
|
||||
screenProps?: {},
|
||||
style?: Style;
|
||||
style?: Style,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -42,15 +36,17 @@ class DrawerSidebar extends PureComponent<void, Props, void> {
|
||||
props: Props;
|
||||
|
||||
_getScreenOptions = (routeKey: string) => {
|
||||
const DrawerScreen = this.props.router.getComponentForRouteName('DrawerClose');
|
||||
const DrawerScreen = this.props.router.getComponentForRouteName(
|
||||
'DrawerClose',
|
||||
);
|
||||
return DrawerScreen.router.getScreenOptions(
|
||||
this.props.childNavigationProps[routeKey],
|
||||
this.props.screenProps
|
||||
this.props.screenProps,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
_getLabel = ({ focused, tintColor, route }: DrawerScene) => {
|
||||
const {drawerLabel, title} = this._getScreenOptions(route.key);
|
||||
const { drawerLabel, title } = this._getScreenOptions(route.key);
|
||||
if (drawerLabel) {
|
||||
return typeof drawerLabel === 'function'
|
||||
? drawerLabel({ tintColor, focused })
|
||||
@ -65,7 +61,7 @@ class DrawerSidebar extends PureComponent<void, Props, void> {
|
||||
};
|
||||
|
||||
_renderIcon = ({ focused, tintColor, route }: DrawerScene) => {
|
||||
const {drawerIcon} = this._getScreenOptions(route.key);
|
||||
const { drawerIcon } = this._getScreenOptions(route.key);
|
||||
if (drawerIcon) {
|
||||
return typeof drawerIcon === 'function'
|
||||
? drawerIcon({ tintColor, focused })
|
||||
|
@ -18,10 +18,10 @@ import type {
|
||||
} from '../../TypeDefinition';
|
||||
|
||||
export type DrawerScene = {
|
||||
route: NavigationRoute;
|
||||
focused: boolean;
|
||||
index: number;
|
||||
tintColor?: string;
|
||||
route: NavigationRoute,
|
||||
focused: boolean,
|
||||
index: number,
|
||||
tintColor?: string,
|
||||
};
|
||||
|
||||
export type DrawerViewConfig = {
|
||||
@ -29,11 +29,11 @@ export type DrawerViewConfig = {
|
||||
drawerPosition: 'left' | 'right',
|
||||
contentComponent: ReactClass<*>,
|
||||
contentOptions?: {},
|
||||
style?: Style;
|
||||
style?: Style,
|
||||
};
|
||||
|
||||
type Props = DrawerViewConfig & {
|
||||
screenProps?: {};
|
||||
screenProps?: {},
|
||||
router: NavigationRouter<NavigationState, NavigationAction, NavigationDrawerScreenOptions>,
|
||||
navigation: NavigationScreenProp<NavigationState, NavigationAction>,
|
||||
};
|
||||
@ -42,7 +42,6 @@ type Props = DrawerViewConfig & {
|
||||
* Component that renders the drawer.
|
||||
*/
|
||||
export default class DrawerView<T: *> extends PureComponent<void, Props, void> {
|
||||
|
||||
static Items = DrawerNavigatorItems;
|
||||
|
||||
props: Props;
|
||||
@ -52,7 +51,9 @@ export default class DrawerView<T: *> extends PureComponent<void, Props, void> {
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: Props) {
|
||||
if (this.props.navigation.state.index !== nextProps.navigation.state.index) {
|
||||
if (
|
||||
this.props.navigation.state.index !== nextProps.navigation.state.index
|
||||
) {
|
||||
const { routes, index } = nextProps.navigation.state;
|
||||
if (routes[index].routeName === 'DrawerOpen') {
|
||||
this._drawer.openDrawer();
|
||||
@ -82,22 +83,29 @@ export default class DrawerView<T: *> extends PureComponent<void, Props, void> {
|
||||
};
|
||||
|
||||
_updateScreenNavigation = (
|
||||
navigation: NavigationScreenProp<NavigationState, NavigationAction>
|
||||
navigation: NavigationScreenProp<NavigationState, NavigationAction>,
|
||||
) => {
|
||||
const navigationState = navigation.state.routes.find((route: *) => route.routeName === 'DrawerClose');
|
||||
if (this._screenNavigationProp && this._screenNavigationProp.state === navigationState) {
|
||||
const navigationState = navigation.state.routes.find(
|
||||
(route: *) => route.routeName === 'DrawerClose',
|
||||
);
|
||||
if (
|
||||
this._screenNavigationProp &&
|
||||
this._screenNavigationProp.state === navigationState
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this._screenNavigationProp = addNavigationHelpers({
|
||||
...navigation,
|
||||
state: navigationState,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_getNavigationState = (
|
||||
navigation: NavigationScreenProp<NavigationState, NavigationAction>
|
||||
navigation: NavigationScreenProp<NavigationState, NavigationAction>,
|
||||
) => {
|
||||
const navigationState = navigation.state.routes.find((route: *) => route.routeName === 'DrawerClose');
|
||||
const navigationState = navigation.state.routes.find(
|
||||
(route: *) => route.routeName === 'DrawerClose',
|
||||
);
|
||||
return navigationState;
|
||||
};
|
||||
|
||||
@ -115,17 +123,22 @@ export default class DrawerView<T: *> extends PureComponent<void, Props, void> {
|
||||
_drawer: any;
|
||||
|
||||
render() {
|
||||
const DrawerScreen = this.props.router.getComponentForRouteName('DrawerClose');
|
||||
const DrawerScreen = this.props.router.getComponentForRouteName(
|
||||
'DrawerClose',
|
||||
);
|
||||
return (
|
||||
<DrawerLayout
|
||||
ref={(c: *) => (this._drawer = c)}
|
||||
ref={(c: *) => {
|
||||
this._drawer = c;
|
||||
}}
|
||||
drawerWidth={this.props.drawerWidth}
|
||||
onDrawerOpen={this._handleDrawerOpen}
|
||||
onDrawerClose={this._handleDrawerClose}
|
||||
renderNavigationView={this._renderNavigationView}
|
||||
drawerPosition={
|
||||
this.props.drawerPosition === 'right' ?
|
||||
DrawerLayout.positions.Right : DrawerLayout.positions.Left
|
||||
this.props.drawerPosition === 'right'
|
||||
? DrawerLayout.positions.Right
|
||||
: DrawerLayout.positions.Left
|
||||
}
|
||||
>
|
||||
<DrawerScreen
|
||||
|
@ -4,12 +4,7 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
Animated,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { Animated, Platform, StyleSheet, View } from 'react-native';
|
||||
|
||||
import HeaderTitle from './HeaderTitle';
|
||||
import HeaderBackButton from './HeaderBackButton';
|
||||
@ -60,7 +55,7 @@ class Header extends React.PureComponent<void, HeaderProps, HeaderState> {
|
||||
}
|
||||
|
||||
_getLastScene(scene: NavigationScene): ?NavigationScene {
|
||||
return this.props.scenes.find(s => s.index === scene.index - 1);
|
||||
return this.props.scenes.find((s: *) => s.index === scene.index - 1);
|
||||
}
|
||||
|
||||
_getBackButtonTitleString(scene: NavigationScene): ?string {
|
||||
@ -80,7 +75,9 @@ class Header extends React.PureComponent<void, HeaderProps, HeaderState> {
|
||||
if (!lastScene) {
|
||||
return null;
|
||||
}
|
||||
return this.props.getScreenDetails(lastScene).options.headerTruncatedBackTitle;
|
||||
return this.props.getScreenDetails(
|
||||
lastScene,
|
||||
).options.headerTruncatedBackTitle;
|
||||
}
|
||||
|
||||
_renderTitleComponent = (props: SceneProps) => {
|
||||
@ -98,13 +95,13 @@ class Header extends React.PureComponent<void, HeaderProps, HeaderState> {
|
||||
// size of the title.
|
||||
const onLayoutIOS = Platform.OS === 'ios'
|
||||
? (e: LayoutEvent) => {
|
||||
this.setState({
|
||||
widths: {
|
||||
...this.state.widths,
|
||||
[props.scene.key]: e.nativeEvent.layout.width,
|
||||
},
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
widths: {
|
||||
...this.state.widths,
|
||||
[props.scene.key]: e.nativeEvent.layout.width,
|
||||
},
|
||||
});
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
@ -126,13 +123,17 @@ class Header extends React.PureComponent<void, HeaderProps, HeaderState> {
|
||||
return null;
|
||||
}
|
||||
const backButtonTitle = this._getBackButtonTitleString(props.scene);
|
||||
const truncatedBackButtonTitle = this._getTruncatedBackButtonTitle(props.scene);
|
||||
const truncatedBackButtonTitle = this._getTruncatedBackButtonTitle(
|
||||
props.scene,
|
||||
);
|
||||
const width = this.state.widths[props.scene.key]
|
||||
? (this.props.layout.initWidth - this.state.widths[props.scene.key]) / 2
|
||||
: undefined;
|
||||
return (
|
||||
<HeaderBackButton
|
||||
onPress={() => { this.props.navigation.goBack(null); }}
|
||||
onPress={() => {
|
||||
this.props.navigation.goBack(null);
|
||||
}}
|
||||
pressColorAndroid={options.headerPressColorAndroid}
|
||||
tintColor={options.headerTintColor}
|
||||
title={backButtonTitle}
|
||||
@ -144,7 +145,7 @@ class Header extends React.PureComponent<void, HeaderProps, HeaderState> {
|
||||
|
||||
_renderRightComponent = (props: SceneProps) => {
|
||||
const details = this.props.getScreenDetails(props.scene);
|
||||
const {headerRight} = details.options;
|
||||
const { headerRight } = details.options;
|
||||
return headerRight || null;
|
||||
};
|
||||
|
||||
@ -261,12 +262,13 @@ class Header extends React.PureComponent<void, HeaderProps, HeaderState> {
|
||||
let appBar;
|
||||
|
||||
if (this.props.mode === 'float') {
|
||||
const scenesProps: Array<SceneProps> = this.props.scenes
|
||||
.map((scene: NavigationScene) => ({
|
||||
const scenesProps: Array<SceneProps> = this.props.scenes.map(
|
||||
(scene: NavigationScene) => ({
|
||||
position: this.props.position,
|
||||
progress: this.props.progress,
|
||||
scene,
|
||||
}));
|
||||
}),
|
||||
);
|
||||
appBar = scenesProps.map(this._renderHeader, this);
|
||||
} else {
|
||||
appBar = this._renderHeader({
|
||||
@ -277,7 +279,14 @@ class Header extends React.PureComponent<void, HeaderProps, HeaderState> {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { scenes, scene, position, screenProps, progress, ...rest } = this.props;
|
||||
const {
|
||||
scenes,
|
||||
scene,
|
||||
position,
|
||||
screenProps,
|
||||
progress,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
const { options } = this.props.getScreenDetails(scene, screenProps);
|
||||
const style = options.headerStyle;
|
||||
@ -322,9 +331,7 @@ const styles = StyleSheet.create({
|
||||
right: TITLE_OFFSET,
|
||||
top: 0,
|
||||
position: 'absolute',
|
||||
alignItems: Platform.OS === 'ios'
|
||||
? 'center'
|
||||
: 'flex-start',
|
||||
alignItems: Platform.OS === 'ios' ? 'center' : 'flex-start',
|
||||
},
|
||||
left: {
|
||||
left: 0,
|
||||
|
@ -54,12 +54,22 @@ class HeaderBackButton extends React.PureComponent<DefaultProps, Props, State> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { onPress, pressColorAndroid, width, title, tintColor, truncatedTitle } = this.props;
|
||||
const {
|
||||
onPress,
|
||||
pressColorAndroid,
|
||||
width,
|
||||
title,
|
||||
tintColor,
|
||||
truncatedTitle,
|
||||
} = this.props;
|
||||
|
||||
const renderTruncated = this.state.initialTextWidth && width
|
||||
? this.state.initialTextWidth > width
|
||||
: false;
|
||||
|
||||
// eslint-disable-next-line global-require
|
||||
const asset = require('./assets/back-icon.png');
|
||||
|
||||
return (
|
||||
<TouchableItem
|
||||
delayPressIn={0}
|
||||
@ -70,22 +80,18 @@ class HeaderBackButton extends React.PureComponent<DefaultProps, Props, State> {
|
||||
>
|
||||
<View style={styles.container}>
|
||||
<Image
|
||||
style={[
|
||||
styles.icon,
|
||||
title && styles.iconWithTitle,
|
||||
{ tintColor },
|
||||
]}
|
||||
source={require('./assets/back-icon.png')}
|
||||
style={[styles.icon, title && styles.iconWithTitle, { tintColor }]}
|
||||
source={asset}
|
||||
/>
|
||||
{Platform.OS === 'ios' && title && (
|
||||
{Platform.OS === 'ios' &&
|
||||
title &&
|
||||
<Text
|
||||
onLayout={this._onTextLayout}
|
||||
style={[styles.title, { color: tintColor }]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{renderTruncated ? truncatedTitle : title}
|
||||
</Text>
|
||||
)}
|
||||
</Text>}
|
||||
</View>
|
||||
</TouchableItem>
|
||||
);
|
||||
@ -104,25 +110,25 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
icon: Platform.OS === 'ios'
|
||||
? {
|
||||
height: 20,
|
||||
width: 12,
|
||||
marginLeft: 10,
|
||||
marginRight: 22,
|
||||
marginVertical: 12,
|
||||
resizeMode: 'contain',
|
||||
transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }],
|
||||
}
|
||||
height: 20,
|
||||
width: 12,
|
||||
marginLeft: 10,
|
||||
marginRight: 22,
|
||||
marginVertical: 12,
|
||||
resizeMode: 'contain',
|
||||
transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }],
|
||||
}
|
||||
: {
|
||||
height: 24,
|
||||
width: 24,
|
||||
margin: 16,
|
||||
resizeMode: 'contain',
|
||||
transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }],
|
||||
},
|
||||
height: 24,
|
||||
width: 24,
|
||||
margin: 16,
|
||||
resizeMode: 'contain',
|
||||
transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }],
|
||||
},
|
||||
iconWithTitle: Platform.OS === 'ios'
|
||||
? {
|
||||
marginRight: 5,
|
||||
}
|
||||
marginRight: 5,
|
||||
}
|
||||
: {},
|
||||
});
|
||||
|
||||
|
@ -1,12 +1,8 @@
|
||||
/* @flow */
|
||||
|
||||
import {
|
||||
I18nManager,
|
||||
} from 'react-native';
|
||||
import { I18nManager } from 'react-native';
|
||||
|
||||
import type {
|
||||
NavigationSceneRendererProps,
|
||||
} from '../TypeDefinition';
|
||||
import type { NavigationSceneRendererProps } from '../TypeDefinition';
|
||||
|
||||
/**
|
||||
* Utility that builds the style for the navigation header.
|
||||
@ -42,9 +38,9 @@ function forCenter(props: NavigationSceneRendererProps): Object {
|
||||
{
|
||||
translateX: position.interpolate({
|
||||
inputRange: [index - 1, index + 1],
|
||||
outputRange: I18nManager.isRTL ?
|
||||
([-200, 200]: Array<number>) :
|
||||
([200, -200]: Array<number>),
|
||||
outputRange: I18nManager.isRTL
|
||||
? ([-200, 200]: Array<number>)
|
||||
: ([200, -200]: Array<number>),
|
||||
}),
|
||||
},
|
||||
],
|
||||
|
@ -2,23 +2,22 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
Platform,
|
||||
StyleSheet,
|
||||
Text,
|
||||
} from 'react-native';
|
||||
import { Platform, StyleSheet, Text } from 'react-native';
|
||||
|
||||
import type {
|
||||
Style,
|
||||
} from '../TypeDefinition';
|
||||
import type { Style } from '../TypeDefinition';
|
||||
|
||||
type Props = {
|
||||
tintColor?: ?string;
|
||||
tintColor?: ?string,
|
||||
style?: Style,
|
||||
};
|
||||
|
||||
const HeaderTitle = ({ style, ...rest }: Props) => (
|
||||
<Text numberOfLines={1} {...rest} style={[styles.title, style]} accessibilityTraits="header" />
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
{...rest}
|
||||
style={[styles.title, style]}
|
||||
accessibilityTraits="header"
|
||||
/>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
@ -6,9 +6,7 @@ import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
import AnimatedValueSubscription from './AnimatedValueSubscription';
|
||||
|
||||
import type {
|
||||
NavigationSceneRendererProps,
|
||||
} from '../TypeDefinition';
|
||||
import type { NavigationSceneRendererProps } from '../TypeDefinition';
|
||||
|
||||
type Props = NavigationSceneRendererProps;
|
||||
|
||||
@ -19,13 +17,11 @@ const MIN_POSITION_OFFSET = 0.01;
|
||||
* `pointerEvents` property for a component whenever navigation position
|
||||
* changes.
|
||||
*/
|
||||
export default function create(
|
||||
Component: ReactClass<*>,
|
||||
): ReactClass<*> {
|
||||
export default function create(Component: ReactClass<*>): ReactClass<*> {
|
||||
class Container extends React.Component<any, Props, any> {
|
||||
_component: any;
|
||||
_onComponentRef: (view: any) => void;
|
||||
_onPositionChange: (data: {value: number}) => void;
|
||||
_onPositionChange: (data: { value: number }) => void;
|
||||
_pointerEvents: string;
|
||||
_positionListener: ?AnimatedValueSubscription;
|
||||
|
||||
@ -101,9 +97,7 @@ export default function create(
|
||||
|
||||
if (scene.isStale || navigation.state.index !== scene.index) {
|
||||
// The scene isn't focused.
|
||||
return scene.index > navigation.state.index ?
|
||||
'box-only' :
|
||||
'none';
|
||||
return scene.index > navigation.state.index ? 'box-only' : 'none';
|
||||
}
|
||||
|
||||
const offset = position.__getAnimatedValue() - navigation.state.index;
|
||||
|
@ -11,10 +11,10 @@ import type {
|
||||
} from '../TypeDefinition';
|
||||
|
||||
type Props = {
|
||||
screenProps?: {};
|
||||
navigation: NavigationScreenProp<NavigationRoute, NavigationAction>;
|
||||
screenProps?: {},
|
||||
navigation: NavigationScreenProp<NavigationRoute, NavigationAction>,
|
||||
navigationOptions: *,
|
||||
component: ReactClass<NavigationNavigatorProps<NavigationRoute>>;
|
||||
component: ReactClass<NavigationNavigatorProps<NavigationRoute>>,
|
||||
};
|
||||
|
||||
export default class SceneView extends PureComponent<void, Props, void> {
|
||||
|
@ -28,10 +28,7 @@ function compareKey(one: string, two: string): number {
|
||||
/**
|
||||
* Helper function to sort scenes based on their index and view key.
|
||||
*/
|
||||
function compareScenes(
|
||||
one: NavigationScene,
|
||||
two: NavigationScene,
|
||||
): number {
|
||||
function compareScenes(one: NavigationScene, two: NavigationScene): number {
|
||||
if (one.index > two.index) {
|
||||
return 1;
|
||||
}
|
||||
@ -39,10 +36,7 @@ function compareScenes(
|
||||
return -1;
|
||||
}
|
||||
|
||||
return compareKey(
|
||||
one.key,
|
||||
two.key,
|
||||
);
|
||||
return compareKey(one.key, two.key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,13 +46,11 @@ function areScenesShallowEqual(
|
||||
one: NavigationScene,
|
||||
two: NavigationScene,
|
||||
): boolean {
|
||||
return (
|
||||
one.key === two.key &&
|
||||
return one.key === two.key &&
|
||||
one.index === two.index &&
|
||||
one.isStale === two.isStale &&
|
||||
one.isActive === two.isActive &&
|
||||
areRoutesShallowEqual(one.route, two.route)
|
||||
);
|
||||
areRoutesShallowEqual(one.route, two.route);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -93,7 +85,7 @@ export default function ScenesReducer(
|
||||
const staleScenes: Map<string, NavigationScene> = new Map();
|
||||
|
||||
// Populate stale scenes from previous scenes marked as stale.
|
||||
scenes.forEach((scene) => {
|
||||
scenes.forEach((scene: *) => {
|
||||
const { key } = scene;
|
||||
if (scene.isStale) {
|
||||
staleScenes.set(key, scene);
|
||||
@ -102,7 +94,7 @@ export default function ScenesReducer(
|
||||
});
|
||||
|
||||
const nextKeys = new Set();
|
||||
nextState.routes.forEach((route, index) => {
|
||||
nextState.routes.forEach((route: *, index: *) => {
|
||||
const key = SCENE_KEY_PREFIX + route.key;
|
||||
const scene = {
|
||||
index,
|
||||
@ -114,7 +106,7 @@ export default function ScenesReducer(
|
||||
invariant(
|
||||
!nextKeys.has(key),
|
||||
`navigation.state.routes[${index}].key "${key}" conflicts with ` +
|
||||
'another route!'
|
||||
'another route!',
|
||||
);
|
||||
nextKeys.add(key);
|
||||
|
||||
@ -128,7 +120,7 @@ export default function ScenesReducer(
|
||||
|
||||
if (prevState) {
|
||||
// Look at the previous routes and classify any removed scenes as `stale`.
|
||||
prevState.routes.forEach((route: NavigationRoute, index) => {
|
||||
prevState.routes.forEach((route: NavigationRoute, index: *) => {
|
||||
const key = SCENE_KEY_PREFIX + route.key;
|
||||
if (freshScenes.has(key)) {
|
||||
return;
|
||||
@ -145,7 +137,7 @@ export default function ScenesReducer(
|
||||
|
||||
const nextScenes = [];
|
||||
|
||||
const mergeScene = ((nextScene) => {
|
||||
const mergeScene = (nextScene: *) => {
|
||||
const { key } = nextScene;
|
||||
const prevScene = prevScenes.has(key) ? prevScenes.get(key) : null;
|
||||
if (prevScene && areScenesShallowEqual(prevScene, nextScene)) {
|
||||
@ -155,7 +147,7 @@ export default function ScenesReducer(
|
||||
} else {
|
||||
nextScenes.push(nextScene);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
staleScenes.forEach(mergeScene);
|
||||
freshScenes.forEach(mergeScene);
|
||||
@ -163,7 +155,7 @@ export default function ScenesReducer(
|
||||
nextScenes.sort(compareScenes);
|
||||
|
||||
let activeScenesCount = 0;
|
||||
nextScenes.forEach((scene, ii) => {
|
||||
nextScenes.forEach((scene: *, ii: *) => {
|
||||
const isActive = !scene.isStale && scene.index === nextState.index;
|
||||
if (isActive !== scene.isActive) {
|
||||
nextScenes[ii] = {
|
||||
@ -186,9 +178,11 @@ export default function ScenesReducer(
|
||||
return nextScenes;
|
||||
}
|
||||
|
||||
if (nextScenes.some(
|
||||
(scene, index) => !areScenesShallowEqual(scenes[index], scene)
|
||||
)) {
|
||||
if (
|
||||
nextScenes.some(
|
||||
(scene: *, index: *) => !areScenesShallowEqual(scenes[index], scene),
|
||||
)
|
||||
) {
|
||||
return nextScenes;
|
||||
}
|
||||
|
||||
|
@ -17,36 +17,34 @@ import type {
|
||||
Style,
|
||||
} from '../../TypeDefinition';
|
||||
|
||||
import type {
|
||||
TabScene,
|
||||
} from './TabView';
|
||||
import type { TabScene } from './TabView';
|
||||
|
||||
type DefaultProps = {
|
||||
activeTintColor: string;
|
||||
activeBackgroundColor: string;
|
||||
inactiveTintColor: string;
|
||||
inactiveBackgroundColor: string;
|
||||
showLabel: boolean;
|
||||
activeTintColor: string,
|
||||
activeBackgroundColor: string,
|
||||
inactiveTintColor: string,
|
||||
inactiveBackgroundColor: string,
|
||||
showLabel: boolean,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
activeTintColor: string;
|
||||
activeBackgroundColor: string;
|
||||
inactiveTintColor: string;
|
||||
inactiveBackgroundColor: string;
|
||||
position: Animated.Value;
|
||||
activeTintColor: string,
|
||||
activeBackgroundColor: string,
|
||||
inactiveTintColor: string,
|
||||
inactiveBackgroundColor: string,
|
||||
position: Animated.Value,
|
||||
navigation: NavigationScreenProp<NavigationState, NavigationAction>,
|
||||
jumpToIndex: (index: number) => void;
|
||||
getLabel: (scene: TabScene) => ?(React.Element<*> | string);
|
||||
renderIcon: (scene: TabScene) => React.Element<*>;
|
||||
showLabel: boolean;
|
||||
style?: Style;
|
||||
labelStyle?: Style;
|
||||
showIcon: boolean;
|
||||
jumpToIndex: (index: number) => void,
|
||||
getLabel: (scene: TabScene) => ?(React.Element<*> | string),
|
||||
renderIcon: (scene: TabScene) => React.Element<*>,
|
||||
showLabel: boolean,
|
||||
style?: Style,
|
||||
labelStyle?: Style,
|
||||
showIcon: boolean,
|
||||
};
|
||||
|
||||
export default class TabBarBottom extends PureComponent<DefaultProps, Props, void> {
|
||||
|
||||
export default class TabBarBottom
|
||||
extends PureComponent<DefaultProps, Props, void> {
|
||||
// See https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/UIKitUICatalog/UITabBar.html
|
||||
static defaultProps = {
|
||||
activeTintColor: '#3478f6', // Default active tint color in iOS 10
|
||||
@ -75,8 +73,9 @@ export default class TabBarBottom extends PureComponent<DefaultProps, Props, voi
|
||||
const { routes } = navigation.state;
|
||||
// Prepend '-1', so there are always at least 2 items in inputRange
|
||||
const inputRange = [-1, ...routes.map((x: *, i: number) => i)];
|
||||
const outputRange = inputRange.map((inputIndex: number) =>
|
||||
(inputIndex === index ? activeTintColor : inactiveTintColor)
|
||||
const outputRange = inputRange.map(
|
||||
(inputIndex: number) =>
|
||||
inputIndex === index ? activeTintColor : inactiveTintColor,
|
||||
);
|
||||
const color = position.interpolate({
|
||||
inputRange,
|
||||
@ -140,8 +139,11 @@ export default class TabBarBottom extends PureComponent<DefaultProps, Props, voi
|
||||
{routes.map((route: NavigationRoute, index: number) => {
|
||||
const focused = index === navigation.state.index;
|
||||
const scene = { route, index, focused };
|
||||
const outputRange = inputRange.map((inputIndex: number) =>
|
||||
(inputIndex === index ? activeBackgroundColor : inactiveBackgroundColor)
|
||||
const outputRange = inputRange.map(
|
||||
(inputIndex: number) =>
|
||||
inputIndex === index
|
||||
? activeBackgroundColor
|
||||
: inactiveBackgroundColor,
|
||||
);
|
||||
const backgroundColor = position.interpolate({
|
||||
inputRange,
|
||||
@ -149,8 +151,13 @@ export default class TabBarBottom extends PureComponent<DefaultProps, Props, voi
|
||||
});
|
||||
const justifyContent = this.props.showIcon ? 'flex-end' : 'center';
|
||||
return (
|
||||
<TouchableWithoutFeedback key={route.key} onPress={() => jumpToIndex(index)}>
|
||||
<Animated.View style={[styles.tab, { backgroundColor, justifyContent }]}>
|
||||
<TouchableWithoutFeedback
|
||||
key={route.key}
|
||||
onPress={() => jumpToIndex(index)}
|
||||
>
|
||||
<Animated.View
|
||||
style={[styles.tab, { backgroundColor, justifyContent }]}
|
||||
>
|
||||
{this._renderIcon(scene)}
|
||||
{this._renderLabel(scene)}
|
||||
</Animated.View>
|
||||
|
@ -1,11 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import {
|
||||
Animated,
|
||||
View,
|
||||
StyleSheet,
|
||||
} from 'react-native';
|
||||
import { Animated, View, StyleSheet } from 'react-native';
|
||||
|
||||
import type {
|
||||
NavigationAction,
|
||||
@ -14,9 +10,7 @@ import type {
|
||||
Style,
|
||||
} from '../../TypeDefinition';
|
||||
|
||||
import type {
|
||||
TabScene,
|
||||
} from './TabView';
|
||||
import type { TabScene } from './TabView';
|
||||
|
||||
type Props = {
|
||||
activeTintColor: string,
|
||||
@ -46,11 +40,11 @@ export default class TabBarIcon extends PureComponent<void, Props, void> {
|
||||
const inputRange = [-1, ...routes.map((x: *, i: number) => i)];
|
||||
const activeOpacity = position.interpolate({
|
||||
inputRange,
|
||||
outputRange: inputRange.map((i: number) => (i === index ? 1 : 0)),
|
||||
outputRange: inputRange.map((i: number) => i === index ? 1 : 0),
|
||||
});
|
||||
const inactiveOpacity = position.interpolate({
|
||||
inputRange,
|
||||
outputRange: inputRange.map((i: number) => (i === index ? 0 : 1)),
|
||||
outputRange: inputRange.map((i: number) => i === index ? 0 : 1),
|
||||
});
|
||||
// We render the icon twice at the same position on top of each other:
|
||||
// active and inactive one, so we can fade between them.
|
||||
|
@ -1,10 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import {
|
||||
Animated,
|
||||
StyleSheet,
|
||||
} from 'react-native';
|
||||
import { Animated, StyleSheet } from 'react-native';
|
||||
import { TabBar } from 'react-native-tab-view';
|
||||
import TabBarIcon from './TabBarIcon';
|
||||
|
||||
@ -15,34 +12,32 @@ import type {
|
||||
Style,
|
||||
} from '../../TypeDefinition';
|
||||
|
||||
import type {
|
||||
TabScene,
|
||||
} from './TabView';
|
||||
import type { TabScene } from './TabView';
|
||||
|
||||
type DefaultProps = {
|
||||
activeTintColor: string;
|
||||
inactiveTintColor: string;
|
||||
showIcon: boolean;
|
||||
showLabel: boolean;
|
||||
upperCaseLabel: boolean;
|
||||
activeTintColor: string,
|
||||
inactiveTintColor: string,
|
||||
showIcon: boolean,
|
||||
showLabel: boolean,
|
||||
upperCaseLabel: boolean,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
activeTintColor: string;
|
||||
inactiveTintColor: string;
|
||||
showIcon: boolean;
|
||||
showLabel: boolean;
|
||||
upperCaseLabel: boolean;
|
||||
position: Animated.Value;
|
||||
navigation: NavigationScreenProp<NavigationState, NavigationAction>;
|
||||
getLabel: (scene: TabScene) => ?(React.Element<*> | string);
|
||||
renderIcon: (scene: TabScene) => React.Element<*>;
|
||||
labelStyle?: Style;
|
||||
iconStyle?: Style;
|
||||
activeTintColor: string,
|
||||
inactiveTintColor: string,
|
||||
showIcon: boolean,
|
||||
showLabel: boolean,
|
||||
upperCaseLabel: boolean,
|
||||
position: Animated.Value,
|
||||
navigation: NavigationScreenProp<NavigationState, NavigationAction>,
|
||||
getLabel: (scene: TabScene) => ?(React.Element<*> | string),
|
||||
renderIcon: (scene: TabScene) => React.Element<*>,
|
||||
labelStyle?: Style,
|
||||
iconStyle?: Style,
|
||||
};
|
||||
|
||||
export default class TabBarTop extends PureComponent<DefaultProps, Props, void> {
|
||||
|
||||
export default class TabBarTop
|
||||
extends PureComponent<DefaultProps, Props, void> {
|
||||
static defaultProps = {
|
||||
activeTintColor: '#fff',
|
||||
inactiveTintColor: '#fff',
|
||||
@ -70,8 +65,9 @@ export default class TabBarTop extends PureComponent<DefaultProps, Props, void>
|
||||
const { routes } = navigation.state;
|
||||
// Prepend '-1', so there are always at least 2 items in inputRange
|
||||
const inputRange = [-1, ...routes.map((x: *, i: number) => i)];
|
||||
const outputRange = inputRange.map((inputIndex: number) =>
|
||||
(inputIndex === index ? activeTintColor : inactiveTintColor)
|
||||
const outputRange = inputRange.map(
|
||||
(inputIndex: number) =>
|
||||
inputIndex === index ? activeTintColor : inactiveTintColor,
|
||||
);
|
||||
const color = position.interpolate({
|
||||
inputRange,
|
||||
|
@ -1,10 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import {
|
||||
Platform,
|
||||
StyleSheet,
|
||||
} from 'react-native';
|
||||
import { Platform, StyleSheet } from 'react-native';
|
||||
import {
|
||||
TabViewAnimated,
|
||||
TabViewPagerAndroid,
|
||||
@ -26,33 +23,35 @@ import type {
|
||||
} from '../../TypeDefinition';
|
||||
|
||||
export type TabViewConfig = {
|
||||
tabBarComponent?: ReactClass<*>;
|
||||
tabBarPosition?: 'top' | 'bottom';
|
||||
tabBarOptions?: {};
|
||||
swipeEnabled?: boolean;
|
||||
animationEnabled?: boolean;
|
||||
lazyLoad?: boolean;
|
||||
tabBarComponent?: ReactClass<*>,
|
||||
tabBarPosition?: 'top' | 'bottom',
|
||||
tabBarOptions?: {},
|
||||
swipeEnabled?: boolean,
|
||||
animationEnabled?: boolean,
|
||||
lazyLoad?: boolean,
|
||||
};
|
||||
|
||||
export type TabScene = {
|
||||
route: NavigationRoute;
|
||||
focused: boolean;
|
||||
index: number;
|
||||
tintColor?: ?string;
|
||||
route: NavigationRoute,
|
||||
focused: boolean,
|
||||
index: number,
|
||||
tintColor?: ?string,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
tabBarComponent?: ReactClass<*>;
|
||||
tabBarPosition?: 'top' | 'bottom';
|
||||
tabBarOptions?: {};
|
||||
swipeEnabled?: boolean;
|
||||
animationEnabled?: boolean;
|
||||
lazyLoad?: boolean;
|
||||
tabBarComponent?: ReactClass<*>,
|
||||
tabBarPosition?: 'top' | 'bottom',
|
||||
tabBarOptions?: {},
|
||||
swipeEnabled?: boolean,
|
||||
animationEnabled?: boolean,
|
||||
lazyLoad?: boolean,
|
||||
|
||||
screenProps?: {},
|
||||
navigation: NavigationScreenProp<NavigationState, NavigationAction>;
|
||||
navigation: NavigationScreenProp<NavigationState, NavigationAction>,
|
||||
router: NavigationRouter<NavigationState, NavigationAction, NavigationTabScreenOptions>,
|
||||
childNavigationProps: { [key: string]: NavigationScreenProp<NavigationRoute, NavigationAction> },
|
||||
childNavigationProps: {
|
||||
[key: string]: NavigationScreenProp<NavigationRoute, NavigationAction>,
|
||||
},
|
||||
};
|
||||
|
||||
let TabViewPager;
|
||||
@ -69,7 +68,6 @@ switch (Platform.OS) {
|
||||
}
|
||||
|
||||
class TabView extends PureComponent<void, Props, void> {
|
||||
|
||||
static TabBarTop = TabBarTop;
|
||||
static TabBarBottom = TabBarBottom;
|
||||
|
||||
@ -77,28 +75,32 @@ class TabView extends PureComponent<void, Props, void> {
|
||||
|
||||
_handlePageChanged = (index: number) => {
|
||||
const { navigation } = this.props;
|
||||
navigation.navigate(
|
||||
navigation.state.routes[index].routeName);
|
||||
navigation.navigate(navigation.state.routes[index].routeName);
|
||||
};
|
||||
|
||||
_renderScene = ({ route }: any) => {
|
||||
const { screenProps } = this.props;
|
||||
const childNavigation = this.props.childNavigationProps[route.key];
|
||||
const TabComponent = this.props.router.getComponentForRouteName(route.routeName);
|
||||
const TabComponent = this.props.router.getComponentForRouteName(
|
||||
route.routeName,
|
||||
);
|
||||
return (
|
||||
<SceneView
|
||||
screenProps={screenProps}
|
||||
component={TabComponent}
|
||||
navigation={childNavigation}
|
||||
navigationOptions={this.props.router.getScreenOptions(childNavigation, screenProps)}
|
||||
navigationOptions={this.props.router.getScreenOptions(
|
||||
childNavigation,
|
||||
screenProps,
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
_getLabel = ({ focused, route, tintColor }: TabScene) => {
|
||||
_getLabel = ({ route, tintColor, focused }: TabScene) => {
|
||||
const options = this.props.router.getScreenOptions(
|
||||
this.props.childNavigationProps[route.key],
|
||||
this.props.screenProps || {}
|
||||
this.props.screenProps || {},
|
||||
);
|
||||
|
||||
if (options.tabBarLabel) {
|
||||
@ -117,7 +119,7 @@ class TabView extends PureComponent<void, Props, void> {
|
||||
_renderIcon = ({ focused, route, tintColor }: TabScene) => {
|
||||
const options = this.props.router.getScreenOptions(
|
||||
this.props.childNavigationProps[route.key],
|
||||
this.props.screenProps || {}
|
||||
this.props.screenProps || {},
|
||||
);
|
||||
if (options.tabBarIcon) {
|
||||
return typeof options.tabBarIcon === 'function'
|
||||
@ -168,7 +170,6 @@ class TabView extends PureComponent<void, Props, void> {
|
||||
render() {
|
||||
const {
|
||||
router,
|
||||
navigation,
|
||||
tabBarComponent,
|
||||
tabBarPosition,
|
||||
animationEnabled,
|
||||
@ -182,10 +183,12 @@ class TabView extends PureComponent<void, Props, void> {
|
||||
const { state } = this.props.navigation;
|
||||
const options = router.getScreenOptions(
|
||||
this.props.childNavigationProps[state.routes[state.index].key],
|
||||
screenProps || {}
|
||||
screenProps || {},
|
||||
);
|
||||
|
||||
const tabBarVisible = options.tabBarVisible == null ? true : options.tabBarVisible;
|
||||
const tabBarVisible = options.tabBarVisible == null
|
||||
? true
|
||||
: options.tabBarVisible;
|
||||
|
||||
if (tabBarComponent !== undefined && tabBarVisible) {
|
||||
if (tabBarPosition === 'bottom') {
|
||||
@ -201,21 +204,21 @@ class TabView extends PureComponent<void, Props, void> {
|
||||
configureTransition = this._configureTransition;
|
||||
}
|
||||
|
||||
return (
|
||||
/* $FlowFixMe */
|
||||
<TabViewAnimated
|
||||
style={styles.container}
|
||||
navigationState={this.props.navigation.state}
|
||||
lazy={lazyLoad}
|
||||
renderHeader={renderHeader}
|
||||
renderFooter={renderFooter}
|
||||
renderScene={this._renderScene}
|
||||
renderPager={this._renderPager}
|
||||
configureTransition={configureTransition}
|
||||
onRequestChangeTab={this._handlePageChanged}
|
||||
screenProps={this.props.screenProps}
|
||||
/>
|
||||
);
|
||||
const props = {
|
||||
style: styles.container,
|
||||
navigationState: this.props.navigation.state,
|
||||
lazy: lazyLoad,
|
||||
renderHeader,
|
||||
renderFooter,
|
||||
renderScene: this._renderScene,
|
||||
renderPager: this._renderPager,
|
||||
configureTransition,
|
||||
onRequestChangeTab: this._handlePageChanged,
|
||||
screenProps: this.props.screenProps,
|
||||
};
|
||||
|
||||
/* $FlowFixMe */
|
||||
return <TabViewAnimated {...props} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,27 +16,26 @@ import {
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import type {
|
||||
Style,
|
||||
} from '../TypeDefinition';
|
||||
import type { Style } from '../TypeDefinition';
|
||||
|
||||
const ANDROID_VERSION_LOLLIPOP = 21;
|
||||
|
||||
type Props = {
|
||||
onPress: Function,
|
||||
delayPressIn?: number;
|
||||
borderless?: boolean;
|
||||
pressColor?: ?string;
|
||||
activeOpacity?: number;
|
||||
children?: React.Element<*>;
|
||||
style?: Style;
|
||||
delayPressIn?: number,
|
||||
borderless?: boolean,
|
||||
pressColor?: ?string,
|
||||
activeOpacity?: number,
|
||||
children?: React.Element<*>,
|
||||
style?: Style,
|
||||
};
|
||||
|
||||
type DefaultProps = {
|
||||
pressColor: ?string;
|
||||
pressColor: ?string,
|
||||
};
|
||||
|
||||
export default class TouchableItem extends Component<DefaultProps, Props, void> {
|
||||
export default class TouchableItem
|
||||
extends Component<DefaultProps, Props, void> {
|
||||
static defaultProps = {
|
||||
pressColor: 'rgba(0, 0, 0, .32)',
|
||||
};
|
||||
@ -50,19 +49,19 @@ export default class TouchableItem extends Component<DefaultProps, Props, void>
|
||||
* platform design guidelines.
|
||||
* We need to pass the background prop to specify a borderless ripple effect.
|
||||
*/
|
||||
if (Platform.OS === 'android' && Platform.Version >= ANDROID_VERSION_LOLLIPOP) {
|
||||
if (
|
||||
Platform.OS === 'android' && Platform.Version >= ANDROID_VERSION_LOLLIPOP
|
||||
) {
|
||||
const { style, ...rest } = this.props; // eslint-disable-line no-unused-vars
|
||||
|
||||
return (
|
||||
<TouchableNativeFeedback
|
||||
{...rest}
|
||||
style={null}
|
||||
background={
|
||||
TouchableNativeFeedback.Ripple(
|
||||
this.props.pressColor,
|
||||
this.props.borderless
|
||||
)
|
||||
}
|
||||
background={TouchableNativeFeedback.Ripple(
|
||||
this.props.pressColor,
|
||||
this.props.borderless,
|
||||
)}
|
||||
>
|
||||
<View style={this.props.style}>
|
||||
{Children.only(this.props.children)}
|
||||
|
@ -1,5 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
import { Animated, Easing, Platform } from 'react-native';
|
||||
|
||||
import type {
|
||||
NavigationSceneRendererProps,
|
||||
NavigationTransitionProps,
|
||||
@ -7,13 +9,6 @@ import type {
|
||||
} from '../TypeDefinition';
|
||||
|
||||
import CardStackStyleInterpolator from './CardStackStyleInterpolator';
|
||||
import HeaderStyleInterpolator from './HeaderStyleInterpolator';
|
||||
|
||||
import {
|
||||
Animated,
|
||||
Easing,
|
||||
Platform,
|
||||
} from 'react-native';
|
||||
|
||||
/**
|
||||
* Describes a visual transition from one screen to another.
|
||||
@ -23,7 +18,7 @@ export type TransitionConfig = {
|
||||
transitionSpec?: NavigationTransitionSpec,
|
||||
// How to animate position and opacity of the screen
|
||||
// based on the value generated by the transitionSpec
|
||||
screenInterpolator?: NavigationSceneRendererProps => Object,
|
||||
screenInterpolator?: (NavigationSceneRendererProps) => Object,
|
||||
};
|
||||
|
||||
// Used for all animations unless overriden
|
||||
@ -33,39 +28,39 @@ const DefaultTransitionSpec = ({
|
||||
timing: Animated.spring,
|
||||
bounciness: 0,
|
||||
speed: 9,
|
||||
} : NavigationTransitionSpec);
|
||||
}: NavigationTransitionSpec);
|
||||
|
||||
// Standard iOS navigation transition
|
||||
const SlideFromRightIOS = ({
|
||||
screenInterpolator: CardStackStyleInterpolator.forHorizontal,
|
||||
} : TransitionConfig);
|
||||
}: TransitionConfig);
|
||||
|
||||
// Standard iOS navigation transition for modals
|
||||
const ModalSlideFromBottomIOS = ({
|
||||
screenInterpolator: CardStackStyleInterpolator.forVertical,
|
||||
} : TransitionConfig);
|
||||
}: TransitionConfig);
|
||||
|
||||
// Standard Android navigation transition when opening an Activity
|
||||
const FadeInFromBottomAndroid = ({
|
||||
// See http://androidxref.com/7.1.1_r6/xref/frameworks/base/core/res/res/anim/activity_open_enter.xml
|
||||
transitionSpec: {
|
||||
duration: 350,
|
||||
easing: Easing.out(Easing.poly(5)), // decelerate
|
||||
easing: Easing.out(Easing.poly(5)), // decelerate
|
||||
timing: Animated.timing,
|
||||
},
|
||||
screenInterpolator: CardStackStyleInterpolator.forFadeFromBottomAndroid,
|
||||
} : TransitionConfig);
|
||||
}: TransitionConfig);
|
||||
|
||||
// Standard Android navigation transition when closing an Activity
|
||||
const FadeOutToBottomAndroid = ({
|
||||
// See http://androidxref.com/7.1.1_r6/xref/frameworks/base/core/res/res/anim/activity_close_exit.xml
|
||||
transitionSpec: {
|
||||
duration: 230,
|
||||
easing: Easing.in(Easing.poly(4)), // accelerate
|
||||
easing: Easing.in(Easing.poly(4)), // accelerate
|
||||
timing: Animated.timing,
|
||||
},
|
||||
screenInterpolator: CardStackStyleInterpolator.forFadeFromBottomAndroid,
|
||||
} : TransitionConfig);
|
||||
}: TransitionConfig);
|
||||
|
||||
function defaultTransitionConfig(
|
||||
// props for the new screen
|
||||
@ -78,19 +73,19 @@ function defaultTransitionConfig(
|
||||
if (Platform.OS === 'android') {
|
||||
// Use the default Android animation no matter if the screen is a modal.
|
||||
// Android doesn't have full-screen modals like iOS does, it has dialogs.
|
||||
if (prevTransitionProps && (transitionProps.index < prevTransitionProps.index)) {
|
||||
if (
|
||||
prevTransitionProps && transitionProps.index < prevTransitionProps.index
|
||||
) {
|
||||
// Navigating back to the previous screen
|
||||
return FadeOutToBottomAndroid;
|
||||
}
|
||||
return FadeInFromBottomAndroid;
|
||||
} else {
|
||||
// iOS and other platforms
|
||||
if (isModal) {
|
||||
return ModalSlideFromBottomIOS;
|
||||
} else {
|
||||
return SlideFromRightIOS;
|
||||
}
|
||||
}
|
||||
// iOS and other platforms
|
||||
if (isModal) {
|
||||
return ModalSlideFromBottomIOS;
|
||||
}
|
||||
return SlideFromRightIOS;
|
||||
}
|
||||
|
||||
function getTransitionConfig(
|
||||
@ -104,7 +99,7 @@ function getTransitionConfig(
|
||||
const defaultConfig = defaultTransitionConfig(
|
||||
transitionProps,
|
||||
prevTransitionProps,
|
||||
isModal
|
||||
isModal,
|
||||
);
|
||||
if (transitionConfigurer) {
|
||||
return {
|
||||
|
@ -2,11 +2,7 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
Animated,
|
||||
StyleSheet,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { Animated, StyleSheet, View } from 'react-native';
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
@ -33,7 +29,7 @@ type Props = {
|
||||
onTransitionStart?: () => void,
|
||||
render: (
|
||||
transitionProps: NavigationTransitionProps,
|
||||
prevTransitionProps: ?NavigationTransitionProps
|
||||
prevTransitionProps: ?NavigationTransitionProps,
|
||||
) => any,
|
||||
style?: any,
|
||||
};
|
||||
@ -107,14 +103,15 @@ class Transitioner extends React.Component<*, Props, State> {
|
||||
const nextScenes = NavigationScenesReducer(
|
||||
this.state.scenes,
|
||||
nextProps.navigation.state,
|
||||
this.props.navigation.state
|
||||
this.props.navigation.state,
|
||||
);
|
||||
|
||||
if (nextScenes === this.state.scenes) {
|
||||
return;
|
||||
}
|
||||
|
||||
const indexHasChanged = nextProps.navigation.state.index !== this.props.navigation.state.index;
|
||||
const indexHasChanged = nextProps.navigation.state.index !==
|
||||
this.props.navigation.state.index;
|
||||
if (this._isTransitionRunning) {
|
||||
this._queuedTransition = { nextProps, nextScenes, indexHasChanged };
|
||||
return;
|
||||
@ -123,7 +120,11 @@ class Transitioner extends React.Component<*, Props, State> {
|
||||
this._startTransition(nextProps, nextScenes, indexHasChanged);
|
||||
}
|
||||
|
||||
_startTransition(nextProps: Props, nextScenes: Array<NavigationScene>, indexHasChanged: boolean) {
|
||||
_startTransition(
|
||||
nextProps: Props,
|
||||
nextScenes: Array<NavigationScene>,
|
||||
indexHasChanged: boolean,
|
||||
) {
|
||||
const nextState = {
|
||||
...this.state,
|
||||
scenes: nextScenes,
|
||||
@ -139,13 +140,13 @@ class Transitioner extends React.Component<*, Props, State> {
|
||||
this._prevTransitionProps = this._transitionProps;
|
||||
this._transitionProps = buildTransitionProps(nextProps, nextState);
|
||||
|
||||
// get the transition spec.
|
||||
const transitionUserSpec = nextProps.configureTransition ?
|
||||
nextProps.configureTransition(
|
||||
this._transitionProps,
|
||||
this._prevTransitionProps,
|
||||
) :
|
||||
null;
|
||||
// get the transition spec.
|
||||
const transitionUserSpec = nextProps.configureTransition
|
||||
? nextProps.configureTransition(
|
||||
this._transitionProps,
|
||||
this._prevTransitionProps,
|
||||
)
|
||||
: null;
|
||||
|
||||
const transitionSpec = {
|
||||
...DefaultTransitionSpec,
|
||||
@ -157,40 +158,32 @@ class Transitioner extends React.Component<*, Props, State> {
|
||||
|
||||
const animations = indexHasChanged
|
||||
? [
|
||||
timing(
|
||||
progress,
|
||||
{
|
||||
timing(progress, {
|
||||
...transitionSpec,
|
||||
toValue: 1,
|
||||
},
|
||||
),
|
||||
timing(
|
||||
position,
|
||||
{
|
||||
}),
|
||||
timing(position, {
|
||||
...transitionSpec,
|
||||
toValue: nextProps.navigation.state.index,
|
||||
},
|
||||
),
|
||||
]
|
||||
}),
|
||||
]
|
||||
: [];
|
||||
|
||||
// update scenes and play the transition
|
||||
this._isTransitionRunning = true;
|
||||
this.setState(nextState, () => {
|
||||
nextProps.onTransitionStart && nextProps.onTransitionStart(
|
||||
this._transitionProps,
|
||||
this._prevTransitionProps,
|
||||
);
|
||||
nextProps.onTransitionStart &&
|
||||
nextProps.onTransitionStart(
|
||||
this._transitionProps,
|
||||
this._prevTransitionProps,
|
||||
);
|
||||
Animated.parallel(animations).start(this._onTransitionEnd);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
onLayout={this._onLayout}
|
||||
style={[styles.main, this.props.style]}
|
||||
>
|
||||
<View onLayout={this._onLayout} style={[styles.main, this.props.style]}>
|
||||
{this.props.render(this._transitionProps, this._prevTransitionProps)}
|
||||
</View>
|
||||
);
|
||||
@ -198,8 +191,10 @@ class Transitioner extends React.Component<*, Props, State> {
|
||||
|
||||
_onLayout(event: any): void {
|
||||
const { height, width } = event.nativeEvent.layout;
|
||||
if (this.state.layout.initWidth === width &&
|
||||
this.state.layout.initHeight === height) {
|
||||
if (
|
||||
this.state.layout.initWidth === width &&
|
||||
this.state.layout.initHeight === height
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const layout = {
|
||||
@ -236,15 +231,13 @@ class Transitioner extends React.Component<*, Props, State> {
|
||||
this._transitionProps = buildTransitionProps(this.props, nextState);
|
||||
|
||||
this.setState(nextState, () => {
|
||||
this.props.onTransitionEnd && this.props.onTransitionEnd(
|
||||
this._transitionProps,
|
||||
prevTransitionProps,
|
||||
);
|
||||
this.props.onTransitionEnd &&
|
||||
this.props.onTransitionEnd(this._transitionProps, prevTransitionProps);
|
||||
if (this._queuedTransition) {
|
||||
this._startTransition(
|
||||
this._queuedTransition.nextProps,
|
||||
this._queuedTransition.nextScenes,
|
||||
this._queuedTransition.indexHasChanged
|
||||
this._queuedTransition.nextProps,
|
||||
this._queuedTransition.nextScenes,
|
||||
this._queuedTransition.indexHasChanged,
|
||||
);
|
||||
this._queuedTransition = null;
|
||||
} else {
|
||||
|
@ -5,15 +5,15 @@ import ScenesReducer from '../ScenesReducer';
|
||||
/**
|
||||
* Simulate scenes transtion with changes of navigation states.
|
||||
*/
|
||||
function testTransition(states) {
|
||||
const routes = states.map(keys => ({
|
||||
function testTransition(states: *) {
|
||||
const routes = states.map((keys: *) => ({
|
||||
index: 0,
|
||||
routes: keys.map(key => ({ key, routeName: '' })),
|
||||
routes: keys.map((key: *) => ({ key, routeName: '' })),
|
||||
}));
|
||||
|
||||
let scenes = [];
|
||||
let prevState = null;
|
||||
routes.forEach((nextState) => {
|
||||
routes.forEach((nextState: *) => {
|
||||
scenes = ScenesReducer(scenes, nextState, prevState);
|
||||
prevState = nextState;
|
||||
});
|
||||
@ -23,9 +23,7 @@ function testTransition(states) {
|
||||
|
||||
describe('ScenesReducer', () => {
|
||||
it('gets initial scenes', () => {
|
||||
const scenes = testTransition([
|
||||
['1', '2'],
|
||||
]);
|
||||
const scenes = testTransition([['1', '2']]);
|
||||
|
||||
expect(scenes).toEqual([
|
||||
{
|
||||
@ -53,10 +51,7 @@ describe('ScenesReducer', () => {
|
||||
|
||||
it('pushes new scenes', () => {
|
||||
// Transition from ['1', '2'] to ['1', '2', '3'].
|
||||
const scenes = testTransition([
|
||||
['1', '2'],
|
||||
['1', '2', '3'],
|
||||
]);
|
||||
const scenes = testTransition([['1', '2'], ['1', '2', '3']]);
|
||||
|
||||
expect(scenes).toEqual([
|
||||
{
|
||||
@ -145,12 +140,18 @@ describe('ScenesReducer', () => {
|
||||
it('gets different scenes when routes are different', () => {
|
||||
const state1 = {
|
||||
index: 0,
|
||||
routes: [{ key: '1', x: 1, routeName: '' }, { key: '2', x: 2, routeName: '' }],
|
||||
routes: [
|
||||
{ key: '1', x: 1, routeName: '' },
|
||||
{ key: '2', x: 2, routeName: '' },
|
||||
],
|
||||
};
|
||||
|
||||
const state2 = {
|
||||
index: 0,
|
||||
routes: [{ key: '1', x: 3, routeName: '' }, { key: '2', x: 4, routeName: '' }],
|
||||
routes: [
|
||||
{ key: '1', x: 3, routeName: '' },
|
||||
{ key: '2', x: 4, routeName: '' },
|
||||
],
|
||||
};
|
||||
|
||||
const scenes1 = ScenesReducer([], state1, null);
|
||||
@ -158,16 +159,21 @@ describe('ScenesReducer', () => {
|
||||
expect(scenes1).not.toBe(scenes2);
|
||||
});
|
||||
|
||||
|
||||
it('gets different scenes when state index changes', () => {
|
||||
const state1 = {
|
||||
index: 0,
|
||||
routes: [{ key: '1', x: 1, routeName: '' }, { key: '2', x: 2, routeName: '' }],
|
||||
routes: [
|
||||
{ key: '1', x: 1, routeName: '' },
|
||||
{ key: '2', x: 2, routeName: '' },
|
||||
],
|
||||
};
|
||||
|
||||
const state2 = {
|
||||
index: 1,
|
||||
routes: [{ key: '1', x: 1, routeName: '' }, { key: '2', x: 2, routeName: '' }],
|
||||
routes: [
|
||||
{ key: '1', x: 1, routeName: '' },
|
||||
{ key: '2', x: 2, routeName: '' },
|
||||
],
|
||||
};
|
||||
|
||||
const scenes1 = ScenesReducer([], state1, null);
|
||||
@ -177,10 +183,7 @@ describe('ScenesReducer', () => {
|
||||
|
||||
it('pops scenes', () => {
|
||||
// Transition from ['1', '2', '3'] to ['1', '2'].
|
||||
const scenes = testTransition([
|
||||
['1', '2', '3'],
|
||||
['1', '2'],
|
||||
]);
|
||||
const scenes = testTransition([['1', '2', '3'], ['1', '2']]);
|
||||
|
||||
expect(scenes).toEqual([
|
||||
{
|
||||
@ -217,10 +220,7 @@ describe('ScenesReducer', () => {
|
||||
});
|
||||
|
||||
it('replaces scenes', () => {
|
||||
const scenes = testTransition([
|
||||
['1', '2'],
|
||||
['3'],
|
||||
]);
|
||||
const scenes = testTransition([['1', '2'], ['3']]);
|
||||
|
||||
expect(scenes).toEqual([
|
||||
{
|
||||
@ -257,11 +257,7 @@ describe('ScenesReducer', () => {
|
||||
});
|
||||
|
||||
it('revives scenes', () => {
|
||||
const scenes = testTransition([
|
||||
['1', '2'],
|
||||
['3'],
|
||||
['2'],
|
||||
]);
|
||||
const scenes = testTransition([['1', '2'], ['3'], ['2']]);
|
||||
|
||||
expect(scenes).toEqual([
|
||||
{
|
||||
|
@ -4,11 +4,7 @@ import React from 'react';
|
||||
import propTypes from 'prop-types';
|
||||
import hoistStatics from 'hoist-non-react-statics';
|
||||
|
||||
import type {
|
||||
NavigationState,
|
||||
NavigationRoute,
|
||||
NavigationAction,
|
||||
} from '../TypeDefinition';
|
||||
import type { NavigationState, NavigationAction } from '../TypeDefinition';
|
||||
|
||||
type Context = {
|
||||
navigation: InjectedProps<NavigationState, NavigationAction>,
|
||||
@ -18,8 +14,9 @@ type InjectedProps = {
|
||||
navigation: InjectedProps<NavigationState, NavigationAction>,
|
||||
};
|
||||
|
||||
|
||||
export default function withNavigation<T: *>(Component: ReactClass<T & InjectedProps>) {
|
||||
export default function withNavigation<T: *>(
|
||||
Component: ReactClass<T & InjectedProps>,
|
||||
) {
|
||||
const componentWithNavigation = (props: T, { navigation }: Context) => (
|
||||
<Component {...props} navigation={navigation} />
|
||||
);
|
||||
|
@ -4,10 +4,7 @@ import React, { PureComponent } from 'react';
|
||||
|
||||
import addNavigationHelpers from './addNavigationHelpers';
|
||||
|
||||
import type {
|
||||
NavigationScreenProp,
|
||||
NavigationAction,
|
||||
} from './TypeDefinition';
|
||||
import type { NavigationScreenProp, NavigationAction } from './TypeDefinition';
|
||||
|
||||
type InjectedProps<N> = {
|
||||
childNavigationProps: {
|
||||
@ -19,10 +16,9 @@ type InjectedProps<N> = {
|
||||
* HOC which caches the child navigation items.
|
||||
*/
|
||||
export default function withCachedChildNavigation<T: *, N: *>(
|
||||
Comp: ReactClass<T & InjectedProps<N>>
|
||||
Comp: ReactClass<T & InjectedProps<N>>,
|
||||
): ReactClass<T> {
|
||||
return class extends PureComponent {
|
||||
|
||||
static displayName = `withCachedChildNavigation(${Comp.displayName || Comp.name})`;
|
||||
|
||||
props: T;
|
||||
@ -40,7 +36,7 @@ export default function withCachedChildNavigation<T: *, N: *>(
|
||||
};
|
||||
|
||||
_updateNavigationProps = (
|
||||
navigation: NavigationScreenProp<N, NavigationAction>
|
||||
navigation: NavigationScreenProp<N, NavigationAction>,
|
||||
) => {
|
||||
// Update props for each child route
|
||||
if (!this._childNavigationProps) {
|
||||
@ -56,7 +52,7 @@ export default function withCachedChildNavigation<T: *, N: *>(
|
||||
state: route,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
|
104
yarn.lock
104
yarn.lock
@ -208,6 +208,14 @@ ast-types-flow@0.0.7:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
|
||||
|
||||
ast-types@0.8.18:
|
||||
version "0.8.18"
|
||||
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.8.18.tgz#c8b98574898e8914e9d8de74b947564a9fe929af"
|
||||
|
||||
ast-types@0.9.4:
|
||||
version "0.9.4"
|
||||
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.4.tgz#410d1f81890aeb8e0a38621558ba5869ae53c91b"
|
||||
|
||||
async-each@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
|
||||
@ -259,7 +267,7 @@ babel-cli@^6.24.0:
|
||||
optionalDependencies:
|
||||
chokidar "^1.6.1"
|
||||
|
||||
babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
|
||||
babel-code-frame@6.22.0, babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
|
||||
version "6.22.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4"
|
||||
dependencies:
|
||||
@ -1109,7 +1117,7 @@ babel-types@^6.15.0, babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.21
|
||||
lodash "^4.2.0"
|
||||
to-fast-properties "^1.0.1"
|
||||
|
||||
babylon@^6.11.0, babylon@^6.13.0, babylon@^6.14.1, babylon@^6.15.0:
|
||||
babylon@6.15.0, babylon@^6.11.0, babylon@^6.13.0, babylon@^6.14.1, babylon@^6.15.0:
|
||||
version "6.15.0"
|
||||
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.15.0.tgz#ba65cfa1a80e1759b0e89fb562e27dccae70348e"
|
||||
|
||||
@ -1278,7 +1286,7 @@ center-align@^0.1.1:
|
||||
align-text "^0.1.3"
|
||||
lazy-cache "^1.0.3"
|
||||
|
||||
chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
|
||||
chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
|
||||
dependencies:
|
||||
@ -1367,6 +1375,10 @@ color-name@^1.1.1:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.2.tgz#5c8ab72b64bd2215d617ae9559ebb148475cf98d"
|
||||
|
||||
colors@>=0.6.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
|
||||
|
||||
combined-stream@^1.0.5, combined-stream@~1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009"
|
||||
@ -1836,6 +1848,12 @@ eslint-config-airbnb@^14.1.0:
|
||||
dependencies:
|
||||
eslint-config-airbnb-base "^11.1.0"
|
||||
|
||||
eslint-config-prettier@^1.5.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-1.6.0.tgz#56e53a8eb461c06eced20cec40d765c185100fd5"
|
||||
dependencies:
|
||||
get-stdin "^5.0.1"
|
||||
|
||||
eslint-import-resolver-node@^0.2.0:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz#5add8106e8c928db2cba232bcd9efa846e3da16c"
|
||||
@ -1883,6 +1901,12 @@ eslint-plugin-jsx-a11y@^4.0.0:
|
||||
jsx-ast-utils "^1.0.0"
|
||||
object-assign "^4.0.1"
|
||||
|
||||
eslint-plugin-prettier@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.0.1.tgz#2ae1216cf053dd728360ca8560bf1aabc8af3fa9"
|
||||
dependencies:
|
||||
requireindex "~1.1.0"
|
||||
|
||||
eslint-plugin-react@^6.10.0:
|
||||
version "6.10.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.10.0.tgz#9c48b48d101554b5355413e7c64238abde6ef1ef"
|
||||
@ -1962,7 +1986,7 @@ estraverse@~4.1.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2"
|
||||
|
||||
esutils@^2.0.0, esutils@^2.0.2:
|
||||
esutils@2.0.2, esutils@^2.0.0, esutils@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
|
||||
|
||||
@ -2149,6 +2173,14 @@ flow-bin@^0.40.0:
|
||||
version "0.40.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.40.0.tgz#e10d60846d923124e47f548f16ba60fd8baff5a5"
|
||||
|
||||
flow-parser@0.40.0:
|
||||
version "0.40.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.40.0.tgz#b3444742189093323c4319c4fe9d35391f46bcbc"
|
||||
dependencies:
|
||||
ast-types "0.8.18"
|
||||
colors ">=0.6.2"
|
||||
minimist ">=0.2.0"
|
||||
|
||||
for-in@^0.1.5:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8"
|
||||
@ -2263,6 +2295,10 @@ get-caller-file@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
|
||||
|
||||
get-stdin@5.0.1, get-stdin@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398"
|
||||
|
||||
getpass@^0.1.1:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6"
|
||||
@ -2282,17 +2318,7 @@ glob-parent@^2.0.0:
|
||||
dependencies:
|
||||
is-glob "^2.0.0"
|
||||
|
||||
glob@^5.0.15:
|
||||
version "5.0.15"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
|
||||
dependencies:
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "2 || 3"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^7.0.0, glob@^7.0.3, glob@^7.0.5:
|
||||
glob@7.1.1, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
|
||||
dependencies:
|
||||
@ -2303,6 +2329,16 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^5.0.15:
|
||||
version "5.0.15"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
|
||||
dependencies:
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "2 || 3"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
global@^4.3.0:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/global/-/global-4.3.1.tgz#5f757908c7cbabce54f386ae440e11e26b7916df"
|
||||
@ -2972,6 +3008,15 @@ jest-util@^19.0.2:
|
||||
leven "^2.0.0"
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
jest-validate@19.0.0:
|
||||
version "19.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-19.0.0.tgz#8c6318a20ecfeaba0ba5378bfbb8277abded4173"
|
||||
dependencies:
|
||||
chalk "^1.1.1"
|
||||
jest-matcher-utils "^19.0.0"
|
||||
leven "^2.0.0"
|
||||
pretty-format "^19.0.0"
|
||||
|
||||
jest-validate@^19.0.2:
|
||||
version "19.0.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-19.0.2.tgz#dc534df5f1278d5b63df32b14241d4dbf7244c0c"
|
||||
@ -3366,7 +3411,7 @@ minimist@0.0.8, minimist@~0.0.1:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
||||
|
||||
minimist@^1.1.0, minimist@^1.1.1, minimist@^1.2.0:
|
||||
minimist@1.2.0, minimist@>=0.2.0, minimist@^1.1.0, minimist@^1.1.1, minimist@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
||||
|
||||
@ -3744,6 +3789,21 @@ preserve@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
|
||||
|
||||
prettier@^0.22.0:
|
||||
version "0.22.0"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-0.22.0.tgz#7b37c4480d0858180407e5a8e13f0f47da7385d2"
|
||||
dependencies:
|
||||
ast-types "0.9.4"
|
||||
babel-code-frame "6.22.0"
|
||||
babylon "6.15.0"
|
||||
chalk "1.1.3"
|
||||
esutils "2.0.2"
|
||||
flow-parser "0.40.0"
|
||||
get-stdin "5.0.1"
|
||||
glob "7.1.1"
|
||||
jest-validate "19.0.0"
|
||||
minimist "1.2.0"
|
||||
|
||||
pretty-format@^19.0.0:
|
||||
version "19.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-19.0.0.tgz#56530d32acb98a3fa4851c4e2b9d37b420684c84"
|
||||
@ -3861,9 +3921,11 @@ react-native-drawer-layout@1.2.0:
|
||||
dependencies:
|
||||
react-native-dismiss-keyboard "1.0.0"
|
||||
|
||||
react-native-tab-view@^0.0.57:
|
||||
version "0.0.57"
|
||||
resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-0.0.57.tgz#715e2ea4100fa50168e134df3947dd76ebd55743"
|
||||
react-native-tab-view@^0.0.59:
|
||||
version "0.0.59"
|
||||
resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-0.0.59.tgz#2cd1d809a5699fcc5d45fa6fc08deb21d3f81fea"
|
||||
dependencies:
|
||||
eslint-plugin-prettier "^2.0.1"
|
||||
|
||||
react-native-vector-icons@^3.0.0:
|
||||
version "3.0.0"
|
||||
@ -4168,6 +4230,10 @@ require-uncached@^1.0.2:
|
||||
caller-path "^0.1.0"
|
||||
resolve-from "^1.0.0"
|
||||
|
||||
requireindex@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.1.0.tgz#e5404b81557ef75db6e49c5a72004893fe03e162"
|
||||
|
||||
resolve-from@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
|
||||
|
Loading…
x
Reference in New Issue
Block a user