├── .eslintignore ├── .babelrc ├── .gitignore ├── .github └── preview.gif ├── examples ├── main │ ├── Layout.jsx │ ├── Login.jsx │ ├── Home.jsx │ ├── Account.jsx │ ├── index.css │ └── index.js └── index.html ├── .travis.yml ├── test ├── helper.js └── functional │ ├── Route_spec.js │ ├── index_spec.js │ └── util_spec.js ├── rollup.config.js ├── CHANGELOG.md ├── src ├── Route.jsx ├── util.js └── index.js ├── .eslintrc ├── webpack.config.js ├── Guids.md ├── package.json ├── README.md └── dist ├── react-rainie-router.min.js ├── react-rainie-router.min.js.map ├── react-rainie-router.js └── react-rainie-router.js.map /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/* 2 | node_modules/* 3 | test/* 4 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "presets": ["es2015-loose", "stage-0", "react"] 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | examples/__build__/ 3 | .nyc_output/ 4 | coverage/ 5 | -------------------------------------------------------------------------------- /.github/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanjingling0510/react-rainie-router/HEAD/.github/preview.gif -------------------------------------------------------------------------------- /examples/main/Layout.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from '../../src/index.js'; 3 | 4 | 5 | export default function Layout({path, children}) { 6 | return children; 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - stable 5 | cache: 6 | directories: 7 | - node_modules 8 | before_install: 9 | - export CHROME_BIN=chromium-browser 10 | - export DISPLAY=:99.0 11 | - sh -e /etc/init.d/xvfb start 12 | after_success: npm run coverage 13 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/main/Login.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from '../../src/index.js'; 3 | 4 | 5 | export default function Login({url}) { 6 | return ( 7 |
8 |

Welcome to my login

9 |

current link: {url}

10 | go homepage 11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /examples/main/Home.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from '../../src/index.js'; 3 | 4 | 5 | export default function Home({url, title}) { 6 | return ( 7 |
8 |

Welcome to my {title}

9 |

current link: {url}

10 | go account 11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /examples/main/Account.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from '../../src/index.js'; 3 | 4 | 5 | export default function Account({url, matches, name}) { 6 | return ( 7 |
8 |

Welcome to my Account

9 |

my ID is : {matches.id}

10 |

my name is : {name}

11 |
current link: {url}
12 | go homepage 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /test/helper.js: -------------------------------------------------------------------------------- 1 | require('babel-polyfill'); 2 | var jsdom = require('jsdom').jsdom; 3 | var exposedProperties = ['window', 'navigator', 'document']; 4 | 5 | global.document = jsdom(''); 6 | global.window = document.defaultView; 7 | Object.keys(document.defaultView).forEach((property) => { 8 | if (typeof global[property] === 'undefined') { 9 | exposedProperties.push(property); 10 | global[property] = document.defaultView[property]; 11 | } 12 | }); 13 | 14 | global.navigator = { 15 | userAgent: 'node.js' 16 | }; 17 | -------------------------------------------------------------------------------- /examples/main/index.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | .home, .account { 7 | position: absolute; 8 | top: 0; 9 | bottom: 0; 10 | left: 0; 11 | right: 0; 12 | display: flex; 13 | flex-direction: column; 14 | align-items: center; 15 | justify-content: center; 16 | text-align: center; 17 | } 18 | 19 | h2 { 20 | color: #fff; 21 | } 22 | 23 | .home { 24 | background: #00BCD4; 25 | } 26 | 27 | .home .active { 28 | color: red; 29 | } 30 | 31 | .account { 32 | background: #68cf52; 33 | } 34 | -------------------------------------------------------------------------------- /examples/main/index.js: -------------------------------------------------------------------------------- 1 | import './index.css'; 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import Router, { Link, listenBefore, Route } from '../../src/index.js'; 5 | import Login from './Login'; 6 | import Layout from './Layout'; 7 | /** Stateless app */ 8 | const App = () => ( 9 |
10 | 11 | 12 | 15 | 16 | { 20 | return new Promise(function(resolve, reject) { 21 | require.ensure([], (require) => { 22 | const Home = require('./Home').default; 23 | resolve(Home); 24 | }, 'home'); 25 | }); 26 | }} /> 27 | { 31 | return new Promise(function(resolve, reject) { 32 | require.ensure([], (require) => { 33 | const Account = require('./Account').default; 34 | resolve(Account); 35 | }, 'account'); 36 | }); 37 | }} /> 38 | 39 | 40 | 41 | 42 | 43 |
44 | ); 45 | 46 | ReactDOM.render(, document.getElementById('react-container')); 47 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import json from 'rollup-plugin-json'; 3 | import memory from 'rollup-plugin-memory'; 4 | import resolve from 'rollup-plugin-node-resolve'; 5 | import cjs from 'rollup-plugin-commonjs'; 6 | import fs from 'fs'; 7 | 8 | var babelRc = JSON.parse(fs.readFileSync('.babelrc','utf8')); // eslint-disable-line 9 | 10 | export default { 11 | entry: 'src/index.js', 12 | exports: 'default', 13 | plugins: [ 14 | json(), 15 | memory({ 16 | path: 'src/index', 17 | contents: "export { default } from './index'" 18 | }), 19 | babel({ 20 | babelrc: false, 21 | presets: ['es2015-minimal-rollup'].concat(babelRc.presets.slice(1)), 22 | plugins: babelRc.plugins, 23 | exclude: 'node_modules/**' 24 | }), 25 | // cjs({ 26 | // exclude: 'node_modules/process-es6/**', 27 | // include: [ 28 | // 'node_modules/fbjs/**', 29 | // 'node_modules/object-assign/**', 30 | // 'node_modules/react/**', 31 | // 'node_modules/react-dom/**' 32 | // ] 33 | // }), 34 | // resolve({ 35 | // jsnext: true, 36 | // main: true 37 | // }) 38 | ], 39 | }; 40 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | v1.1.0 - Mon, 05 Dec 2016 11:41:35 GMT 2 | -------------------------------------- 3 | 4 | - 5 | 6 | 7 | v1.0.9 - Mon, 05 Dec 2016 11:37:17 GMT 8 | -------------------------------------- 9 | 10 | - 11 | 12 | 13 | v1.0.8 - Mon, 05 Dec 2016 10:09:55 GMT 14 | -------------------------------------- 15 | 16 | - 17 | 18 | 19 | v1.0.6 - Mon, 05 Sep 2016 08:33:24 GMT 20 | -------------------------------------- 21 | 22 | - 23 | 24 | 25 | v1.0.5 - Mon, 05 Sep 2016 08:31:50 GMT 26 | -------------------------------------- 27 | 28 | - 29 | 30 | 31 | v1.0.5 - Mon, 05 Sep 2016 08:25:41 GMT 32 | -------------------------------------- 33 | 34 | - 35 | 36 | 37 | v1.0.4 - Mon, 05 Sep 2016 08:25:25 GMT 38 | -------------------------------------- 39 | 40 | - 41 | 42 | 43 | v1.0.4 - Mon, 05 Sep 2016 08:24:43 GMT 44 | -------------------------------------- 45 | 46 | - 47 | 48 | 49 | v1.0.3 - Mon, 05 Sep 2016 05:41:30 GMT 50 | -------------------------------------- 51 | 52 | - [150f351](../../commit/150f351) [added] ✅ add Link property of "activeClassName" 53 | 54 | 55 | v1.0.2 - Mon, 05 Sep 2016 03:22:52 GMT 56 | -------------------------------------- 57 | 58 | - [6b53bf9](../../commit/6b53bf9) [added] ✅ add Link property of "to" 59 | 60 | 61 | v1.0.1 - Sun, 04 Sep 2016 08:41:38 GMT 62 | -------------------------------------- 63 | 64 | - -------------------------------------------------------------------------------- /src/Route.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function isPromise(obj) { 4 | return !!obj && 5 | (typeof obj === 'object' || typeof obj === 'function') && 6 | typeof obj.then === 'function'; 7 | } 8 | 9 | 10 | class Route extends React.Component { 11 | static PropTypes = { 12 | component: React.PropTypes.node, 13 | getComponent: React.PropTypes.func, 14 | } 15 | 16 | state = { 17 | SplitComponent: undefined, 18 | } 19 | 20 | componentWillMount() { 21 | const { component, getComponent} = this.props; 22 | if (!component && getComponent) { 23 | const promise = getComponent(); 24 | if (isPromise(promise)) { 25 | promise.then(SplitComponent => { 26 | this.setState({ SplitComponent }); 27 | }); 28 | } 29 | } 30 | } 31 | 32 | render () { 33 | const { 34 | component: RoutedComponent, 35 | getComponent, 36 | ..._props, 37 | } = this.props; 38 | 39 | const { SplitComponent } = this.state; 40 | 41 | 42 | if (RoutedComponent) 43 | return ; 44 | 45 | return SplitComponent ? 46 | : null; 47 | } 48 | } 49 | 50 | export default Route; 51 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "parser": "babel-eslint", 4 | "extends": "eslint:recommended", 5 | "plugins": [ 6 | "react" 7 | ], 8 | "env": { 9 | "browser": true, 10 | "mocha": true, 11 | "es6": true, 12 | "node": true 13 | }, 14 | "parserOptions": { 15 | "ecmaFeatures": { 16 | "modules": true, 17 | "jsx": true 18 | } 19 | }, 20 | "globals": { 21 | "sinon": true, 22 | "expect": true 23 | }, 24 | "rules": { 25 | "react/jsx-uses-react": 2, 26 | "react/jsx-uses-vars": 2, 27 | "no-unused-vars": [1, { "varsIgnorePattern": "^h$" }], 28 | "no-cond-assign": 1, 29 | "no-empty": 0, 30 | "no-console": 1, 31 | "semi": 2, 32 | "camelcase": 0, 33 | "comma-style": 2, 34 | "comma-dangle": 0, 35 | "indent": [2, 4, {"SwitchCase": 1}], 36 | "no-mixed-spaces-and-tabs": [2, "smart-tabs"], 37 | "no-trailing-spaces": [2, { "skipBlankLines": true }], 38 | "max-nested-callbacks": [2, 5], 39 | "no-eval": 2, 40 | "no-implied-eval": 2, 41 | "no-new-func": 2, 42 | "guard-for-in": 2, 43 | "eqeqeq": 0, 44 | "no-else-return": 2, 45 | "no-redeclare": 2, 46 | "no-dupe-keys": 2, 47 | "radix": 2, 48 | "strict": [2, "never"], 49 | "no-shadow": 0, 50 | "callback-return": [1, ["callback", "cb", "next", "done"]], 51 | "no-delete-var": 2, 52 | "no-undef-init": 2, 53 | "no-shadow-restricted-names": 2, 54 | "handle-callback-err": 0, 55 | "no-lonely-if": 2, 56 | "keyword-spacing": 2, 57 | "constructor-super": 2, 58 | "no-this-before-super": 2, 59 | "no-dupe-class-members": 2, 60 | "no-const-assign": 2, 61 | "prefer-spread": 2, 62 | "no-useless-concat": 2, 63 | "no-var": 2, 64 | "object-shorthand": 2, 65 | "prefer-arrow-callback": 2 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var webpack = require('webpack'); 5 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 6 | var EXAMPLES_DIR = path.resolve(__dirname, 'examples'); 7 | 8 | 9 | module.exports = { 10 | entry: buildEntries(), 11 | resolve: { 12 | extensions: ['', '.js', '.jsx'] 13 | }, 14 | module: { 15 | loaders: [{ 16 | test: /\.jsx?$/, 17 | loaders: ['babel'], 18 | exclude: /node_modules/, 19 | }, { 20 | test: /\.css$/, 21 | loader: 'style!css' 22 | }], 23 | }, 24 | output: { 25 | path: 'examples/__build__', 26 | publicPath: '/', 27 | filename: '[name].js', 28 | chunkFilename: "[name].chunk.js" 29 | }, 30 | devTool: 'eval-source-map', 31 | devServer: { 32 | historyApiFallback: true, 33 | hot: true, 34 | inline: true, 35 | progress: true, 36 | host: '0.0.0.0', 37 | }, 38 | plugins: [ 39 | new HtmlWebpackPlugin({ 40 | title: 'example', 41 | template: 'examples/index.html', // Load a custom template 42 | inject: 'body' // Inject all scripts into the body 43 | }), 44 | new webpack.DefinePlugin({ 45 | 'process.env.NODE_ENV': '"development"' 46 | }), 47 | new webpack.HotModuleReplacementPlugin(), 48 | ] 49 | }; 50 | 51 | 52 | function buildEntries() { 53 | return fs.readdirSync(EXAMPLES_DIR).reduce(function (a, b) { 54 | if (b === '__build__') { 55 | return a; 56 | } 57 | 58 | var isDraft = b.charAt(0) === '_'; 59 | 60 | if (!isDraft && isDirectory(path.join(EXAMPLES_DIR, b))) { 61 | a[b] = path.join(EXAMPLES_DIR, b, 'index.js'); 62 | } 63 | 64 | return a; 65 | }, {}); 66 | } 67 | 68 | 69 | function isDirectory(dir) { 70 | return fs.lstatSync(dir).isDirectory(); 71 | } 72 | -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | const EMPTY = {}; 2 | 3 | export function exec(url, route, opts = EMPTY) { 4 | let reg = /(?:\?([^#]*))?(#.*)?$/, 5 | c = url.match(reg), 6 | matches = {}, 7 | ret; 8 | if (c && c[1]) { 9 | let p = c[1].split('&'); 10 | for (let i = 0; i < p.length; i++) { 11 | let r = p[i].split('='); 12 | matches[decodeURIComponent(r[0])] = decodeURIComponent(r.slice(1).join('=')); 13 | } 14 | } 15 | url = segmentize(url.replace(reg, '')); 16 | route = segmentize(route || ''); 17 | let max = Math.max(url.length, route.length); 18 | for (let i = 0; i < max; i++) { 19 | if (route[i] && route[i].charAt(0) === ':') { 20 | let param = route[i].replace(/(^\:|[+*?]+$)/g, ''), 21 | flags = (route[i].match(/[+*?]+$/) || EMPTY)[0] || '', 22 | plus = ~flags.indexOf('+'), 23 | star = ~flags.indexOf('*'), 24 | val = url[i] || ''; 25 | if (!val && !star && (flags.indexOf('?') < 0 || plus)) { 26 | ret = false; 27 | break; 28 | } 29 | matches[param] = decodeURIComponent(val); 30 | if (plus || star) { 31 | matches[param] = url.slice(i).map(decodeURIComponent).join('/'); 32 | break; 33 | } 34 | } else if (route[i] !== url[i]) { 35 | ret = false; 36 | break; 37 | } 38 | } 39 | if (opts.default !== true && ret === false) return false; 40 | return matches; 41 | } 42 | 43 | 44 | export function pathRankSort(a, b) { 45 | let aAttr = a.props || EMPTY, 46 | bAttr = b.props || EMPTY; 47 | if (aAttr.default) return 1; 48 | if (bAttr.default) return -1; 49 | let diff = rank(aAttr.path) - rank(bAttr.path); 50 | return diff || (aAttr.path.length - bAttr.path.length); 51 | } 52 | 53 | export function segmentize(url) { 54 | return strip(url).split('/'); 55 | } 56 | 57 | export function rank(url) { 58 | return (strip(url).match(/\/+/g) || '').length; 59 | } 60 | 61 | export function strip(url) { 62 | return url.replace(/(^\/+|\/+$)/g, ''); 63 | } 64 | -------------------------------------------------------------------------------- /Guids.md: -------------------------------------------------------------------------------- 1 | 2 | ## Router 3 | Primary component of React Router. It keeps your UI and the URL in sync. 4 | ### Props 5 | 6 | ##### `url` 7 | if not set, using `location.pathname + location.search`. 8 | 9 | ## Router.listenBefore 10 | 11 | There is an opportunity to prevent or delay routing Jump 12 | ```js 13 | 14 | // prevent routing navigate 15 | Router.listenBefore(() => { 16 | return Promise.reject(); 17 | }) 18 | 19 | // delay routing navigate 20 | Router.listenBefore(() => { 21 | return new Promise(function(resolve, reject) { 22 | setTimeout(() => { 23 | resolve(); 24 | }, 500) 25 | }); 26 | }) 27 | 28 | ``` 29 | 30 | ## Router.route 31 | 32 | dynamic routing navigate 33 | 34 | ```js 35 | const App = () => ( 36 |
37 | 38 | 39 | 40 | 41 |
42 | ); 43 | 44 | ReactDOM.render(, document.getElementById('react-container')); 45 | Router.route('/account/123'); 46 | 47 | ``` 48 | 49 | 50 | ## Router.Link 51 | The primary way to allow users to navigate around your application. will render a fully accessible anchor tag with the proper href. 52 | 53 | ```js 54 | 55 | // Given a route like : 56 | {user.name} 57 | ``` 58 | 59 | ## Router.Route 60 | A is used to declaratively map routes to your application's component hierarchy. 61 | 62 | ### Props 63 | 64 | ##### `path` 65 | The path used in the URL. 66 | 67 | ##### `component` 68 | A single component to be rendered when the route matches the URL. 69 | 70 | ##### `getComponent(callback)` 71 | Same as component but asynchronous, useful for code-splitting. 72 | 73 | ```js 74 | 75 | { 79 | return new Promise(function(resolve, reject) { 80 | require.ensure([], (require) => { 81 | const Account = require('./Account').default; 82 | resolve(Account); 83 | }, 'account'); 84 | }); 85 | }} /> 86 | ``` 87 | -------------------------------------------------------------------------------- /test/functional/Route_spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { assert, expect } from 'chai'; 3 | import sinon from 'sinon'; 4 | import { mount, shallow } from 'enzyme'; 5 | import Route from '../../src/Route.jsx'; 6 | 7 | describe('Route', () => { 8 | 9 | it('should render RoutedComponent', () => { 10 | const Component = (props) => ( 11 |
component
12 | ); 13 | 14 | const router = mount( 15 | 18 | ); 19 | expect(router.find('Component').first().prop('path')).to.equal('/path'); 20 | expect(router.find('.routed-component')).to.have.length(1); 21 | }); 22 | 23 | it ('should render null', () => { 24 | const Component = (props) => ( 25 |
component
26 | ); 27 | 28 | const router = mount( 29 | { 32 | return new Promise(function(resolve, reject) { 33 | require.ensure([], (require) => { 34 | setTimeout(() => { 35 | resolve(Component); 36 | }, 100); 37 | }, 'account'); 38 | }); 39 | }} /> 40 | ); 41 | 42 | expect(router.find('.routed-component')).to.have.length(0); 43 | }); 44 | 45 | it('should render SplitComponent', () => { 46 | const Component = (props) => ( 47 |
component
48 | ); 49 | 50 | const router = mount( 51 | { 55 | return new Promise(function(resolve, reject) { 56 | setTimeout(() => { 57 | resolve(Component); 58 | }, 100); 59 | }); 60 | }} /> 61 | ); 62 | 63 | return new Promise(function(resolve, reject) { 64 | setTimeout(() => { 65 | resolve(router.find('.routed-component')); 66 | }, 200); 67 | }).then((component => { 68 | expect(component).to.have.length(1); 69 | expect(router.find('Component').prop('path')).to.equal('/path/:id'); 70 | expect(router.find('Component').prop('name')).to.equal('rainie'); 71 | })); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /test/functional/index_spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { assert, expect } from 'chai'; 3 | import sinon from 'sinon'; 4 | import { mount, shallow } from 'enzyme'; 5 | import Router, { Link, listenBefore, Route, route } from '../../src/index.js'; 6 | 7 | 8 | describe('Router', () => { 9 | let Home, 10 | Account, 11 | Default, 12 | App, 13 | router; 14 | 15 | before (function () { 16 | Home = ({url, title}) => { 17 | return ( 18 |
19 |

Welcome to my {title}

20 |

current link: {url}

21 | go account 22 |
23 | ); 24 | } 25 | 26 | Account = ({url, matches, name}) => { 27 | return ( 28 |
29 |

Account: {matches.id}

30 |

my name is : {name}

31 |
current link: {url}
32 | go homepage 33 |
34 | ); 35 | } 36 | 37 | Default = () => ( 38 |
39 |

default

40 | go homepage 41 |
42 | ) 43 | 44 | 45 | 46 | router = mount( 47 | 49 | 50 | 51 | 52 | 53 | ); 54 | }); 55 | 56 | 57 | it('should jump to another default route', () => { 58 | expect(router.find('.default')).to.have.length(1); 59 | }); 60 | 61 | 62 | it('should jump to home router', () => { 63 | const spyFunction1 = sinon.spy(Router.prototype, 'shouldComponentUpdate'); 64 | router.instance().routeTo('/'); 65 | sinon.assert.calledOnce(spyFunction1); 66 | spyFunction1.restore(); 67 | expect(router.find('.home')).to.have.length(1); 68 | }); 69 | 70 | it('should jump to account router, and get param id', () => { 71 | router.instance().routeTo('/account/123'); 72 | expect(router.find('Account')).to.have.length(1); 73 | expect(router.find('Account').props().matches).to.eql({ id: '123' }); 74 | }); 75 | 76 | it ('should not jump to home router', () => { 77 | listenBefore(() => { 78 | return Promise.reject(); 79 | }); 80 | route('/'); 81 | expect(router.find('.home')).to.have.length(0); 82 | }) 83 | 84 | 85 | }); 86 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-rainie-router", 3 | "version": "1.1.0", 4 | "description": "", 5 | "amdName": "reactRainieRouter", 6 | "main": "dist/react-rainie-router.js", 7 | "minified:main": "dist/react-rainie-router.min.js", 8 | "scripts": { 9 | "clean": "rm -rf dist/", 10 | "build": "npm-run-all clean transpile minify size", 11 | "transpile": "rollup -c rollup.config.js -m ${npm_package_main}.map -f umd -n $npm_package_amdName -o $npm_package_main", 12 | "minify": "uglifyjs $npm_package_main -cm -o $npm_package_minified_main -p relative --in-source-map ${npm_package_main}.map --source-map ${npm_package_minified_main}.map", 13 | "size": "size=$(gzip-size $npm_package_minified_main | pretty-bytes) && echo \"gzip size: $size\"", 14 | "start": "webpack-dev-server", 15 | "release": "npm run build && node_modules/.bin/release", 16 | "preview-release": "changelog -t preview -s", 17 | "mocha": "./node_modules/.bin/mocha --compilers js:babel-core/register --require ./test/helper.js --recursive", 18 | "test": "./node_modules/.bin/nyc npm run mocha", 19 | "coverage": "./node_modules/.bin/nyc report --reporter=text-lcov | ./node_modules/.bin/coveralls" 20 | }, 21 | "author": "", 22 | "license": "ISC", 23 | "devDependencies": { 24 | "babel-core": "^6.14.0", 25 | "babel-eslint": "^6.1.2", 26 | "babel-loader": "^6.2.4", 27 | "babel-polyfill": "^6.13.0", 28 | "babel-preset-es2015": "^6.14.0", 29 | "babel-preset-es2015-loose": "^7.0.0", 30 | "babel-preset-es2015-minimal-rollup": "^2.0.0", 31 | "babel-preset-react": "^6.11.1", 32 | "babel-preset-stage-0": "^6.5.0", 33 | "chai": "^3.5.0", 34 | "coveralls": "^2.11.9", 35 | "css-loader": "^0.24.0", 36 | "enzyme": "^2.4.1", 37 | "eslint": "^3.3.1", 38 | "eslint-config-airbnb": "^10.0.1", 39 | "eslint-plugin-import": "^1.13.0", 40 | "eslint-plugin-jsx-a11y": "^2.1.0", 41 | "eslint-plugin-react": "^6.1.2", 42 | "gzip-size-cli": "^1.0.0", 43 | "html-webpack-plugin": "^2.21.0", 44 | "jsdom": "^9.8.3", 45 | "mocha": "2.3.3", 46 | "mocha-lcov-reporter": "^1.2.0", 47 | "npm-run-all": "^3.0.0", 48 | "nyc": "^8.1.0", 49 | "pretty-bytes-cli": "^2.0.0", 50 | "react": "^15.3.1", 51 | "react-addons-test-utils": "^15.3.1", 52 | "react-dom": "^15.3.1", 53 | "rf-release": "^0.4.0", 54 | "rollup": "^0.36.4", 55 | "rollup-plugin-babel": "^2.6.1", 56 | "rollup-plugin-commonjs": "^3.3.1", 57 | "rollup-plugin-json": "^2.0.1", 58 | "rollup-plugin-memory": "^2.0.0", 59 | "rollup-plugin-node-globals": "^1.0.6", 60 | "rollup-plugin-node-resolve": "2.0.0", 61 | "rollup-plugin-replace": "^1.1.1", 62 | "sinon": "^1.17.4", 63 | "style-loader": "^0.13.1", 64 | "uglify-js": "^2.7.3", 65 | "webpack": "^1.13.1", 66 | "webpack-dev-server": "^1.14.1" 67 | } 68 | } -------------------------------------------------------------------------------- /test/functional/util_spec.js: -------------------------------------------------------------------------------- 1 | import { exec, pathRankSort, segmentize, rank, strip } from '../../src/util'; 2 | import { expect } from 'chai'; 3 | 4 | describe('util', () => { 5 | describe('strip', () => { 6 | it('should strip preceeding slashes', () => { 7 | expect(strip('')).to.equal(''); 8 | expect(strip('/')).to.equal(''); 9 | expect(strip('/a')).to.equal('a'); 10 | expect(strip('//a')).to.equal('a'); 11 | expect(strip('//a/')).to.equal('a'); 12 | }); 13 | 14 | it('should strip trailing slashes', () => { 15 | expect(strip('')).to.equal(''); 16 | expect(strip('/')).to.equal(''); 17 | expect(strip('a/')).to.equal('a'); 18 | expect(strip('/a//')).to.equal('a'); 19 | }); 20 | }); 21 | 22 | describe('rank', () => { 23 | it('should return number of path segments', () => { 24 | expect(rank('')).to.equal(0); 25 | expect(rank('/')).to.equal(0); 26 | expect(rank('//')).to.equal(0); 27 | expect(rank('a/b/c')).to.equal(2); 28 | expect(rank('/a/b/c/')).to.equal(2); 29 | }); 30 | }); 31 | 32 | describe('segmentize', () => { 33 | it('should split path on slashes', () => { 34 | expect(segmentize('')).to.eql(['']); 35 | expect(segmentize('/')).to.eql(['']); 36 | expect(segmentize('//')).to.eql(['']); 37 | expect(segmentize('a/b/c')).to.eql(['a','b','c']); 38 | expect(segmentize('/a/b/c/')).to.eql(['a','b','c']); 39 | }); 40 | }); 41 | 42 | describe('pathRankSort', () => { 43 | it('should sort by segment count', () => { 44 | let paths = arr => arr.map( path => ({props:{path}}) ); 45 | 46 | expect( 47 | paths(['/a/b/','/a/b','/','b']).sort(pathRankSort) 48 | ).to.eql( 49 | paths(['/','b','/a/b','/a/b/']) 50 | ); 51 | }); 52 | 53 | it('should return default routes last', () => { 54 | let paths = arr => arr.map( path => ({props:{path}}) ); 55 | 56 | let defaultPath = {props:{default:true}}; 57 | let p = paths(['/a/b/','/a/b','/','b']); 58 | p.splice(2,0,defaultPath); 59 | 60 | expect( 61 | p.sort(pathRankSort) 62 | ).to.eql( 63 | paths(['/','b','/a/b','/a/b/']).concat(defaultPath) 64 | ); 65 | }); 66 | }); 67 | 68 | describe('exec', () => { 69 | it('should match explicit equality', () => { 70 | expect(exec('/','/')).to.eql({}); 71 | expect(exec('/a','/a')).to.eql({}); 72 | expect(exec('/a','/b')).to.eql(false); 73 | expect(exec('/a/b','/a/b')).to.eql({}); 74 | expect(exec('/a/b','/a/a')).to.eql(false); 75 | expect(exec('/a/b','/b/b')).to.eql(false); 76 | }); 77 | 78 | it('should match param segments', () => { 79 | expect(exec('/', '/:foo')).to.eql(false); 80 | expect(exec('/bar', '/:foo')).to.eql({ foo:'bar' }); 81 | }); 82 | 83 | it('should match optional param segments', () => { 84 | expect(exec('/', '/:foo?')).to.eql({ foo:'' }); 85 | expect(exec('/bar', '/:foo?')).to.eql({ foo:'bar' }); 86 | expect(exec('/', '/:foo?/:bar?')).to.eql({ foo:'', bar:'' }); 87 | expect(exec('/bar', '/:foo?/:bar?')).to.eql({ foo:'bar', bar:'' }); 88 | expect(exec('/bar', '/:foo?/bar')).to.eql(false); 89 | expect(exec('/foo/bar', '/:foo?/bar')).to.eql({ foo:'foo' }); 90 | }); 91 | 92 | it('should match splat param segments', () => { 93 | expect(exec('/', '/:foo*')).to.eql({ foo:'' }); 94 | expect(exec('/a', '/:foo*')).to.eql({ foo:'a' }); 95 | expect(exec('/a/b', '/:foo*')).to.eql({ foo:'a/b' }); 96 | expect(exec('/a/b/c', '/:foo*')).to.eql({ foo:'a/b/c' }); 97 | }); 98 | 99 | it('should match required splat param segments', () => { 100 | expect(exec('/', '/:foo+')).to.eql(false); 101 | expect(exec('/a', '/:foo+')).to.eql({ foo:'a' }); 102 | expect(exec('/a/b', '/:foo+')).to.eql({ foo:'a/b' }); 103 | expect(exec('/a/b/c', '/:foo+')).to.eql({ foo:'a/b/c' }); 104 | }); 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-rainie-router 2 | 3 | [![Travis][build-badge]][build] [![npm package][npm-badge]][npm] [![Coveralls][coveralls-badge]][coveralls] 4 | 5 | **It has a similar and simple features for react-router,react-rainie-router is a fast, 2kb alternative to react-router.** 6 | 7 | react-rainie-router provides a component that conditionally renders its children when the URL matches their path. It also automatically wires up elements up to the router. 8 | 9 |
10 | 11 |
12 | 13 | 14 | ## Features 15 | 16 | - It is only 2k!, We only use our frequently used functions. Including Route Mathing, Nesting, default routing, Link, dynamic routing... 17 | - We can delay or prevent routing navigate by `listenBefore` before routing jump to another. And `listenBefore` should return a promise object. 18 | - It's no limit to the number of `Router` component nested other component. 19 | - Support coding Spliting by `getComponent` from `Route` component. 20 | - It does not depend on `history`, but using html5 history API. 21 | 22 | ## Docs & Help 23 | 24 | * [Guides and Api docs](Guids.md) 25 | * [Changelog](CHANGELOG.md) 26 | 27 | 28 | 29 | ## Getting Started 30 | ### Install 31 | 32 | Using [npm](https://www.npmjs.com/): 33 | 34 | $ npm install react-rainie-router --save 35 | 36 | ### Import what you need 37 | 38 | The following guide assumes you have some sort of ES2015 build set up using babel and/or webpack/browserify/gulp/grunt/etc. 39 | 40 | ```js 41 | import React from 'react'; 42 | import ReactDOM from 'react-dom'; 43 | import Router, { Link, listenBefore, Route } from 'react-rainie-router'; 44 | ``` 45 | 46 | ### Usage Example 47 | 48 | ```js 49 | 50 | /** Stateless app */ 51 | 52 | function Home({url, title}) { 53 | return ( 54 |
55 |

Welcome to my {title}

56 |

current link: {url}

57 | go account 58 |
59 | ); 60 | } 61 | 62 | function Account({url, matches, name}) { 63 | return ( 64 |
65 |

Account: {matches.id}

66 |

my name is : {name}

67 |
current link: {url}
68 | go homepage 69 |
70 | ); 71 | } 72 | 73 | const App = () => ( 74 |
75 | 76 | 77 | 78 | 79 |
80 | ); 81 | 82 | ReactDOM.render(, document.getElementById('react-container')); 83 | 84 | ``` 85 | 86 | ## How to Contribute 87 | 88 | Anyone and everyone is welcome to contribute to this project. The best way to 89 | start is by checking our [open issues](https://github.com/lanjingling0510/react-rainie-router/issues), 90 | [submit a new issues](https://github.com/lanjingling0510/react-rainie-router/issues/new?labels=bug) or 91 | [feature request](https://github.com/lanjingling0510/react-rainie-router/issues/new?labels=enhancement), 92 | participate in discussions, upvote or downvote the issues you like or dislike. 93 | 94 | 95 | 96 | [npm-badge]: https://img.shields.io/npm/v/react-rainie-router.svg?style=flat-square 97 | [npm]: https://www.npmjs.com/package/react-rainie-router 98 | [build-badge]: https://img.shields.io/travis/lanjingling0510/react-rainie-router/master.svg?style=flat-square 99 | [build]: https://travis-ci.org/lanjingling0510/react-rainie-router 100 | [coveralls-badge]: https://img.shields.io/coveralls/lanjingling0510/react-rainie-router.svg?style=flat-square 101 | [coveralls]: https://coveralls.io/github/lanjingling0510/react-rainie-router 102 | -------------------------------------------------------------------------------- /dist/react-rainie-router.min.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("react")):"function"==typeof define&&define.amd?define(["react"],e):t.reactRainieRouter=e(t.React)}(this,function(t){"use strict";function e(t,e){var n=arguments.length<=2||void 0===arguments[2]?v:arguments[2],r=/(?:\?([^#]*))?(#.*)?$/,i=t.match(r),u={},a=void 0;if(i&&i[1])for(var p=i[1].split("&"),c=0;c=0||Object.prototype.hasOwnProperty.call(t,o)&&(n[o]=t[o]);return n},O=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e},R=function(e){function n(){var t,o,r;y(this,n);for(var i=arguments.length,u=Array(i),a=0;a func(url))) 38 | .then(() => { 39 | if (typeof url !== 'string' && url.url) { 40 | replace = url.replace; 41 | url = url.url; 42 | } 43 | setUrl(url, replace 44 | ? 'replace' 45 | : 'push'); 46 | return routeTo(url); 47 | }).catch(() => {}); 48 | } 49 | 50 | function routeTo(url) { 51 | let didRoute = false; 52 | ROUTERS.forEach(router => { 53 | if (router.routeTo(url) === true) { 54 | didRoute = true; 55 | } 56 | }); 57 | return didRoute; 58 | } 59 | 60 | function routeFromLink(node) { 61 | // only valid elements 62 | if (!node || !node.getAttribute) 63 | return; 64 | 65 | let href = node.getAttribute('href') || node.getAttribute('to'), 66 | target = node.getAttribute('target'); 67 | 68 | // ignore links with targets and non-path URLs 69 | if (!href || !href.match(/^\//g) || (target && !target.match(/^_?self$/i))) 70 | return; 71 | 72 | // attempt to route, if no match simply cede control to browser 73 | return route(href); 74 | } 75 | 76 | function handleLinkClick(delay) { 77 | return e => _handleLinkClick(e, delay); 78 | } 79 | 80 | function _handleLinkClick(e, delay) { 81 | const target = e.currentTarget || e.target || this; 82 | if (e.button !== 0) 83 | return; 84 | 85 | if (typeof delay === 'number') { 86 | // access the event properties in an asynchronous way 87 | e.persist(); 88 | setTimeout(() => routeFromLink(target), delay); 89 | return prevent(e); 90 | } 91 | 92 | routeFromLink(target); 93 | return prevent(e); 94 | } 95 | 96 | function prevent(e) { 97 | if (e) { 98 | if (e.stopImmediatePropagation) 99 | e.stopImmediatePropagation(); 100 | if (e.stopPropagation) 101 | e.stopPropagation(); 102 | e.preventDefault(); 103 | } 104 | return false; 105 | } 106 | 107 | if (typeof addEventListener === 'function') { 108 | addEventListener('popstate', () => routeTo(getCurrentUrl())); 109 | } 110 | 111 | class Router extends React.Component { 112 | constructor(props) { 113 | super(props); 114 | if (props.history) { 115 | customHistory = props.history; 116 | } 117 | 118 | this.state = { 119 | url: this.props.url || getCurrentUrl() 120 | }; 121 | } 122 | 123 | shouldComponentUpdate(props, state) { 124 | return props.url !== this.props.url || 125 | props.onChange !== this.props.onChange || 126 | state.url !== this.state.url; 127 | } 128 | 129 | componentWillMount() { 130 | ROUTERS.push(this); 131 | } 132 | 133 | componentWillUnmount() { 134 | ROUTERS.splice(ROUTERS.indexOf(this), 1); 135 | } 136 | 137 | routeTo(url) { 138 | this._didRoute = false; 139 | this.setState({url}); 140 | return this._didRoute; 141 | } 142 | 143 | render() { 144 | const {children, onChange} = this.props; 145 | const {url} = this.state; 146 | let routeElement = null; 147 | 148 | const childrenElements = React.Children 149 | .toArray(children) 150 | .sort(pathRankSort); 151 | 152 | for (let i = 0; i < childrenElements.length; i++) { 153 | let child = childrenElements[i], 154 | props = child.props, 155 | path = props.path, 156 | matches = exec(url, path, props); 157 | if (matches) { 158 | routeElement = React.cloneElement( 159 | child, 160 | {...child.props, url, matches} 161 | ); 162 | 163 | this._didRoute = true; 164 | break; 165 | } 166 | } 167 | 168 | 169 | let previous = this.previousUrl; 170 | if (url !== previous) { 171 | this.previousUrl = url; 172 | if (typeof onChange === 'function') { 173 | onChange({ url, previous, router: this }); 174 | } 175 | } 176 | return routeElement; 177 | } 178 | } 179 | 180 | const Link = ({ 181 | children, 182 | activeClassName, 183 | delay, 184 | ...props 185 | }) => ( 186 |
{children} 193 | ); 194 | 195 | Router.listenBefore = listenBefore; 196 | Router.route = route; 197 | Router.Router = Router; 198 | Router.Route = Route; 199 | Router.Link = Link; 200 | 201 | export {route, Router, Route, Link, listenBefore}; 202 | export default Router; 203 | -------------------------------------------------------------------------------- /dist/react-rainie-router.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/util.js","../src/Route.jsx","../src/index.js"],"names":["exec","url","route","opts","arguments","length","undefined","EMPTY","reg","c","match","matches","ret","p","split","i","r","decodeURIComponent","slice","join","segmentize","replace","max","Math","charAt","param","flags","plus","indexOf","star","val","map","default","pathRankSort","a","b","aAttr","props","bAttr","diff","rank","path","strip","isPromise","obj","then","setUrl","type","customHistory","history","getCurrentUrl","getCurrentLocation","location","pathname","search","listenBefore","func","push","Promise","all","LISTENRS","routeTo","catch","didRoute","forEach","router","routeFromLink","node","getAttribute","href","target","handleLinkClick","delay","e","_handleLinkClick","currentTarget","this","button","persist","prevent","stopImmediatePropagation","stopPropagation","preventDefault","Route","state","componentWillMount","component","_props2","getComponent","promise","SplitComponent","setState","render","RoutedComponent","_props3","_props","objectWithoutProperties","React","createElement","Component","PropTypes","ROUTERS","addEventListener","Router","_React$Component","call","_this","shouldComponentUpdate","onChange","componentWillUnmount","splice","_didRoute","children","routeElement","childrenElements","Children","toArray","sort","child","cloneElement","_extends","previous","previousUrl","Link","_ref","activeClassName","to","className"],"mappings":"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","file":"react-rainie-router.min.js","sourcesContent":["const EMPTY = {};\n\nexport function exec(url, route, opts = EMPTY) {\n let reg = /(?:\\?([^#]*))?(#.*)?$/,\n c = url.match(reg),\n matches = {},\n ret;\n if (c && c[1]) {\n let p = c[1].split('&');\n for (let i = 0; i < p.length; i++) {\n let r = p[i].split('=');\n matches[decodeURIComponent(r[0])] = decodeURIComponent(r.slice(1).join('='));\n }\n }\n url = segmentize(url.replace(reg, ''));\n route = segmentize(route || '');\n let max = Math.max(url.length, route.length);\n for (let i = 0; i < max; i++) {\n if (route[i] && route[i].charAt(0) === ':') {\n let param = route[i].replace(/(^\\:|[+*?]+$)/g, ''),\n flags = (route[i].match(/[+*?]+$/) || EMPTY)[0] || '',\n plus = ~flags.indexOf('+'),\n star = ~flags.indexOf('*'),\n val = url[i] || '';\n if (!val && !star && (flags.indexOf('?') < 0 || plus)) {\n ret = false;\n break;\n }\n matches[param] = decodeURIComponent(val);\n if (plus || star) {\n matches[param] = url.slice(i).map(decodeURIComponent).join('/');\n break;\n }\n } else if (route[i] !== url[i]) {\n ret = false;\n break;\n }\n }\n if (opts.default !== true && ret === false) return false;\n return matches;\n}\n\n\nexport function pathRankSort(a, b) {\n let aAttr = a.props || EMPTY,\n bAttr = b.props || EMPTY;\n if (aAttr.default) return 1;\n if (bAttr.default) return -1;\n let diff = rank(aAttr.path) - rank(bAttr.path);\n return diff || (aAttr.path.length - bAttr.path.length);\n}\n\nexport function segmentize(url) {\n return strip(url).split('/');\n}\n\nexport function rank(url) {\n return (strip(url).match(/\\/+/g) || '').length;\n}\n\nexport function strip(url) {\n return url.replace(/(^\\/+|\\/+$)/g, '');\n}\n","import React from 'react';\n\nfunction isPromise(obj) {\n return !!obj &&\n (typeof obj === 'object' || typeof obj === 'function') &&\n typeof obj.then === 'function';\n}\n\n\nclass Route extends React.Component {\n static PropTypes = {\n component: React.PropTypes.node,\n getComponent: React.PropTypes.func,\n }\n\n state = {\n SplitComponent: undefined,\n }\n\n componentWillMount() {\n const { component, getComponent} = this.props;\n if (!component && getComponent) {\n const promise = getComponent();\n if (isPromise(promise)) {\n promise.then(SplitComponent => {\n this.setState({ SplitComponent });\n });\n }\n }\n }\n\n render () {\n const {\n component: RoutedComponent,\n getComponent,\n ..._props,\n } = this.props;\n\n const { SplitComponent } = this.state;\n\n\n if (RoutedComponent)\n return ;\n\n return SplitComponent ?\n : null;\n }\n}\n\nexport default Route;\n","import React from 'react';\nimport {exec, pathRankSort} from './util';\nimport Route from './Route.jsx';\n\nlet customHistory = null;\n\nconst ROUTERS = [];\nconst EMPTY = {};\nconst LISTENRS = [];\n\nfunction setUrl(url, type = 'push') {\n if (customHistory && customHistory[type]) {\n customHistory[type](url);\n } else if (typeof history !== 'undefined' && history[type + 'State']) {\n history[type + 'State'](null, null, url);\n }\n}\n\nfunction getCurrentUrl() {\n let url;\n if (customHistory && customHistory.getCurrentLocation) {\n url = customHistory.getCurrentLocation();\n } else {\n url = typeof location !== 'undefined'\n ? location\n : EMPTY;\n }\n return `${url.pathname || ''}${url.search || ''}`;\n}\n\nfunction listenBefore(func) {\n LISTENRS.push(func);\n}\n\n\nfunction route(url, replace = false) {\n return Promise.all(LISTENRS.map(func => func(url)))\n .then(() => {\n if (typeof url !== 'string' && url.url) {\n replace = url.replace;\n url = url.url;\n }\n setUrl(url, replace\n ? 'replace'\n : 'push');\n return routeTo(url);\n }).catch(() => {});\n}\n\nfunction routeTo(url) {\n let didRoute = false;\n ROUTERS.forEach(router => {\n if (router.routeTo(url) === true) {\n didRoute = true;\n }\n });\n return didRoute;\n}\n\nfunction routeFromLink(node) {\n // only valid elements\n if (!node || !node.getAttribute)\n return;\n\n let href = node.getAttribute('href') || node.getAttribute('to'),\n target = node.getAttribute('target');\n\n // ignore links with targets and non-path URLs\n if (!href || !href.match(/^\\//g) || (target && !target.match(/^_?self$/i)))\n return;\n\n // attempt to route, if no match simply cede control to browser\n return route(href);\n}\n\nfunction handleLinkClick(delay) {\n return e => _handleLinkClick(e, delay);\n}\n\nfunction _handleLinkClick(e, delay) {\n const target = e.currentTarget || e.target || this;\n if (e.button !== 0)\n return;\n\n if (typeof delay === 'number') {\n // access the event properties in an asynchronous way\n e.persist();\n setTimeout(() => routeFromLink(target), delay);\n return prevent(e);\n }\n\n routeFromLink(target);\n return prevent(e);\n}\n\nfunction prevent(e) {\n if (e) {\n if (e.stopImmediatePropagation)\n e.stopImmediatePropagation();\n if (e.stopPropagation)\n e.stopPropagation();\n e.preventDefault();\n }\n return false;\n}\n\nif (typeof addEventListener === 'function') {\n addEventListener('popstate', () => routeTo(getCurrentUrl()));\n}\n\nclass Router extends React.Component {\n constructor(props) {\n super(props);\n if (props.history) {\n customHistory = props.history;\n }\n\n this.state = {\n url: this.props.url || getCurrentUrl()\n };\n }\n\n shouldComponentUpdate(props, state) {\n return props.url !== this.props.url ||\n props.onChange !== this.props.onChange ||\n state.url !== this.state.url;\n }\n\n componentWillMount() {\n ROUTERS.push(this);\n }\n\n componentWillUnmount() {\n ROUTERS.splice(ROUTERS.indexOf(this), 1);\n }\n\n routeTo(url) {\n this._didRoute = false;\n this.setState({url});\n return this._didRoute;\n }\n\n render() {\n const {children, onChange} = this.props;\n const {url} = this.state;\n let routeElement = null;\n\n const childrenElements = React.Children\n .toArray(children)\n .sort(pathRankSort);\n\n for (let i = 0; i < childrenElements.length; i++) {\n let child = childrenElements[i],\n props = child.props,\n path = props.path,\n matches = exec(url, path, props);\n if (matches) {\n routeElement = React.cloneElement(\n child,\n {...child.props, url, matches}\n );\n\n this._didRoute = true;\n break;\n }\n }\n\n\n let previous = this.previousUrl;\n if (url !== previous) {\n this.previousUrl = url;\n if (typeof onChange === 'function') {\n onChange({ url, previous, router: this });\n }\n }\n return routeElement;\n }\n}\n\nconst Link = ({\n children,\n activeClassName,\n delay,\n ...props\n}) => (\n {children}\n);\n\nRouter.listenBefore = listenBefore;\nRouter.route = route;\nRouter.Router = Router;\nRouter.Route = Route;\nRouter.Link = Link;\n\nexport {route, Router, Route, Link, listenBefore};\nexport default Router;\n"]} -------------------------------------------------------------------------------- /dist/react-rainie-router.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('react')) : 3 | typeof define === 'function' && define.amd ? define(['react'], factory) : 4 | (global.reactRainieRouter = factory(global.React)); 5 | }(this, (function (React) { 'use strict'; 6 | 7 | React = 'default' in React ? React['default'] : React; 8 | 9 | var EMPTY$1 = {}; 10 | 11 | function exec(url, route) { 12 | var opts = arguments.length <= 2 || arguments[2] === undefined ? EMPTY$1 : arguments[2]; 13 | 14 | var reg = /(?:\?([^#]*))?(#.*)?$/, 15 | c = url.match(reg), 16 | matches = {}, 17 | ret = void 0; 18 | if (c && c[1]) { 19 | var p = c[1].split('&'); 20 | for (var i = 0; i < p.length; i++) { 21 | var r = p[i].split('='); 22 | matches[decodeURIComponent(r[0])] = decodeURIComponent(r.slice(1).join('=')); 23 | } 24 | } 25 | url = segmentize(url.replace(reg, '')); 26 | route = segmentize(route || ''); 27 | var max = Math.max(url.length, route.length); 28 | for (var _i = 0; _i < max; _i++) { 29 | if (route[_i] && route[_i].charAt(0) === ':') { 30 | var param = route[_i].replace(/(^\:|[+*?]+$)/g, ''), 31 | flags = (route[_i].match(/[+*?]+$/) || EMPTY$1)[0] || '', 32 | plus = ~flags.indexOf('+'), 33 | star = ~flags.indexOf('*'), 34 | val = url[_i] || ''; 35 | if (!val && !star && (flags.indexOf('?') < 0 || plus)) { 36 | ret = false; 37 | break; 38 | } 39 | matches[param] = decodeURIComponent(val); 40 | if (plus || star) { 41 | matches[param] = url.slice(_i).map(decodeURIComponent).join('/'); 42 | break; 43 | } 44 | } else if (route[_i] !== url[_i]) { 45 | ret = false; 46 | break; 47 | } 48 | } 49 | if (opts.default !== true && ret === false) return false; 50 | return matches; 51 | } 52 | 53 | function pathRankSort(a, b) { 54 | var aAttr = a.props || EMPTY$1, 55 | bAttr = b.props || EMPTY$1; 56 | if (aAttr.default) return 1; 57 | if (bAttr.default) return -1; 58 | var diff = rank(aAttr.path) - rank(bAttr.path); 59 | return diff || aAttr.path.length - bAttr.path.length; 60 | } 61 | 62 | function segmentize(url) { 63 | return strip(url).split('/'); 64 | } 65 | 66 | function rank(url) { 67 | return (strip(url).match(/\/+/g) || '').length; 68 | } 69 | 70 | function strip(url) { 71 | return url.replace(/(^\/+|\/+$)/g, ''); 72 | } 73 | 74 | var classCallCheck = function (instance, Constructor) { 75 | if (!(instance instanceof Constructor)) { 76 | throw new TypeError("Cannot call a class as a function"); 77 | } 78 | }; 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | var _extends = Object.assign || function (target) { 89 | for (var i = 1; i < arguments.length; i++) { 90 | var source = arguments[i]; 91 | 92 | for (var key in source) { 93 | if (Object.prototype.hasOwnProperty.call(source, key)) { 94 | target[key] = source[key]; 95 | } 96 | } 97 | } 98 | 99 | return target; 100 | }; 101 | 102 | var get = function get(object, property, receiver) { 103 | if (object === null) object = Function.prototype; 104 | var desc = Object.getOwnPropertyDescriptor(object, property); 105 | 106 | if (desc === undefined) { 107 | var parent = Object.getPrototypeOf(object); 108 | 109 | if (parent === null) { 110 | return undefined; 111 | } else { 112 | return get(parent, property, receiver); 113 | } 114 | } else if ("value" in desc) { 115 | return desc.value; 116 | } else { 117 | var getter = desc.get; 118 | 119 | if (getter === undefined) { 120 | return undefined; 121 | } 122 | 123 | return getter.call(receiver); 124 | } 125 | }; 126 | 127 | var inherits = function (subClass, superClass) { 128 | if (typeof superClass !== "function" && superClass !== null) { 129 | throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); 130 | } 131 | 132 | subClass.prototype = Object.create(superClass && superClass.prototype, { 133 | constructor: { 134 | value: subClass, 135 | enumerable: false, 136 | writable: true, 137 | configurable: true 138 | } 139 | }); 140 | if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; 141 | }; 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | var objectWithoutProperties = function (obj, keys) { 152 | var target = {}; 153 | 154 | for (var i in obj) { 155 | if (keys.indexOf(i) >= 0) continue; 156 | if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; 157 | target[i] = obj[i]; 158 | } 159 | 160 | return target; 161 | }; 162 | 163 | var possibleConstructorReturn = function (self, call) { 164 | if (!self) { 165 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); 166 | } 167 | 168 | return call && (typeof call === "object" || typeof call === "function") ? call : self; 169 | }; 170 | 171 | 172 | 173 | var set = function set(object, property, value, receiver) { 174 | var desc = Object.getOwnPropertyDescriptor(object, property); 175 | 176 | if (desc === undefined) { 177 | var parent = Object.getPrototypeOf(object); 178 | 179 | if (parent !== null) { 180 | set(parent, property, value, receiver); 181 | } 182 | } else if ("value" in desc && desc.writable) { 183 | desc.value = value; 184 | } else { 185 | var setter = desc.set; 186 | 187 | if (setter !== undefined) { 188 | setter.call(receiver, value); 189 | } 190 | } 191 | 192 | return value; 193 | }; 194 | 195 | function isPromise(obj) { 196 | return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'; 197 | } 198 | 199 | var Route = function (_React$Component) { 200 | inherits(Route, _React$Component); 201 | 202 | function Route() { 203 | var _temp, _this, _ret; 204 | 205 | classCallCheck(this, Route); 206 | 207 | for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 208 | args[_key] = arguments[_key]; 209 | } 210 | 211 | return _ret = (_temp = (_this = possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.state = { 212 | SplitComponent: undefined 213 | }, _temp), possibleConstructorReturn(_this, _ret); 214 | } 215 | 216 | Route.prototype.componentWillMount = function componentWillMount() { 217 | var _this2 = this; 218 | 219 | var _props2 = this.props; 220 | var component = _props2.component; 221 | var getComponent = _props2.getComponent; 222 | 223 | if (!component && getComponent) { 224 | var promise = getComponent(); 225 | if (isPromise(promise)) { 226 | promise.then(function (SplitComponent) { 227 | _this2.setState({ SplitComponent: SplitComponent }); 228 | }); 229 | } 230 | } 231 | }; 232 | 233 | Route.prototype.render = function render() { 234 | var _props3 = this.props; 235 | var RoutedComponent = _props3.component; 236 | var getComponent = _props3.getComponent; 237 | 238 | var _props = objectWithoutProperties(_props3, ['component', 'getComponent']); 239 | 240 | var SplitComponent = this.state.SplitComponent; 241 | 242 | 243 | if (RoutedComponent) return React.createElement(RoutedComponent, _props); 244 | 245 | return SplitComponent ? React.createElement(SplitComponent, _props) : null; 246 | }; 247 | 248 | return Route; 249 | }(React.Component); 250 | 251 | Route.PropTypes = { 252 | component: React.PropTypes.node, 253 | getComponent: React.PropTypes.func 254 | }; 255 | 256 | var customHistory = null; 257 | 258 | var ROUTERS = []; 259 | var EMPTY = {}; 260 | var LISTENRS = []; 261 | 262 | function setUrl(url) { 263 | var type = arguments.length <= 1 || arguments[1] === undefined ? 'push' : arguments[1]; 264 | 265 | if (customHistory && customHistory[type]) { 266 | customHistory[type](url); 267 | } else if (typeof history !== 'undefined' && history[type + 'State']) { 268 | history[type + 'State'](null, null, url); 269 | } 270 | } 271 | 272 | function getCurrentUrl() { 273 | var url = void 0; 274 | if (customHistory && customHistory.getCurrentLocation) { 275 | url = customHistory.getCurrentLocation(); 276 | } else { 277 | url = typeof location !== 'undefined' ? location : EMPTY; 278 | } 279 | return '' + (url.pathname || '') + (url.search || ''); 280 | } 281 | 282 | function listenBefore(func) { 283 | LISTENRS.push(func); 284 | } 285 | 286 | function route(url) { 287 | var replace = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; 288 | 289 | return Promise.all(LISTENRS.map(function (func) { 290 | return func(url); 291 | })).then(function () { 292 | if (typeof url !== 'string' && url.url) { 293 | replace = url.replace; 294 | url = url.url; 295 | } 296 | setUrl(url, replace ? 'replace' : 'push'); 297 | return routeTo(url); 298 | }).catch(function () {}); 299 | } 300 | 301 | function routeTo(url) { 302 | var didRoute = false; 303 | ROUTERS.forEach(function (router) { 304 | if (router.routeTo(url) === true) { 305 | didRoute = true; 306 | } 307 | }); 308 | return didRoute; 309 | } 310 | 311 | function routeFromLink(node) { 312 | // only valid elements 313 | if (!node || !node.getAttribute) return; 314 | 315 | var href = node.getAttribute('href') || node.getAttribute('to'), 316 | target = node.getAttribute('target'); 317 | 318 | // ignore links with targets and non-path URLs 319 | if (!href || !href.match(/^\//g) || target && !target.match(/^_?self$/i)) return; 320 | 321 | // attempt to route, if no match simply cede control to browser 322 | return route(href); 323 | } 324 | 325 | function handleLinkClick(delay) { 326 | return function (e) { 327 | return _handleLinkClick(e, delay); 328 | }; 329 | } 330 | 331 | function _handleLinkClick(e, delay) { 332 | var target = e.currentTarget || e.target || this; 333 | if (e.button !== 0) return; 334 | 335 | if (typeof delay === 'number') { 336 | // access the event properties in an asynchronous way 337 | e.persist(); 338 | setTimeout(function () { 339 | return routeFromLink(target); 340 | }, delay); 341 | return prevent(e); 342 | } 343 | 344 | routeFromLink(target); 345 | return prevent(e); 346 | } 347 | 348 | function prevent(e) { 349 | if (e) { 350 | if (e.stopImmediatePropagation) e.stopImmediatePropagation(); 351 | if (e.stopPropagation) e.stopPropagation(); 352 | e.preventDefault(); 353 | } 354 | return false; 355 | } 356 | 357 | if (typeof addEventListener === 'function') { 358 | addEventListener('popstate', function () { 359 | return routeTo(getCurrentUrl()); 360 | }); 361 | } 362 | 363 | var Router = function (_React$Component) { 364 | inherits(Router, _React$Component); 365 | 366 | function Router(props) { 367 | classCallCheck(this, Router); 368 | 369 | var _this = possibleConstructorReturn(this, _React$Component.call(this, props)); 370 | 371 | if (props.history) { 372 | customHistory = props.history; 373 | } 374 | 375 | _this.state = { 376 | url: _this.props.url || getCurrentUrl() 377 | }; 378 | return _this; 379 | } 380 | 381 | Router.prototype.shouldComponentUpdate = function shouldComponentUpdate(props, state) { 382 | return props.url !== this.props.url || props.onChange !== this.props.onChange || state.url !== this.state.url; 383 | }; 384 | 385 | Router.prototype.componentWillMount = function componentWillMount() { 386 | ROUTERS.push(this); 387 | }; 388 | 389 | Router.prototype.componentWillUnmount = function componentWillUnmount() { 390 | ROUTERS.splice(ROUTERS.indexOf(this), 1); 391 | }; 392 | 393 | Router.prototype.routeTo = function routeTo(url) { 394 | this._didRoute = false; 395 | this.setState({ url: url }); 396 | return this._didRoute; 397 | }; 398 | 399 | Router.prototype.render = function render() { 400 | var _props = this.props; 401 | var children = _props.children; 402 | var onChange = _props.onChange; 403 | var url = this.state.url; 404 | 405 | var routeElement = null; 406 | 407 | var childrenElements = React.Children.toArray(children).sort(pathRankSort); 408 | 409 | for (var i = 0; i < childrenElements.length; i++) { 410 | var child = childrenElements[i], 411 | props = child.props, 412 | path = props.path, 413 | matches = exec(url, path, props); 414 | if (matches) { 415 | routeElement = React.cloneElement(child, _extends({}, child.props, { url: url, matches: matches })); 416 | 417 | this._didRoute = true; 418 | break; 419 | } 420 | } 421 | 422 | var previous = this.previousUrl; 423 | if (url !== previous) { 424 | this.previousUrl = url; 425 | if (typeof onChange === 'function') { 426 | onChange({ url: url, previous: previous, router: this }); 427 | } 428 | } 429 | return routeElement; 430 | }; 431 | 432 | return Router; 433 | }(React.Component); 434 | 435 | var Link = function (_ref) { 436 | var children = _ref.children; 437 | var activeClassName = _ref.activeClassName; 438 | var delay = _ref.delay; 439 | var props = objectWithoutProperties(_ref, ['children', 'activeClassName', 'delay']); 440 | return React.createElement( 441 | 'a', 442 | _extends({}, props, { 443 | className: exec(props.href || props.to, getCurrentUrl()) ? activeClassName + ' ' + props.className : props.className, 444 | onClick: handleLinkClick(delay) }), 445 | children 446 | ); 447 | }; 448 | 449 | Router.listenBefore = listenBefore; 450 | Router.route = route; 451 | Router.Router = Router; 452 | Router.Route = Route; 453 | Router.Link = Link; 454 | 455 | return Router; 456 | 457 | }))); 458 | //# sourceMappingURL=react-rainie-router.js.map 459 | -------------------------------------------------------------------------------- /dist/react-rainie-router.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":null,"sources":["../src/util.js","../src/Route.jsx","../src/index.js"],"sourcesContent":["const EMPTY = {};\n\nexport function exec(url, route, opts = EMPTY) {\n let reg = /(?:\\?([^#]*))?(#.*)?$/,\n c = url.match(reg),\n matches = {},\n ret;\n if (c && c[1]) {\n let p = c[1].split('&');\n for (let i = 0; i < p.length; i++) {\n let r = p[i].split('=');\n matches[decodeURIComponent(r[0])] = decodeURIComponent(r.slice(1).join('='));\n }\n }\n url = segmentize(url.replace(reg, ''));\n route = segmentize(route || '');\n let max = Math.max(url.length, route.length);\n for (let i = 0; i < max; i++) {\n if (route[i] && route[i].charAt(0) === ':') {\n let param = route[i].replace(/(^\\:|[+*?]+$)/g, ''),\n flags = (route[i].match(/[+*?]+$/) || EMPTY)[0] || '',\n plus = ~flags.indexOf('+'),\n star = ~flags.indexOf('*'),\n val = url[i] || '';\n if (!val && !star && (flags.indexOf('?') < 0 || plus)) {\n ret = false;\n break;\n }\n matches[param] = decodeURIComponent(val);\n if (plus || star) {\n matches[param] = url.slice(i).map(decodeURIComponent).join('/');\n break;\n }\n } else if (route[i] !== url[i]) {\n ret = false;\n break;\n }\n }\n if (opts.default !== true && ret === false) return false;\n return matches;\n}\n\n\nexport function pathRankSort(a, b) {\n let aAttr = a.props || EMPTY,\n bAttr = b.props || EMPTY;\n if (aAttr.default) return 1;\n if (bAttr.default) return -1;\n let diff = rank(aAttr.path) - rank(bAttr.path);\n return diff || (aAttr.path.length - bAttr.path.length);\n}\n\nexport function segmentize(url) {\n return strip(url).split('/');\n}\n\nexport function rank(url) {\n return (strip(url).match(/\\/+/g) || '').length;\n}\n\nexport function strip(url) {\n return url.replace(/(^\\/+|\\/+$)/g, '');\n}\n","import React from 'react';\n\nfunction isPromise(obj) {\n return !!obj &&\n (typeof obj === 'object' || typeof obj === 'function') &&\n typeof obj.then === 'function';\n}\n\n\nclass Route extends React.Component {\n static PropTypes = {\n component: React.PropTypes.node,\n getComponent: React.PropTypes.func,\n }\n\n state = {\n SplitComponent: undefined,\n }\n\n componentWillMount() {\n const { component, getComponent} = this.props;\n if (!component && getComponent) {\n const promise = getComponent();\n if (isPromise(promise)) {\n promise.then(SplitComponent => {\n this.setState({ SplitComponent });\n });\n }\n }\n }\n\n render () {\n const {\n component: RoutedComponent,\n getComponent,\n ..._props,\n } = this.props;\n\n const { SplitComponent } = this.state;\n\n\n if (RoutedComponent)\n return ;\n\n return SplitComponent ?\n : null;\n }\n}\n\nexport default Route;\n","import React from 'react';\nimport {exec, pathRankSort} from './util';\nimport Route from './Route.jsx';\n\nlet customHistory = null;\n\nconst ROUTERS = [];\nconst EMPTY = {};\nconst LISTENRS = [];\n\nfunction setUrl(url, type = 'push') {\n if (customHistory && customHistory[type]) {\n customHistory[type](url);\n } else if (typeof history !== 'undefined' && history[type + 'State']) {\n history[type + 'State'](null, null, url);\n }\n}\n\nfunction getCurrentUrl() {\n let url;\n if (customHistory && customHistory.getCurrentLocation) {\n url = customHistory.getCurrentLocation();\n } else {\n url = typeof location !== 'undefined'\n ? location\n : EMPTY;\n }\n return `${url.pathname || ''}${url.search || ''}`;\n}\n\nfunction listenBefore(func) {\n LISTENRS.push(func);\n}\n\n\nfunction route(url, replace = false) {\n return Promise.all(LISTENRS.map(func => func(url)))\n .then(() => {\n if (typeof url !== 'string' && url.url) {\n replace = url.replace;\n url = url.url;\n }\n setUrl(url, replace\n ? 'replace'\n : 'push');\n return routeTo(url);\n }).catch(() => {});\n}\n\nfunction routeTo(url) {\n let didRoute = false;\n ROUTERS.forEach(router => {\n if (router.routeTo(url) === true) {\n didRoute = true;\n }\n });\n return didRoute;\n}\n\nfunction routeFromLink(node) {\n // only valid elements\n if (!node || !node.getAttribute)\n return;\n\n let href = node.getAttribute('href') || node.getAttribute('to'),\n target = node.getAttribute('target');\n\n // ignore links with targets and non-path URLs\n if (!href || !href.match(/^\\//g) || (target && !target.match(/^_?self$/i)))\n return;\n\n // attempt to route, if no match simply cede control to browser\n return route(href);\n}\n\nfunction handleLinkClick(delay) {\n return e => _handleLinkClick(e, delay);\n}\n\nfunction _handleLinkClick(e, delay) {\n const target = e.currentTarget || e.target || this;\n if (e.button !== 0)\n return;\n\n if (typeof delay === 'number') {\n // access the event properties in an asynchronous way\n e.persist();\n setTimeout(() => routeFromLink(target), delay);\n return prevent(e);\n }\n\n routeFromLink(target);\n return prevent(e);\n}\n\nfunction prevent(e) {\n if (e) {\n if (e.stopImmediatePropagation)\n e.stopImmediatePropagation();\n if (e.stopPropagation)\n e.stopPropagation();\n e.preventDefault();\n }\n return false;\n}\n\nif (typeof addEventListener === 'function') {\n addEventListener('popstate', () => routeTo(getCurrentUrl()));\n}\n\nclass Router extends React.Component {\n constructor(props) {\n super(props);\n if (props.history) {\n customHistory = props.history;\n }\n\n this.state = {\n url: this.props.url || getCurrentUrl()\n };\n }\n\n shouldComponentUpdate(props, state) {\n return props.url !== this.props.url ||\n props.onChange !== this.props.onChange ||\n state.url !== this.state.url;\n }\n\n componentWillMount() {\n ROUTERS.push(this);\n }\n\n componentWillUnmount() {\n ROUTERS.splice(ROUTERS.indexOf(this), 1);\n }\n\n routeTo(url) {\n this._didRoute = false;\n this.setState({url});\n return this._didRoute;\n }\n\n render() {\n const {children, onChange} = this.props;\n const {url} = this.state;\n let routeElement = null;\n\n const childrenElements = React.Children\n .toArray(children)\n .sort(pathRankSort);\n\n for (let i = 0; i < childrenElements.length; i++) {\n let child = childrenElements[i],\n props = child.props,\n path = props.path,\n matches = exec(url, path, props);\n if (matches) {\n routeElement = React.cloneElement(\n child,\n {...child.props, url, matches}\n );\n\n this._didRoute = true;\n break;\n }\n }\n\n\n let previous = this.previousUrl;\n if (url !== previous) {\n this.previousUrl = url;\n if (typeof onChange === 'function') {\n onChange({ url, previous, router: this });\n }\n }\n return routeElement;\n }\n}\n\nconst Link = ({\n children,\n activeClassName,\n delay,\n ...props\n}) => (\n {children}\n);\n\nRouter.listenBefore = listenBefore;\nRouter.route = route;\nRouter.Router = Router;\nRouter.Route = Route;\nRouter.Link = Link;\n\nexport {route, Router, Route, Link, listenBefore};\nexport default Router;\n"],"names":["EMPTY","exec","url","route","opts","reg","c","match","matches","ret","p","split","i","length","r","decodeURIComponent","slice","join","segmentize","replace","max","Math","charAt","param","flags","plus","indexOf","star","val","map","default","pathRankSort","a","b","aAttr","props","bAttr","diff","rank","path","strip","isPromise","obj","then","Route","state","undefined","componentWillMount","component","getComponent","promise","setState","SplitComponent","render","RoutedComponent","_props","React","Component","PropTypes","node","func","customHistory","ROUTERS","LISTENRS","setUrl","type","history","getCurrentUrl","getCurrentLocation","location","pathname","search","listenBefore","push","Promise","all","routeTo","catch","didRoute","forEach","router","routeFromLink","getAttribute","href","target","handleLinkClick","delay","_handleLinkClick","e","currentTarget","button","persist","prevent","stopImmediatePropagation","stopPropagation","preventDefault","addEventListener","Router","shouldComponentUpdate","onChange","componentWillUnmount","splice","_didRoute","children","routeElement","childrenElements","Children","toArray","sort","child","cloneElement","previous","previousUrl","Link","activeClassName","to","className"],"mappings":"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"} --------------------------------------------------------------------------------