├── .gitignore ├── README.md ├── scripts └── buildAll.sh ├── apps ├── react │ ├── components │ │ ├── NotFound.js │ │ ├── InboxList.js │ │ ├── Message.js │ │ ├── App.js │ │ ├── Main.js │ │ ├── Nav.js │ │ ├── Inbox.js │ │ ├── InboxItem.js │ │ └── Compose.js │ ├── main.js │ └── api.js ├── react-redux │ ├── components │ │ ├── NotFound.js │ │ ├── InboxItem.js │ │ ├── App.js │ │ ├── Inbox.js │ │ ├── Message.js │ │ ├── Main.js │ │ ├── InboxList.js │ │ ├── Link.js │ │ ├── Nav.js │ │ └── Compose.js │ ├── actions │ │ └── draft.js │ ├── reducers │ │ ├── draft.js │ │ └── emails.js │ ├── main.js │ └── store.js ├── routes.js ├── deku │ ├── components │ │ ├── NotFound.js │ │ ├── App.js │ │ ├── InboxList.js │ │ ├── Message.js │ │ ├── Nav.js │ │ ├── InboxItem.js │ │ ├── Main.js │ │ ├── Inbox.js │ │ └── Compose.js │ ├── main.js │ └── api.js ├── deku-redux │ ├── components │ │ ├── NotFound.js │ │ ├── App.js │ │ ├── InboxList.js │ │ ├── Message.js │ │ ├── InboxItem.js │ │ ├── Main.js │ │ ├── Inbox.js │ │ ├── Nav.js │ │ ├── Link.js │ │ └── Compose.js │ ├── actions │ │ └── draft.js │ ├── reducers │ │ ├── draft.js │ │ └── emails.js │ ├── main.js │ ├── store.js │ └── api.js ├── cycle │ ├── components │ │ ├── Message.js │ │ ├── InboxList.js │ │ ├── Nav.js │ │ ├── Inbox.js │ │ ├── Main.js │ │ └── Compose.js │ ├── README.md │ ├── routes.js │ ├── create-router.js │ ├── router5 │ │ ├── link-interceptor-plugin.js │ │ ├── router-to-observable.js │ │ ├── link-on-click.js │ │ └── driver.js │ ├── data │ │ └── emails.js │ └── main.js ├── cycle2 │ ├── routes.js │ ├── create-router.js │ ├── components │ │ ├── Route.js │ │ └── Nav.js │ └── main.js └── create-router.js ├── .editorconfig ├── index.html ├── example2.css ├── styles2.css ├── webpack.config.js ├── LICENSE ├── package.json └── styles.css /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Moved to router5 repository](https://github.com/router5/router5/tree/master/packages/examples) 2 | -------------------------------------------------------------------------------- /scripts/buildAll.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | npm run build:prod -- --app=react 3 | npm run build:prod -- --app=react-redux 4 | npm run build:prod -- --app=deku 5 | npm run build:prod -- --app=deku-redux 6 | -------------------------------------------------------------------------------- /apps/react/components/NotFound.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function NotFound(props) { 4 | return
Purposely Not found (not a bug)
; 5 | } 6 | -------------------------------------------------------------------------------- /apps/react-redux/components/NotFound.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function NotFound(props) { 4 | return
Purposely Not found (not a bug)
; 5 | } 6 | -------------------------------------------------------------------------------- /apps/routes.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { name: 'inbox', path: '/inbox' }, 3 | { name: 'inbox.message', path: '/message/:id' }, 4 | { name: 'compose', path: '/compose' }, 5 | { name: 'contacts', path: '/contacts' } 6 | ]; 7 | -------------------------------------------------------------------------------- /apps/deku/components/NotFound.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | 3 | const NotFound = { 4 | render() { 5 | return element('div', { class: 'not-found' }, 'Purposely not found (not a bug)'); 6 | } 7 | }; 8 | 9 | export default NotFound; 10 | -------------------------------------------------------------------------------- /apps/deku-redux/components/NotFound.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | 3 | const NotFound = { 4 | render() { 5 | return element('div', { class: 'not-found' }, 'Purposely not found (not a bug)'); 6 | } 7 | }; 8 | 9 | export default NotFound; 10 | -------------------------------------------------------------------------------- /apps/cycle/components/Message.js: -------------------------------------------------------------------------------- 1 | import { h, h4, p } from '@cycle/dom'; 2 | 3 | function Message({ email }) { 4 | return h('section', { className: 'mail' }, [ 5 | h4(email.mailTitle), 6 | p(email.mailMessage) 7 | ]); 8 | } 9 | 10 | export default Message; 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | 9 | charset = utf-8 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /apps/deku-redux/actions/draft.js: -------------------------------------------------------------------------------- 1 | export function updateTitle(title) { 2 | return { 3 | type: 'UPDATE_TITLE', 4 | title 5 | }; 6 | } 7 | 8 | export function updateMessage(message) { 9 | return { 10 | type: 'UPDATE_MESSAGE', 11 | message 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /apps/cycle/README.md: -------------------------------------------------------------------------------- 1 | # Cycle and router5 2 | 3 | This is development work for a new router5 cycle driver. 4 | 5 | ## Build 6 | 7 | From the root directory this repo: 8 | 9 | ```sh 10 | npm run build -- --app cycle 11 | http-server -p 8080 12 | ``` 13 | 14 | Navigate to http://localhost:8080. 15 | 16 | -------------------------------------------------------------------------------- /apps/react-redux/actions/draft.js: -------------------------------------------------------------------------------- 1 | export function updateTitle(title) { 2 | return { 3 | type: 'UPDATE_TITLE', 4 | title 5 | }; 6 | } 7 | 8 | export function updateMessage(message) { 9 | return { 10 | type: 'UPDATE_MESSAGE', 11 | message 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /apps/cycle2/routes.js: -------------------------------------------------------------------------------- 1 | const a = { 2 | name: 'a', 3 | path: '/a' 4 | }; 5 | 6 | const b = { 7 | name: 'b', 8 | path: '/b' 9 | }; 10 | 11 | const c = { 12 | name: 'c', 13 | path: '/c' 14 | }; 15 | 16 | export default [ 17 | { ...a, children: [a, b, c] }, 18 | { ...b, children: [a, b, c] } 19 | ]; 20 | -------------------------------------------------------------------------------- /apps/react/components/InboxList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import InboxItem from './InboxItem'; 3 | 4 | export default function InboxList(props) { 5 | return ( 6 | 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /apps/cycle/routes.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | name: 'inbox', 4 | path: '/inbox' 5 | }, 6 | { 7 | name: 'inbox.message', 8 | path: '/message/:id' 9 | }, 10 | { 11 | name: 'compose', 12 | path: '/compose' 13 | }, 14 | { 15 | name: 'contacts', 16 | path: '/contacts' 17 | } 18 | ]; 19 | -------------------------------------------------------------------------------- /apps/react-redux/components/InboxItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function InboxItem(props) { 4 | const { mailTitle, mailMessage, onClick } = props; 5 | 6 | return ( 7 |
  • 8 |

    { mailTitle }

    9 |

    { mailMessage }

    10 |
  • 11 | ); 12 | } 13 | 14 | export default InboxItem; 15 | -------------------------------------------------------------------------------- /apps/react/components/Message.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { getEmail } from '../api'; 3 | 4 | export default function Message(props) { 5 | const { mailTitle, mailMessage } = getEmail(props.messageId); 6 | 7 | return ( 8 |
    9 |

    { mailTitle }

    10 |

    { mailMessage }

    11 |
    12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /apps/deku/components/App.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | import Nav from './Nav'; 3 | import Main from './Main'; 4 | 5 | const App = { 6 | render({ props }) { 7 | return element('div', {class: 'mail-client'}, [ 8 | element('aside', {}, element(Nav)), 9 | element('main', {}, element(Main)) 10 | ]); 11 | } 12 | } 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /apps/deku-redux/components/App.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | import Nav from './Nav'; 3 | import Main from './Main'; 4 | 5 | const App = { 6 | render({ props }) { 7 | return element('div', {class: 'mail-client'}, [ 8 | element('aside', {}, element(Nav)), 9 | element('main', {}, element(Main)) 10 | ]); 11 | } 12 | } 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /apps/deku/components/InboxList.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | import InboxItem from './InboxItem'; 3 | 4 | const InboxList = { 5 | render({ props }) { 6 | return element( 7 | 'ul', 8 | { class: 'mail-list' }, 9 | props.emails.map(mail => element(InboxItem, { ...mail, key: mail.id })) 10 | ); 11 | } 12 | }; 13 | 14 | export default InboxList; 15 | -------------------------------------------------------------------------------- /apps/react/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Nav from './Nav'; 3 | import Main from './Main'; 4 | 5 | export default function App(props) { 6 | return ( 7 |
    8 | 11 | 12 |
    13 |
    14 |
    15 |
    16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /apps/deku-redux/components/InboxList.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | import InboxItem from './InboxItem'; 3 | 4 | const InboxList = { 5 | render({ props }) { 6 | return element( 7 | 'ul', 8 | { class: 'mail-list' }, 9 | props.emails.map(mail => element(InboxItem, { ...mail, key: mail.id })) 10 | ); 11 | } 12 | }; 13 | 14 | export default InboxList; 15 | -------------------------------------------------------------------------------- /apps/react-redux/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Nav from './Nav'; 3 | import Main from './Main'; 4 | 5 | export default function App(props) { 6 | return ( 7 |
    8 | 11 | 12 |
    13 |
    14 |
    15 |
    16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /apps/cycle/create-router.js: -------------------------------------------------------------------------------- 1 | import createRouter, { loggerPlugin } from 'router5'; 2 | import browserPlugin from 'router5/plugins/browser'; 3 | 4 | const configureRouter = (routes) => { 5 | return createRouter(routes, { 6 | defaultRoute: 'inbox' 7 | }) 8 | .usePlugin(loggerPlugin) 9 | .usePlugin(browserPlugin({ 10 | useHash: true 11 | })); 12 | }; 13 | 14 | export default configureRouter; 15 | -------------------------------------------------------------------------------- /apps/deku/components/Message.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | import { getEmail } from '../api'; 3 | 4 | const Message = { 5 | render({ props }) { 6 | const { mailTitle, mailMessage } = getEmail(props.messageId); 7 | 8 | return element('section', { class: 'mail' }, [ 9 | element('h4', {}, mailTitle), 10 | element('p', {}, mailMessage) 11 | ]); 12 | } 13 | }; 14 | 15 | export default Message; 16 | -------------------------------------------------------------------------------- /apps/cycle/components/InboxList.js: -------------------------------------------------------------------------------- 1 | import { ul, li, a, h4, p } from '@cycle/dom'; 2 | 3 | function InboxList({ emails, buildUrl }) { 4 | return ul({ className: 'mail-list' }, [ 5 | emails.map(({ id, mailTitle, mailMessage }) => li( 6 | a({ href: buildUrl('inbox.message', { id }) }, [ 7 | h4(mailTitle), 8 | p(mailMessage) 9 | ]) 10 | )) 11 | ]); 12 | } 13 | 14 | export default InboxList; 15 | -------------------------------------------------------------------------------- /apps/deku-redux/components/Message.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | import { getEmail } from '../api'; 3 | 4 | const Message = { 5 | render({ props }) { 6 | const { mailTitle, mailMessage } = getEmail(props.messageId); 7 | 8 | return element('section', { class: 'mail' }, [ 9 | element('h4', {}, mailTitle), 10 | element('p', {}, mailMessage) 11 | ]); 12 | } 13 | }; 14 | 15 | export default Message; 16 | -------------------------------------------------------------------------------- /apps/react/main.js: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom'; 2 | import React from 'react'; 3 | import App from './components/App'; 4 | import { RouterProvider } from 'react-router5'; 5 | import createRouter from '../create-router'; 6 | 7 | const router = createRouter(true); 8 | const app = ; 9 | 10 | router.start(() => { 11 | ReactDOM.render( 12 | app, 13 | document.getElementById('app') 14 | ); 15 | }); 16 | -------------------------------------------------------------------------------- /apps/deku/main.js: -------------------------------------------------------------------------------- 1 | import { tree, render } from 'deku'; 2 | import element from 'virtual-element'; 3 | import { routerPlugin } from 'deku-router5'; 4 | import App from './components/App'; 5 | import createRouter from '../create-router'; 6 | 7 | const router = createRouter(true); 8 | 9 | const app = tree() 10 | .use(routerPlugin(router)) 11 | .mount(element(App)); 12 | 13 | router.start(function (err, state) { 14 | render(app, document.getElementById('app')); 15 | }); 16 | -------------------------------------------------------------------------------- /apps/deku/components/Nav.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | import { Link } from 'deku-router5'; 3 | 4 | const Nav = { 5 | render({ props }) { 6 | return element('nav', {}, [ 7 | element(Link, { routeName: 'inbox', routeOptions: { reload: true } }, 'Inbox'), 8 | element(Link, { routeName: 'compose' }, 'Compose'), 9 | element(Link, { routeName: 'contacts' }, 'Contacts') 10 | ]); 11 | } 12 | }; 13 | 14 | export default Nav; 15 | -------------------------------------------------------------------------------- /apps/deku/components/InboxItem.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | 3 | const InboxItem = { 4 | propTypes: { 5 | router: {source: 'router'}, 6 | }, 7 | 8 | render({ props }) { 9 | const { mailTitle, mailMessage, router, id } = props; 10 | 11 | return element('li', { onClick: () => router.navigate('inbox.message', { id }) }, [ 12 | element('h4', {}, mailTitle), 13 | element('p', {}, mailMessage) 14 | ]); 15 | } 16 | }; 17 | 18 | export default InboxItem; 19 | -------------------------------------------------------------------------------- /apps/deku-redux/components/InboxItem.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | 3 | const InboxItem = { 4 | propTypes: { 5 | router: {source: 'router'}, 6 | }, 7 | 8 | render({ props }) { 9 | const { mailTitle, mailMessage, router, id } = props; 10 | 11 | return element('li', { onClick: () => router.navigate('inbox.message', { id }) }, [ 12 | element('h4', {}, mailTitle), 13 | element('p', {}, mailMessage) 14 | ]); 15 | } 16 | }; 17 | 18 | export default InboxItem; 19 | -------------------------------------------------------------------------------- /apps/react/components/Main.js: -------------------------------------------------------------------------------- 1 | import React, { createElement } from 'react'; 2 | import { routeNode } from 'react-router5'; 3 | import Inbox from './Inbox'; 4 | import Compose from './Compose'; 5 | import NotFound from './NotFound'; 6 | 7 | const components = { 8 | 'inbox': Inbox, 9 | 'compose': Compose 10 | }; 11 | 12 | function Main(props) { 13 | const { route } = props; 14 | const segment = route.name.split('.')[0]; 15 | 16 | return createElement(components[segment] || NotFound); 17 | } 18 | 19 | export default routeNode('')(Main); 20 | -------------------------------------------------------------------------------- /apps/react/components/Nav.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { BaseLink, withRoute } from 'react-router5'; 3 | 4 | function Nav(props) { 5 | const { router } = props; 6 | 7 | return ( 8 | 13 | ); 14 | } 15 | 16 | export default withRoute(Nav); 17 | -------------------------------------------------------------------------------- /apps/cycle2/create-router.js: -------------------------------------------------------------------------------- 1 | import createRouter, { loggerPlugin } from 'router5'; 2 | import browserPlugin from 'router5/plugins/browser'; 3 | import linkInterceptorPlugin from '../cycle/router5/link-interceptor-plugin'; 4 | 5 | const configureRouter = (routes) => { 6 | return createRouter(routes, { 7 | defaultRoute: 'a' 8 | }) 9 | .usePlugin(loggerPlugin) 10 | .usePlugin(browserPlugin({ 11 | useHash: true 12 | })) 13 | .usePlugin(linkInterceptorPlugin()); 14 | }; 15 | 16 | export default configureRouter; 17 | -------------------------------------------------------------------------------- /apps/deku-redux/reducers/draft.js: -------------------------------------------------------------------------------- 1 | const initialState = { 2 | title: '', 3 | message: '' 4 | }; 5 | 6 | export default function draft(state = initialState, action) { 7 | switch (action.type) { 8 | case 'UPDATE_TITLE': 9 | return { 10 | ...state, 11 | title: action.title 12 | }; 13 | 14 | case 'UPDATE_MESSAGE': 15 | return { 16 | ...state, 17 | message: action.message 18 | }; 19 | 20 | default: 21 | return state; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/react-redux/reducers/draft.js: -------------------------------------------------------------------------------- 1 | const initialState = { 2 | title: '', 3 | message: '' 4 | }; 5 | 6 | export default function draft(state = initialState, action) { 7 | switch (action.type) { 8 | case 'UPDATE_TITLE': 9 | return { 10 | ...state, 11 | title: action.title 12 | }; 13 | 14 | case 'UPDATE_MESSAGE': 15 | return { 16 | ...state, 17 | message: action.message 18 | }; 19 | 20 | default: 21 | return state; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/cycle2/components/Route.js: -------------------------------------------------------------------------------- 1 | import { div } from '@cycle/dom'; 2 | import randomColor from 'randomcolor'; 3 | 4 | const randomBgColor = () => ({ backgroundColor: randomColor() }); 5 | 6 | const Route = node => sources => { 7 | console.log(node); 8 | const route$ = sources.router.routeNode$(node); 9 | 10 | const vDom$ = route$ 11 | .map(route => div( 12 | { className: 'item row', style: randomBgColor() }, 13 | route ? route.name : '') 14 | ); 15 | 16 | return { 17 | DOM: vDom$ 18 | }; 19 | } 20 | 21 | export default Route; 22 | -------------------------------------------------------------------------------- /apps/deku/components/Main.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | import { routeNode } from 'deku-router5'; 3 | import Inbox from './Inbox'; 4 | import Compose from './Compose'; 5 | import NotFound from './NotFound'; 6 | 7 | const components = { 8 | 'inbox': Inbox, 9 | 'compose': Compose 10 | }; 11 | 12 | const Main = { 13 | render({ props }) { 14 | const { route } = props; 15 | const segment = route ? route.name.split('.')[0] : undefined; 16 | 17 | return element(components[segment] || NotFound); 18 | } 19 | }; 20 | 21 | export default routeNode('')(Main); 22 | -------------------------------------------------------------------------------- /apps/react/components/Inbox.js: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import InboxList from './InboxList'; 4 | import Message from './Message'; 5 | import { routeNode } from 'react-router5'; 6 | import { getEmails } from '../api'; 7 | 8 | function Inbox(props) { 9 | const { route } = props; 10 | 11 | return ( 12 |
    13 | 14 | { route.name === 'inbox.message' ? : null } 15 |
    16 | ); 17 | } 18 | 19 | export default routeNode('inbox')(Inbox); 20 | -------------------------------------------------------------------------------- /apps/deku-redux/main.js: -------------------------------------------------------------------------------- 1 | import { tree, render } from 'deku'; 2 | import element from 'virtual-element'; 3 | import { storePlugin } from 'deku-redux'; 4 | import { routerPlugin } from 'deku-router5'; 5 | import App from './components/App'; 6 | import createRouter from '../create-router' 7 | import configureStore from './store'; 8 | 9 | const router = createRouter(); 10 | const store = configureStore(router); 11 | 12 | const app = tree() 13 | .use(storePlugin(store)) 14 | .set('router', router) 15 | .mount(element(App)); 16 | 17 | router.start((err, state) => { 18 | render(app, document.getElementById('app')); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/react-redux/components/Inbox.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import InboxList from './InboxList'; 3 | import Message from './Message'; 4 | import { connect } from 'react-redux'; 5 | import { routeNodeSelector } from 'redux-router5'; 6 | 7 | function Inbox(props) { 8 | const { route } = props; 9 | 10 | return ( 11 |
    12 | 13 | { route.name === 'inbox.message' ? : null } 14 |
    15 | ); 16 | } 17 | 18 | export default connect((state) => routeNodeSelector('inbox'))(Inbox); 19 | -------------------------------------------------------------------------------- /apps/react-redux/components/Message.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import find from 'lodash.find'; 4 | 5 | function mapStateToProps(state, props) { 6 | return { 7 | email: find(state.emails, { id: props.messageId }) 8 | }; 9 | } 10 | 11 | export default function Message(props) { 12 | const { mailTitle, mailMessage } = props.email; 13 | 14 | return ( 15 |
    16 |

    { mailTitle }

    17 |

    { mailMessage }

    18 |
    19 | ); 20 | } 21 | 22 | export default connect(mapStateToProps)(Message); 23 | -------------------------------------------------------------------------------- /apps/cycle/components/Nav.js: -------------------------------------------------------------------------------- 1 | import { h, a } from '@cycle/dom'; 2 | 3 | function Nav(sources) { 4 | const routerSource = sources.router; 5 | 6 | const nav$ = routerSource 7 | .route$ 8 | .map(route => 9 | h('nav', [ 10 | a({ href: sources.router.buildUrl('inbox'), className: routerSource.isActive('inbox') ? 'active' : '' }, 'Inbox'), 11 | a({ href: sources.router.buildUrl('compose'), className: routerSource.isActive('compose') ? 'active' : '' }, 'Compose') 12 | ]) 13 | ); 14 | 15 | return { 16 | DOM: nav$ 17 | }; 18 | }; 19 | 20 | export default Nav; 21 | -------------------------------------------------------------------------------- /apps/react-redux/components/Main.js: -------------------------------------------------------------------------------- 1 | import React, { createElement } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { routeNodeSelector } from 'redux-router5'; 4 | import Inbox from './Inbox'; 5 | import Compose from './Compose'; 6 | import NotFound from './NotFound'; 7 | 8 | const components = { 9 | 'inbox': Inbox, 10 | 'compose': Compose 11 | }; 12 | 13 | function Main(props) { 14 | const { route } = props; 15 | const segment = route ? route.name.split('.')[0] : undefined; 16 | 17 | return createElement(components[segment] || NotFound); 18 | } 19 | 20 | export default connect((state) => routeNodeSelector(''))(Main); 21 | -------------------------------------------------------------------------------- /apps/cycle/router5/link-interceptor-plugin.js: -------------------------------------------------------------------------------- 1 | import { shouldInterceptEvent, onClick } from './link-on-click'; 2 | 3 | const linkInterceptorPlugin = () => (router) => { 4 | const listener = (evt) => { 5 | if (shouldInterceptEvent(router)(evt)) { 6 | onClick(router)(evt); 7 | } 8 | }; 9 | 10 | return { 11 | name: 'LINK_INTERCEPTOR', 12 | onStart: () => { 13 | document.addEventListener('click', listener, false); 14 | }, 15 | onStop: () => { 16 | document.removeEventListener('click', listener); 17 | } 18 | } 19 | }; 20 | 21 | export default linkInterceptorPlugin; 22 | -------------------------------------------------------------------------------- /apps/deku-redux/components/Main.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | import Inbox from './Inbox'; 3 | import Compose from './Compose'; 4 | import NotFound from './NotFound'; 5 | import { connect } from 'deku-redux'; 6 | import { routeNodeSelector } from 'redux-router5'; 7 | 8 | const components = { 9 | 'inbox': Inbox, 10 | 'compose': Compose 11 | }; 12 | 13 | const Main = { 14 | render({ props }) { 15 | const { route } = props; 16 | const segment = route ? route.name.split('.')[0] : undefined; 17 | 18 | return element(components[segment] || NotFound); 19 | } 20 | }; 21 | 22 | export default connect((state) => routeNodeSelector(''))(Main); 23 | -------------------------------------------------------------------------------- /apps/deku/components/Inbox.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | import InboxList from './InboxList'; 3 | import Message from './Message'; 4 | import { routeNode } from 'deku-router5'; 5 | import { getEmails } from '../api'; 6 | 7 | const Inbox = { 8 | displayName: 'Inbox', 9 | render({ props }) { 10 | const { route } = props; 11 | 12 | return element('div', { class: 'inbox' }, [ 13 | element(InboxList, { emails: getEmails() }), 14 | route && route.name === 'inbox.message' ? element(Message, { messageId: route.params.id, key: route.params.id }) : null 15 | ]); 16 | } 17 | }; 18 | 19 | export default routeNode('inbox')(Inbox); 20 | -------------------------------------------------------------------------------- /apps/react-redux/main.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Provider } from 'react-redux'; 3 | import { RouterProvider } from 'react-router5'; 4 | import ReactDOM from 'react-dom'; 5 | import App from './components/App'; 6 | import createRouter from '../create-router' 7 | import configureStore from './store'; 8 | 9 | const router = createRouter(); 10 | const store = configureStore(router); 11 | const wrappedApp = ( 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | 19 | router.start((err, state) => { 20 | ReactDOM.render(wrappedApp, document.getElementById('app')); 21 | }); 22 | -------------------------------------------------------------------------------- /apps/react-redux/components/InboxList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import InboxItem from './InboxItem'; 3 | import { connect } from 'react-redux'; 4 | import { bindActionCreators } from 'redux'; 5 | import { actions } from 'redux-router5'; 6 | 7 | function InboxList({ emails, navigateTo }) { 8 | return ( 9 | 12 | ); 13 | } 14 | 15 | export default connect( 16 | state => ({ emails: state.emails }), 17 | dispatch => bindActionCreators({ navigateTo: actions.navigateTo }, dispatch) 18 | )(InboxList); 19 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Router5 examples 14 | 15 | 16 | 17 |
    18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /apps/create-router.js: -------------------------------------------------------------------------------- 1 | import createRouter from 'router5'; 2 | import loggerPlugin from 'router5/plugins/logger'; 3 | import listenersPlugin from 'router5/plugins/listeners'; 4 | import browserPlugin from 'router5/plugins/browser'; 5 | import routes from './routes'; 6 | 7 | export default function configureRouter(useListenersPlugin = false) { 8 | const router = createRouter(routes, { 9 | defaultRoute: 'inbox' 10 | }) 11 | // Plugins 12 | .usePlugin(loggerPlugin) 13 | .usePlugin(browserPlugin({ 14 | useHash: true 15 | })); 16 | 17 | if (useListenersPlugin) { 18 | router.usePlugin(listenersPlugin()); 19 | } 20 | 21 | return router; 22 | } 23 | -------------------------------------------------------------------------------- /apps/react-redux/store.js: -------------------------------------------------------------------------------- 1 | import { compose, createStore, applyMiddleware, combineReducers } from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import { router5Middleware, router5Reducer } from 'redux-router5'; 4 | import emails from './reducers/emails'; 5 | import draft from './reducers/draft'; 6 | import logger from 'redux-logger'; 7 | 8 | export default function configureStore(router, initialState = {}) { 9 | const createStoreWithMiddleware = applyMiddleware(router5Middleware(router), logger())(createStore); 10 | const store = createStoreWithMiddleware(combineReducers({ 11 | router: router5Reducer, 12 | emails, 13 | draft 14 | }), initialState); 15 | 16 | window.store = store; 17 | return store; 18 | } 19 | -------------------------------------------------------------------------------- /apps/deku-redux/store.js: -------------------------------------------------------------------------------- 1 | import { compose, createStore, applyMiddleware, combineReducers } from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import { router5Middleware, router5Reducer } from 'redux-router5'; 4 | import emails from './reducers/emails'; 5 | import draft from './reducers/draft'; 6 | import logger from 'redux-logger'; 7 | 8 | export default function configureStore(router, initialState = {}) { 9 | const createStoreWithMiddleware = applyMiddleware(router5Middleware(router), logger())(createStore); 10 | 11 | const store = createStoreWithMiddleware(combineReducers({ 12 | router: router5Reducer, 13 | emails, 14 | draft 15 | }), initialState); 16 | 17 | window.store = store; 18 | return store; 19 | } 20 | -------------------------------------------------------------------------------- /apps/deku-redux/components/Inbox.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | import InboxList from './InboxList'; 3 | import Message from './Message'; 4 | import { connect } from 'deku-redux'; 5 | import { routeNodeSelector } from 'redux-router5'; 6 | import { getEmails } from '../api'; 7 | 8 | const Inbox = { 9 | displayName: 'Inbox', 10 | render({ props }) { 11 | const { route } = props; 12 | 13 | return element('div', { class: 'inbox' }, [ 14 | element(InboxList, { emails: getEmails() }), 15 | route && route.name === 'inbox.message' ? element(Message, { messageId: route.params.id, key: route.params.id }) : null 16 | ]); 17 | } 18 | }; 19 | 20 | export default connect((state) => routeNodeSelector('inbox'))(Inbox); 21 | -------------------------------------------------------------------------------- /apps/deku-redux/components/Nav.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | import Link from './Link'; 3 | import { connect } from 'deku-redux'; 4 | import { actions } from 'redux-router5'; 5 | 6 | const Nav = { 7 | propTypes: { 8 | router: { source: 'router' } 9 | }, 10 | 11 | render({ props }) { 12 | const { router, navigateTo } = props; 13 | 14 | return element('nav', {}, [ 15 | element(Link, { router, navigateTo, name: 'inbox', options: { reload: true } }, 'Inbox'), 16 | element(Link, { router, navigateTo, name: 'compose' }, 'Compose'), 17 | element(Link, { router, navigateTo, name: 'contacts' }, 'Contacts') 18 | ]); 19 | } 20 | }; 21 | 22 | export default connect( 23 | state => state.router.route, 24 | { navigateTo: actions.navigateTo } 25 | )(Nav); 26 | -------------------------------------------------------------------------------- /apps/react/components/InboxItem.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | class InboxItem extends Component { 4 | constructor(props, context) { 5 | super(props, context); 6 | 7 | this.router = context.router; 8 | this.clickHandler = this.clickHandler.bind(this); 9 | } 10 | 11 | clickHandler() { 12 | this.router.navigate('inbox.message', {id: this.props.id}) 13 | } 14 | 15 | render() { 16 | var { mailTitle, mailMessage } = this.props; 17 | 18 | return ( 19 |
  • 20 |

    { mailTitle }

    21 |

    { mailMessage }

    22 |
  • 23 | ); 24 | } 25 | } 26 | 27 | InboxItem.contextTypes = { 28 | router: PropTypes.object.isRequired 29 | }; 30 | 31 | export default InboxItem; 32 | -------------------------------------------------------------------------------- /apps/cycle2/components/Nav.js: -------------------------------------------------------------------------------- 1 | import { h, a } from '@cycle/dom'; 2 | 3 | const LinkFactory = router => route => a({ 4 | href: router.buildUrl(route), 5 | className: router.isActive(route, {}, true) ? 'active' : '' 6 | }, route); 7 | 8 | function Nav(sources) { 9 | const routerSource = sources.router; 10 | const Link = LinkFactory(routerSource); 11 | 12 | const nav$ = routerSource 13 | .route$ 14 | .map(route => 15 | h('nav', [ 16 | Link('a'), 17 | Link('a.a'), 18 | Link('a.b'), 19 | Link('a.c'), 20 | Link('b'), 21 | Link('b.a'), 22 | Link('b.b'), 23 | Link('b.c') 24 | ]) 25 | ); 26 | 27 | return { 28 | DOM: nav$ 29 | }; 30 | }; 31 | 32 | export default Nav; 33 | -------------------------------------------------------------------------------- /apps/react-redux/components/Link.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | function Link(props) { 4 | const { name, params, options, router, navigateTo } = props; 5 | 6 | const href = router.buildUrl(name, params); 7 | const onClick = (evt) => { 8 | evt.preventDefault(); 9 | navigateTo(name, params, options); 10 | }; 11 | const className = router.isActive(name, params) ? 'active' : ''; 12 | 13 | return { props.children } 14 | } 15 | 16 | Link.propTypes = { 17 | name: PropTypes.string.isRequired, 18 | params: PropTypes.object, 19 | options: PropTypes.object, 20 | navigateTo: PropTypes.func.isRequired 21 | }; 22 | 23 | Link.defaultProps = { 24 | params: {}, 25 | options: {} 26 | }; 27 | 28 | export default Link; 29 | -------------------------------------------------------------------------------- /apps/deku/components/Compose.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | import { routeNode } from 'deku-router5'; 3 | 4 | const Compose = { 5 | intitalState(props) { 6 | return { title: '', message: '' }; 7 | }, 8 | 9 | render({ state }, setState) { 10 | const { title, message } = state; 11 | 12 | const updateState = prop => evt => setState(prop, evt.target.value); 13 | 14 | return element('div', { class: 'compose' }, [ 15 | element('h4', {}, 'Compose a new message'), 16 | element('input', { name: 'title', value: title, onChange: updateState('title') }), 17 | element('textarea', { name: 'message', value: message, onChange: updateState('message') }) 18 | ]); 19 | // { warning ?

    Clear inputs before continuing

    : null } 20 | } 21 | }; 22 | 23 | export default routeNode('compose')(Compose); 24 | -------------------------------------------------------------------------------- /apps/deku-redux/components/Link.js: -------------------------------------------------------------------------------- 1 | import element from 'virtual-element'; 2 | 3 | const Link = { 4 | propTypes: { 5 | name: { type: 'string' }, 6 | params: { type: 'object' }, 7 | options: { type: 'object' }, 8 | navigateTo: { type: 'function' } 9 | }, 10 | 11 | defaultProps: { 12 | params: {}, 13 | options: {} 14 | }, 15 | 16 | render({ props }) { 17 | const { name, params, options, router, navigateTo } = props; 18 | 19 | const href = router.buildUrl(name); 20 | const onClick = (evt) => { 21 | evt.preventDefault(); 22 | navigateTo(name, params, options); 23 | }; 24 | const className = router.isActive(name, params) ? 'active' : ''; 25 | 26 | return element('a', { href, onClick, 'class': className }, props.children); 27 | } 28 | }; 29 | 30 | export default Link; 31 | -------------------------------------------------------------------------------- /example2.css: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Example 2 4 | */ 5 | 6 | .box { 7 | position: absolute: 8 | top: 0; 9 | right: 0; 10 | bottom: 0; 11 | left: 0; 12 | } 13 | 14 | .box > aside { 15 | background-color: #ddd; 16 | position: absolute; 17 | top: 0; 18 | width: 40%; 19 | bottom: 0; 20 | left: 0; 21 | } 22 | 23 | .box > main { 24 | position: absolute; 25 | display: flex; 26 | top: 0; 27 | right: 0; 28 | bottom: 0; 29 | left: 40%; 30 | } 31 | 32 | .box > aside a { 33 | padding: 10px 40px 10px 20px; 34 | display: block; 35 | text-align: right; 36 | } 37 | 38 | .box > aside a.active { 39 | color: white; 40 | background-color: black; 41 | } 42 | 43 | .column { 44 | flex: 1; 45 | display: flex; 46 | flex-direction: column; 47 | } 48 | 49 | .item { 50 | display: flex; 51 | flex: 1; 52 | justify-content: center; 53 | align-items: center; 54 | font-weight: bold; 55 | font-size: 30px; 56 | color: white; 57 | } 58 | -------------------------------------------------------------------------------- /styles2.css: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Example 2 4 | */ 5 | 6 | .box { 7 | position: absolute: 8 | top: 0; 9 | right: 0; 10 | bottom: 0; 11 | left: 0; 12 | } 13 | 14 | .box > aside { 15 | background-color: #ddd; 16 | position: absolute; 17 | top: 0; 18 | width: 40%; 19 | bottom: 0; 20 | left: 0; 21 | } 22 | 23 | .box > main { 24 | position: absolute; 25 | display: flex; 26 | top: 0; 27 | right: 0; 28 | bottom: 0; 29 | left: 40%; 30 | } 31 | 32 | .box > aside a { 33 | padding: 10px 40px 10px 20px; 34 | display: block; 35 | text-align: right; 36 | } 37 | 38 | .box > aside a.active { 39 | color: white; 40 | background-color: black; 41 | } 42 | 43 | .column { 44 | flex: 1; 45 | display: flex; 46 | flex-direction: column; 47 | } 48 | 49 | .item { 50 | display: flex; 51 | flex: 1; 52 | justify-content: center; 53 | align-items: center; 54 | font-weight: bold; 55 | font-size: 30px; 56 | color: white; 57 | } 58 | -------------------------------------------------------------------------------- /apps/cycle/data/emails.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | "id": "1", 4 | "mailTitle": "Why router5?", 5 | "mailMessage": "I imagine a lot of developers who will first see router5 will ask themselves the question: is it yet another router? is it any good? Why oh why do people keep writing new routers all the time? It is not always easy to see the potential of something straight away, or understand the motivations behind. I therefore decided to try to tell you more about router5, why I decided to develop an entire new routing solution, and what problems it tries to solve." 6 | }, 7 | { 8 | "id": "2", 9 | "mailTitle": "Use with Cycle", 10 | "mailMessage": "Make a driver using your router instance, observe route and node changes and request navigation" 11 | }, 12 | { 13 | "id": "3", 14 | "mailTitle": "Compose a new message", 15 | "mailMessage": "Click on compose, start to fill title and message fields and then try to navigate away by clicking on app links, or by using the back button." 16 | } 17 | ]; 18 | -------------------------------------------------------------------------------- /apps/cycle/components/Inbox.js: -------------------------------------------------------------------------------- 1 | import Rx from 'rx'; 2 | import { h, div } from '@cycle/dom'; 3 | import InboxList from './InboxList'; 4 | import Message from './Message'; 5 | 6 | function Inbox(sources) { 7 | const emails$ = Rx.Observable.combineLatest( 8 | sources.router.routeNode$('inbox'), 9 | sources.data.emails$, 10 | (route, emails) => { 11 | const email = route.name === 'inbox.message' 12 | ? emails.find(({ id }) => id === route.params.id) 13 | : null; 14 | 15 | return { emails, email }; 16 | } 17 | ); 18 | 19 | const inbox$ = emails$ 20 | .map(({ emails, email }) => { 21 | const inboxList = InboxList({ emails, buildUrl: sources.router.buildUrl }); 22 | const message = email && Message({ email }); 23 | 24 | return div({ className: 'inbox' }, [ 25 | inboxList, 26 | message 27 | ]); 28 | }); 29 | 30 | return { 31 | DOM: inbox$ 32 | }; 33 | }; 34 | 35 | export default Inbox; 36 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | 4 | var argv = require('yargs').argv; 5 | var app = argv.app || 'react'; 6 | var isProd = process.env.NODE_ENV === 'production'; 7 | 8 | module.exports = { 9 | entry: [ 10 | './apps/' + app + '/main.js' 11 | ], 12 | output: { 13 | path: 'build/', 14 | filename: isProd ? 'router5-' + app + '-example.js' : 'app.js' 15 | }, 16 | plugins: [ 17 | new webpack.optimize.OccurenceOrderPlugin() 18 | ].concat(!isProd ? [] : [ 19 | new webpack.optimize.UglifyJsPlugin({ 20 | compressor: { 21 | warnings: false 22 | } 23 | }), 24 | new webpack.DefinePlugin({ 25 | 'process.env': { 26 | 'NODE_ENV': JSON.stringify('production') 27 | } 28 | }) 29 | ]), 30 | module: { 31 | loaders: [{ 32 | test: /\.js$/, 33 | loaders: ['babel'], 34 | include: path.join(__dirname, 'apps') 35 | }] 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /apps/cycle/components/Main.js: -------------------------------------------------------------------------------- 1 | import Rx from 'rx'; 2 | import { h, div, a, makeDOMDriver } from '@cycle/dom'; 3 | import { startsWithSegment } from 'router5.helpers'; 4 | import Inbox from './Inbox'; 5 | import Compose from './Compose'; 6 | 7 | function Main(sources) { 8 | const routerSource = sources.router; 9 | 10 | const routeComponent$ = routerSource 11 | .routeNode$('') 12 | .map(route => { 13 | const startsWith = startsWithSegment(route); 14 | 15 | if (startsWith('inbox')) { 16 | return Inbox(sources); 17 | } 18 | 19 | if (startsWith('compose')) { 20 | return Compose(sources); 21 | } 22 | 23 | return { 24 | DOM: Rx.Observable.of(div('Route component not implemented')) 25 | }; 26 | }); 27 | 28 | return { 29 | DOM: routeComponent$ 30 | .flatMapLatest(component => component.DOM), 31 | router: routeComponent$ 32 | .flatMapLatest(component => component.router || Rx.Observable.empty()) 33 | }; 34 | }; 35 | 36 | export default Main; 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 router5 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /apps/react-redux/components/Nav.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import Link from './Link'; 3 | import { connect } from 'react-redux'; 4 | import { bindActionCreators } from 'redux'; 5 | import { actions } from 'redux-router5'; 6 | 7 | class Nav extends Component { 8 | constructor(props, context) { 9 | super(props); 10 | this.router = context.router; 11 | } 12 | 13 | render() { 14 | const { navigateTo, route } = this.props; 15 | 16 | return ( 17 | 22 | ); 23 | } 24 | } 25 | 26 | Nav.contextTypes = { 27 | router: PropTypes.object.isRequired 28 | }; 29 | 30 | export default connect( 31 | state => state.router.route, 32 | dispatch => bindActionCreators({ navigateTo: actions.navigateTo }, dispatch) 33 | )(Nav); 34 | -------------------------------------------------------------------------------- /apps/cycle/router5/router-to-observable.js: -------------------------------------------------------------------------------- 1 | const routerToObservable = (router, onCreate) => 2 | Rx.Observable.create(observer => { 3 | const pushState = (type, isError) => (toState, fromState, ...args) => { 4 | const routerEvt = { type, toState, fromState }; 5 | observer.onNext(isError ? { ...routerEvt, error: args[0] } : routerEvt); 6 | }; 7 | const push = type => () => observer.onNext({ type }); 8 | 9 | // A Router5 plugin to push any router event to the observer 10 | const cyclePlugin = () => ({ 11 | name: 'CYCLE_DRIVER', 12 | onStart: push('start'), 13 | onStop: push('stop'), 14 | onTransitionSuccess: pushState('transitionSuccess'), 15 | onTransitionError: pushState('transitionError', true), 16 | onTransitionStart: pushState('transitionStart'), 17 | onTransitionCancel: pushState('transitionCancel') 18 | }); 19 | 20 | // Register plugin and start 21 | router.usePlugin(cyclePlugin); 22 | 23 | // On observable create callback (used to start router) 24 | onCreate && onCreate(); 25 | }); 26 | 27 | export default routerToObservable; 28 | -------------------------------------------------------------------------------- /apps/deku-redux/reducers/emails.js: -------------------------------------------------------------------------------- 1 | const initialState = [ 2 | { 3 | "id": "1", 4 | "mailTitle": "Why router5?", 5 | "mailMessage": "I imagine a lot of developers who will first see router5 will ask themselves the question: is it yet another router? is it any good? Why oh why do people keep writing new routers all the time? It is not always easy to see the potential of something straight away, or understand the motivations behind. I therefore decided to try to tell you more about router5, why I decided to develop an entire new routing solution, and what problems it tries to solve." 6 | }, 7 | { 8 | "id": "2", 9 | "mailTitle": "Use with React", 10 | "mailMessage": "I have just started playing with it. It does make sense to use a flux-like implementation, to provide a layer between the router and view updates." 11 | }, 12 | { 13 | "id": "3", 14 | "mailTitle": "Compose a new message", 15 | "mailMessage": "Click on compose, start to fill title and message fields and then try to navigate away by clicking on app links, or by using the back button." 16 | } 17 | ]; 18 | 19 | export default function emails(state = initialState, action) { 20 | switch (action.type) { 21 | default: 22 | return state; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /apps/react-redux/reducers/emails.js: -------------------------------------------------------------------------------- 1 | const initialState = [ 2 | { 3 | "id": "1", 4 | "mailTitle": "Why router5?", 5 | "mailMessage": "I imagine a lot of developers who will first see router5 will ask themselves the question: is it yet another router? is it any good? Why oh why do people keep writing new routers all the time? It is not always easy to see the potential of something straight away, or understand the motivations behind. I therefore decided to try to tell you more about router5, why I decided to develop an entire new routing solution, and what problems it tries to solve." 6 | }, 7 | { 8 | "id": "2", 9 | "mailTitle": "Use with React", 10 | "mailMessage": "I have just started playing with it. It does make sense to use a flux-like implementation, to provide a layer between the router and view updates." 11 | }, 12 | { 13 | "id": "3", 14 | "mailTitle": "Compose a new message", 15 | "mailMessage": "Click on compose, start to fill title and message fields and then try to navigate away by clicking on app links, or by using the back button." 16 | } 17 | ]; 18 | 19 | export default function emails(state = initialState, action) { 20 | switch (action.type) { 21 | default: 22 | return state; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /apps/deku/api.js: -------------------------------------------------------------------------------- 1 | const emails = [ 2 | { 3 | "id": "1", 4 | "mailTitle": "Why router5?", 5 | "mailMessage": "I imagine a lot of developers who will first see router5 will ask themselves the question: is it yet another router? is it any good? Why oh why do people keep writing new routers all the time? It is not always easy to see the potential of something straight away, or understand the motivations behind. I therefore decided to try to tell you more about router5, why I decided to develop an entire new routing solution, and what problems it tries to solve." 6 | }, 7 | { 8 | "id": "2", 9 | "mailTitle": "Use with React", 10 | "mailMessage": "I have just started playing with it. It does make sense to use a flux-like implementation, to provide a layer between the router and view updates." 11 | }, 12 | { 13 | "id": "3", 14 | "mailTitle": "Compose a new message", 15 | "mailMessage": "Click on compose, start to fill title and message fields and then try to navigate away by clicking on app links, or by using the back button." 16 | } 17 | ]; 18 | 19 | export function getEmails() { 20 | return emails; 21 | } 22 | 23 | export function getEmail(id) { 24 | let index; 25 | 26 | if (emails) { 27 | for (index in emails) { 28 | if (emails[index].id === id) return emails[index]; 29 | } 30 | } 31 | return null; 32 | } 33 | -------------------------------------------------------------------------------- /apps/react/api.js: -------------------------------------------------------------------------------- 1 | const emails = [ 2 | { 3 | "id": "1", 4 | "mailTitle": "Why router5?", 5 | "mailMessage": "I imagine a lot of developers who will first see router5 will ask themselves the question: is it yet another router? is it any good? Why oh why do people keep writing new routers all the time? It is not always easy to see the potential of something straight away, or understand the motivations behind. I therefore decided to try to tell you more about router5, why I decided to develop an entire new routing solution, and what problems it tries to solve." 6 | }, 7 | { 8 | "id": "2", 9 | "mailTitle": "Use with React", 10 | "mailMessage": "I have just started playing with it. It does make sense to use a flux-like implementation, to provide a layer between the router and view updates." 11 | }, 12 | { 13 | "id": "3", 14 | "mailTitle": "Compose a new message", 15 | "mailMessage": "Click on compose, start to fill title and message fields and then try to navigate away by clicking on app links, or by using the back button." 16 | } 17 | ]; 18 | 19 | export function getEmails() { 20 | return emails; 21 | } 22 | 23 | export function getEmail(id) { 24 | let index; 25 | 26 | if (emails) { 27 | for (index in emails) { 28 | if (emails[index].id === id) return emails[index]; 29 | } 30 | } 31 | return null; 32 | } 33 | -------------------------------------------------------------------------------- /apps/deku-redux/api.js: -------------------------------------------------------------------------------- 1 | const emails = [ 2 | { 3 | "id": "1", 4 | "mailTitle": "Why router5?", 5 | "mailMessage": "I imagine a lot of developers who will first see router5 will ask themselves the question: is it yet another router? is it any good? Why oh why do people keep writing new routers all the time? It is not always easy to see the potential of something straight away, or understand the motivations behind. I therefore decided to try to tell you more about router5, why I decided to develop an entire new routing solution, and what problems it tries to solve." 6 | }, 7 | { 8 | "id": "2", 9 | "mailTitle": "Use with React", 10 | "mailMessage": "I have just started playing with it. It does make sense to use a flux-like implementation, to provide a layer between the router and view updates." 11 | }, 12 | { 13 | "id": "3", 14 | "mailTitle": "Compose a new message", 15 | "mailMessage": "Click on compose, start to fill title and message fields and then try to navigate away by clicking on app links, or by using the back button." 16 | } 17 | ]; 18 | 19 | export function getEmails() { 20 | return emails; 21 | } 22 | 23 | export function getEmail(id) { 24 | let index; 25 | 26 | if (emails) { 27 | for (index in emails) { 28 | if (emails[index].id === id) return emails[index]; 29 | } 30 | } 31 | return null; 32 | } 33 | -------------------------------------------------------------------------------- /apps/cycle/components/Compose.js: -------------------------------------------------------------------------------- 1 | import { h, div, h4, input, textarea, p } from '@cycle/dom'; 2 | import Rx from 'rx'; 3 | 4 | function Compose(sources) { 5 | const initialState = { title: '', message: '' }; 6 | 7 | const title$ = sources.DOM.select('.mail-title') 8 | .events('input') 9 | .map(evt => evt.target.value); 10 | 11 | const message$ = sources.DOM.select('.mail-message') 12 | .events('input') 13 | .map(evt => evt.target.value); 14 | 15 | const messageAndTitle$ = Rx.Observable.combineLatest( 16 | title$.startWith(''), 17 | message$.startWith(''), 18 | (title, message) => ({ title, message }) 19 | ); 20 | 21 | const compose$ = Rx.Observable.combineLatest( 22 | messageAndTitle$, 23 | sources.router.error$ 24 | .filter(err => err && err.code === 'CANNOT_DEACTIVATE') 25 | .startWith(''), 26 | ({ title, message }, routerError) => div({ className: 'compose' }, [ 27 | h4('Compose a new message'), 28 | input({ className: 'mail-title', name: 'title', value: title }), 29 | textarea({ className: 'mail-message', name: 'message', value: message }), 30 | routerError ? p('Clear inputs before continuing') : null 31 | ]) 32 | ); 33 | 34 | return { 35 | DOM: compose$, 36 | router: messageAndTitle$ 37 | .map(({ message, title }) => [ 'canDeactivate', 'compose', !message && !title ]) 38 | .distinctUntilChanged(_ => _[2]) 39 | }; 40 | }; 41 | 42 | export default Compose; 43 | -------------------------------------------------------------------------------- /apps/react/components/Compose.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { routeNode } from 'react-router5'; 3 | 4 | class Compose extends Component { 5 | constructor(props, context) { 6 | super(props, context); 7 | this.state = { 8 | title: undefined, 9 | message: undefined 10 | }; 11 | this.updateTitle = this.updateTitle.bind(this); 12 | this.updateMessage = this.updateMessage.bind(this); 13 | this.canDeactivate = this.canDeactivate.bind(this); 14 | this.props.router.canDeactivate('compose', () => this.canDeactivate.bind(this)); 15 | } 16 | 17 | canDeactivate() { 18 | if (this.state.title || this.state.message) { 19 | this.setState({ warning: true }) 20 | return false; 21 | } 22 | 23 | return true; 24 | } 25 | 26 | updateTitle(evt) { 27 | this.setState({title: evt.target.value, warning: false}); 28 | } 29 | 30 | updateMessage(evt) { 31 | this.setState({message: evt.target.value, warning: false}); 32 | } 33 | 34 | render() { 35 | const {title, message, warning} = this.state; 36 | 37 | return ( 38 |
    39 |

    Compose a new message

    40 | 41 | 42 |