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 0734e48..50f3aeb 100644
--- a/sample-components-tictactoe/rollup.config.js
+++ b/sample-components-tictactoe/rollup.config.js
@@ -1,20 +1,41 @@
-// 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';
+import typescript from 'rollup-plugin-typescript';
-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()
+ commonjs(),
+ typescript()
]
};
+
+export default [
+ // Build for MagicScript on LuminOS
+ {
+ ...common,
+ external: ['uv', 'lumin', 'ssl', 'jpeg', 'png', 'gl'],
+ input: 'src/main.tsx',
+ preserveModules: true,
+ output: {
+ dir: 'bin',
+ format: 'es'
+ }
+ },
+ // Build for MagicScript on Magicverse (iOS, Android)
+ {
+ ...common,
+ input: 'src/app.tsx',
+ external: ['react'],
+ output: {
+ globals: {
+ 'react': 'React'
+ },
+ file: 'bin/bundle.js',
+ format: 'iife',
+ name: '_'
+ }
+ }
+];
diff --git a/sample-components-tictactoe/src/app.js b/sample-components-tictactoe/src/app.js
deleted file mode 100644
index 0c67a4b..0000000
--- a/sample-components-tictactoe/src/app.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import firebase from 'firebase/app';
-
-import { View } from 'magic-script-components';
-import { PlayerChooser, Game } from './components/index.js';
-
-// Top-level application component -- renders either a player chooser or the game
-export class TicTacToeApp extends React.Component {
- constructor (props) {
- super(props);
-
- this.state = { player: '?' };
- this.onPlayerChosen = this.onPlayerChosen.bind(this);
- }
-
- useFirebase () {
- return this.props.firebaseConfig && this.props.firebaseConfig.hasOwnProperty('apiKey');
- }
-
- componentDidMount () {
- if (this.useFirebase()) {
- firebase.initializeApp(this.props.firebaseConfig);
- }
- }
-
- onPlayerChosen (player) {
- this.setState({ player: player });
- }
-
- render () {
- return (
-
- {this.state.player === '?'
- ?
- : (
- this.onPlayerChosen('?')}
- enableFirebase={this.useFirebase()}
- />
- )}
-
- );
- }
-}
-
-TicTacToeApp.propTypes = {
- firebaseConfig: PropTypes.object
-};
diff --git a/sample-components-tictactoe/src/app.tsx b/sample-components-tictactoe/src/app.tsx
new file mode 100644
index 0000000..40bf563
--- /dev/null
+++ b/sample-components-tictactoe/src/app.tsx
@@ -0,0 +1,53 @@
+import React from 'react';
+
+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 {
+ // Use null to indicate that no player has been chosen yet
+ // (Player.None 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: null };
+
+ 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!);
+ }
+ }
+
+ onPlayerChosen = (player: Player | null) => {
+ this.setState({ player });
+ }
+
+ render () {
+ return (
+
+ {this.state.player === null
+ ?
+ : (
+ this.onPlayerChosen(null)}
+ enableFirebase={this.useFirebase()}
+ />
+ )}
+
+ );
+ }
+}
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..61df618 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, { Player } from './square';
+
+interface Props {
+ squares: Player[];
+ 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 72%
rename from sample-components-tictactoe/src/components/calculate-winner.js
rename to sample-components-tictactoe/src/components/calculate-winner.ts
index c0a5797..2e65dca 100644
--- a/sample-components-tictactoe/src/components/calculate-winner.js
+++ b/sample-components-tictactoe/src/components/calculate-winner.ts
@@ -1,4 +1,6 @@
-export function calculateWinner (squares) {
+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) {
return squares[a];
}
}
- return null;
+ return Player.None;
}
diff --git a/sample-components-tictactoe/src/components/game-info.js b/sample-components-tictactoe/src/components/game-info.tsx
similarity index 67%
rename from sample-components-tictactoe/src/components/game-info.js
rename to sample-components-tictactoe/src/components/game-info.tsx
index 39e0749..3a8030d 100644
--- a/sample-components-tictactoe/src/components/game-info.js
+++ b/sample-components-tictactoe/src/components/game-info.tsx
@@ -1,18 +1,21 @@
+import { ListView, ListViewItem, Text, View, Button } from 'magic-script-components';
import React from 'react';
-import PropTypes from 'prop-types';
+import calculateWinner from './calculate-winner';
+import { HistoryItem } from './game';
+import { Player } from './square';
-import {
- Button,
- ListView,
- ListViewItem,
- Text,
- View
-} from 'magic-script-components';
-import { calculateWinner } from './calculate-winner.js';
+interface Props {
+ history: HistoryItem[];
+ onHistoryClick: (move: number) => void;
+ player: Player;
+ 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);
@@ -39,16 +42,16 @@ export default function GameInfo (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)';
}
}
@@ -60,12 +63,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 67%
rename from sample-components-tictactoe/src/components/game.js
rename to sample-components-tictactoe/src/components/game.tsx
index fe17b8c..3379d08 100644
--- a/sample-components-tictactoe/src/components/game.js
+++ b/sample-components-tictactoe/src/components/game.tsx
@@ -1,27 +1,39 @@
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 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: Player;
+ onChoosePlayer: () => void;
+}
+
+const initialState = {
+ history: [
+ {
+ squares: Array(9).fill(Player.None)
+ }
+ ],
+ 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) {
- super(props);
- this.state = {
- history: [
- {
- squares: Array(9).fill('')
- }
- ],
- stepNumber: 0,
- xIsNext: true
- };
- }
+export default class Game extends React.Component {
+ state: State = initialState;
componentDidMount () {
if (this.props.enableFirebase) {
@@ -41,11 +53,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,15 +65,15 @@ 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();
- 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;
@@ -76,7 +88,7 @@ export default class Game extends React.Component {
});
}
- jumpTo (step) {
+ jumpTo (step: number) {
this.updateState({
stepNumber: step,
xIsNext: (step % 2) === 0
@@ -109,9 +121,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 50%
rename from sample-components-tictactoe/src/components/player-chooser.js
rename to sample-components-tictactoe/src/components/player-chooser.tsx
index 674be09..3b202d6 100644
--- a/sample-components-tictactoe/src/components/player-chooser.js
+++ b/sample-components-tictactoe/src/components/player-chooser.tsx
@@ -1,32 +1,31 @@
import React from 'react';
-import PropTypes from 'prop-types';
import { LinearLayout, Text } from 'magic-script-components';
-import { Square } from './index.js';
+import Square, { Player } from './square';
-export default function PlayerChooser (props) {
+interface Props {
+ onPlayerChosen: (player: Player) => void;
+}
+
+export default function PlayerChooser (props: Props) {
return (
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)}
/>
);
}
-
-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..49bb86b
--- /dev/null
+++ b/sample-components-tictactoe/src/components/square.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+
+import { Button, vec4 } from 'magic-script-components';
+
+export enum Player {
+ X = 'X',
+ O = 'O',
+ None = ' '
+}
+
+interface Props {
+ name: string;
+ value: Player;
+ 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 === Player.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
+ }
}
-