Bläddra i källkod

use react-router-redux

John 9 år sedan
förälder
incheckning
41b4adb415
42 ändrade filer med 477 tillägg och 636 borttagningar
  1. 4 8
      package.json
  2. 29 0
      src/components/Counter/Counter.js
  3. 11 0
      src/components/Counter/Counter.scss
  4. 3 0
      src/components/Counter/index.js
  5. 18 0
      src/components/Header/Header.js
  6. 4 0
      src/components/Header/Header.scss
  7. 3 0
      src/components/Header/index.js
  8. 0 28
      src/components/content/Content.js
  9. 0 12
      src/components/index.js
  10. 0 38
      src/components/layout/Header.js
  11. 0 15
      src/components/layout/Layout.css
  12. 0 37
      src/components/layout/Layout.js
  13. 0 36
      src/components/layout/Navigation.js
  14. 26 0
      src/containers/AppContainer.js
  15. 0 23
      src/core/app.js
  16. 0 27
      src/core/store.js
  17. 19 0
      src/layouts/CoreLayout/CoreLayout.js
  18. 3 0
      src/layouts/CoreLayout/CoreLayout.scss
  19. 3 0
      src/layouts/CoreLayout/index.js
  20. 77 0
      src/main.js
  21. 38 0
      src/routes/Counter/containers/CounterContainer.js
  22. 24 0
      src/routes/Counter/index.js
  23. 55 0
      src/routes/Counter/modules/counter.js
  24. 0 60
      src/routes/README.md
  25. 0 36
      src/routes/about/index.js
  26. 0 68
      src/routes/about/index.md
  27. 0 55
      src/routes/error/index.js
  28. BIN
      src/routes/home/assets/Duck.jpg
  29. 15 0
      src/routes/home/components/HomeView.js
  30. 5 0
      src/routes/home/components/HomeView.scss
  31. 5 35
      src/routes/home/index.js
  32. 0 10
      src/routes/home/index.md
  33. 30 21
      src/routes/index.js
  34. 1 3
      src/static/index.html
  35. 44 0
      src/store/createStore.js
  36. 17 0
      src/store/reducers.js
  37. 12 0
      src/styles/_base.scss
  38. 21 0
      src/styles/core.scss
  39. 0 51
      tools/deploy.gh.js
  40. 0 31
      tools/deploy.s3.js
  41. 10 2
      tools/webpack.config.js
  42. 0 40
      tools/webpack.markdown-loader.js

+ 4 - 8
package.json

@@ -37,12 +37,8 @@
     "fastclick": "^1.0.6",
     "fbjs": "^0.8.2",
     "file-loader": "^0.8.5",
-    "front-matter": "^2.0.7",
-    "git-repository": "^0.1.4",
-    "highlight.js": "^9.4.0",
     "history": "^2.1.1",
     "json-loader": "^0.5.4",
-    "markdown-it": "^6.0.2",
     "mocha": "^2.4.5",
     "node-sass": "^3.4.2",
     "pixrem": "^3.0.0",
@@ -64,9 +60,11 @@
     "react-dom": "^0.14.8",
     "react-hot-loader": "^3.0.0-beta.2",
     "react-redux": "^4.4.5",
+    "react-router": "^2.4.1",
     "react-router-redux": "^4.0.5",
+    "redbox-react": "^1.2.6",
     "redux": "^3.5.2",
-    "s3": "^4.4.0",
+    "redux-thunk": "^2.1.0",
     "sass-loader": "^3.1.2",
     "style-loader": "^0.13.1",
     "stylelint": "^6.4.1",
@@ -74,8 +72,7 @@
     "url-loader": "^0.5.7",
     "webpack": "^1.13.0",
     "webpack-dev-middleware": "^1.6.1",
-    "webpack-hot-middleware": "^2.10.0",
-    "whatwg-fetch": "^1.0.0"
+    "webpack-hot-middleware": "^2.10.0"
   },
   "eslintConfig": {
     "parser": "babel-eslint",
@@ -96,7 +93,6 @@
     "clean": "node tools/clean",
     "build": "node tools/build --release",
     "build:debug": "node tools/build",
-    "deploy": "node tools/deploy.gh --release",
     "start": "node tools/start"
   }
 }

+ 29 - 0
src/components/Counter/Counter.js

@@ -0,0 +1,29 @@
+import React from 'react'
+import classes from './Counter.scss'
+
+export const Counter = (props) => (
+  <div>
+    <h2 className={classes.counterContainer}>
+      Counter:
+      {' '}
+      <span className={classes['counterGreen']}>
+        {props.counter}
+      </span>
+    </h2>
+    <button className='btn btn-default' onClick={props.increment}>
+      Increment
+    </button>
+    {' '}
+    <button className='btn btn-default' onClick={props.doubleAsync}>
+      Double (Async)
+    </button>
+  </div>
+)
+
+Counter.propTypes = {
+  counter: React.PropTypes.number.isRequired,
+  doubleAsync: React.PropTypes.func.isRequired,
+  increment: React.PropTypes.func.isRequired
+}
+
+export default Counter

+ 11 - 0
src/components/Counter/Counter.scss

@@ -0,0 +1,11 @@
+.counter {
+  font-weight: bold;
+}
+
+.counterGreen {
+  color: rgb(25,200,25);
+}
+
+.counterContainer {
+  margin: 1em auto;
+}

+ 3 - 0
src/components/Counter/index.js

@@ -0,0 +1,3 @@
+import Counter from './Counter'
+
+export default Counter

+ 18 - 0
src/components/Header/Header.js

@@ -0,0 +1,18 @@
+import React from 'react'
+import { IndexLink, Link } from 'react-router'
+import classes from './Header.scss'
+
+export const Header = () => (
+  <div>
+    <h1>React Redux Starter Kit</h1>
+    <IndexLink to='/' activeClassName={classes.activeRoute}>
+      Home
+    </IndexLink>
+    {' · '}
+    <Link to='/counter' activeClassName={classes.activeRoute}>
+      Counter
+    </Link>
+  </div>
+)
+
+export default Header

+ 4 - 0
src/components/Header/Header.scss

@@ -0,0 +1,4 @@
+.activeRoute {
+  font-weight: bold;
+  text-decoration: underline;
+}

+ 3 - 0
src/components/Header/index.js

@@ -0,0 +1,3 @@
+import Header from './Header'
+
+export default Header

+ 0 - 28
src/components/content/Content.js

@@ -1,28 +0,0 @@
-/**
- * React Static Boilerplate
- * https://github.com/koistya/react-static-boilerplate
- *
- * Copyright © 2015-2016 Konstantin Tarkus (@koistya)
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-import React from 'react';
-import Layout from '../layout/Layout.js';
-
-function Content({ title, html }) {
-  return (
-    <Layout>
-      <h1>{title}</h1>
-      <div dangerouslySetInnerHTML={{ __html: html }} />
-    </Layout>
-  );
-}
-
-Content.propTypes = {
-  title: React.PropTypes.string.isRequired,
-  html: React.PropTypes.string.isRequired,
-};
-
-module.exports = Content;

+ 0 - 12
src/components/index.js

@@ -1,12 +0,0 @@
-/**
- * React Static Boilerplate
- * https://github.com/koistya/react-static-boilerplate
- *
- * Copyright © 2015-2016 Konstantin Tarkus (@koistya)
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-export Content from './content/Content';
-export Layout from './layout/Layout';

+ 0 - 38
src/components/layout/Header.js

@@ -1,38 +0,0 @@
-/**
- * React Static Boilerplate
- * https://github.com/koistya/react-static-boilerplate
- *
- * Copyright © 2015-2016 Konstantin Tarkus (@koistya)
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-import React from 'react';
-import Navigation from './Navigation';
-
-class Header extends React.Component {
-
-  componentDidMount() {
-    this.refs.root;
-  }
-
-  componentWillUnmount() {
-    this.refs.root;
-  }
-
-  render() {
-    return (
-      <header className="mdl-layout__header" ref="root">
-        <div className="mdl-layout__header-row">
-          <span className="mdl-layout-title">React Static Boilerplate</span>
-          <div className="mdl-layout-spacer"></div>
-          <Navigation />
-        </div>
-      </header>
-    );
-  }
-
-}
-
-module.exports = Header;

+ 0 - 15
src/components/layout/Layout.css

@@ -1,15 +0,0 @@
-/**
- * React Static Boilerplate
- * https://github.com/koistya/react-static-boilerplate
- *
- * Copyright © 2015-2016 Konstantin Tarkus (@koistya)
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-.content {
-  width: 100%;
-  max-width: 1000px;
-  margin: 0 auto;
-}

+ 0 - 37
src/components/layout/Layout.js

@@ -1,37 +0,0 @@
-/**
- * React Static Boilerplate
- * https://github.com/koistya/react-static-boilerplate
- *
- * Copyright © 2015-2016 Konstantin Tarkus (@koistya)
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-import React from 'react';
-import Header from './Header';
-import s from './Layout.css';
-
-class Layout extends React.Component {
-
-  componentDidMount() {
-    this.refs.root;
-  }
-
-  componentWillUnmount() {
-    this.refs.root;
-  }
-
-  render() {
-    return (
-      <div className="mdl-layout mdl-js-layout" ref="root">
-        <div className="mdl-layout__inner-container">
-          <Header />
-          <main {...this.props} className={s.content} />
-        </div>
-      </div>
-    );
-  }
-}
-
-module.exports = Layout;

+ 0 - 36
src/components/layout/Navigation.js

@@ -1,36 +0,0 @@
-/**
- * React Static Boilerplate
- * https://github.com/koistya/react-static-boilerplate
- *
- * Copyright © 2015-2016 Konstantin Tarkus (@koistya)
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-import React from 'react';
-import { Link } from 'react-app';
-
-class Navigation extends React.Component {
-
-  componentDidMount() {
-    this.refs.root;
-  }
-
-  componentWillUnmount() {
-    this.refs.root;
-  }
-
-  render() {
-    return (
-      <nav className="mdl-navigation" ref="root">
-        <Link className="mdl-navigation__link" to="/">Home</Link>
-        <Link className="mdl-navigation__link" to="/about">About</Link>
-        <Link className="mdl-navigation__link" to="/not-found">Not Found</Link>
-      </nav>
-    );
-  }
-
-}
-
-module.exports = Navigation;

+ 26 - 0
src/containers/AppContainer.js

@@ -0,0 +1,26 @@
+import React, { PropTypes } from 'react'
+import { Router } from 'react-router'
+import { Provider } from 'react-redux'
+
+class AppContainer extends React.Component {
+  static propTypes = {
+    history: PropTypes.object.isRequired,
+    routes: PropTypes.object.isRequired,
+    routerKey: PropTypes.number,
+    store: PropTypes.object.isRequired
+  }
+
+  render () {
+    const { history, routes, routerKey, store } = this.props
+
+    return (
+      <Provider store={store}>
+        <div style={{ height: '100%' }}>
+          <Router history={history} children={routes} key={routerKey} />
+        </div>
+      </Provider>
+    )
+  }
+}
+
+export default AppContainer

+ 0 - 23
src/core/app.js

@@ -1,23 +0,0 @@
-/**
- * React Static Boilerplate
- * https://github.com/koistya/react-static-boilerplate
- *
- * Copyright © 2015-2016 Konstantin Tarkus (@koistya)
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-import 'es5-shim';
-import 'es5-shim/es5-sham';
-import 'babel-polyfill';
-
-import 'whatwg-fetch';
-import { createApp } from 'react-app';
-import store from './store';
-import routes from '../routes';
-
-createApp({
-  routes,
-  context: { store },
-  container: document.getElementById('container'),
-});

+ 0 - 27
src/core/store.js

@@ -1,27 +0,0 @@
-/**
- * React Static Boilerplate
- * https://github.com/koistya/react-static-boilerplate
- *
- * Copyright © 2015-2016 Konstantin Tarkus (@koistya)
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-import { createStore } from 'redux';
-
-/**
- * Centralized application state
- * See http://redux.js.org/
- */
-const store = createStore((state, action) => {
-  // TODO: Add action handlers (aka "reduces")
-  switch (action) {
-    case 'COUNT':
-      return { ...state, count: (state.count || 0) + 1 };
-    default:
-      return state;
-  }
-});
-
-module.exports = store;

+ 19 - 0
src/layouts/CoreLayout/CoreLayout.js

@@ -0,0 +1,19 @@
+import React from 'react'
+import Header from '../../components/Header'
+import classes from './CoreLayout.scss'
+import '../../styles/core.scss'
+
+export const CoreLayout = ({ children }) => (
+  <div className='container text-center'>
+    <Header />
+    <div className={classes.mainContainer}>
+      {children}
+    </div>
+  </div>
+)
+
+CoreLayout.propTypes = {
+  children: React.PropTypes.element.isRequired
+}
+
+export default CoreLayout

+ 3 - 0
src/layouts/CoreLayout/CoreLayout.scss

@@ -0,0 +1,3 @@
+.mainContainer {
+  padding-top:20px;
+}

+ 3 - 0
src/layouts/CoreLayout/index.js

@@ -0,0 +1,3 @@
+import CoreLayout from './CoreLayout'
+
+export default CoreLayout

+ 77 - 0
src/main.js

@@ -0,0 +1,77 @@
+import React from 'react'
+import ReactDOM from 'react-dom'
+import createBrowserHistory from 'history/lib/createBrowserHistory'
+import { useRouterHistory } from 'react-router'
+import syncHistoryWithStore from 'react-router-redux/lib/sync'
+import createStore from './store/createStore'
+import AppContainer from './containers/AppContainer'
+
+// ========================================================
+// Browser History Setup
+// ========================================================
+const browserHistory = useRouterHistory(createBrowserHistory)()
+
+// ========================================================
+// Store and History Instantiation
+// ========================================================
+// Create redux store and sync with react-router-redux. We have installed the
+// react-router-redux reducer under the routerKey "router" in src/routes/index.js,
+// so we need to provide a custom `selectLocationState` to inform
+// react-router-redux of its location.
+const initialState = window.___INITIAL_STATE__
+const store = createStore(initialState, browserHistory)
+const history = syncHistoryWithStore(browserHistory, store, {
+  selectLocationState: (state) => state.router
+})
+
+// ========================================================
+// Developer Tools Setup
+// ========================================================
+if (__DEV__) {
+  if (window.devToolsExtension) {
+    window.devToolsExtension.open()
+  }
+}
+
+// ========================================================
+// Render Setup
+// ========================================================
+const MOUNT_NODE = document.getElementById('container')
+
+let render = (routerKey = null) => {
+  const routes = require('./routes/index').default(store)
+
+  ReactDOM.render(
+    <AppContainer
+      store={store}
+      history={history}
+      routes={routes}
+      routerKey={routerKey}
+    />,
+    MOUNT_NODE
+  )
+}
+
+// Enable HMR and catch runtime errors in RedBox
+// This code is excluded from production bundle
+if (__DEV__ && module.hot) {
+  const renderApp = render
+  const renderError = (error) => {
+    const RedBox = require('redbox-react')
+
+    ReactDOM.render(<RedBox error={error} />, MOUNT_NODE)
+  }
+  render = () => {
+    try {
+      renderApp(Math.random())
+    } catch (error) {
+      renderError(error)
+    }
+  }
+  module.hot.accept(['./routes/index'], () => render())
+}
+
+// ========================================================
+// Go!
+// ========================================================
+render()

+ 38 - 0
src/routes/Counter/containers/CounterContainer.js

@@ -0,0 +1,38 @@
+import { connect } from 'react-redux'
+import { increment, doubleAsync } from '../modules/counter'
+
+/*  This is a container component. Notice it does not contain any JSX,
+    nor does it import React. This component is **only** responsible for
+    wiring in the actions and state necessary to render a presentational
+    component - in this case, the counter:   */
+
+import Counter from '../../../components/Counter'
+
+/*  Object of action creators (can also be function that returns object).
+    Keys will be passed as props to presentational components. Here we are
+    implementing our wrapper around increment; the component doesn't care   */
+
+const mapActionCreators = {
+  increment: () => increment(1),
+  doubleAsync
+}
+
+const mapStateToProps = (state) => ({
+  counter: state.counter
+})
+
+/*  Note: mapStateToProps is where you should use `reselect` to create selectors, ie:
+
+    import { createSelector } from 'reselect'
+    const counter = (state) => state.counter
+    const tripleCount = createSelector(counter, (count) => count * 3)
+    const mapStateToProps = (state) => ({
+      counter: tripleCount(state)
+    })
+
+    Selectors can compute derived data, allowing Redux to store the minimal possible state.
+    Selectors are efficient. A selector is not recomputed unless one of its arguments change.
+    Selectors are composable. They can be used as input to other selectors.
+    https://github.com/reactjs/reselect    */
+
+export default connect(mapStateToProps, mapActionCreators)(Counter)

+ 24 - 0
src/routes/Counter/index.js

@@ -0,0 +1,24 @@
+import { injectReducer } from '../../store/reducers'
+
+export default (store) => ({
+  path: 'counter',
+  /*  Async getComponent is only invoked when route matches   */
+  getComponent (nextState, cb) {
+    /*  Webpack - use 'require.ensure' to create a split point
+        and embed an async module loader (jsonp) when bundling   */
+    require.ensure([], (require) => {
+      /*  Webpack - use require callback to define
+          dependencies for bundling   */
+      const Counter = require('./containers/CounterContainer').default
+      const reducer = require('./modules/counter').default
+
+      /*  Add the reducer to the store on key 'counter'  */
+      injectReducer(store, { key: 'counter', reducer })
+
+      /*  Return getComponent   */
+      cb(null, Counter)
+
+    /* Webpack named bundle   */
+    }, 'counter')
+  }
+})

+ 55 - 0
src/routes/Counter/modules/counter.js

@@ -0,0 +1,55 @@
+// ------------------------------------
+// Constants
+// ------------------------------------
+export const COUNTER_INCREMENT = 'COUNTER_INCREMENT'
+
+// ------------------------------------
+// Actions
+// ------------------------------------
+export function increment (value = 1) {
+  return {
+    type: COUNTER_INCREMENT,
+    payload: value
+  }
+}
+
+/*  This is a thunk, meaning it is a function that immediately
+    returns a function for lazy evaluation. It is incredibly useful for
+    creating async actions, especially when combined with redux-thunk!
+
+    NOTE: This is solely for demonstration purposes. In a real application,
+    you'd probably want to dispatch an action of COUNTER_DOUBLE and let the
+    reducer take care of this logic.  */
+
+export const doubleAsync = () => {
+  return (dispatch, getState) => {
+    return new Promise((resolve) => {
+      setTimeout(() => {
+        dispatch(increment(getState().counter))
+        resolve()
+      }, 200)
+    })
+  }
+}
+
+export const actions = {
+  increment,
+  doubleAsync
+}
+
+// ------------------------------------
+// Action Handlers
+// ------------------------------------
+const ACTION_HANDLERS = {
+  [COUNTER_INCREMENT]: (state, action) => state + action.payload
+}
+
+// ------------------------------------
+// Reducer
+// ------------------------------------
+const initialState = 0
+export default function counterReducer (state = initialState, action) {
+  const handler = ACTION_HANDLERS[action.type]
+
+  return handler ? handler(state, action) : state
+}

+ 0 - 60
src/routes/README.md

@@ -1,60 +0,0 @@
-## Application Routes
-
-Each routes is just a plain JavaScript object having `path` (string or regular expression), `action`
-(function) and optionally `children` (array with child routes) attributes, for example:
-
-```js
-import AboutPage from './AboutPage';
-
-export default {
-
-  path: '/about',
-
-  action: () => ({
-    title: 'About Us',
-    component: AboutPage,
-  }),
-
-};
-```
-
-The `action()` (aka route handler) must return an object having `title` (string), `component`
-(React component) and optionally `props` fields. It can be asynchronous, as the following example
-demonstrates:
-
-```js
-import Post from './Post';
-
-export default {
-
-  path: '/posts/:id',
-
-  async action({ params }) {
-    const resp = await fetch(`/api/posts/${params.id}`);
-    const data = await resp.json();
-    return {
-      title: data.title,
-      component: Post,
-      props: data,
-    };
-  },
-
-};
-```
-
-In order to keep your project more organized, create a separate folder for each route (view/screen),
-where each route may contain routing and data fetching logic, as well as all the UI components and
-graphics required to render that particular view, for example:
-
-```shell
-├── /routes/                    # View/screen UI components + routing information
-│   ├── /profile/               # User profile page, e.g. www.example.com/username
-│   │   ├── /index.js           # Routing and fetching, e.g. { path: '/:username', action() { .. } }
-│   │   ├── /ProfilePage.css    # Styles for the profile page
-│   │   ├── /ProfilePage.js     # Profile page component
-│   │   ├── /ProfilePicture.css # Styles for user's photo component
-│   │   ├── /ProfilePicture.js  # User's photo component
-│   │   ├── /UserAcitivy.css    # Style for user's activity component
-│   │   └── /UserAcitivy.js     # User's activity component
-│   └── /...                    # etc.
-```

+ 0 - 36
src/routes/about/index.js

@@ -1,36 +0,0 @@
-/**
- * React Static Boilerplate
- * https://github.com/koistya/react-static-boilerplate
- *
- * Copyright © 2015-2016 Konstantin Tarkus (@koistya)
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-import { Content } from '../../components';
-
-const about = {
-
-  path: '/about',
-
-  async action() {
-    return new Promise((resolve, reject) => {
-      require.ensure([], require => {
-        try {
-          const content = require('./index.md');
-          resolve({
-            title: content.title,
-            component: Content,
-            props: content,
-          });
-        } catch (err) {
-          reject(err);
-        }
-      });
-    });
-  },
-
-};
-
-module.exports = about;

+ 0 - 68
src/routes/about/index.md

@@ -1,68 +0,0 @@
----
-title: About Us
----
-
-## Cadme comitum fecere
-
-Lorem markdownum velis auras figuram spes solebat spectabat, cum alium,
-plenissima aratri visae herbarum in corpore silvas consumpta. Subito virgae nec
-paratae flexit et niveae repperit erat paratu cum albis steterat conclamat hic!
-
-Nocte suae ligat! *Si* nitidum pervia, illa tua, ab minimo pasci dabitur? In
-fictus concurreret pennis, illis cum accipe rogavi in et nostro cum lacertis
-hostibus ab saxo ne. Genibusque vixque; sine videt terribili lucos ipsum vobis
-resque, et suum pietatis fulvis, est velle. Semele oscula ferat frigidus mactata
-montes, es me parari, piae.
-
-## Inflataque ait leves frigida
-
-Letum per ipsa nostro animae, mari illuc in levi corpus aestibus excussam
-deflentem sic cuius. Venere dedit illa cui in quo senecta artus bella inficit,
-Achaica. Videbatur crinem resonantia alto dea umida dicitur igne; meus signa
-habet; est. Cognovit coepta: similes fugis: habuissem votivi liquida: ictus visi
-nostra me Adoni.
-
-## Laedar cum margine quoque
-
-Quam dato ullis, acer venturi volantes! Tuam non non cursu acta hic, novem
-nutrit, in sidera viscera iam fontes tempora, omnes. Saturnius artus inquit,
-conatoque erectos lenius, carinae, ora est infamia elige per Medusaei induitur.
-Quem quem ab postquam tunc frondescere nodis capiam labique. Voluere luce
-Semeles.
-
-```
-    if (delete(digital, hibernateSoft, dynamicExcelVpn) > io_secondary_led /
-            84) {
-        disk = load;
-        orientationPci.matrix_laptop(modelSsdTweet);
-    } else {
-        kdeEmoticonLed.mebibyte_algorithm_domain(2,
-                hackerCtr.rom_iso_desktop.scarewarePrimaryBankruptcy(station,
-                disk_mask_matrix, restore_crt));
-        cameraSpyware(4, multitasking(-3, log_dfs_controller));
-        menuCisc.swappable -= w(mount_vle_unicode, 5);
-    }
-    var optic_spider = newbieFunctionThick(-3, esportsKbpsUnix);
-    var dvd_ctp_resolution = dithering;
-```
-
-## Usus fixurus illi petunt
-
-Domosque tune amas mihi adhuc et *alter per* suasque versavitque iners
-crescentemque nomen verba nunc. Acervos hinc natus si habet. Et cervix imago
-quod! Arduus dolet!
-
-```
-    cpcDdrCommand.window(moodleAlpha, im, server_alpha.doubleVrmlMonochrome(
-            iosBar - -2, white_dual, ad(2, 94, 83)));
-    mbps_typeface_publishing.bit.host_flash_capacity(click(90,
-            cyberspace_srgb_pup - mpeg, marketing_trackback +
-            table_plagiarism_domain));
-    syn_e = powerExtension * defragmentNntpOsd(alertOutputNode(pop,
-            pageResponsiveDrive));
-    method -= switch_newsgroup_flaming;
-```
-
-Aliquid mansura arida altismunera **in illi**. Dignus vir pontum *crimen
-versabat* carpunt omnes rotis Canentem erant in Oebalio, et manu senecta
-iungere. Prima diurnis!

+ 0 - 55
src/routes/error/index.js

@@ -1,55 +0,0 @@
-/**
- * React Static Boilerplate
- * https://github.com/koistya/react-static-boilerplate
- *
- * Copyright © 2015-2016 Konstantin Tarkus (@koistya)
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-import React from 'react';
-import { Layout } from '../../components';
-
-function ErrorPage({ title, message, stackTrace }) {
-  return (
-    <Layout>
-      <h1>{title}</h1>
-      <p>{message}</p>
-      {stackTrace && <pre>{stackTrace}</pre>}
-    </Layout>
-  );
-}
-
-ErrorPage.propTypes = {
-  title: React.PropTypes.string.isRequired,
-  message: React.PropTypes.string.isRequired,
-  stackTrace: React.PropTypes.string.isRequired,
-};
-
-const error = {
-
-  path: '/error',
-
-  action({ error = {} }) {
-    const props = {
-      title: 'Error',
-      message: 'Oups, something went wrong!',
-      stackTrace: process.env.NODE_ENV === 'production' ? null : error.stack,
-    };
-
-    if (error.status === 404) {
-      props.title = 'Page Not Found';
-      props.message = 'This page does not exist.';
-    }
-
-    return {
-      title: props.title,
-      component: ErrorPage,
-      props,
-    };
-  },
-
-};
-
-module.exports = error;

BIN
src/routes/home/assets/Duck.jpg


+ 15 - 0
src/routes/home/components/HomeView.js

@@ -0,0 +1,15 @@
+import React from 'react'
+import DuckImage from '../assets/Duck.jpg'
+import classes from './HomeView.scss'
+
+export const HomeView = () => (
+  <div>
+    <h4>Welcome!</h4>
+    <img
+      alt='This is a duck, because Redux!'
+      className={classes.duck}
+      src={DuckImage} />
+  </div>
+)
+
+export default HomeView

+ 5 - 0
src/routes/home/components/HomeView.scss

@@ -0,0 +1,5 @@
+.duck {
+  display: block;
+  width: 120px;
+  margin: 1.5rem auto;
+}

+ 5 - 35
src/routes/home/index.js

@@ -1,36 +1,6 @@
-/**
- * React Static Boilerplate
- * https://github.com/koistya/react-static-boilerplate
- *
- * Copyright © 2015-2016 Konstantin Tarkus (@koistya)
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
+import HomeView from './components/HomeView'
 
-import { Content } from '../../components';
-
-const home = {
-
-  path: '/',
-
-  async action() {
-    return new Promise((resolve, reject) => {
-      require.ensure([], require => {
-        try {
-          const content = require('./index.md');
-          resolve({
-            title: content.title,
-            component: Content,
-            props: content,
-          });
-        } catch (err) {
-          reject(err);
-        }
-      });
-    });
-  },
-
-};
-
-module.exports = home;
+// Sync route definition
+export default {
+  component: HomeView
+}

+ 0 - 10
src/routes/home/index.md

@@ -1,10 +0,0 @@
----
-title: React Static Boilerplate
----
-
-## Welcome!
-
-This is a single-page application powered by React and Material Design Lite (MDL).
-
-https://github.com/koistya/react-static-boilerplate
-

+ 30 - 21
src/routes/index.js

@@ -1,27 +1,36 @@
-/**
- * React Static Boilerplate
- * https://github.com/koistya/react-static-boilerplate
- *
- * Copyright © 2015-2016 Konstantin Tarkus (@koistya)
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
+// We only need to import the modules necessary for initial render
+import CoreLayout from '../layouts/CoreLayout/CoreLayout'
+import Home from './Home'
+import CounterRoute from './Counter'
 
-import home from './home';
-import about from './about';
-import error from './error';
-
-const routes = {
+/*  Note: Instead of using JSX, we recommend using react-router
+    PlainRoute objects to build route definitions.   */
 
+export const createRoutes = (store) => ({
   path: '/',
+  component: CoreLayout,
+  indexRoute: Home,
+  childRoutes: [
+    CounterRoute(store)
+  ]
+})
+
+/*  Note: childRoutes can be chunked or otherwise loaded programmatically
+    using getChildRoutes with the following signature:
 
-  children: [
-    home,
-    about,
-    error,
-  ],
+    getChildRoutes (location, cb) {
+      require.ensure([], (require) => {
+        cb(null, [
+          // Remove imports!
+          require('./Counter').default(store)
+        ])
+      })
+    }
 
-};
+    However, this is not necessary for code-splitting! It simply provides
+    an API for async route definitions. Your code splitting should occur
+    inside the route `getComponent` function, since it is only invoked
+    when the route exists and matches.
+*/
 
-module.exports = routes;
+export default createRoutes

+ 1 - 3
src/static/index.html

@@ -7,9 +7,7 @@
     <meta name="description" content="">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <link rel="apple-touch-icon" href="apple-touch-icon.png">
-    <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
-    <link rel="stylesheet" href="https://cdn.rawgit.com/tleunen/react-mdl/master/extra/material.min.css">
-    <link rel="stylesheet" href="https://cdn.rawgit.com/isagalaev/highlight.js/master/src/styles/default.css">
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
   </head>
   <body>
     <div id="container"></div>

+ 44 - 0
src/store/createStore.js

@@ -0,0 +1,44 @@
+import { applyMiddleware, compose, createStore } from 'redux'
+import routerMiddleware from 'react-router-redux/lib/middleware'
+import thunk from 'redux-thunk'
+import makeRootReducer from './reducers'
+
+export default (initialState = {}, history) => {
+  // ======================================================
+  // Middleware Configuration
+  // ======================================================
+  const middleware = [thunk, routerMiddleware(history)]
+
+  // ======================================================
+  // Store Enhancers
+  // ======================================================
+  const enhancers = []
+  if (__DEV__) {
+    const devToolsExtension = window.devToolsExtension
+    if (typeof devToolsExtension === 'function') {
+      enhancers.push(devToolsExtension())
+    }
+  }
+
+  // ======================================================
+  // Store Instantiation and HMR Setup
+  // ======================================================
+  const store = createStore(
+    makeRootReducer(),
+    initialState,
+    compose(
+      applyMiddleware(...middleware),
+      ...enhancers
+    )
+  )
+  store.asyncReducers = {}
+
+  if (module.hot) {
+    module.hot.accept('./reducers', () => {
+      const reducers = require('./reducers').default
+      store.replaceReducer(reducers)
+    })
+  }
+
+  return store
+}

+ 17 - 0
src/store/reducers.js

@@ -0,0 +1,17 @@
+import { combineReducers } from 'redux'
+import { routerReducer as router } from 'react-router-redux/lib/reducer'
+
+export const makeRootReducer = (asyncReducers) => {
+  return combineReducers({
+    // Add sync reducers here
+    router,
+    ...asyncReducers
+  })
+}
+
+export const injectReducer = (store, { key, reducer }) => {
+  store.asyncReducers[key] = reducer
+  store.replaceReducer(makeRootReducer(store.asyncReducers))
+}
+
+export default makeRootReducer

+ 12 - 0
src/styles/_base.scss

@@ -0,0 +1,12 @@
+/*
+Application Settings Go Here
+------------------------------------
+This file acts as a bundler for all variables/mixins/themes, so they
+can easily be swapped out without `core.scss` ever having to know.
+
+For example:
+
+@import './variables/colors';
+@import './variables/components';
+@import './themes/default';
+*/

+ 21 - 0
src/styles/core.scss

@@ -0,0 +1,21 @@
+:global {
+  @import 'base';
+
+  // Some best-practice CSS that's useful for most apps
+  // Just remove them if they're not what you want
+  html {
+    box-sizing: border-box;
+  }
+
+  html,
+  body {
+    margin: 0;
+    padding: 0;
+  }
+
+  *,
+  *:before,
+  *:after {
+    box-sizing: inherit;
+  }
+}

+ 0 - 51
tools/deploy.gh.js

@@ -1,51 +0,0 @@
-/**
- * React Static Boilerplate
- * https://github.com/koistya/react-static-boilerplate
- *
- * Copyright © 2015-2016 Konstantin Tarkus (@koistya)
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-const GitRepo = require('git-repository');
-const task = require('./task');
-
-const remote = {
-  name: 'github',
-  url: 'https://github.com/{user}/{repo}.git',
-  branch: 'gh-pages',
-};
-
-/**
- * Deploy the contents of the `/build` folder to GitHub Pages.
- */
-module.exports = task('deploy', () => new Promise((resolve, reject) => {
-  // Initialize a new Git repository inside the `/build` folder
-  // if it doesn't exist yet
-  let p = GitRepo.open('build', { init: true });
-  p = p.then(repo => {
-    p = p.then(() => repo.setRemote(remote.name, remote.url));
-    p = p.then(() => repo.hasRef(remote.url, remote.branch).then(exists => {
-      if (exists) {
-        p = p.then(() => repo.fetch(remote.name));
-        p = p.then(() => repo.reset(`${remote.name}/${remote.branch}`, { hard: true }));
-        p = p.then(() => repo.clean({ force: true }));
-      }
-    }));
-
-    // Build the project in RELEASE mode which
-    // generates optimized and minimized bundles
-    process.argv.push('release');
-    p = p.then(() => require('./build').default);
-
-    // Push the contents of the build folder to the remote server via Git
-    p = p.then(() => repo.add('--all .'));
-    p = p.then(() => repo.commit(`Update ${new Date().toISOString()}`));
-    p = p.then(() => repo.push(remote.name, `master:${remote.branch}`));
-
-    resolve(p);
-  });
-
-  p.catch(reject);
-}));

+ 0 - 31
tools/deploy.s3.js

@@ -1,31 +0,0 @@
-/**
- * React Static Boilerplate
- * https://github.com/koistya/react-static-boilerplate
- *
- * Copyright © 2015-2016 Konstantin Tarkus (@koistya)
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-const s3 = require('s3');
-const task = require('./task');
-
-module.exports = task('deploy', () => Promise.resolve()
-  .then(() => require('./build'))
-  .then(() => new Promise((resolve, reject) => {
-    const client = s3.createClient({
-      s3Options: {
-        region: 'us-east-1',
-        sslEnabled: true,
-      },
-    });
-    const uploader = client.uploadDir({
-      localDir: 'build',
-      deleteRemoved: true,
-      s3Params: { Bucket: 'www.example.com' },
-    });
-    uploader.on('error', reject);
-    uploader.on('end', resolve);
-  }))
-);

+ 10 - 2
tools/webpack.config.js

@@ -35,7 +35,12 @@ const config = {
   context: path.resolve(__dirname, '../src'),
 
   // The entry point for the bundle
-  entry: ['./core/app.js'],
+  entry: [
+    'es5-shim',
+    'es5-shim/es5-sham',
+    'babel-polyfill',
+    './main.js'
+  ],
 
   // Options affecting the output of the compilation
   output: {
@@ -81,8 +86,11 @@ const config = {
         test: /\.jsx?$/,
         include: [
           path.resolve(__dirname, '../src/components'),
-          path.resolve(__dirname, '../src/core'),
+          path.resolve(__dirname, '../src/containers'),
+          path.resolve(__dirname, '../src/layouts'),
           path.resolve(__dirname, '../src/routes'),
+          path.resolve(__dirname, '../src/store'),
+          path.resolve(__dirname, '../src/main'),
         ],
         loader: 'babel-loader',
         query: {

+ 0 - 40
tools/webpack.markdown-loader.js

@@ -1,40 +0,0 @@
-/**
- * React Static Boilerplate
- * https://github.com/koistya/react-static-boilerplate
- *
- * Copyright © 2015-2016 Konstantin Tarkus (@koistya)
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-const MarkdownIt = require('markdown-it');
-const hljs = require('highlight.js');
-const fm = require('front-matter');
-
-module.exports = function markdown(source) {
-  this.cacheable();
-
-  const md = new MarkdownIt({
-    html: true,
-    linkify: true,
-    highlight: (str, lang) => {
-      if (lang && hljs.getLanguage(lang)) {
-        try {
-          return hljs.highlight(lang, str).value;
-        } catch (err) { console.error(err.stack); }
-      }
-
-      try {
-        return hljs.highlightAuto(str).value;
-      } catch (err) { console.error(err.stack); }
-
-      return '';
-    },
-  });
-
-  const frontmatter = fm(source);
-  frontmatter.attributes.html = md.render(frontmatter.body);
-
-  return `module.exports = ${JSON.stringify(frontmatter.attributes)};`;
-};