From 30fa8d9247a057fcab76fd27352685948d7b5126 Mon Sep 17 00:00:00 2001 From: Tony Cuadra Date: Mon, 4 Nov 2019 22:29:06 -0500 Subject: [PATCH 1/4] Update tic-tac-toe to use latest rollup config --- sample-components-tictactoe/rollup.config.js | 41 ++++++++++++++------ 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/sample-components-tictactoe/rollup.config.js b/sample-components-tictactoe/rollup.config.js index 0734e48..4938c38 100644 --- a/sample-components-tictactoe/rollup.config.js +++ b/sample-components-tictactoe/rollup.config.js @@ -1,20 +1,39 @@ -// Rollup config for consuming some npm modules in MagicScript - import babel from 'rollup-plugin-babel'; import resolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; -export default { - external: ['uv', 'lumin', 'ssl'], - input: 'src/main.js', - preserveModules: true, - output: { - dir: 'bin', - format: 'es' - }, +const common = { plugins: [ - babel({ exclude: "node_modules/**" }), + babel({ exclude: 'node_modules/**' }), resolve(), commonjs() ] }; + +export default [ + // Build for MagicScript on LuminOS + { + ...common, + external: ['uv', 'lumin', 'ssl', 'jpeg', 'png', 'gl'], + input: 'src/main.js', + preserveModules: true, + output: { + dir: 'bin', + format: 'es' + } + }, + // Build for MagicScript on Magicverse (iOS, Android) + { + ...common, + input: 'src/app.js', + external: ['react'], + output: { + globals: { + 'react': 'React' + }, + file: 'bin/bundle.js', + format: 'iife', + name: '_' + } + } +]; From dcf37a880c2f306391d6066e99758a9c17bd05a9 Mon Sep 17 00:00:00 2001 From: Tony Cuadra Date: Mon, 4 Nov 2019 22:32:49 -0500 Subject: [PATCH 2/4] Port tic-tac-toe sample to TypeScript --- sample-components-tictactoe/package.json | 14 +++-- sample-components-tictactoe/rollup.config.js | 8 ++- .../src/{app.js => app.tsx} | 31 ++++++----- .../src/components/{board.js => board.tsx} | 17 +++--- ...alculate-winner.js => calculate-winner.ts} | 2 +- .../{game-info.js => game-info.tsx} | 22 ++++---- .../src/components/{game.js => game.tsx} | 55 +++++++++++-------- .../src/components/index.js | 5 -- .../src/components/index.ts | 5 ++ .../{player-chooser.js => player-chooser.tsx} | 13 ++--- .../src/components/square.js | 20 ------- .../src/components/square.tsx | 19 +++++++ .../src/{main.js => main.tsx} | 7 ++- sample-components-tictactoe/tsconfig.json | 14 +++-- 14 files changed, 128 insertions(+), 104 deletions(-) rename sample-components-tictactoe/src/{app.js => app.tsx} (59%) rename sample-components-tictactoe/src/components/{board.js => board.tsx} (66%) rename sample-components-tictactoe/src/components/{calculate-winner.js => calculate-winner.ts} (83%) rename sample-components-tictactoe/src/components/{game-info.js => game-info.tsx} (84%) rename sample-components-tictactoe/src/components/{game.js => game.tsx} (77%) delete mode 100644 sample-components-tictactoe/src/components/index.js create mode 100644 sample-components-tictactoe/src/components/index.ts rename sample-components-tictactoe/src/components/{player-chooser.js => player-chooser.tsx} (77%) delete mode 100644 sample-components-tictactoe/src/components/square.js create mode 100644 sample-components-tictactoe/src/components/square.tsx rename sample-components-tictactoe/src/{main.js => main.tsx} (85%) diff --git a/sample-components-tictactoe/package.json b/sample-components-tictactoe/package.json index 7b27cfe..dd98aaf 100644 --- a/sample-components-tictactoe/package.json +++ b/sample-components-tictactoe/package.json @@ -6,26 +6,30 @@ "private": true, "devDependencies": { "@babel/core": "^7.5.5", - "@babel/plugin-transform-react-jsx": "^7.3.0", "@babel/plugin-proposal-class-properties": "^7.5.5", + "@babel/plugin-transform-react-jsx": "^7.3.0", + "@types/react": "^16.9.11", "eslint-config-semistandard": "^13.0.0", "eslint-config-standard": "^12.0.0", "eslint-plugin-import": "^2.18.2", "eslint-plugin-node": "^8.0.1", "eslint-plugin-promise": "^4.0.1", "eslint-plugin-standard": "^4.0.0", - "magic-script-typings": "1.4.0", + "magic-script-typings": "~1.5.0", "rollup": "^1.1.2", "rollup-plugin-babel": "^4.3.2", "rollup-plugin-commonjs": "https://github.com/creationix/rollup-plugin-commonjs.git", "rollup-plugin-node-resolve": "^4.0.0", - "semistandard": "^13.0.1" + "rollup-plugin-typescript": "^1.0.1", + "semistandard": "^13.0.1", + "tslib": "^1.10.0", + "typescript": "^3.6.4" }, "dependencies": { "eslint-plugin-react": "^7.15.1", "firebase": "^7.0.0", - "magic-script-components": "2.0.0", - "magic-script-components-lumin": "1.0.0", + "magic-script-components": "^2.0.0", + "magic-script-components-lumin": "^1.0.7", "magic-script-polyfills": "^2.4.3" } } diff --git a/sample-components-tictactoe/rollup.config.js b/sample-components-tictactoe/rollup.config.js index 4938c38..50f3aeb 100644 --- a/sample-components-tictactoe/rollup.config.js +++ b/sample-components-tictactoe/rollup.config.js @@ -1,12 +1,14 @@ import babel from 'rollup-plugin-babel'; import resolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; +import typescript from 'rollup-plugin-typescript'; const common = { plugins: [ babel({ exclude: 'node_modules/**' }), resolve(), - commonjs() + commonjs(), + typescript() ] }; @@ -15,7 +17,7 @@ export default [ { ...common, external: ['uv', 'lumin', 'ssl', 'jpeg', 'png', 'gl'], - input: 'src/main.js', + input: 'src/main.tsx', preserveModules: true, output: { dir: 'bin', @@ -25,7 +27,7 @@ export default [ // Build for MagicScript on Magicverse (iOS, Android) { ...common, - input: 'src/app.js', + input: 'src/app.tsx', external: ['react'], output: { globals: { diff --git a/sample-components-tictactoe/src/app.js b/sample-components-tictactoe/src/app.tsx similarity index 59% rename from sample-components-tictactoe/src/app.js rename to sample-components-tictactoe/src/app.tsx index 0c67a4b..6fcd270 100644 --- a/sample-components-tictactoe/src/app.js +++ b/sample-components-tictactoe/src/app.tsx @@ -1,31 +1,40 @@ import React from 'react'; -import PropTypes from 'prop-types'; import firebase from 'firebase/app'; -import { View } from 'magic-script-components'; +import { View, AppProps } from 'magic-script-components'; import { PlayerChooser, Game } from './components/index.js'; +interface Props extends AppProps { + firebaseConfig?: object; +} + +interface State { + player: string; +} + // Top-level application component -- renders either a player chooser or the game -export class TicTacToeApp extends React.Component { - constructor (props) { +export class TicTacToeApp extends React.Component { + state: State = { player: '?' }; + + constructor (props: Props) { super(props); - this.state = { player: '?' }; this.onPlayerChosen = this.onPlayerChosen.bind(this); } - useFirebase () { - return this.props.firebaseConfig && this.props.firebaseConfig.hasOwnProperty('apiKey'); + useFirebase (): boolean { + return this.props.firebaseConfig !== undefined && this.props.firebaseConfig !== null + && this.props.firebaseConfig.hasOwnProperty('apiKey'); } componentDidMount () { if (this.useFirebase()) { - firebase.initializeApp(this.props.firebaseConfig); + firebase.initializeApp(this.props.firebaseConfig!); } } - onPlayerChosen (player) { + onPlayerChosen (player: string) { this.setState({ player: player }); } @@ -45,7 +54,3 @@ export class TicTacToeApp extends React.Component { ); } } - -TicTacToeApp.propTypes = { - firebaseConfig: PropTypes.object -}; diff --git a/sample-components-tictactoe/src/components/board.js b/sample-components-tictactoe/src/components/board.tsx similarity index 66% rename from sample-components-tictactoe/src/components/board.js rename to sample-components-tictactoe/src/components/board.tsx index 3e4d923..3886dc2 100644 --- a/sample-components-tictactoe/src/components/board.js +++ b/sample-components-tictactoe/src/components/board.tsx @@ -1,12 +1,16 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { GridLayout } from 'magic-script-components'; -import { Square } from './index.js'; +import Square from './square'; + +interface Props { + squares: string[]; + onClick: (i: number) => void; +} // Component that renders the full Tic Tac Toe board -export default function Board (props) { - const renderSquare = (props, i) => { +export default function Board (props: Props) { + const renderSquare = (props: Props, i: number) => { return ( {items}; } - -Board.propTypes = { - squares: PropTypes.arrayOf(PropTypes.string), - onClick: PropTypes.func -}; diff --git a/sample-components-tictactoe/src/components/calculate-winner.js b/sample-components-tictactoe/src/components/calculate-winner.ts similarity index 83% rename from sample-components-tictactoe/src/components/calculate-winner.js rename to sample-components-tictactoe/src/components/calculate-winner.ts index c0a5797..737729a 100644 --- a/sample-components-tictactoe/src/components/calculate-winner.js +++ b/sample-components-tictactoe/src/components/calculate-winner.ts @@ -1,4 +1,4 @@ -export function calculateWinner (squares) { +export function calculateWinner (squares: string[]): string | null { const lines = [ [0, 1, 2], [3, 4, 5], diff --git a/sample-components-tictactoe/src/components/game-info.js b/sample-components-tictactoe/src/components/game-info.tsx similarity index 84% rename from sample-components-tictactoe/src/components/game-info.js rename to sample-components-tictactoe/src/components/game-info.tsx index 39e0749..e2f81df 100644 --- a/sample-components-tictactoe/src/components/game-info.js +++ b/sample-components-tictactoe/src/components/game-info.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { Button, @@ -10,9 +9,19 @@ import { } from 'magic-script-components'; import { calculateWinner } from './calculate-winner.js'; +import { HistoryItem } from './game'; + +interface Props { + history: HistoryItem[]; + onHistoryClick: (move: number) => void; + player: string; + onChoosePlayer: () => void; + stepNumber: number; + xIsNext: boolean; +} // Component that renders current game status and history of moves -export default function GameInfo (props) { +export default function GameInfo (props: Props) { const current = props.history[props.stepNumber]; const winner = calculateWinner(current.squares); @@ -60,12 +69,3 @@ export default function GameInfo (props) { ); } - -GameInfo.propTypes = { - history: PropTypes.array, - onHistoryClick: PropTypes.func, - player: PropTypes.string, - onChoosePlayer: PropTypes.func, - stepNumber: PropTypes.number, - xIsNext: PropTypes.bool -}; diff --git a/sample-components-tictactoe/src/components/game.js b/sample-components-tictactoe/src/components/game.tsx similarity index 77% rename from sample-components-tictactoe/src/components/game.js rename to sample-components-tictactoe/src/components/game.tsx index fe17b8c..c94e80d 100644 --- a/sample-components-tictactoe/src/components/game.js +++ b/sample-components-tictactoe/src/components/game.tsx @@ -1,26 +1,41 @@ import React from 'react'; -import PropTypes from 'prop-types'; import firebase from 'firebase/app'; import 'firebase/database'; import { View } from 'magic-script-components'; -import { Board, GameInfo } from './index.js'; import { calculateWinner } from './calculate-winner.js'; +import Board from './board'; +import GameInfo from './game-info'; + +interface Props { + enableFirebase: boolean; + player: string; + onChoosePlayer: () => void; +} + +export interface HistoryItem { + squares: string[] +} + +const initialState = { + history: [ + { + squares: Array(9).fill('') + } + ], + stepNumber: 0, + xIsNext: true +}; + +type State = Readonly; // The main gameplay component that renders the Tic Tac Toe board -export default class Game extends React.Component { - constructor (props) { +export default class Game extends React.Component { + state: State = initialState; + + constructor (props: Props) { super(props); - this.state = { - history: [ - { - squares: Array(9).fill('') - } - ], - stepNumber: 0, - xIsNext: true - }; } componentDidMount () { @@ -41,11 +56,11 @@ export default class Game extends React.Component { return firebase.database().ref('tic-tac-toe'); } - updateState (partialNewState) { + updateState (partialNewState: Pick) { this.setState(partialNewState, () => this.syncStateChange(this.state)); } - syncStateChange (state) { + syncStateChange (state: State) { if (this.props.enableFirebase) { this.dbref().set(state, (error) => { console.error('Error writing state update', error); @@ -53,7 +68,7 @@ export default class Game extends React.Component { } } - handleClick (i) { + handleClick (i: number) { const history = this.state.history.slice(0, this.state.stepNumber + 1); const current = history[history.length - 1]; const squares = current.squares.slice(); @@ -76,7 +91,7 @@ export default class Game extends React.Component { }); } - jumpTo (step) { + jumpTo (step: number) { this.updateState({ stepNumber: step, xIsNext: (step % 2) === 0 @@ -109,9 +124,3 @@ export default class Game extends React.Component { ); } } - -Game.propTypes = { - enableFirebase: PropTypes.bool, - player: PropTypes.string, - onChoosePlayer: PropTypes.func -}; diff --git a/sample-components-tictactoe/src/components/index.js b/sample-components-tictactoe/src/components/index.js deleted file mode 100644 index 0637ec7..0000000 --- a/sample-components-tictactoe/src/components/index.js +++ /dev/null @@ -1,5 +0,0 @@ -export { default as Board } from './board.js'; -export { default as Game } from './game.js'; -export { default as GameInfo } from './game-info.js'; -export { default as PlayerChooser } from './player-chooser.js'; -export { default as Square } from './square.js'; diff --git a/sample-components-tictactoe/src/components/index.ts b/sample-components-tictactoe/src/components/index.ts new file mode 100644 index 0000000..809efce --- /dev/null +++ b/sample-components-tictactoe/src/components/index.ts @@ -0,0 +1,5 @@ +export { default as Board } from './board'; +export { default as Game } from './game'; +export { default as GameInfo } from './game-info'; +export { default as PlayerChooser } from './player-chooser'; +export { default as Square } from './square'; diff --git a/sample-components-tictactoe/src/components/player-chooser.js b/sample-components-tictactoe/src/components/player-chooser.tsx similarity index 77% rename from sample-components-tictactoe/src/components/player-chooser.js rename to sample-components-tictactoe/src/components/player-chooser.tsx index 674be09..2384553 100644 --- a/sample-components-tictactoe/src/components/player-chooser.js +++ b/sample-components-tictactoe/src/components/player-chooser.tsx @@ -1,10 +1,13 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { LinearLayout, Text } from 'magic-script-components'; -import { Square } from './index.js'; +import Square from './square'; -export default function PlayerChooser (props) { +interface Props { + onPlayerChosen: (player: string) => void; +} + +export default function PlayerChooser (props: Props) { return ( Choose a player: @@ -26,7 +29,3 @@ export default function PlayerChooser (props) { ); } - -PlayerChooser.propTypes = { - onPlayerChosen: PropTypes.func -}; diff --git a/sample-components-tictactoe/src/components/square.js b/sample-components-tictactoe/src/components/square.js deleted file mode 100644 index 73a0d3a..0000000 --- a/sample-components-tictactoe/src/components/square.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import { Button } from 'magic-script-components'; - -// Component that renders a single square in the Tic Tac Toe board -export default function Square (props) { - const color = props.value === 'X' ? [1, 0.1, 0.1, 1] : [0.1, 0.1, 1, 1]; - return ( - - ); -} - -Square.propTypes = { - name: PropTypes.string, - onClick: PropTypes.func, - value: PropTypes.string -}; diff --git a/sample-components-tictactoe/src/components/square.tsx b/sample-components-tictactoe/src/components/square.tsx new file mode 100644 index 0000000..7fa733e --- /dev/null +++ b/sample-components-tictactoe/src/components/square.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +import { Button, vec4 } from 'magic-script-components'; + +interface Props { + name: string; + value: string; + onClick: (event: any) => void; +} + +// Component that renders a single square in the Tic Tac Toe board +export default function Square (props: Props) { + const color: vec4 = props.value === 'X' ? [1, 0.1, 0.1, 1] : [0.1, 0.1, 1, 1]; + return ( + + ); +} diff --git a/sample-components-tictactoe/src/main.js b/sample-components-tictactoe/src/main.tsx similarity index 85% rename from sample-components-tictactoe/src/main.js rename to sample-components-tictactoe/src/main.tsx index 247d80a..c0ae1b2 100644 --- a/sample-components-tictactoe/src/main.js +++ b/sample-components-tictactoe/src/main.tsx @@ -2,12 +2,15 @@ // Simply importing this sets all these as global definitions. // They are declared in the .eslintrc so your editor won't complain. import 'magic-script-polyfills'; -import process from './global-scope.js'; // eslint-disable-line no-unused-vars +import process from './global-scope'; import React from 'react'; import mxs from 'magic-script-components-lumin'; +// Reference process so it isn't stripped +typeof process; + // Load main app logic from the app class. -import { TicTacToeApp } from './app.js'; +import { TicTacToeApp } from './app'; // To enable firebase, copy firebaseConfig from your Firebase project console diff --git a/sample-components-tictactoe/tsconfig.json b/sample-components-tictactoe/tsconfig.json index 41eb6d1..2459787 100644 --- a/sample-components-tictactoe/tsconfig.json +++ b/sample-components-tictactoe/tsconfig.json @@ -1,8 +1,12 @@ { "compilerOptions": { - "allowJs": true, - "noEmit": true, - "typeRoots" : ["./node_modules/magic-script-typings", "./node_modules/@types"] - } + "allowJs": true, + "noEmit": true, + "strict": true, + "target": "esnext", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true + } } - From 2310dc3cb77d7aeeae9507f667e6e0384ee1f923 Mon Sep 17 00:00:00 2001 From: Tony Cuadra Date: Thu, 7 Nov 2019 17:24:51 -0500 Subject: [PATCH 3/4] Change Player from a string to a union of strings --- sample-components-tictactoe/src/app.tsx | 21 ++++++++----------- .../src/components/board.tsx | 4 ++-- .../src/components/calculate-winner.ts | 6 ++++-- .../src/components/game-info.tsx | 18 ++++++---------- .../src/components/game.tsx | 21 ++++++++----------- .../src/components/player-chooser.tsx | 4 ++-- .../src/components/square.tsx | 4 +++- 7 files changed, 35 insertions(+), 43 deletions(-) diff --git a/sample-components-tictactoe/src/app.tsx b/sample-components-tictactoe/src/app.tsx index 6fcd270..173e899 100644 --- a/sample-components-tictactoe/src/app.tsx +++ b/sample-components-tictactoe/src/app.tsx @@ -4,24 +4,21 @@ import firebase from 'firebase/app'; import { View, AppProps } from 'magic-script-components'; import { PlayerChooser, Game } from './components/index.js'; +import { Player } from './components/square.js'; interface Props extends AppProps { firebaseConfig?: object; } interface State { - player: string; + // Use null to indicate that no player has been chosen yet + // (Blank ' ' is used to play for both 'X' and 'O') + player: Player | null; } // Top-level application component -- renders either a player chooser or the game export class TicTacToeApp extends React.Component { - state: State = { player: '?' }; - - constructor (props: Props) { - super(props); - - this.onPlayerChosen = this.onPlayerChosen.bind(this); - } + state: State = { player: null }; useFirebase (): boolean { return this.props.firebaseConfig !== undefined && this.props.firebaseConfig !== null @@ -34,19 +31,19 @@ export class TicTacToeApp extends React.Component { } } - onPlayerChosen (player: string) { - this.setState({ player: player }); + onPlayerChosen = (player: Player | null) => { + this.setState({ player }); } render () { return ( - {this.state.player === '?' + {this.state.player === null ? : ( this.onPlayerChosen('?')} + onChoosePlayer={() => this.onPlayerChosen(null)} enableFirebase={this.useFirebase()} /> )} diff --git a/sample-components-tictactoe/src/components/board.tsx b/sample-components-tictactoe/src/components/board.tsx index 3886dc2..61df618 100644 --- a/sample-components-tictactoe/src/components/board.tsx +++ b/sample-components-tictactoe/src/components/board.tsx @@ -1,10 +1,10 @@ import React from 'react'; import { GridLayout } from 'magic-script-components'; -import Square from './square'; +import Square, { Player } from './square'; interface Props { - squares: string[]; + squares: Player[]; onClick: (i: number) => void; } diff --git a/sample-components-tictactoe/src/components/calculate-winner.ts b/sample-components-tictactoe/src/components/calculate-winner.ts index 737729a..5b30707 100644 --- a/sample-components-tictactoe/src/components/calculate-winner.ts +++ b/sample-components-tictactoe/src/components/calculate-winner.ts @@ -1,4 +1,6 @@ -export function calculateWinner (squares: string[]): string | null { +import { Player } from "./square"; + +export default function calculateWinner (squares: Player[]): Player { const lines = [ [0, 1, 2], [3, 4, 5], @@ -15,5 +17,5 @@ export function calculateWinner (squares: string[]): string | null { return squares[a]; } } - return null; + return ' '; } diff --git a/sample-components-tictactoe/src/components/game-info.tsx b/sample-components-tictactoe/src/components/game-info.tsx index e2f81df..a1b4b8c 100644 --- a/sample-components-tictactoe/src/components/game-info.tsx +++ b/sample-components-tictactoe/src/components/game-info.tsx @@ -1,20 +1,14 @@ +import { ListView, ListViewItem, Text, View, Button } from 'magic-script-components'; import React from 'react'; - -import { - Button, - ListView, - ListViewItem, - Text, - View -} from 'magic-script-components'; - -import { calculateWinner } from './calculate-winner.js'; +import calculateWinner from './calculate-winner'; import { HistoryItem } from './game'; +import { Player } from './square'; + interface Props { history: HistoryItem[]; onHistoryClick: (move: number) => void; - player: string; + player: Player; onChoosePlayer: () => void; stepNumber: number; xIsNext: boolean; @@ -48,7 +42,7 @@ export default function GameInfo (props: Props) { ); let status; - if (winner) { + if (winner != ' ') { if (props.player === ' ') { status = winner + ' Wins!'; } else { diff --git a/sample-components-tictactoe/src/components/game.tsx b/sample-components-tictactoe/src/components/game.tsx index c94e80d..23feb12 100644 --- a/sample-components-tictactoe/src/components/game.tsx +++ b/sample-components-tictactoe/src/components/game.tsx @@ -4,24 +4,25 @@ import firebase from 'firebase/app'; import 'firebase/database'; import { View } from 'magic-script-components'; -import { calculateWinner } from './calculate-winner.js'; +import calculateWinner from './calculate-winner.js'; import Board from './board'; import GameInfo from './game-info'; +import { Player } from './square.js'; + +export interface HistoryItem { + squares: Player[] +} interface Props { enableFirebase: boolean; - player: string; + player: Player; onChoosePlayer: () => void; } -export interface HistoryItem { - squares: string[] -} - const initialState = { history: [ { - squares: Array(9).fill('') + squares: Array(9).fill(' ') } ], stepNumber: 0, @@ -34,10 +35,6 @@ type State = Readonly; export default class Game extends React.Component { state: State = initialState; - constructor (props: Props) { - super(props); - } - componentDidMount () { if (this.props.enableFirebase) { this.dbref().on('value', snapshot => { @@ -76,7 +73,7 @@ export default class Game extends React.Component { if (this.props.player !== ' ' && this.props.player !== nextPlayer) { return; } - if (calculateWinner(squares) || squares[i]) { + if (calculateWinner(squares) !== ' ' || squares[i] !== ' ') { return; } squares[i] = nextPlayer; diff --git a/sample-components-tictactoe/src/components/player-chooser.tsx b/sample-components-tictactoe/src/components/player-chooser.tsx index 2384553..eaaa34f 100644 --- a/sample-components-tictactoe/src/components/player-chooser.tsx +++ b/sample-components-tictactoe/src/components/player-chooser.tsx @@ -1,10 +1,10 @@ import React from 'react'; import { LinearLayout, Text } from 'magic-script-components'; -import Square from './square'; +import Square, { Player } from './square'; interface Props { - onPlayerChosen: (player: string) => void; + onPlayerChosen: (player: Player) => void; } export default function PlayerChooser (props: Props) { diff --git a/sample-components-tictactoe/src/components/square.tsx b/sample-components-tictactoe/src/components/square.tsx index 7fa733e..2503da7 100644 --- a/sample-components-tictactoe/src/components/square.tsx +++ b/sample-components-tictactoe/src/components/square.tsx @@ -2,9 +2,11 @@ import React from 'react'; import { Button, vec4 } from 'magic-script-components'; +export type Player = 'X' | 'O' | ' '; + interface Props { name: string; - value: string; + value: Player; onClick: (event: any) => void; } From 1f3f44516d25bf9995c48d8b279097e532e8d6dc Mon Sep 17 00:00:00 2001 From: Tony Cuadra Date: Thu, 7 Nov 2019 17:36:05 -0500 Subject: [PATCH 4/4] Convert Player to an enum --- sample-components-tictactoe/src/app.tsx | 2 +- .../src/components/calculate-winner.ts | 2 +- .../src/components/game-info.tsx | 8 ++++---- sample-components-tictactoe/src/components/game.tsx | 8 ++++---- .../src/components/player-chooser.tsx | 12 ++++++------ .../src/components/square.tsx | 8 ++++++-- 6 files changed, 22 insertions(+), 18 deletions(-) diff --git a/sample-components-tictactoe/src/app.tsx b/sample-components-tictactoe/src/app.tsx index 173e899..40bf563 100644 --- a/sample-components-tictactoe/src/app.tsx +++ b/sample-components-tictactoe/src/app.tsx @@ -12,7 +12,7 @@ interface Props extends AppProps { interface State { // Use null to indicate that no player has been chosen yet - // (Blank ' ' is used to play for both 'X' and 'O') + // (Player.None is used to play for both X and O) player: Player | null; } diff --git a/sample-components-tictactoe/src/components/calculate-winner.ts b/sample-components-tictactoe/src/components/calculate-winner.ts index 5b30707..2e65dca 100644 --- a/sample-components-tictactoe/src/components/calculate-winner.ts +++ b/sample-components-tictactoe/src/components/calculate-winner.ts @@ -17,5 +17,5 @@ export default function calculateWinner (squares: Player[]): Player { return squares[a]; } } - return ' '; + return Player.None; } diff --git a/sample-components-tictactoe/src/components/game-info.tsx b/sample-components-tictactoe/src/components/game-info.tsx index a1b4b8c..3a8030d 100644 --- a/sample-components-tictactoe/src/components/game-info.tsx +++ b/sample-components-tictactoe/src/components/game-info.tsx @@ -42,16 +42,16 @@ export default function GameInfo (props: Props) { ); let status; - if (winner != ' ') { - if (props.player === ' ') { + if (winner != Player.None) { + if (props.player === Player.None) { status = winner + ' Wins!'; } else { status = (winner === props.player) ? 'You Win!' : 'You Lose!'; } } else { - const nextPlayer = props.xIsNext ? 'X' : 'O'; + const nextPlayer = props.xIsNext ? Player.X : Player.O; status = 'Next player: ' + nextPlayer; - if (props.player !== ' ') { + if (props.player !== Player.None) { status += (nextPlayer === props.player) ? ' (You)' : ' (Opponent)'; } } diff --git a/sample-components-tictactoe/src/components/game.tsx b/sample-components-tictactoe/src/components/game.tsx index 23feb12..3379d08 100644 --- a/sample-components-tictactoe/src/components/game.tsx +++ b/sample-components-tictactoe/src/components/game.tsx @@ -22,7 +22,7 @@ interface Props { const initialState = { history: [ { - squares: Array(9).fill(' ') + squares: Array(9).fill(Player.None) } ], stepNumber: 0, @@ -69,11 +69,11 @@ export default class Game extends React.Component { const history = this.state.history.slice(0, this.state.stepNumber + 1); const current = history[history.length - 1]; const squares = current.squares.slice(); - const nextPlayer = this.state.xIsNext ? 'X' : 'O'; - if (this.props.player !== ' ' && this.props.player !== nextPlayer) { + const nextPlayer = this.state.xIsNext ? Player.X : Player.O + if (this.props.player !== Player.None && this.props.player !== nextPlayer) { return; } - if (calculateWinner(squares) !== ' ' || squares[i] !== ' ') { + if (calculateWinner(squares) !== Player.None || squares[i] !== Player.None) { return; } squares[i] = nextPlayer; diff --git a/sample-components-tictactoe/src/components/player-chooser.tsx b/sample-components-tictactoe/src/components/player-chooser.tsx index eaaa34f..3b202d6 100644 --- a/sample-components-tictactoe/src/components/player-chooser.tsx +++ b/sample-components-tictactoe/src/components/player-chooser.tsx @@ -12,19 +12,19 @@ export default function PlayerChooser (props: Props) { Choose a player: props.onPlayerChosen('X')} + onClick={event => props.onPlayerChosen(Player.X)} /> props.onPlayerChosen('O')} + onClick={event => props.onPlayerChosen(Player.O)} /> props.onPlayerChosen(' ')} + onClick={event => props.onPlayerChosen(Player.None)} /> ); diff --git a/sample-components-tictactoe/src/components/square.tsx b/sample-components-tictactoe/src/components/square.tsx index 2503da7..49bb86b 100644 --- a/sample-components-tictactoe/src/components/square.tsx +++ b/sample-components-tictactoe/src/components/square.tsx @@ -2,7 +2,11 @@ import React from 'react'; import { Button, vec4 } from 'magic-script-components'; -export type Player = 'X' | 'O' | ' '; +export enum Player { + X = 'X', + O = 'O', + None = ' ' +} interface Props { name: string; @@ -12,7 +16,7 @@ interface Props { // Component that renders a single square in the Tic Tac Toe board export default function Square (props: Props) { - const color: vec4 = props.value === 'X' ? [1, 0.1, 0.1, 1] : [0.1, 0.1, 1, 1]; + const color: vec4 = props.value === Player.X ? [1, 0.1, 0.1, 1] : [0.1, 0.1, 1, 1]; return (