diff --git a/Example/.buckconfig b/Example/.buckconfig
new file mode 100644
index 000000000..934256cb2
--- /dev/null
+++ b/Example/.buckconfig
@@ -0,0 +1,6 @@
+
+[android]
+ target = Google Inc.:Google APIs:23
+
+[maven_repositories]
+ central = https://repo1.maven.org/maven2
diff --git a/Example/.flowconfig b/Example/.flowconfig
index 66b57e096..ee340094c 100644
--- a/Example/.flowconfig
+++ b/Example/.flowconfig
@@ -42,6 +42,12 @@
# Ignore Website
.*/website/.*
+# Ignore generators
+.*/local-cli/generator.*
+
+# Ignore BUCK generated folders
+.*\.buckd/
+
.*/node_modules/is-my-json-valid/test/.*\.json
.*/node_modules/iconv-lite/encodings/tables/.*\.json
.*/node_modules/y18n/test/.*\.json
@@ -59,6 +65,7 @@
.*/node_modules/isemail/.*\.json
.*/node_modules/tr46/.*\.json
+
[include]
[libs]
@@ -86,4 +93,4 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-2]\\|1[0-9]\\|[0-9
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
[version]
-0.22.0
+^0.22.0
diff --git a/Example/.gitignore b/Example/.gitignore
index 94fc86711..42c9490e5 100644
--- a/Example/.gitignore
+++ b/Example/.gitignore
@@ -32,3 +32,9 @@ local.properties
#
node_modules/
npm-debug.log
+
+# BUCK
+buck-out/
+\.buckd/
+android/app/libs
+android/keystores/debug.keystore
diff --git a/Example/Example.js b/Example/Example.js
index 97783a69f..1c1d2e494 100644
--- a/Example/Example.js
+++ b/Example/Example.js
@@ -59,7 +59,7 @@ export default class Example extends React.Component {
-
+
alert("Right button")} rightTitle="Right" />
diff --git a/Example/android/app/BUCK b/Example/android/app/BUCK
new file mode 100644
index 000000000..bdec72cd9
--- /dev/null
+++ b/Example/android/app/BUCK
@@ -0,0 +1,66 @@
+import re
+
+# To learn about Buck see [Docs](https://buckbuild.com/).
+# To run your application with Buck:
+# - install Buck
+# - `npm start` - to start the packager
+# - `cd android`
+# - `cp ~/.android/debug.keystore keystores/debug.keystore`
+# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
+# - `buck install -r android/app` - compile, install and run application
+#
+
+lib_deps = []
+for jarfile in glob(['libs/*.jar']):
+ name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile)
+ lib_deps.append(':' + name)
+ prebuilt_jar(
+ name = name,
+ binary_jar = jarfile,
+ )
+
+for aarfile in glob(['libs/*.aar']):
+ name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile)
+ lib_deps.append(':' + name)
+ android_prebuilt_aar(
+ name = name,
+ aar = aarfile,
+ )
+
+android_library(
+ name = 'all-libs',
+ exported_deps = lib_deps
+)
+
+android_library(
+ name = 'app-code',
+ srcs = glob([
+ 'src/main/java/**/*.java',
+ ]),
+ deps = [
+ ':all-libs',
+ ':build_config',
+ ':res',
+ ],
+)
+
+android_build_config(
+ name = 'build_config',
+ package = 'com.example',
+)
+
+android_resource(
+ name = 'res',
+ res = 'src/main/res',
+ package = 'com.example',
+)
+
+android_binary(
+ name = 'app',
+ package_type = 'debug',
+ manifest = 'src/main/AndroidManifest.xml',
+ keystore = '//android/keystores:debug',
+ deps = [
+ ':app-code',
+ ],
+)
diff --git a/Example/android/app/build.gradle b/Example/android/app/build.gradle
index 5f726247a..156630953 100644
--- a/Example/android/app/build.gradle
+++ b/Example/android/app/build.gradle
@@ -9,7 +9,7 @@ import com.android.build.OutputFile
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
- * `apply from: "react.gradle"` line.
+ * `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
@@ -59,7 +59,7 @@ import com.android.build.OutputFile
* ]
*/
-apply from: "react.gradle"
+apply from: "../../node_modules/react-native/react.gradle"
/**
* Set this to true to create two separate APKs instead of one:
@@ -124,3 +124,10 @@ dependencies {
compile "com.android.support:appcompat-v7:23.0.1"
compile "com.facebook.react:react-native:+" // From node_modules
}
+
+// Run this once to be able to run the application with BUCK
+// puts all compile dependencies into folder libs for BUCK to use
+task copyDownloadableDepsToLibs(type: Copy) {
+ from configurations.compile
+ into 'libs'
+}
diff --git a/Example/package.json b/Example/package.json
index 72672d1a2..728fb489f 100644
--- a/Example/package.json
+++ b/Example/package.json
@@ -7,9 +7,9 @@
},
"dependencies": {
"react": "^0.14.7",
- "react-native": "^0.22.2",
+ "react-native": "^0.24.0",
"react-native-button": "^1.2.1",
- "react-native-drawer": "^1.16.7",
+ "react-native-drawer": "^2.0.0",
"react-native-modalbox": "^1.3.0",
"react-native-router-flux": "file:../"
}
diff --git a/README.md b/README.md
index 46c386bf7..7831b4d20 100644
--- a/README.md
+++ b/README.md
@@ -49,8 +49,8 @@ class App extends React.Component {
}
}
```
-Alternatively you could define all your scenes during compile time and use it later within Router:
-```
+Alternatively you could define all your scenes during compile time and use it later within `Router`:
+```javascript
const scenes = Actions.create(
@@ -67,21 +67,21 @@ class App extends React.Component {
```
2. In any app screen:
- * import {Actions} from 'react-native-router-flux'
- * Actions.ACTION_NAME(PARAMS) will call the appropriate action and params will be passed to the scene.
- * Actions.pop() will pop the current screen.
- * Actions.refresh(PARAMS) will update the properties of the current screen.
+ * `import {Actions} from 'react-native-router-flux'`
+ * `Actions.ACTION_NAME(PARAMS)` will call the appropriate action and params will be passed to the scene.
+ * `Actions.pop()` will pop the current screen.
+ * `Actions.refresh(PARAMS)` will update the properties of the current screen.
## Available imports
-- Router
-- Scene
-- Modal
-- TabBar
-- getInitialState
-- Reducer
-- DefaultRenderer
-- Switch
-- Actions
+- `Router`
+- `Scene`
+- `Modal`
+- `TabBar`
+- `getInitialState`
+- `Reducer`
+- `DefaultRenderer`
+- `Switch`
+- `Actions`
## Configuration
@@ -132,7 +132,7 @@ class App extends React.Component {
| sceneStyle | View style | { flex: 1 } | optional style override for the Scene's component |
| other props | | | all properties that will be passed to your component instance |
| getSceneStyle | function | optional | Optionally override the styles for NavigationCard's Animated.View rendering the scene. |
-
+| renderTitle | function | optional | Optionally closure to render the title
## Example
![launch](https://cloud.githubusercontent.com/assets/1321329/11692367/7337cfe2-9e9f-11e5-8515-e8b7a9f230ec.gif)
@@ -237,22 +237,159 @@ To display a modal use `Modal` as root renderer, so it will render the first ele
## Redux/Flux
This component doesn't depend on any redux/flux library. It uses new React Native Navigation API and provide own reducer for its navigation state.
-You may provide your own reducer if needed. To avoid the creation of initial state, you may pass a reducer creator. Example to print all actions:
+You may provide your own reducer if needed. To avoid the creation of initial state, you may pass a reducer creator.
+
+The following example will dispatch the `focus` action when a new scene comes into focus. The current scene will be available to components via the `props.scene` property.
+
+##### Step 1
+
+First create a reducer for the routing actions that will be dispatched by RNRF.
+
```javascript
-// remember to add the 'Reducer' to your imports along with Router, Scene, ... like so
-// import { Reducer } from 'react-native-router-flux'
-const reducerCreate = params=>{
- const defaultReducer = Reducer(params);
- return (state, action)=>{
- console.log("ACTION:", action);
- return defaultReducer(state, action);
- }
+// reducers/routes.js
+
+const initialState = {
+ scene: {},
};
-// within your App render() method
-return ;
+export default function reducer(state = initialState, action = {}) {
+ switch (action.type) {
+ // focus action is dispatched when a new screen comes into focus
+ case "focus":
+ return {
+ ...state,
+ scene: action.scene,
+ };
+
+ // ...other actions
+
+ default:
+ return state;
+ }
+}
+```
+
+##### Step 2
+
+Combine this reducer with the rest of the reducers from your app.
+
+```javascript
+// reducers/index.js
+
+import { combineReducers } from 'redux';
+import routes from './routes';
+// ... other reducers
+
+export default combineReducers({
+ routes,
+ // ... other reducers
+});
+
+```
+
+##### Step 3
+
+Connect the `Router` to your redux dispatching system.
+
+```javascript
+// routes.js
+
+import { Actions, Router, Reducer } from 'react-native-router-flux';
+import { connect } from 'react-redux';
+// other imports...
+
+
+const scenes = Actions.create({
+
+ {/* create scenes */}
+
+});
+
+class Routes extends React.Component {
+ static propTypes = {
+ dispatch: PropTypes.func,
+ };
+
+ reducerCreate(params) {
+ const defaultReducer = Reducer(params);
+ return (state, action) => {
+ this.props.dispatch(action)
+ return defaultReducer(state, action);
+ };
+ }
+
+ render () {
+ return (
+
+ );
+ }
+}
+export default connect()(Routes);
```
+
+##### Step 4
+
+Create your store, wrap your routes with the redux `Provider` component.
+
+
+```js
+// app.js
+
+import Routes from './routes';
+import { Provider } from 'react-redux';
+import { createStore, applyMiddleware, compose } from 'redux';
+import reducers from './reducers';
+// other imports...
+
+// create store...
+const middleware = [/* ...your middleware (i.e. thunk) */];
+const store = compose(
+ applyMiddleware(...middleware)
+)(createStore)(reducers);
+
+
+class App extends React.Component {
+ render () {
+ return (
+
+
+
+ );
+ }
+}
+
+export default App;
+```
+
+##### Step 5
+
+Now you can access the current scene from any connected component.
+
+```js
+// components/MyComponent.js
+import React, { PropTypes, Text } from 'react-native';
+import { connect } from 'react-redux';
+
+class MyComponent extends React.Component {
+ static propTypes = {
+ routes: PropTypes.object,
+ };
+
+ render () {
+ return (
+
+ The current scene is titled {this.props.routes.scene.title}.
+
+ );
+ }
+}
+
+export default connect(({routes}) => ({routes}))(MyComponent);
+```
+
## Tabbar
Every tab has its own navigation bar. However, if you do not set its parent `` with `hideNavBar={true}`, the tabs' navigation bar will be overrided by their parient.
@@ -263,12 +400,12 @@ Your scene `component` class could implement _static_ renderNavigationBar(props)
New feature for 3.x release is custom scene renderer that should be used together with tabs={true} property. It allows to select `tab` scene to show depending from app state.
It could be useful for authentication, restricted scenes, etc. Usually you should wrap `Switch` with redux `connect` to pass application state to it:
Following example chooses scene depending from sessionID using Redux:
-```
- ({profile:state.profile}))(Switch)} tabs={true}
- selector={props=>props.profile.sessionID ? "main" : "signUp"}>
-
-
-
+```javascript
+({profile:state.profile}))(Switch)} tabs={true}
+ selector={props=>props.profile.sessionID ? "main" : "signUp"}>
+
+
+
```
## Drawer (side menu) integration
diff --git a/package.json b/package.json
index 61a3ad961..42c15b0e6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "react-native-router-flux",
- "version": "3.22.0",
+ "version": "3.24.1",
"description": "React Native Router using Flux architecture",
"repository": {
"type": "git",
@@ -53,7 +53,7 @@
"react": "^0.14.8",
"react-addons-test-utils": "^0.14.7",
"react-dom": "^0.14.7",
- "react-native": "^0.22.2",
+ "react-native": "^0.24.0",
"react-native-mock": "0.0.6",
"sinon": "^1.17.3"
}
diff --git a/src/DefaultRenderer.js b/src/DefaultRenderer.js
index 5b29bae8a..55f1d7837 100644
--- a/src/DefaultRenderer.js
+++ b/src/DefaultRenderer.js
@@ -9,10 +9,14 @@
import React, {Component, Animated, PropTypes, StyleSheet, View, NavigationExperimental} from "react-native";
const {
AnimatedView: NavigationAnimatedView,
- Card: NavigationCard,
- RootContainer: NavigationRootContainer,
- Header: NavigationHeader,
- } = NavigationExperimental;
+ Card: NavigationCard
+} = NavigationExperimental;
+
+const {
+ CardStackPanResponder: NavigationCardStackPanResponder,
+ CardStackStyleInterpolator: NavigationCardStackStyleInterpolator
+} = NavigationCard;
+
import TabBar from "./TabBar";
import NavBar from "./NavBar";
import Actions from './Actions';
@@ -59,13 +63,13 @@ export default class DefaultRenderer extends Component {
return null;
}
let Component = navigationState.component;
- if (navigationState.tabs && !Component){
+ if (navigationState.tabs && !Component) {
Component = TabBar;
}
if (Component) {
return (
-
+
)
}
@@ -75,7 +79,6 @@ export default class DefaultRenderer extends Component {
let applyAnimation = selected.applyAnimation || navigationState.applyAnimation;
let style = selected.style || navigationState.style;
- let direction = selected.direction || navigationState.direction || "horizontal";
let optionals = {};
if (applyAnimation) {
@@ -85,7 +88,11 @@ export default class DefaultRenderer extends Component {
if (duration === null || duration === undefined) duration = navigationState.duration;
if (duration !== null && duration !== undefined) {
optionals.applyAnimation = function (pos, navState) {
- Animated.timing(pos, {toValue: navState.index, duration}).start();
+ if (duration === 0) {
+ pos.setValue(navState.index);
+ } else {
+ Animated.timing(pos, {toValue: navState.index, duration}).start();
+ }
};
}
}
@@ -95,7 +102,6 @@ export default class DefaultRenderer extends Component {
navigationState={navigationState}
style={[styles.animatedView, style]}
renderOverlay={this._renderHeader}
- direction={direction}
renderScene={this._renderCard}
{...optionals}
/>
@@ -104,25 +110,40 @@ export default class DefaultRenderer extends Component {
_renderHeader(/*NavigationSceneRendererProps*/ props) {
return state.title}
- />;
+ {...props}
+ getTitle={state => state.title}
+ />;
}
_renderCard(/*NavigationSceneRendererProps*/ props) {
- const { key, direction, panHandlers, getSceneStyle } = props.scene.navigationState;
+ const {key, direction, getSceneStyle} = props.scene.navigationState;
+ let {panHandlers, animationStyle} = props.scene.navigationState;
+
+ // Since we always need to pass a style for the direction, we can avoid #526
+ let style = {};
+ if (getSceneStyle) style = getSceneStyle(props);
- const optionals = {};
- if (getSceneStyle) optionals.style = getSceneStyle(props);
+ const isVertical = direction === "vertical";
+
+ if (typeof(animationStyle) === 'undefined') {
+ animationStyle = (isVertical ?
+ NavigationCardStackStyleInterpolator.forVertical(props) :
+ NavigationCardStackStyleInterpolator.forHorizontal(props));
+ }
+
+ if (typeof(panHandlers) === 'undefined') {
+ panHandlers = panHandlers || (isVertical ?
+ NavigationCardStackPanResponder.forVertical(props) :
+ NavigationCardStackPanResponder.forHorizontal(props));
+ }
return (
);
}
@@ -136,6 +157,6 @@ export default class DefaultRenderer extends Component {
const styles = StyleSheet.create({
animatedView: {
flex: 1,
- backgroundColor:"transparent"
+ backgroundColor: "transparent"
},
});
diff --git a/src/NavBar.js b/src/NavBar.js
index 011802509..b9fc95884 100644
--- a/src/NavBar.js
+++ b/src/NavBar.js
@@ -54,7 +54,7 @@ export default class NavBar extends React.Component {
if (selected.component && selected.component.renderNavigationBar){
return selected.component.renderNavigationBar({...this.props,...selected});
}
- if (state.hideNavBar || child.hideNavBar || selected.hideNavBar){
+ if (child.hideNavBar || selected.hideNavBar) {
return null;
}
@@ -65,8 +65,8 @@ export default class NavBar extends React.Component {
{state.children.map(this._renderTitle, this)}
- {renderLeftButton() || renderBackButton()}
- {renderRightButton()}
+ {renderLeftButton(selected) || renderBackButton(selected)}
+ {renderRightButton(selected)}
);
}
@@ -135,6 +135,8 @@ export default class NavBar extends React.Component {
}
_renderTitle(childState: NavigationState, index:number) {
+ const title = childState.renderTitle ?
+ childState.renderTitle():this.props.getTitle ? this.props.getTitle(childState) : childState.title;
return (
- {this.props.getTitle ? this.props.getTitle(childState) : childState.title }
+ {title}
);
}