├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── app.js ├── components ├── Blinds.js ├── Clock.js ├── Debts.js ├── Logger.js ├── Pot.js └── Timer.js ├── index.html ├── package.json ├── react-poker-screenshot.png ├── style.css ├── variables.js └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | bundle.js 3 | node_modules/ 4 | build/ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jeff Pickelman 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: build 3 | 4 | build: 5 | npm install 6 | npm install webpack 7 | ./node_modules/.bin/webpack 8 | mkdir build 9 | cp index.html bundle.js style.css ./node_modules/normalize.css/normalize.css build/ 10 | 11 | clean: 12 | rm -rf build/ 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-poker 2 | A React component simulating a Texas holdem tournament game. 3 | 4 | ### Usage 5 | Run `npm install`, `npm install -g webpack`, `webpack` to generate `bundle.js`. 6 | 7 | ### Screenshot of the app: 8 | 9 | ![react-poker example screenshot](react-poker-screenshot.png) 10 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | import React from 'react'; 5 | import Timer from './components/Timer.js'; 6 | import Blinds from './components/Blinds.js'; 7 | import Pot from './components/Pot.js'; 8 | import Debts from './components/Debts.js'; 9 | import Clock from './components/Clock.js'; 10 | 11 | import { startingSeconds, payouts, buyIn, smallBlinds } from './variables.js'; 12 | 13 | class App extends React.Component { 14 | componentWillMount() { 15 | window.addEventListener( 'blinds-up', this.handleBlindsUp, false ); 16 | } 17 | 18 | componentWillUnmount() { 19 | window.removeEventListener( 'blinds-up', this.handleBlindsUp, false ); 20 | } 21 | 22 | handleBlindsUp() { 23 | let body = document.getElementsByTagName( 'body' )[0]; 24 | body.classList.add( 'flashing' ); 25 | setTimeout( function() { body.classList.remove( 'flashing' ); }, 1700 ); 26 | setTimeout( function() { body.classList.add( 'flashing' ); }, 1800 ); 27 | setTimeout( function() { body.classList.remove( 'flashing' ); }, 3500 ); 28 | } 29 | 30 | render() { 31 | return ( 32 |
33 |
34 |
35 |
36 | 37 |
38 |
39 |

Poker Simulator

40 |
41 |
42 |   43 |
44 |
45 |
46 |
47 |
48 |
49 | 50 |
51 |
52 | 53 |
54 |
55 | 56 | 57 |
58 |
59 |
60 |
61 | ); 62 | } 63 | } 64 | 65 | React.render( , document.getElementById( 'app' ) ); 66 | -------------------------------------------------------------------------------- /components/Blinds.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | import React from 'react'; 4 | 5 | class Blind extends React.Component { 6 | render() { 7 | let classString = ''; 8 | if ( this.props.active ) { 9 | classString += 'active'; 10 | } 11 | return
  • { this.props.blind } / { this.props.blind * 2 }
  • 12 | } 13 | } 14 | 15 | class Blinds extends React.Component { 16 | constructor( props ) { 17 | this.state = { activeIndex: 0 }; 18 | this.raiseBlinds = this.raiseBlinds.bind( this ); 19 | this.lowerBlinds = this.lowerBlinds.bind( this ); 20 | } 21 | 22 | componentWillMount() { 23 | window.addEventListener( 'blinds-up', this.raiseBlinds, false ); 24 | } 25 | 26 | componentWillUnmount() { 27 | window.removeEventListener( 'blinds-up', this.raiseBlinds, false ); 28 | } 29 | 30 | raiseBlinds() { 31 | if ( this.state.activeIndex + 1 > this.props.smallBlinds.length - 1 ) { return; } 32 | this.setState( { activeIndex: this.state.activeIndex + 1 } ); 33 | } 34 | 35 | lowerBlinds() { 36 | if ( this.state.activeIndex === 0 ) { return; } 37 | this.setState( { activeIndex: this.state.activeIndex - 1 } ); 38 | } 39 | 40 | render() { 41 | return ( 42 |
    43 |

    Blinds:

    44 | 48 | 53 | 54 | 55 |
    56 | ); 57 | } 58 | } 59 | 60 | export default Blinds; 61 | -------------------------------------------------------------------------------- /components/Clock.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | import React from 'react'; 5 | 6 | class Clock extends React.Component { 7 | constructor() { 8 | this.state = ( { now: new Date() } ); 9 | 10 | this.updateClock = this.updateClock.bind( this ); 11 | setInterval( this.updateClock, 1000 ); 12 | } 13 | 14 | // componentDidRender() { 15 | // this.updateClock(); 16 | // setTimeout( this.updateClock, 1000 ); 17 | // } 18 | 19 | updateClock() { 20 | this.setState( { now: new Date() } ); 21 | } 22 | 23 | render() { 24 | let now = this.state.now; 25 | let hours = now.getHours(); 26 | let minutes = now.getMinutes(); 27 | let seconds = now.getSeconds(); 28 | return
    { hours > 12 ? hours - 12 : hours }:{ minutes < 10 ? '0' + minutes : minutes } PM
    29 | } 30 | } 31 | 32 | // Seconds: :{ seconds < 10 ? '0' + seconds : seconds } 33 | 34 | export default Clock; 35 | -------------------------------------------------------------------------------- /components/Debts.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | import React from 'react'; 5 | 6 | let debts = []; 7 | 8 | class Debt extends React.Component { 9 | constructor( props ) { 10 | this.state = { paid: false }; 11 | 12 | this.togglePaid = this.togglePaid.bind( this ); 13 | } 14 | 15 | togglePaid() { 16 | this.setState( { paid: !this.state.paid } ); 17 | } 18 | 19 | render() { 20 | let classString = ''; 21 | if ( this.state.paid ) { 22 | classString += 'paid'; 23 | } 24 | return
  • { this.props.debt }
  • ; 25 | } 26 | } 27 | 28 | class Debts extends React.Component { 29 | constructor( props ) { 30 | this.state = { debts: debts }; 31 | this.handleSubmit = this.handleSubmit.bind( this ); 32 | } 33 | 34 | handleSubmit( event ) { 35 | event.preventDefault(); 36 | let debt = React.findDOMNode( this.refs['debt-input'] ).value.trim(); 37 | this.state.debts.push( debt ); 38 | this.setState( { debts: this.state.debts } ); 39 | React.findDOMNode( this.refs['debt-input'] ).value = ''; 40 | return; 41 | } 42 | 43 | listDebts() { 44 | let debtsList = []; 45 | if ( !debts.length ) { 46 | return
  • (No debts!)
  • ; 47 | } 48 | debts.map( ( debt, index ) => { 49 | debtsList.push( ); 50 | } ); 51 | return debtsList; 52 | } 53 | 54 | render() { 55 | return ( 56 |
    57 |

    Debts:

    58 |
      59 | { this.listDebts() } 60 |
    61 |
    62 | 63 |
    64 |
    Settle debts with cash or Venmo.
    65 |
    66 | ); 67 | } 68 | } 69 | 70 | export default Debts; 71 | -------------------------------------------------------------------------------- /components/Logger.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | import React from 'react'; 5 | 6 | class Logger extends React.Component { 7 | componentWillMount() { 8 | console.log('Logger logging!!'); 9 | } 10 | } 11 | 12 | export default Logger; 13 | -------------------------------------------------------------------------------- /components/Pot.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | import React from 'react'; 5 | 6 | let ordinalAbbrev = ( ordinal ) => { 7 | if ( ordinal === 1 ) { 8 | return 'st'; 9 | } else if ( ordinal === 2 ) { 10 | return 'nd'; 11 | } else if ( ordinal === 3 ) { 12 | return 'rd'; 13 | } else { 14 | return 'th'; 15 | } 16 | } 17 | 18 | class Pot extends React.Component { 19 | constructor() { 20 | this.state = { pot: 0 }; 21 | } 22 | 23 | registerBuyIn( modifier ) { 24 | if ( this.state.pot + modifier * this.props.buyIn < 0 ) { 25 | return; 26 | } 27 | this.setState( { pot: this.state.pot + modifier * this.props.buyIn } ); 28 | } 29 | 30 | render() { 31 | return ( 32 |
    33 |

    Pot:

    34 |
    35 | { this.props.payouts( this.state.pot ).map( ( payout, index ) => { 36 | return ( 37 |
    38 |
    { index + 1 }{ ordinalAbbrev( index + 1) }:
    39 |
    ${ payout }
    40 |
    41 |
    42 | ); 43 | } ) } 44 |
    45 |
    46 |
    Pot:
    47 |
    ${ this.state.pot }
    48 |
    49 |
    50 |
    51 | 52 | 53 |
    54 |
    55 | ); 56 | } 57 | } 58 | 59 | export default Pot; 60 | -------------------------------------------------------------------------------- /components/Timer.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | import React from 'react'; 5 | 6 | class Timer extends React.Component { 7 | constructor(props) { 8 | this.state = { secondsLeft: props.startingTime }; 9 | this.tickInterval; 10 | 11 | this.resetTimer = this.resetTimer.bind( this ); 12 | this.tick = this.tick.bind( this ); 13 | this.play = this.play.bind( this ); 14 | this.pause = this.pause.bind( this ); 15 | } 16 | 17 | resetTimer() { 18 | this.setState( { secondsLeft: this.props.startingTime } ); 19 | this.play(); 20 | } 21 | 22 | tick() { 23 | let nextSecondsLeft = this.state.secondsLeft - 1; 24 | if ( nextSecondsLeft === 0 ) { 25 | this.resetTimer(); 26 | window.dispatchEvent( new Event( 'blinds-up' ) ); 27 | } 28 | this.setState( { secondsLeft: this.state.secondsLeft - 1 } ); 29 | } 30 | 31 | play() { 32 | this.pause(); 33 | this.tickInterval = setInterval( this.tick, 1000 ); 34 | } 35 | 36 | pause() { 37 | clearInterval( this.tickInterval ); 38 | } 39 | 40 | displayTime( secondsLeft ) { 41 | let displayMinutes = Math.floor( secondsLeft / 60 ); 42 | let displaySeconds = ( secondsLeft % 60 ) < 10 ? '0' + secondsLeft % 60 : secondsLeft % 60; 43 | return displayMinutes + ':' + displaySeconds; 44 | } 45 | 46 | render() { 47 | return ( 48 |
    49 |

    Blinds raise in:

    50 |
    { this.displayTime( this.state.secondsLeft ) }
    51 |
    52 | 53 | 54 | 55 |
    56 |
    57 | ); 58 | } 59 | } 60 | 61 | export default Timer; 62 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Poker Simulator 6 | 7 | 8 | 9 | 10 |
    11 | 12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-poker", 3 | "version": "0.0.1", 4 | "description": "A React component simulating a Texas holdem tournament game", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "license": "MIT", 10 | "devDependencies": { 11 | "babel-core": "^4.7.16", 12 | "babel-loader": "^4.2.0", 13 | "babel-runtime": "^5.0.0-beta1", 14 | "normalize.css": "^3.0.3", 15 | "webpack": "^1.7.3" 16 | }, 17 | "dependencies": { 18 | "react": "^0.13.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /react-poker-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pattern/react-poker/aabcbb2c944166285216c783a91be13fdd516fb4/react-poker-screenshot.png -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | 2 | /* General */ 3 | 4 | ul { list-style-type: none; margin: 0; padding: 0; } 5 | ol { margin: 0; margin-left: 23px; padding: 0; } 6 | ol li { padding: 3px 0; } 7 | 8 | button { 9 | padding: 6px 10px 5px 10px; 10 | margin-right: 20px; 11 | background: #4479BA; 12 | color: #FFF; 13 | -webkit-border-radius: 4px; 14 | -moz-border-radius: 4px; 15 | border-radius: 4px; 16 | border: solid 1px #20538D; 17 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.4); 18 | -webkit-user-select:none; 19 | -moz-user-select:none; 20 | -ms-user-select:none; 21 | user-select:none; 22 | } 23 | button:hover { 24 | background: #356094; 25 | border: solid 1px #2A4E77; 26 | text-decoration: none; 27 | } 28 | button:active { 29 | -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); 30 | -moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); 31 | box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); 32 | background: #2E5481; 33 | border: solid 1px #203E5F; 34 | } 35 | 36 | button:focus { outline:0; } 37 | 38 | /* App / Containers */ 39 | 40 | .container { width: 1000px; margin-left: auto; margin-right: auto; } 41 | .orange-bg { background-color: #ff5800; margin-bottom: 40px; } 42 | .header { color: #fff; font-size: 20px; padding-top: 15px; padding-bottom: 15px; } 43 | .col1 { float: left; width: 250px; } 44 | .col2 { float: left; width: 550px; } 45 | .col3 { float: left; width: 200px; } 46 | .clear-both { clear: both; } 47 | .poker-title { margin: 0; -webkit-margin-before: 0; -webkit-margin-after: 0; } 48 | 49 | body.flashing { 50 | -webkit-animation-name: flashing; /* Chrome, Safari, Opera */ 51 | -webkit-animation-duration: 1.5s; /* Chrome, Safari, Opera */ 52 | animation-name: flashing; 53 | animation-duration: 1.5s; 54 | } 55 | 56 | /* Chrome, Safari, Opera */ 57 | @-webkit-keyframes flashing { 58 | 0% { background-color:red; } 59 | 25% { background-color:yellow; } 60 | 50% { background-color:blue; } 61 | 75% { background-color:green; } 62 | 100% { background-color:red; } 63 | } 64 | 65 | /* Standard syntax */ 66 | @keyframes flashing { 67 | 0% { background-color:red; } 68 | 25% { background-color:yellow; } 69 | 50% { background-color:blue; } 70 | 75% { background-color:green; } 71 | 100% { background-color:red; } 72 | } 73 | 74 | 75 | /* Timer */ 76 | 77 | .current-time { font-size: 200px; margin-top: 50px; margin-bottom: 30px; } 78 | .timer .buttons { margin-left: 50px; } 79 | 80 | 81 | /* Clock */ 82 | 83 | .clock { margin-top: 6px; } 84 | 85 | 86 | /* Blinds */ 87 | 88 | li.active { font-weight: bold; color: #ff5800; } 89 | .current-blinds { margin-bottom: 30px; } 90 | .current-blinds li:first-child { margin-bottom: 3px;} 91 | .current-blinds li:nth-child(2) { font-size: 40px; } 92 | .blinds-list { margin-bottom: 30px; } 93 | .blinds-list li { margin: 7px 0;} 94 | 95 | /* Debts */ 96 | 97 | .debts { margin-top: 80px; } 98 | .debts li { cursor: pointer; } 99 | li.paid { color: #999; text-decoration: line-through; } 100 | .debts input { margin-top: 15px; margin-bottom: 10px; } 101 | .settle { font-size: 12px; color: #999; } 102 | .no-debts { color: #999; } 103 | 104 | /* Pot */ 105 | 106 | .designation { float: left; width: 60px; } 107 | .payouts > div { margin-bottom: 10px; } 108 | .payout { font-size: 20px; } 109 | .payout div:first-child { float: left; width: 60px; } 110 | .payout div:nth-child(2) { float: left; width: 60px; font-weight: bold; } 111 | .pot .buttons { margin-top: 20px; } 112 | 113 | -------------------------------------------------------------------------------- /variables.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | export const buyIn = 20; 5 | 6 | export const startingSeconds = 15 * 60; 7 | 8 | export const smallBlinds = [ 100, 200, 300, 400, 500, 800, 1000, 1500, 2000, 3000, 5000]; 9 | 10 | export function payouts( pot ) { 11 | return [ pot * 0.5, pot * 0.3, pot * 0.2 ]; 12 | } 13 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | module.exports = { 5 | entry: './app.js', 6 | 7 | output: { 8 | path: __dirname, 9 | filename: 'bundle.js' 10 | }, 11 | 12 | module: { 13 | loaders: [ 14 | { 15 | test: /\.js$/, 16 | exclude: /node_modules/, 17 | loaders: [ 'babel-loader?experimental' ] 18 | } 19 | ] 20 | } 21 | }; 22 | --------------------------------------------------------------------------------