Skip to content
Merged

2.0 #102

Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
new 2.0 (alpha) version
  • Loading branch information
Pavlo Aksonov authored and Pavlo Aksonov committed Dec 30, 2015
commit 29b9b61eaf0227b6d298c4d36289c26acbf7ee6c
90 changes: 90 additions & 0 deletions Actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import Route from './Route';
import Router from './Router';

function isNumeric(n){
return !isNaN(parseFloat(n)) && isFinite(n);
}

function filterParam(data){
if (data.toString()!='[object Object]')
return data;
if (!data){
return;
}
var proto = (data||{}).constructor.name;
// avoid passing React Native parameters
if (proto != 'Object'){
data = {};
}
if (data.data){
data.data = filterParam(data.data);
}
return data;
}

class Actions {
currentRouter: ?Router;

constructor(){
this.pop = this.pop.bind(this);
this.route = this.route.bind(this);
}

route(name: string, props: { [key: string]: any} = {}){
if (!this.currentRouter){
throw new Error("No current router is set");
}
if (props.toString()!='[object Object]')
props = {data : props};

props = filterParam(props);
// check if route is in children, current or parent routers
let router: Router = this.currentRouter;
// deep into child router
while (router.currentRoute.childRouter){
router = router.currentRoute.childRouter;
}
while (!router.routes[name]){
const route = router.parentRoute;
if (!route || !route.parent){
throw new Error("Cannot find router for route="+name);
}
router = route.parent;
}
if (router.route(name, props)){
this.currentRouter = router;
return true;
}
return false;
}
pop(num: number = 1){
if (!isNumeric(num)){
num = 1;
}
if (!this.currentRouter){
throw new Error("No current router is set");
}
if (num > 1){
for (let i=0;i<num;i++){
if (!Actions.pop()){
return false;
}
}
return true;
} else {
let router: Router = this.currentRouter;
while (router.stack.length <= 1 || router.currentRoute.type === 'switch'){
router = router.parentRoute.parent;
}
if (router.pop()){
this.currentRouter = router;
return true;
} else {
return false;
}

}
}
}

export default new Actions();
23 changes: 23 additions & 0 deletions Common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react-native';

// schema class represents schema for routes and it is processed inside Router component
export class Schema extends React.Component {
className(){
return "Schema";
}
render(){
return null;
}
}

// route class processed inside Router component
export class Route extends React.Component {
className(){
return "Route";
}
render(){
return null;
}

}

171 changes: 171 additions & 0 deletions ExRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import React from 'react-native';
import Router from './Router';
import Route from './Route';
import * as Components from './Common';
import ExNavigator from '@exponent/react-native-navigator';
import Animations from './Animations';
const {TouchableOpacity, StyleSheet, View, Text} = React;
import ReactRouter from './ReactRouter';

export class ExRoute {
name: string;
navigator: ExNavigator;
route: Route;
props: { [key: string]: any};

constructor(route: Route, props:{ [key: string]: any} = {}){
if (!route){
throw new Error("route is not defined ");
}
this.route = route;
this.name = route.name;
if (!this.name){
throw new Error("name is not defined for route");
}
this.props = props;
this.renderScene = this.renderScene.bind(this);
}

configureScene() {
return this.route.type === 'switch' ? Animations.None : this.route.props.sceneConfig || Animations.None;
}

renderScene(navigator) {
console.log("RENDER SCENE:"+this.route.name);
const Component = this.route.component;
const child = Component ?
!this.route.wrapRouter ? <Component key={this.route.name} name={this.route.name} {...this.route.props} {...this.props} route={this.route}/>:
<ReactRouter name={this.route.name+"Router"} {...this.route.props} {...this.props} route={this.route} router={ExRouter} >
<Components.Route {...this.route.props} {...this.props} component={Component} name={"_"+this.route.name} type="push" wrapRouter={false}/>
</ReactRouter>
:
React.cloneElement(React.Children.only(this.route.children), {...this.route.props, data:this.props, route:this.route});
return child;
}

getName(){
return this.route.name;
}

getTitle() {
return this.route.title || "";
}

getBackButtonTitle(navigator, index, state){
let previousIndex = index - 1;
let previousRoute = state.routeStack[previousIndex];
let title = previousRoute.getTitle(navigator, previousIndex, state);
return title.length>10 ? null : title;
}

renderRightButton() {
if (this.route.onRight && this.route.rightTitle){
return (<TouchableOpacity
touchRetentionOffset={ExNavigator.Styles.barButtonTouchRetentionOffset}
onPress={() => this.route.onRight({...this.route.props, ...this.props})}
style={[ExNavigator.Styles.barRightButton, this.route.rightButtonStyle]}>
<Text style={[ExNavigator.Styles.barRightButtonText, this.route.rightButtonTextStyle]}>{this.route.rightTitle}</Text>
</TouchableOpacity>);
} else {
return null;
}
}
}

const defaultCreateRoute = function(route, data){
return new ExRoute(route, data);
};

export default class ExRouter extends React.Component {
router: Router;

constructor(props){
super(props);
this.onPop = this.onPop.bind(this);
this.onPush = this.onPush.bind(this);
this.onPop = this.onPop.bind(this);
this.onPush = this.onPush.bind(this);
this.onReplace = this.onReplace.bind(this);
this.onJump = this.onJump.bind(this);
}

onPush(route: Route, props:{ [key: string]: any}):boolean {
this.refs.nav.push(new ExRoute(route, props));
return true;
}

onReplace(route: Route, props:{ [key: string]: any}):boolean {
this.refs.nav.replace(new ExRoute(route, props));
return true;
}

onJump(route: Route, props:{ [key: string]: any}):boolean {
const navigator = this.refs.nav;
const routes = navigator.getCurrentRoutes();
const exist = routes.filter(el=>el.getName()==route.name);
if (exist.length){
navigator.jumpTo(exist[0]);
} else {
navigator.push(new ExRoute(route, props));

}
this.setState({selected: route.name});
return true;
}

onPop(num: number){
this.refs.nav.pop();
return true;
}

render() {
const router = this.props.router;
console.log("RE-RENDER selected",router.name,this.state && this.state.selected);

const Header = this.props.header;
const header = Header ? <Header {...this.props} {...this.state}/> : null;

const Footer = this.props.footer;
const footer = Footer ? <Footer {...this.props} {...this.state}/> : null;

return (
<View style={styles.transparent}>
{header}
<ExNavigator ref="nav" initialRouteStack={router.stack.map(route => new ExRoute(router.routes[route]))}
style={styles.transparent}
sceneStyle={{ paddingTop: 0 }}
showNavigationBar={!this.props.hideNavBar}
{...this.props}
/>
{footer}
</View>
);
}


}


var styles = StyleSheet.create({
container: {
position: 'absolute',
top:0,
bottom:0,
left:0,
right:0,
backgroundColor:'transparent',
justifyContent: 'center',
alignItems: 'center',
},
transparent: {
flex:1,
backgroundColor: "transparent"
},
barTitleText: {
fontFamily: '.HelveticaNeueInterface-MediumP4',
fontSize: 17,
marginTop: 11,
},

});

37 changes: 37 additions & 0 deletions ReactRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* 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.
*
*/

import React from 'react-native'
import Router from './Router';
import ExRouter from './ExRouter';
const {StyleSheet, View} = React;
import Actions from './Actions';
export default class extends React.Component {

constructor(props){
super(props);
const createRouter = props.createRouter || this.createRouter;
this.router = createRouter(props);
}

createRouter(props){
const schemas = React.Children.map(props.children, child=>child).filter(child=>child.type.prototype.className() === "Schema").map(child=>child.props);
const routes = React.Children.map(props.children, child=>child).filter(child=>child.type.prototype.className() === "Route").map(child=>child.props);
return new Router(routes, schemas, props.initialRoutes || (props.initial && [props.initial]), props);
}

componentDidMount(){
this.router.delegate = this.refs.router;
}

render(){
const Component = this.props.router || ExRouter;
return (<Component ref="router" {...this.props} router={this.router} />);
}
}
50 changes: 50 additions & 0 deletions Route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* 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.
*
*/

import type Router from './Router';
export default class Route {
name: string;
type: string;
title: string;
header: any;
footer: any;
component: any;
children: any;
props: { [key: string]: any};
parent: Router;
navigator: any;
childRouter: ?Router;

constructor({name, type, component, children, header, footer, wrapRouter, ...props}: { [key: string]: any} = {}, parent: Router = null) {
if (!name) {
throw new Error("no name is defined for Route=" + name);
}
if (!props) {
throw new Error("no props is defined for Route=" + name);
}
this.name = name;
this.type = type || 'push';
this.component = component;
this.children = children;
if (!this.component && !this.children) {
throw new Error("component or children should be defined for route=" + name);
}
if (!parent) {
throw new Error("Parent router is not set!");
}
this.title = props.title;
this.parent = parent;
this.header = header;
this.footer = footer;
this.props = props;
this.wrapRouter = wrapRouter || this.type == 'switch';

}

}
Loading