From 58baab69dfacf391639f254ae4398b3482978575 Mon Sep 17 00:00:00 2001 From: Daniel Weinmann Date: Wed, 10 Feb 2016 20:21:38 -0200 Subject: [PATCH] Renames Router to BaseRouter and ReactRouter to Router and router prop on Router to plugin --- Actions.js | 10 +-- BaseRouter.js | 223 +++++++++++++++++++++++++++++++++++++++++++++++++ ExRouter.js | 10 +-- ReactRouter.js | 38 --------- Route.js | 8 +- Router.js | 223 +++++-------------------------------------------- RouterIOS.js | 10 +-- index.js | 2 +- 8 files changed, 262 insertions(+), 262 deletions(-) create mode 100644 BaseRouter.js delete mode 100644 ReactRouter.js diff --git a/Actions.js b/Actions.js index 4c5bd4e8a..bb0869c5d 100644 --- a/Actions.js +++ b/Actions.js @@ -1,5 +1,5 @@ import Route from './Route'; -import Router from './Router'; +import BaseRouter from './BaseRouter'; import debug from './debug'; const BEFORE_ROUTE = 'BEFORE_ROUTER_ROUTE'; @@ -31,7 +31,7 @@ function filterParam(data){ } class Actions { - currentRouter: ?Router; + currentRouter: ?BaseRouter; constructor(){ this.pop = this.pop.bind(this); @@ -48,7 +48,7 @@ class Actions { props = filterParam(props); // check if route is in children, current or parent routers - let router: Router = this.currentRouter; + let router: BaseRouter = this.currentRouter; // deep into child router while (router.currentRoute.childRouter){ @@ -87,7 +87,7 @@ class Actions { } dismiss(props: { [key: string]: any} = {}){ props = filterParam(props); - let router: Router = this.currentRouter; + let router: BaseRouter = this.currentRouter; // go to root router while (router.parentRoute){ router = router.parentRoute.parent; @@ -118,7 +118,7 @@ class Actions { } return true; } else { - let router: Router = this.currentRouter; + let router: BaseRouter = this.currentRouter; debug("Pop, router="+router.name+" stack length:"+router.stack.length); debug("Current route="+router.currentRoute.name+" type="+router.currentRoute.type); while (router.stack.length <= 1 || router.currentRoute.type === 'switch'){ diff --git a/BaseRouter.js b/BaseRouter.js new file mode 100644 index 000000000..0f423f2c3 --- /dev/null +++ b/BaseRouter.js @@ -0,0 +1,223 @@ +/** + * 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 Route from './Route'; +import Actions from './Actions'; +import debug from './debug'; + +export class RouterDelegate { + onPush(route:Route, props:{ [key: string]: any}):boolean { + return true; + } + onPop(num: number = 1, route:Route, props:{ [key: string]: any}): boolean { + return true; + } + onReplace(route:Route, props:{ [key: string]: any}):boolean { + return true; + } + onReset(route:Route, props:{ [key: string]: any}):boolean { + return true; + } + onSwitch(route:Route, props:{ [key: string]: any}):boolean { + return true; + } +} + +export default class BaseRouter { + name:string; + routes: {[key: string]: Route }; + schemas: {[key: string]: {[key:string]:any } }; + props: { [key: string]: any}; + parentRoute: ?Route; + nextRoute: ?Route; + _stack: Array; + delegate:RouterDelegate; + + set stack(stack:Array) { + if (!stack || !stack.length) { + throw new Error("Cannot be set to empty stack"); + } + this._stack = stack; + } + + get stack():Array { + return this._stack; + } + + get currentRoute():Route { + return this.routes[this._stack[this._stack.length-1]]; + } + + get previousRoute():Route { + if (this._stack.length > 1){ + return this.routes[this._stack[this._stack.length-2]]; + } else { + return null; + } + } + + constructor(routes: Array<{ [key: string]: any}>, + schemas:Array<{ [key: string]: any}> = [], + stack:Array = null, props:{ [key: string]: any} = {}){ + this.schemas = {}; + this.routes = {}; + this.pop = this.pop.bind(this); + this.route = this.route.bind(this); + this.delegate = new RouterDelegate(); + if (!routes || !routes.length){ + throw new Error("routes is not defined"); + } + this.props = props; + this.name = props && props.name; + this.parentRoute = props && props.route; + if (this.parentRoute){ + if (this.parentRoute.parent){ + // copy parent schemas + Object.keys(this.parentRoute.parent.schemas).forEach(el=> + this._addSchema(this.parentRoute.parent.schemas[el].name, this.parentRoute.parent.schemas[el])); + } + this.parentRoute.childRouter = this; + } + + + schemas.forEach(el=>this._addSchema(el.name, el)); + let selected = null; + routes.forEach(el=>{if (el.initial) selected = el.name;this._addRoute(el.name, el)}); + + // select first one as initial + if (!stack || !stack.length){ + stack = [selected || routes[0].name]; + } + this.stack = stack; + // add actions + this._addActions(); + } + + _addSchema(name: string, props:{ [key: string]: any}){ + if (!name){ + throw new Error("Schema name is not defined"); + } + if (this.schemas[name]){ + throw new Error("Schema="+name+" is not unique!"); + } + this.schemas[name] = props; + } + + _addRoute(routeName: string, props:{ [key: string]: any}){ + if (!routeName){ + throw new Error("Route name is not defined"); + } + const schemaName: string = props.schema || 'default'; + const schema = this.schemas[schemaName] || {}; + // pass router data to inner routes + const {children, name, header, footer, showNavigationBar, route, component, hideNavBar, sceneConfig, type, ...routerProps} = this.props; + const routeProps = Object.assign({}, schema, routerProps, props); + + if (this.routes[routeName]){ + throw new Error("Route="+routeName+" is not unique!"); + } + + this.routes[routeName] = new Route(routeProps, this); + + } + + _addActions(){ + if (!Actions.currentRouter){ + Actions.currentRouter = this; + debug("Set current router:"+this.name); + } + Object.keys(this.routes).forEach(name=>{ + if (!Actions[name]){ + Actions[name] = function(data){ + return Actions.route(name, data); + } + } + }); + } + + route(name: string, props:{ [key: string]: any} = {}){ + if (!this.routes[name]){ + throw new Error("No route is defined for name="+name); + } + const type = props.type ? props.type : this.routes[name].type; + const action = type === "switch" ? "jump": type; + if (!action){ + throw new Error("No type is defined for name="+name); + } + if (!this["_"+action]){ + throw new Error("No type="+action+" is supported"); + } + this.nextRoute = this.routes[name]; + + const handler = "on"+capitalizeFirstLetter(action); + if (this.delegate[handler]) { + debug("Run handler "+handler); + const res:boolean = this.delegate[handler](this.routes[name], props); + if (!res) { + console.log("Ignore "+action+", handler returns false"); + return false; + } + } else { + throw new Error("No handler "+handler+" for route="+name); + } + this["_"+action](name, props); + return true; + } + + _modal(name:string, props:{ [key: string]: any} ) { + } + + _push(name:string, props:{ [key: string]: any} ){ + this._stack.push(name); + } + + _replace(name:string, props:{ [key: string]: any} ) { + this._stack[this._stack.length - 1] = name; + } + /*** + * Reset every scene with an array of routes + * @param route defined route + */ + _reset(name:string, props:{ [key: string]: any} ) { + this._stack = [name]; + } + + _jump(name:string, props:{ [key: string]: any} ) { + if (this._stack.indexOf(name) != -1){ + const pos = this._stack.indexOf(name); + // swap latest with found element (because currentRoute always points to latest) + this._stack[pos] = this._stack[this._stack.length - 1]; + this._stack[this._stack.length - 1] = name; + } else { + this._stack.push(name); + } + } + + pop(num: number = 1, props:{ [key: string]: any} = {}){ + if (this._stack.length <= num){ + return false; + } + this.nextRoute = null; + if (this.delegate.onPop && this.delegate.onPop(num, this.currentRoute, props)){ + const routes = this._stack.splice(-num, num); + return true; + } + return false; + } + + dismiss() { + return this.delegate.onDismiss && this.delegate.onDismiss(); + } + + +} + +function capitalizeFirstLetter(string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} diff --git a/ExRouter.js b/ExRouter.js index ca80f6f6d..dcd278eca 100644 --- a/ExRouter.js +++ b/ExRouter.js @@ -1,5 +1,5 @@ import React from 'react-native'; -import Router from './Router'; +import BaseRouter from './BaseRouter'; import Route from './Route'; import * as Components from './Common'; import ExNavigator from '@exponent/react-native-navigator'; @@ -7,7 +7,7 @@ import ExNavigatorStyles from '@exponent/react-native-navigator/ExNavigatorStyle import { BackIcon } from '@exponent/react-native-navigator/ExNavigatorIcons'; import Animations from './Animations'; const {TouchableOpacity, Navigator, StyleSheet, View, Text} = React; -import ReactRouter from './ReactRouter'; +import Router from './Router'; import Actions from './Actions'; import debug from './debug'; @@ -50,9 +50,9 @@ export class ExRouteAdapter { const {initial, ...routeProps} = this.route.props; const child = Component ? !this.route.wrapRouter ? : - + - + : React.cloneElement(React.Children.only(this.route.children), {...routeProps, ...this.props, route:this.route}); @@ -173,7 +173,7 @@ class ExNavigationBar extends Navigator.NavigationBar { } export default class ExRouter extends React.Component { - router: Router; + router: BaseRouter; constructor(props){ super(props); diff --git a/ReactRouter.js b/ReactRouter.js deleted file mode 100644 index f24ab2166..000000000 --- a/ReactRouter.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 debug from './debug'; -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 (); - } -} diff --git a/Route.js b/Route.js index 1e5538e46..ba10fdfb5 100644 --- a/Route.js +++ b/Route.js @@ -7,7 +7,7 @@ * */ -import type Router from './Router'; +import type BaseRouter from './BaseRouter'; export default class Route { name: string; type: string; @@ -17,11 +17,11 @@ export default class Route { component: any; children: any; props: { [key: string]: any}; - parent: Router; + parent: BaseRouter; navigator: any; - childRouter: ?Router; + childRouter: ?BaseRouter; - constructor({name, type, component, schema, children, header, footer, wrapRouter, ...props}: { [key: string]: any} = {}, parent: Router = null) { + constructor({name, type, component, schema, children, header, footer, wrapRouter, ...props}: { [key: string]: any} = {}, parent: BaseRouter = null) { if (!name) { throw new Error("no name is defined for Route=" + name); } diff --git a/Router.js b/Router.js index 806ede7e8..9a06908cf 100644 --- a/Router.js +++ b/Router.js @@ -7,217 +7,32 @@ * */ -import Route from './Route'; -import Actions from './Actions'; +import React from 'react-native' +import BaseRouter from './BaseRouter'; +import ExRouter from './ExRouter'; +const {StyleSheet, View} = React; import debug from './debug'; +import Actions from './Actions'; +export default class Router extends React.Component { -export class RouterDelegate { - onPush(route:Route, props:{ [key: string]: any}):boolean { - return true; - } - onPop(num: number = 1, route:Route, props:{ [key: string]: any}): boolean { - return true; - } - onReplace(route:Route, props:{ [key: string]: any}):boolean { - return true; - } - onReset(route:Route, props:{ [key: string]: any}):boolean { - return true; - } - onSwitch(route:Route, props:{ [key: string]: any}):boolean { - return true; - } -} - -export default class Router { - name:string; - routes: {[key: string]: Route }; - schemas: {[key: string]: {[key:string]:any } }; - props: { [key: string]: any}; - parentRoute: ?Route; - nextRoute: ?Route; - _stack: Array; - delegate:RouterDelegate; - - set stack(stack:Array) { - if (!stack || !stack.length) { - throw new Error("Cannot be set to empty stack"); - } - this._stack = stack; - } - - get stack():Array { - return this._stack; - } - - get currentRoute():Route { - return this.routes[this._stack[this._stack.length-1]]; - } - - get previousRoute():Route { - if (this._stack.length > 1){ - return this.routes[this._stack[this._stack.length-2]]; - } else { - return null; - } - } - - constructor(routes: Array<{ [key: string]: any}>, - schemas:Array<{ [key: string]: any}> = [], - stack:Array = null, props:{ [key: string]: any} = {}){ - this.schemas = {}; - this.routes = {}; - this.pop = this.pop.bind(this); - this.route = this.route.bind(this); - this.delegate = new RouterDelegate(); - if (!routes || !routes.length){ - throw new Error("routes is not defined"); - } - this.props = props; - this.name = props && props.name; - this.parentRoute = props && props.route; - if (this.parentRoute){ - if (this.parentRoute.parent){ - // copy parent schemas - Object.keys(this.parentRoute.parent.schemas).forEach(el=> - this._addSchema(this.parentRoute.parent.schemas[el].name, this.parentRoute.parent.schemas[el])); - } - this.parentRoute.childRouter = this; - } - - - schemas.forEach(el=>this._addSchema(el.name, el)); - let selected = null; - routes.forEach(el=>{if (el.initial) selected = el.name;this._addRoute(el.name, el)}); - - // select first one as initial - if (!stack || !stack.length){ - stack = [selected || routes[0].name]; - } - this.stack = stack; - // add actions - this._addActions(); - } - - _addSchema(name: string, props:{ [key: string]: any}){ - if (!name){ - throw new Error("Schema name is not defined"); - } - if (this.schemas[name]){ - throw new Error("Schema="+name+" is not unique!"); - } - this.schemas[name] = props; - } - - _addRoute(routeName: string, props:{ [key: string]: any}){ - if (!routeName){ - throw new Error("Route name is not defined"); - } - const schemaName: string = props.schema || 'default'; - const schema = this.schemas[schemaName] || {}; - // pass router data to inner routes - const {children, name, header, footer, showNavigationBar, route, component, hideNavBar, sceneConfig, type, ...routerProps} = this.props; - const routeProps = Object.assign({}, schema, routerProps, props); - - if (this.routes[routeName]){ - throw new Error("Route="+routeName+" is not unique!"); - } - - this.routes[routeName] = new Route(routeProps, this); - - } - - _addActions(){ - if (!Actions.currentRouter){ - Actions.currentRouter = this; - debug("Set current router:"+this.name); - } - Object.keys(this.routes).forEach(name=>{ - if (!Actions[name]){ - Actions[name] = function(data){ - return Actions.route(name, data); - } - } - }); - } - - route(name: string, props:{ [key: string]: any} = {}){ - if (!this.routes[name]){ - throw new Error("No route is defined for name="+name); - } - const type = props.type ? props.type : this.routes[name].type; - const action = type === "switch" ? "jump": type; - if (!action){ - throw new Error("No type is defined for name="+name); - } - if (!this["_"+action]){ - throw new Error("No type="+action+" is supported"); - } - this.nextRoute = this.routes[name]; - - const handler = "on"+capitalizeFirstLetter(action); - if (this.delegate[handler]) { - debug("Run handler "+handler); - const res:boolean = this.delegate[handler](this.routes[name], props); - if (!res) { - console.log("Ignore "+action+", handler returns false"); - return false; - } - } else { - throw new Error("No handler "+handler+" for route="+name); - } - this["_"+action](name, props); - return true; - } - - _modal(name:string, props:{ [key: string]: any} ) { - } - - _push(name:string, props:{ [key: string]: any} ){ - this._stack.push(name); + constructor(props){ + super(props); + const createRouter = props.createRouter || this.createRouter; + this.router = createRouter(props); } - _replace(name:string, props:{ [key: string]: any} ) { - this._stack[this._stack.length - 1] = name; - } - /*** - * Reset every scene with an array of routes - * @param route defined route - */ - _reset(name:string, props:{ [key: string]: any} ) { - this._stack = [name]; + 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 BaseRouter(routes, schemas, props.initialRoutes || (props.initial && [props.initial]), props); } - _jump(name:string, props:{ [key: string]: any} ) { - if (this._stack.indexOf(name) != -1){ - const pos = this._stack.indexOf(name); - // swap latest with found element (because currentRoute always points to latest) - this._stack[pos] = this._stack[this._stack.length - 1]; - this._stack[this._stack.length - 1] = name; - } else { - this._stack.push(name); - } + componentDidMount(){ + this.router.delegate = this.refs.router; } - pop(num: number = 1, props:{ [key: string]: any} = {}){ - if (this._stack.length <= num){ - return false; - } - this.nextRoute = null; - if (this.delegate.onPop && this.delegate.onPop(num, this.currentRoute, props)){ - const routes = this._stack.splice(-num, num); - return true; - } - return false; + render(){ + const Component = this.props.plugin || ExRouter; + return (); } - - dismiss() { - return this.delegate.onDismiss && this.delegate.onDismiss(); - } - - -} - -function capitalizeFirstLetter(string) { - return string.charAt(0).toUpperCase() + string.slice(1); } diff --git a/RouterIOS.js b/RouterIOS.js index 1eae6a6fd..27d748462 100644 --- a/RouterIOS.js +++ b/RouterIOS.js @@ -8,13 +8,13 @@ */ import React from 'react-native'; -import Router from './Router'; +import BaseRouter from './BaseRouter'; import Route from './Route'; import * as Components from './Common'; import ExNavigator from '@exponent/react-native-navigator'; import Animations from './Animations'; const {TouchableOpacity, NavigatorIOS, StyleSheet, View, Text} = React; -import ReactRouter from './ReactRouter'; +import Router from './Router'; class RouteIOS extends React.Component { constructor(props){ @@ -25,9 +25,9 @@ class RouteIOS extends React.Component { const Component = this.route.component; const child = Component ? !this.route.wrapRouter ? : - + - + : React.cloneElement(React.Children.only(this.route.children), {...this.route.props, data:this.props, route:this.route}); @@ -38,7 +38,7 @@ class RouteIOS extends React.Component { } } export default class RouterIOS extends React.Component { - router: Router; + router: BaseRouter; constructor(props){ super(props); diff --git a/index.js b/index.js index 41425f91c..cdbd78ef7 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,7 @@ import {Schema, Route} from './Common'; import TabBar from './TabBar'; import Actions from './Actions'; -import Router from './ReactRouter'; +import Router from './Router'; import Animations from './Animations'; module.exports = {Schema, Route, TabBar, Actions, Router, Animations}; \ No newline at end of file