diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 5f37fb561..d2b187fee 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,7 +1,13 @@ ### Version Tell us which versions you are using: + +- react-native-router-flux v4.?.? - react v16.?.? - react-native v0.?.? diff --git a/Example/Example.js b/Example/Example.js index d2e0a64c0..14fb8f712 100644 --- a/Example/Example.js +++ b/Example/Example.js @@ -61,7 +61,7 @@ const Example = () => ( ({ screenInterpolator: CardStackStyleInterpolator.forFadeFromBottomAndroid })}> - + navigation.state.key} /> diff --git a/README.md b/README.md index b61f21529..7859eb00e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,11 @@ [Follow author @PAksonov](https://twitter.com/PAksonov) -#### NOTE: v4 based on [React Navigation v2.x](https://reactnavigation.org/). See [this branch](https://github.com/aksonov/react-native-router-flux/tree/v3) and [docs](https://github.com/aksonov/react-native-router-flux/blob/master/README3.md) for v3 based on deprecated React Native Experimental Navigation API. It is not supported and may not work with latest React Native versions. +### IMPORTANT NOTES + +#### v4 is based on [React Navigation v2.x](https://reactnavigation.org/). See [this branch](https://github.com/aksonov/react-native-router-flux/tree/v3) and [docs](https://github.com/aksonov/react-native-router-flux/blob/master/README3.md) for v3 based on deprecated React Native Experimental Navigation API. It is not supported and may not work with latest React Native versions. + +#### v4.0.0-beta.x is based on [React Navigation v1.5.x](https://reactnavigation.org/). It is also not supported and may not work with the latest React Native versions. ___ diff --git a/docs/API.md b/docs/API.md index dbb0e1ead..2ca6e22f6 100644 --- a/docs/API.md +++ b/docs/API.md @@ -85,6 +85,29 @@ All properties of type `React.Component` will receive the same properties availa ## Tabs (`` or ``) Can use all `props` listed above in `` as `` is syntatic sugar for ``. +| Property | Type | Default | Description | +|-----------------|----------|----------|--------------------------------------------| +| `wrap`     | `boolean` | `true` | Wrap each scene with own navbar automatically (if it is not another container). | +| `activeBackgroundColor` | `string` | | Specifies the active background color for the tab in focus | +| `activeTintColor`     | `string` | | Specifies the active tint color for tabbar icons | +| `inactiveBackgroundColor` | `string` | | Specifies the inactive background color for the tabs not in focus | +| `inactiveTintColor`     | `string` | | Specifies the inactive tint color for tabbar icons | +| `labelStyle` | `object` | | Overrides the styles for the tab label | +| `lazy`     | `boolean` | `false` | Won't render/mount the tab scene until the tab is active | +| `tabBarComponent` | `React.Component` | | React component to render custom tab bar | +| `tabBarPosition`     | `string` | | Specifies tabbar position. Defaults to `bottom` on iOS and `top` on Android. | +| `tabBarStyle` | `object` | | Override the tabbar styles | +| `tabStyle` | `object` | | Override the style for an individual tab of the tabbar | +| `showLabel`     | `boolean` | `true` | Boolean to show or not the tabbar icons labels | +| `tabBarOnPress`     | `function` | | Custom tab bar icon press. | +| `backToInitial`     | `boolean` | `false` | Back to initial screen on focused tab if tab icon was tapped. | +| `upperCaseLabel`     | `boolean` | `true` | Whether to make label uppercase, default is true. | +| `indicatorStyle`     | `object` | | Override the style for active tab indicator. | + + +## LegacyTabs (`` or ``) +Can use all `props` listed above in `` as `` is syntatic sugar for ``. + | Property | Type | Default | Description | |-----------------|----------|----------|--------------------------------------------| | `wrap`     | `boolean` | `true` | Wrap each scene with own navbar automatically (if it is not another container). | diff --git a/index.d.ts b/index.d.ts index 2f1119dfa..4a9e23365 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,162 +1,170 @@ /// /// -import * as React from "react"; -import { StyleProp, Image, ViewStyle, TextStyle, ImageStyle } from "react-native"; +import * as React from 'react'; +import { StyleProp, Image, ViewStyle, TextStyle, ImageStyle } from 'react-native'; export var Router: RouterStatic; export type Router = RouterStatic; // Router interface RouterProps extends React.Props { - sceneStyle?: StyleProp; - backAndroidHandler?: Function; - wrapBy?: Function; - scenes?: any; + sceneStyle?: StyleProp; + backAndroidHandler?: Function; + wrapBy?: Function; + scenes?: any; } -interface RouterStatic extends React.ComponentClass { } +interface RouterStatic extends React.ComponentClass {} // Scene export var Scene: SceneStatic; export type Scene = SceneStatic; interface SceneProps extends React.Props { - key?: string; - component?: React.ComponentType - back?: boolean; - init?: boolean; - clone?: boolean; - contentComponent?: React.ComponentType - backButtonImage?: string; - backButtonTintColor?: string; - drawer?: boolean; - failure?: () => void; - headerBackTitle?: string; - headerMode?: HeaderModeType; - hideNavBar?: boolean; - hideTabBar?: boolean; - hideBackImage?: boolean; - initial?: boolean; - leftButtonImage?: Image; - modal?: boolean; - navigationBarTitleImage?: Image; - navigationBarTitleImageStyle?: StyleProp; - navTransparent?: boolean; - on?: (props: any) => void; - onEnter?: (props: any) => void; - onExit?: (props: any) => void; - onLeft?: (props: any) => void; - onRight?: (props: any) => void; - renderTitle?: React.ComponentType - renderLeftButton?: React.ComponentType - renderRightButton?: React.ComponentType - renderBackButton?: React.ComponentType - rightButtonImage?: Image; - rightButtonTextStyle?: StyleProp; - success?: () => void; - tabs?: boolean; - title?: string; - titleStyle?: StyleProp; - type?: ActionConstShort; - [name: string]: any; // These are passed through to the scenes + key?: string; + component?: React.ComponentType; + back?: boolean; + init?: boolean; + clone?: boolean; + contentComponent?: React.ComponentType; + backButtonImage?: string; + backButtonTintColor?: string; + drawer?: boolean; + failure?: (() => void) | string; + headerBackTitle?: string; + headerMode?: HeaderModeType; + hideNavBar?: boolean; + hideTabBar?: boolean; + hideBackImage?: boolean; + initial?: boolean; + leftButtonImage?: Image; + modal?: boolean; + navigationBarTitleImage?: Image; + navigationBarTitleImageStyle?: StyleProp; + navTransparent?: boolean; + on?: (props: any) => void; + onEnter?: (props: any) => void; + onExit?: (props: any) => void; + onLeft?: (props: any) => void; + onRight?: (props: any) => void; + renderTitle?: React.ComponentType; + renderLeftButton?: React.ComponentType; + renderRightButton?: React.ComponentType; + renderBackButton?: React.ComponentType; + rightButtonImage?: Image; + rightButtonTextStyle?: StyleProp; + success?: (() => void) | string; + tabs?: boolean; + title?: string; + titleStyle?: StyleProp; + type?: ActionConstShort; + [name: string]: any; // These are passed through to the scenes } interface TabSceneProps extends React.Props { - icon?: React.ComponentType - tabBarLabel?: string; + icon?: React.ComponentType; + tabBarLabel?: string; } -interface SceneStatic extends React.ComponentClass { } -export type HeaderModeType = "float" | "screen" | "none"; +interface SceneStatic extends React.ComponentClass {} +export type HeaderModeType = 'float' | 'screen' | 'none'; // Tabs export var Tabs: TabsStatic; export type Tabs = TabsStatic; interface TabsProps extends React.Props { - wrap?: boolean; - activeBackgroundColor?: string; - activeTintColor?: string; - inactiveBackgroundColor?: string; - inactiveTintColor?: string; - labelStyle?: StyleProp; - lazy?: boolean; - tabBarComponent?: React.ComponentType - tabBarPosition?: TabBarPositionType; - tabBarStyle?: StyleProp; - tabStyle?: StyleProp; - showLabel?: boolean; - swipeEnabled?: boolean; - tabBarOnPress?: Function; - backToInitial?: boolean; + wrap?: boolean; + activeBackgroundColor?: string; + activeTintColor?: string; + inactiveBackgroundColor?: string; + inactiveTintColor?: string; + labelStyle?: StyleProp; + lazy?: boolean; + hideTabBar?: boolean; + tabBarComponent?: React.ComponentType; + tabBarPosition?: TabBarPositionType; + tabBarStyle?: StyleProp; + tabStyle?: StyleProp; + showLabel?: boolean; + swipeEnabled?: boolean; + tabBarOnPress?: Function; + backToInitial?: boolean; } -interface TabsStatic extends React.ComponentClass { } -export type TabBarPositionType = "top" | "bottom"; +interface TabsStatic extends React.ComponentClass {} +export type TabBarPositionType = 'top' | 'bottom'; // Drawer export var Drawer: DrawerStatic; export type Drawer = DrawerStatic; interface DrawerProps extends React.Props { - drawerImage?: Image; - drawerIcon?: React.ComponentType - drawerPosition?: DrawerPositionType; + drawerImage?: Image; + drawerIcon?: React.ComponentType; + drawerPosition?: DrawerPositionType; } -interface DrawerStatic extends React.ComponentClass { } -export type DrawerPositionType = "right" | "left"; +interface DrawerStatic extends React.ComponentClass {} +export type DrawerPositionType = 'right' | 'left'; // Modal export var Modal: ModalStatic; export type Modal = ModalStatic; -interface ModalProps extends React.Props { } -interface ModalStatic extends React.ComponentClass { } +interface ModalProps extends React.Props {} +interface ModalStatic extends React.ComponentClass {} // Overlay export var Overlay: OverlayStatic; export type Overlay = OverlayStatic; -interface OverlayProps extends React.Props { } -interface OverlayStatic extends React.ComponentClass { } +interface OverlayProps extends React.Props {} +interface OverlayStatic extends React.ComponentClass {} // Lightbox -export var Lightbox : LightboxStatic; -export type Lightbox = LightboxStatic; -interface LightboxProps extends React.Props { } -interface LightboxStatic extends React.ComponentClass { } +export var Lightbox: LightboxStatic; +export type Lightbox = LightboxStatic; +interface LightboxProps extends React.Props {} +interface LightboxStatic extends React.ComponentClass {} // Stack export var Stack: StackStatic; export type Stack = StackStatic; interface StackProps extends React.Props { - navigationBarStyle?: StyleProp; - icon?: any; - tintColor?: string; - hideNavBar?: boolean; - title?: string; - titleStyle?: StyleProp; -} -interface StackStatic extends React.ComponentClass { + navigationBarStyle?: StyleProp; + icon?: any; + tintColor?: string; + hideNavBar?: boolean; + title?: string; + titleStyle?: StyleProp; } +interface StackStatic extends React.ComponentClass {} export var Actions: ActionsGenericStatic; export type Actions = ActionsGenericStatic; interface ActionsStatic { - currentScene: any; - jump: (sceneKey: string, props?: any) => void; - pop: () => void; - popAndPush: (sceneKey: string, props?: any) => void; - popTo: (sceneKey: string, props?: any) => void; - push: (sceneKey: string, props?: any) => void; - refresh: (props?: any) => void; - replace: (sceneKey: string, props?: any) => void; - reset: (sceneKey: string, props?: any) => void; - drawerOpen?: any; - drawerClose?: any; - + currentScene: any; + jump: (sceneKey: string, props?: any) => void; + pop: () => void; + popAndPush: (sceneKey: string, props?: any) => void; + popTo: (sceneKey: string, props?: any) => void; + push: (sceneKey: string, props?: any) => void; + refresh: (props?: any) => void; + replace: (sceneKey: string, props?: any) => void; + reset: (sceneKey: string, props?: any) => void; + drawerOpen?: any; + drawerClose?: any; } interface ActionsGenericStatic extends ActionsStatic { - [key: string]: (props?: any) => void; + [key: string]: (props?: any) => void; } -export type ActionConstShort = "jump" | "push" | "replace" | "pop" | "popTo" | "refresh" | "reset"; +export type ActionConstShort = 'jump' | 'push' | 'replace' | 'pop' | 'popTo' | 'refresh' | 'reset'; export declare const ActionConst: ActionConst; export type ActionConst = { - JUMP: string; PUSH: string; PUSH_OR_POP: string; REPLACE: string; - BACK: string; BACK_ACTION: string; POP_TO: string; REFRESH: string; - RESET: string; FOCUS: string; BLUR: string; ANDROID_BACK: string; -} + JUMP: string; + PUSH: string; + PUSH_OR_POP: string; + REPLACE: string; + BACK: string; + BACK_ACTION: string; + POP_TO: string; + REFRESH: string; + RESET: string; + FOCUS: string; + BLUR: string; + ANDROID_BACK: string; +}; diff --git a/src/LegacyTabs.js b/src/LegacyTabs.js new file mode 100644 index 000000000..cac8b838d --- /dev/null +++ b/src/LegacyTabs.js @@ -0,0 +1,9 @@ +/** + * Copyright (c) 2015-present, Pavel Aksonov + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + * + */ +export default () => null; diff --git a/src/navigationStore.js b/src/navigationStore.js index 0273f50e6..e0deb537a 100644 --- a/src/navigationStore.js +++ b/src/navigationStore.js @@ -3,6 +3,7 @@ import { StatusBar, Image, Animated, Easing, } from 'react-native'; import { + createTabNavigator as DEPRECATED_createTabNavigator, createBottomTabNavigator, createMaterialTopTabNavigator, createDrawerNavigator, @@ -24,6 +25,7 @@ import Modal from './Modal'; import Lightbox from './Lightbox'; import Drawer from './Drawer'; import Tabs from './Tabs'; +import LegacyTabs from './LegacyTabs'; import Overlay from './Overlay'; import OverlayRenderer from './OverlayRenderer'; import createStackNavigatorHOC from './createStackNavigatorHOC'; @@ -47,60 +49,60 @@ export const actionMap = { }; const reservedKeys = [ - 'children', - 'refs', 'addRef', - 'removeRef', + 'back', + 'children', 'create', - 'execute', - 'popTo', - 'navigate', - 'replace', - 'refresh', 'dispatch', - 'push', - 'setParams', - 'run', - 'onEnter', - 'onRight', - 'onLeft', + 'drawerClose', + 'drawerOpen', + 'execute', 'left', - 'back', - 'right', - 'rightButton', 'leftButton', + 'navBar', + 'navigate', 'on', + 'onEnter', 'onExit', + 'onLeft', + 'onRight', 'pop', + 'popTo', + 'push', + 'refresh', + 'refs', + 'removeRef', 'renderLeftButton', 'renderRightButton', 'renderTitle', - 'navBar', + 'replace', + 'right', + 'rightButton', + 'run', + 'setParams', 'title', - 'drawerOpen', - 'drawerClose', ]; const dontInheritKeys = [ + 'backToInitial', + 'children', 'component', 'contentComponent', - 'tabBarComponent', - 'modal', 'drawer', + 'hideNavBar', + 'hideTabBar', + 'key', 'lightbox', - 'overlay', - 'tabs', + 'modal', 'navigator', - 'children', - 'key', + 'navTransparent', + 'overlay', 'ref', 'style', + 'tabBarComponent', + 'tabs', 'title', - 'navTransparent', 'type', - 'hideNavBar', - 'hideTabBar', - 'backToInitial', ]; function getValue(value, params) { @@ -603,7 +605,7 @@ class NavigationStore { navigator, renderer, contentComponent, drawerWidth, drawerLockMode, tabBarComponent, tabBarPosition, lazy, duration, ...parentProps } = scene.props; let { - tabs, modal, lightbox, overlay, drawer, transitionConfig, + legacy, tabs, modal, lightbox, overlay, drawer, transitionConfig, } = parentProps; if (scene.type === Modal) { modal = true; @@ -613,6 +615,9 @@ class NavigationStore { lightbox = true; } else if (scene.type === Tabs) { tabs = true; + } else if (scene.type === LegacyTabs) { + tabs = true; + legacy = true; } else if (scene.type === Overlay) { overlay = true; } @@ -691,7 +696,7 @@ class NavigationStore { if (path) { this.states[key].path = path; } - // console.log(`KEY ${key} PATH ${path} DRAWER ${drawer} TABS ${tabs} WRAP ${wrap}`, JSON.stringify(commonProps)); + // console.log(`KEY ${key} LEGACY {legacy} PATH ${path} DRAWER ${drawer} TABS ${tabs} WRAP ${wrap}`, JSON.stringify(commonProps)); const screen = { screen: createWrapper(component, wrapBy, this) || this.processScene(child, commonProps, clones) || (lightbox && (() => null)), navigationOptions: createNavigationOptions({ @@ -746,7 +751,7 @@ class NavigationStore { `return function ${ key.replace(/\W/g, '_') // eslint-disable-line no-new-func }(params){ actions.execute(type, '${key}', props, params)}`, - )(this, { ...commonProps, ...props }, type); + )(this, { error: '', ...commonProps, ...props }, type); } if ((onEnter || on || (component && component.onEnter)) && !this[key + OnEnter]) { @@ -791,7 +796,9 @@ class NavigationStore { if (tabs) { let createTabNavigator = createMaterialTopTabNavigator; - if (tabBarPosition !== 'top') { + if (legacy) { + createTabNavigator = DEPRECATED_createTabNavigator; + } else if (tabBarPosition !== 'top') { createTabNavigator = createBottomTabNavigator; } return createTabNavigator(res, { @@ -926,11 +933,11 @@ class NavigationStore { reset = (routeName, data) => { const params = filterParam(data); - const { key } = getParent(this.state, routeName); + const parent = getParent(this.state, routeName); this.dispatch( StackActions.reset({ index: 0, - key, + key: parent ? parent.key : null, actions: [ NavigationActions.navigate({ routeName,