From be7f760ccdb4316f8c2e3a75fb9f19a1f77ff5ff Mon Sep 17 00:00:00 2001 From: Ben Burlingham Date: Wed, 13 Apr 2016 18:13:42 -0700 Subject: [PATCH] Re-architecting to implement redux. --- .babelrc | 5 --- js/State.js | 33 -------------- js/app/App.js | 27 +++++++++++ js/app/Constants.js | 8 ++++ js/app/Creators.js | 4 ++ js/app/Reducers.js | 15 +++++++ js/board/Board.js | 14 +++--- js/board/Grid.js | 86 ++++++++++++++++-------------------- js/board/GridCell.js | 9 ++++ js/board/Message.js | 76 +++++++++++++++---------------- js/board/Muncher.js | 25 ++++++----- js/board/Scorebar.js | 58 ++++++++++++------------ js/board/Titlebar.js | 26 ++++------- js/index.js | 21 +++++++++ js/main.js | 36 --------------- js/welcome/HighScoreEntry.js | 13 ++++++ js/welcome/HighScores.js | 36 +++++++-------- js/welcome/NewGame.js | 31 +++++++++++++ js/welcome/Welcome.js | 57 +++++++++--------------- npm-debug.log | 24 ++++++++++ package.json | 12 ++--- readme.md | 2 +- webpack.config.js | 7 ++- 23 files changed, 337 insertions(+), 288 deletions(-) delete mode 100644 .babelrc delete mode 100644 js/State.js create mode 100644 js/app/App.js create mode 100644 js/app/Constants.js create mode 100644 js/app/Creators.js create mode 100644 js/app/Reducers.js create mode 100644 js/board/GridCell.js create mode 100644 js/index.js delete mode 100644 js/main.js create mode 100644 js/welcome/HighScoreEntry.js create mode 100644 js/welcome/NewGame.js create mode 100644 npm-debug.log diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 811d140..0000000 --- a/.babelrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "presets": [ - "react" - ] -} diff --git a/js/State.js b/js/State.js deleted file mode 100644 index 642717d..0000000 --- a/js/State.js +++ /dev/null @@ -1,33 +0,0 @@ -var State = require('./State'); -var actions = Object.create(null); - -/** - * - */ -module.exports = { - level: 0, - - subscribe(event, callback) { - if (actions[event] === undefined) { - actions[event] = []; - } - - actions[event].push(callback); - }, - - publish(event) { - if (actions[event] === undefined) { - return; - } - - var args = Array.prototype.slice.call(arguments, 1); - - actions[event].forEach(function(callback) { - callback.apply(null, args); - }); - } -}; - -module.exports.subscribe('level/complete', function() { - module.exports.level++; -}); diff --git a/js/app/App.js b/js/app/App.js new file mode 100644 index 0000000..3128b2f --- /dev/null +++ b/js/app/App.js @@ -0,0 +1,27 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; + +import Board from '../board/Board'; +import Welcome from '../welcome/Welcome'; +import { MODE } from './Constants'; + +export class App extends Component { + render() { + const { mode } = this.props; + + switch (mode) { + case MODE.WELCOME: + return ; + case MODE.BOARD: + return ; + } + }; +}; + +const select = (state) => { + return { + mode: state.mode + } +}; + +export default connect(select)(App); diff --git a/js/app/Constants.js b/js/app/Constants.js new file mode 100644 index 0000000..ddecf67 --- /dev/null +++ b/js/app/Constants.js @@ -0,0 +1,8 @@ +export const MODE = { + WELCOME: 'WELCOME', + BOARD: 'BOARD' +}; + +export const ACTIONS = { + NEXT: 'NEXT' +}; diff --git a/js/app/Creators.js b/js/app/Creators.js new file mode 100644 index 0000000..cf1f0d0 --- /dev/null +++ b/js/app/Creators.js @@ -0,0 +1,4 @@ +import { ACTIONS } from './Constants'; + +export const nextMode = + () => ({ type: ACTIONS.NEXT }); diff --git a/js/app/Reducers.js b/js/app/Reducers.js new file mode 100644 index 0000000..f7fc38d --- /dev/null +++ b/js/app/Reducers.js @@ -0,0 +1,15 @@ +import { MODE, ACTIONS } from './Constants.js'; + +export const modeReducer = (state = MODE.WELCOME, action) => { + if (action.type === ACTIONS.NEXT) { + switch (state) { + case MODE.BOARD: + return MODE.WELCOME; + case MODE.WELCOME: + return MODE.BOARD; + }; + } + else { + return MODE.WELCOME; + } +}; diff --git a/js/board/Board.js b/js/board/Board.js index 6f6d014..f0ebc2c 100644 --- a/js/board/Board.js +++ b/js/board/Board.js @@ -1,12 +1,12 @@ require('../../sass/board/board.scss'); -var React = require('react'); -var Scorebar = require('./Scorebar'); -var Titlebar = require('./Titlebar'); -var Grid = require('./Grid'); -var Message = require('./Message'); -var Muncher = require('./Muncher'); -var Input = require('./Input'); +import { Component } from 'react'; +import Scorebar from './Scorebar'; +import Titlebar from './Titlebar'; +import Grid from './Grid'; +import Message from './Message'; +import Muncher from './Muncher'; +import Input from './Input'; module.exports = React.createClass({ componentDidMount() { diff --git a/js/board/Grid.js b/js/board/Grid.js index 1499f85..7560431 100644 --- a/js/board/Grid.js +++ b/js/board/Grid.js @@ -1,68 +1,60 @@ require('../../sass/board/grid.scss'); -var React = require('react'); -var Values = require('./Values'); -var State = require('../State'); +import { Component } from 'react'; +import GridCell from './GridCell'; +// var Values = require('./Values'); -var Cell = React.createClass({ - render() { - var classname = ['cell', 'x' + this.props.x, 'y' + this.props.y]; - - return (
{this.props.value}
); - } -}); - -module.exports = React.createClass({ +export default class Grid extends Component { generateValues() { - return Values.generate(this.props.width * this.props.height, State.level); - }, + // return Values.generate(this.props.width * this.props.height, State.level); + }; - getInitialState() { - return { values: this.generateValues() }; - }, + // getInitialState() { + // return { values: this.generateValues() }; + // }, componentDidMount() { - State.subscribe('level/next', this.levelNext); - }, + // State.subscribe('level/next', this.levelNext); + }; checkComplete() { - if (Values.checkComplete(this.state.values, State.level) === true) { - State.publish('level/complete'); - } - }, + // if (Values.checkComplete(this.state.values, State.level) === true) { + // State.publish('level/complete'); + // } + }; levelNext() { - this.setState({ values: this.generateValues() }); - }, + // this.setState({ values: this.generateValues() }); + }; munch(x, y) { - var i = y * this.props.width + x; - - if (this.state.values[i] === "") { - return; - } - - if (Values.validate(this.state.values[i], State.level)) { - this.state.values[i] = ""; - this.setState({ values: this.state.values }, this.checkComplete); - State.publish('munch/successful'); - } - else { - State.publish('munch/failed', this.state.values[i]); - } - }, + // var i = y * this.props.width + x; + // + // if (this.state.values[i] === "") { + // return; + // } + // + // if (Values.validate(this.state.values[i], State.level)) { + // this.state.values[i] = ""; + // this.setState({ values: this.state.values }, this.checkComplete); + // State.publish('munch/successful'); + // } + // else { + // State.publish('munch/failed', this.state.values[i]); + // } + }; render() { - var cells = []; - var i; + const cells = []; + let i; - for (var x = 0; x < this.props.width; x++) { - for (var y = 0; y < this.props.height; y++) { + for (let x = 0; x < this.props.width; x++) { + for (let y = 0; y < this.props.height; y++) { i = y * this.props.width + x; - cells.push(); + // cells.push(); } } return (
{cells}
); - } -}); + }; +}; diff --git a/js/board/GridCell.js b/js/board/GridCell.js new file mode 100644 index 0000000..749d770 --- /dev/null +++ b/js/board/GridCell.js @@ -0,0 +1,9 @@ +import { Component } from 'react'; + +export default class GridCell extends Component { + render() { + const classname = ['cell', 'x' + this.props.x, 'y' + this.props.y]; + + return (
{this.props.value}
); + } +}; diff --git a/js/board/Message.js b/js/board/Message.js index 7185ea2..b4c22fe 100644 --- a/js/board/Message.js +++ b/js/board/Message.js @@ -1,8 +1,7 @@ require('../../sass/board/message.scss'); -var React = require('react'); -var State = require('../State'); -var Values = require('./Values'); +import { Component } from 'react'; +// var Values = require('./Values'); var exclamations = [ 'Congratulations!', @@ -15,66 +14,69 @@ var exclamations = [ 'Shazam!' ]; -module.exports = React.createClass({ - getInitialState() { - return { - message1: 'Congratulations!', - message2: 'Press spacebar to continue.', - hidden: true - }; - }, +export default class Message extends Component { + // getInitialState() { + // return { + // message1: 'Congratulations!', + // message2: 'Press spacebar to continue.', + // hidden: true + // }; + // }, componentDidMount() { - State.subscribe('level/complete', this.levelComplete); - State.subscribe('level/next', this.levelNext); - State.subscribe('munch/failed', this.munchFailed); - }, + // State.subscribe('level/complete', this.levelComplete); + // State.subscribe('level/next', this.levelNext); + // State.subscribe('munch/failed', this.munchFailed); + }; munchFailed(value) { var self = this; - + function keydown(e) { if (e.keyCode === 32) { window.removeEventListener('keydown', keydown); - self.setState({ hidden: true }); + // self.setState({ hidden: true }); } }; - var msg = Values.getError(value, State.level); - this.setState({ hidden: false, message1: msg }); + // var msg = Values.getError(value, State.level); + // this.setState({ hidden: false, message1: msg }); window.addEventListener('keydown', keydown); - }, + }; levelComplete() { function keydown(e) { if (e.keyCode === 32) { window.removeEventListener('keydown', keydown); - State.publish('level/next'); + // State.publish('level/next'); } }; var msg = exclamations[Math.floor(Math.random() * exclamations.length)]; - this.setState({ hidden: false, message1: msg }); + // this.setState({ hidden: false, message1: msg }); window.addEventListener('keydown', keydown); - }, + }; levelNext() { - this.setState({ hidden: true }); - }, + // this.setState({ hidden: true }); + }; render() { var classname = ['message']; - if (this.state.hidden === true) { - classname.push('hidden'); - } + // if (this.state.hidden === true) { + // classname.push('hidden'); + // } + + return (
message
); + - return ( -
- {this.state.message1} -
- {this.state.message2} -
- ); - } -}); + // return ( + //
+ // {this.state.message1} + //
+ // {this.state.message2} + //
+ // ); + }; +}; diff --git a/js/board/Muncher.js b/js/board/Muncher.js index ce5aa64..a26cbbc 100644 --- a/js/board/Muncher.js +++ b/js/board/Muncher.js @@ -1,20 +1,21 @@ require('../../sass/board/muncher.scss'); -var React = require('react'); +import { Component } from 'react'; -module.exports = React.createClass({ - getInitialState() { - return { - x: 0, - y: 0 - }; - }, +export default class Muncher extends Component{ + // getInitialState() { + // return { + // x: 0, + // y: 0 + // }; + // }, render() { - var classname = ['muncher', 'x' + this.state.x, 'y' + this.state.y]; + // const classname = ['muncher', 'x' + this.state.x, 'y' + this.state.y]; return ( -
+
+ //
); - } -}); + }; +}; diff --git a/js/board/Scorebar.js b/js/board/Scorebar.js index 43dd60b..79abf1a 100644 --- a/js/board/Scorebar.js +++ b/js/board/Scorebar.js @@ -1,42 +1,42 @@ require('../../sass/board/scorebar.scss'); -var React = require('react'); -var State = require('../State'); +import { Component } from 'react'; -module.exports = React.createClass({ - getInitialState() { - return { - currentScore: 0, - highScore: 0, - lives: 3 - }; - }, +export default class Scorebar extends Component { + // getInitialState() { + // return { + // currentScore: 0, + // highScore: 0, + // lives: 3 + // }; + // }, componentDidMount() { - State.subscribe('munch/successful', this.updateScore); - State.subscribe('munch/failed', this.updateLives); - }, + // // State.subscribe('munch/successful', this.updateScore); + // State.subscribe('munch/failed', this.updateLives); + }; updateScore() { - var score = this.state.currentScore; - this.setState({ currentScore: score + 10 }); - }, + // var score = this.state.currentScore; + // this.setState({ currentScore: score + 10 }); + }; updateLives() { - var lives = this.state.lives; - this.setState({ lives: lives - 1 }); - }, + // var lives = this.state.lives; + // this.setState({ lives: lives - 1 }); + }; render() { var lives = []; - for (var i = 0; i < this.state.lives; i++) { - lives.push(
); - } + // for (var i = 0; i < this.state.lives; i++) { + // lives.push(
); + // } - return (
-
{this.state.currentScore}
-
{this.state.highScore}
-
{lives}
-
); - } -}); + // return (
+ //
{this.state.currentScore}
+ //
{this.state.highScore}
+ //
{lives}
+ //
); + return

Scorebar

; + }; +}; diff --git a/js/board/Titlebar.js b/js/board/Titlebar.js index 81d0b76..26ee142 100644 --- a/js/board/Titlebar.js +++ b/js/board/Titlebar.js @@ -1,25 +1,17 @@ require('../../sass/board/titlebar.scss'); -var React = require('react'); -var State = require('../State'); -var Values = require('./Values'); - -module.exports = React.createClass({ - getInitialState() { - return { - title: Values.getDescription(State.level) - }; - }, +import { Component } from 'react'; +export default class Titlebar extends Component { componentDidMount() { - State.subscribe('level/next', this.levelNext); - }, + // State.subscribe('level/next', this.levelNext); + }; levelNext() { - this.setState({ title: Values.getDescription(State.level) }); - }, + // this.setState({ title: Values.getDescription(State.level) }); + }; render() { - return (
{this.state.title}
); - } -}); + return (
DYNAMIC TITLE
); + }; +}; diff --git a/js/index.js b/js/index.js new file mode 100644 index 0000000..a5b79eb --- /dev/null +++ b/js/index.js @@ -0,0 +1,21 @@ +require('../sass/reset.scss'); + +import { render } from 'react-dom'; +import { createStore, combineReducers } from 'redux' +import { Provider } from 'react-redux'; + +import App from './app/App'; +import * as reducers from './app/Reducers'; + +const combinedReducers = combineReducers({ + mode: reducers.modeReducer +}); + +const store = createStore(combinedReducers); + +ReactDOM.render( + + + , + document.getElementById('app') +); diff --git a/js/main.js b/js/main.js deleted file mode 100644 index 19d8211..0000000 --- a/js/main.js +++ /dev/null @@ -1,36 +0,0 @@ -require('../sass/reset.scss'); - -var React = require('react'); -var ReactDom = require('react-dom'); -var Board = require('./board/Board'); -var Welcome = require('./welcome/Welcome'); -var State = require('./State'); - -var App = React.createClass({ - getInitialState() { - return { - screen: 'board' - }; - }, - - handleKeydown(e) { - if (this.state.screen === 'welcome' && e.keyCode === 32) { - this.setState({ screen: 'board' }); - } - }, - - componentDidMount() { - window.addEventListener('keydown', this.handleKeydown); - }, - - render() { - if (this.state.screen === 'welcome') { - return (); - } - else if (this.state.screen === 'board') { - return (); - } - } -}); - -ReactDom.render(, document.getElementById('app')); diff --git a/js/welcome/HighScoreEntry.js b/js/welcome/HighScoreEntry.js new file mode 100644 index 0000000..0f9884c --- /dev/null +++ b/js/welcome/HighScoreEntry.js @@ -0,0 +1,13 @@ +import { Component } from 'react'; + +export default class HighScoreEntry extends Component { + render() { + return ( +
+
{this.props.rank}
+
{this.props.initials}
+
{this.props.score}
+
+ ); + } +}; diff --git a/js/welcome/HighScores.js b/js/welcome/HighScores.js index 94c3769..f4d2808 100644 --- a/js/welcome/HighScores.js +++ b/js/welcome/HighScores.js @@ -1,32 +1,26 @@ require('../../sass/welcome/highscores.scss'); -var React = require('react'); +import { Component } from 'react'; +import HighScoreEntry from './HighScoreEntry'; -var Entry = React.createClass({ +export default class HighScores extends Component { render() { - return ( -
-
{this.props.rank}
-
{this.props.initials}
-
{this.props.score}
-
- ); - } -}); + const vals = [ + { initials: "ABA", score: "219283", rank: "1" }, + { initials: "ABA", score: "107112", rank: "2" }, + { initials: "ABA", score: "81091", rank: "3" }, + { initials: "ABA", score: "67747", rank: "4" }, + { initials: "ABA", score: "9283", rank: "5" }, + { initials: "ABA", score: "928", rank: "6" } + ]; - -module.exports = React.createClass({ - render() { var entries = []; - entries.push(); - entries.push(); - entries.push(); - entries.push(); - entries.push(); - entries.push(); + vals.map(function(v, i) { + entries.push(); + }); return (
{entries}
); } -}); +}; diff --git a/js/welcome/NewGame.js b/js/welcome/NewGame.js new file mode 100644 index 0000000..059638a --- /dev/null +++ b/js/welcome/NewGame.js @@ -0,0 +1,31 @@ +import { Component } from 'react'; + +const blinkTimer = null; + +const toggleTimeout = function() { + //var hidden = this.state.hidden; which was false + //this.setState({ hidden: !hidden }); + //blinkTimer = setTimeout(toggleTimeout.bind(this), 600); +}; + +export default class NewGame extends Component { + componentDidMount() { + //toggleTimeout.call(this); + }; + + componentWillUnmount() { + clearTimeout(blinkTimer); + }; + + render() { + const classname = ['newgame']; + + //if (this.state.hidden === true) { + // classname.push('hidden'); + //} + + return ( +
Press Space Bar To Play
+ ); + }; +}; diff --git a/js/welcome/Welcome.js b/js/welcome/Welcome.js index a041540..e41a9c2 100644 --- a/js/welcome/Welcome.js +++ b/js/welcome/Welcome.js @@ -1,46 +1,29 @@ require('../../sass/welcome/welcome.scss'); -var React = require('react'); -var HighScores = require('./HighScores'); - -var blinkTimer = null; - -var toggleTimeout = function() { - var hidden = this.state.hidden; - this.setState({ hidden: !hidden }); - - blinkTimer = setTimeout(toggleTimeout.bind(this), 600); -}; - -var NewGame = React.createClass({ - getInitialState() { - return { - hidden: false +import { Component } from 'react'; +import { connect } from 'react-redux'; +import * as creators from '../app/Creators.js'; + +import NewGame from './NewGame'; +import HighScores from './HighScores'; + +export class Welcome extends Component { + handleKeydown(e) { + if (e.keyCode === 32) { + this.props.dispatch(creators.nextMode()); + //this.setState({ screen: 'board' }); } - }, + }; componentDidMount() { - toggleTimeout.call(this); - }, + window.addEventListener('keydown', this.handleKeydown.bind(this)); + }; componentWillUnmount() { - clearTimeout(blinkTimer); - }, + console.log('need to remove listener') + // remove keydown listener + }; - render() { - var classname = ['newgame']; - - if (this.state.hidden === true) { - classname.push('hidden'); - } - - return ( -
Press Space Bar To Play
- ); - } -}); - -module.exports = React.createClass({ render() { return (
@@ -50,4 +33,6 @@ module.exports = React.createClass({
); } -}); +}; + +export default connect(() => ({}))(Welcome); diff --git a/npm-debug.log b/npm-debug.log new file mode 100644 index 0000000..f5eb9d4 --- /dev/null +++ b/npm-debug.log @@ -0,0 +1,24 @@ +0 info it worked if it ends with ok +1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'run', 'start' ] +2 info using npm@3.7.3 +3 info using node@v5.8.0 +4 verbose stack Error: missing script: start +4 verbose stack at run (/usr/local/lib/node_modules/npm/lib/run-script.js:147:19) +4 verbose stack at /usr/local/lib/node_modules/npm/lib/run-script.js:57:5 +4 verbose stack at /usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:345:5 +4 verbose stack at checkBinReferences_ (/usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:309:45) +4 verbose stack at final (/usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:343:3) +4 verbose stack at then (/usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:113:5) +4 verbose stack at /usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:300:12 +4 verbose stack at /usr/local/lib/node_modules/npm/node_modules/graceful-fs/graceful-fs.js:78:16 +4 verbose stack at tryToString (fs.js:414:3) +4 verbose stack at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:401:12) +5 verbose cwd /Users/tonklin/Development/bb/number-munchers +6 error Darwin 15.3.0 +7 error argv "/usr/local/bin/node" "/usr/local/bin/npm" "run" "start" +8 error node v5.8.0 +9 error npm v3.7.3 +10 error missing script: start +11 error If you need help, you may report this error at: +11 error +12 verbose exit [ 1, true ] diff --git a/package.json b/package.json index 1e862c5..ea4729b 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,15 @@ "description": "MECC's classic Number Munchers game rebuilt with React and Webpack.", "main": "./js/main.js", "scripts": { - "start": "./node_modules/webpack-dev-server/bin/webpack-dev-server.js --progress --colors" + "dev": "./node_modules/webpack-dev-server/bin/webpack-dev-server.js --progress --colors" }, "author": "Ben Burlingham", "license": "ISC", - "dependencies": { - "babel-loader": "^6.2.4", - "babel-preset-react": "^6.5.0" - }, + "dependencies": {}, "devDependencies": { + "babel-loader": "^6.2.4", + "babel-preset-es2015": "^6.6.0", + "babel-preset-react": "^6.5.0", "css-loader": "^0.23.1", "express": "^4.13.4", "extract-text-webpack-plugin": "^1.0.1", @@ -21,6 +21,8 @@ "react": "^0.14.7", "react-dom": "^0.14.7", "react-hot-loader": "^1.3.0", + "react-redux": "^4.4.4", + "redux": "^3.4.0", "sass-loader": "^3.2.0", "style-loader": "^0.13.1", "webpack": "^1.12.14", diff --git a/readme.md b/readme.md index c4863db..193cd1a 100644 --- a/readme.md +++ b/readme.md @@ -8,6 +8,6 @@ Number Munchers uses Webpack for live reloading and ES6 transpilation. Run `npm install` to install the required Node packages. -Run `npm run start` in the project root to start the [webpack dev server](https://webpack.github.io/docs/webpack-dev-server.html). +Run `npm run dev` in the project root to start the [webpack dev server](https://webpack.github.io/docs/webpack-dev-server.html). Navigate to http://localhost:8080/. diff --git a/webpack.config.js b/webpack.config.js index 210f6a6..24b0167 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -3,7 +3,7 @@ var ExtractTextPlugin = require('extract-text-webpack-plugin'); function getEntrySources() { var sources = [ - './js/main.js' + './js/index.js' ]; if (process.env.NODE_ENV !== 'production') { @@ -22,7 +22,10 @@ module.exports = { { test: /\.js$/, include: __dirname + '/js', - loaders: ['babel'] + loader: 'babel', + query: { + presets: ['react', 'es2015'] + } }, { test: /\.scss$/,