diff --git a/examples/2048/Game2048.js b/examples/2048/Game2048.js
new file mode 100644
index 00000000..3be6adbd
--- /dev/null
+++ b/examples/2048/Game2048.js
@@ -0,0 +1,324 @@
+/**
+ * The examples provided by Facebook are for non-commercial testing and
+ * evaluation purposes only.
+ *
+ * Facebook reserves all rights not expressly granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
+ * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * @providesModule Game2048
+ * @flow
+ */
+'use strict';
+
+var React = require('react-native');
+var {
+ Animated,
+ AppRegistry,
+ StyleSheet,
+ Text,
+ TouchableBounce,
+ View,
+} = React;
+
+var GameBoard = require('./GameBoard');
+
+var BOARD_PADDING = 3;
+var CELL_MARGIN = 4;
+var CELL_SIZE = 60;
+
+class Cell extends React.Component {
+ render() {
+ return ;
+ }
+}
+
+class Board extends React.Component {
+ render() {
+ return (
+
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ {this.props.children}
+
+ );
+ }
+}
+
+class Tile extends React.Component {
+ state: any;
+
+ static _getPosition(index): number {
+ return BOARD_PADDING + (index * (CELL_SIZE + CELL_MARGIN * 2) + CELL_MARGIN);
+ }
+
+ constructor(props: {}) {
+ super(props);
+
+ var tile = this.props.tile;
+
+ this.state = {
+ opacity: new Animated.Value(0),
+ top: new Animated.Value(Tile._getPosition(tile.toRow())),
+ left: new Animated.Value(Tile._getPosition(tile.toColumn())),
+ };
+ }
+
+ calculateOffset(): {top: number; left: number; opacity: number} {
+ var tile = this.props.tile;
+
+ var offset = {
+ top: this.state.top,
+ left: this.state.left,
+ opacity: this.state.opacity,
+ };
+
+ if (tile.isNew()) {
+ Animated.timing(this.state.opacity, {
+ duration: 100,
+ toValue: 1,
+ }).start();
+ } else {
+ Animated.parallel([
+ Animated.timing(offset.top, {
+ duration: 100,
+ toValue: Tile._getPosition(tile.toRow()),
+ }),
+ Animated.timing(offset.left, {
+ duration: 100,
+ toValue: Tile._getPosition(tile.toColumn()),
+ }),
+ ]).start();
+ }
+ return offset;
+ }
+
+ render() {
+ var tile = this.props.tile;
+
+ var tileStyles = [
+ styles.tile,
+ styles['tile' + tile.value],
+ this.calculateOffset(),
+ ];
+
+ var textStyles = [
+ styles.value,
+ tile.value > 4 && styles.whiteText,
+ tile.value > 100 && styles.threeDigits,
+ tile.value > 1000 && styles.fourDigits,
+ ];
+
+ return (
+
+ {tile.value}
+
+ );
+ }
+}
+
+class GameEndOverlay extends React.Component {
+ render() {
+ var board = this.props.board;
+
+ if (!board.hasWon() && !board.hasLost()) {
+ return ;
+ }
+
+ var message = board.hasWon() ?
+ 'Good Job!' : 'Game Over';
+
+ return (
+
+ {message}
+
+ Try Again?
+
+
+ );
+ }
+}
+
+class Game2048 extends React.Component {
+ startX: number;
+ startY: number;
+ state: any;
+
+ constructor(props: {}) {
+ super(props);
+ this.state = {
+ board: new GameBoard(),
+ };
+ this.startX = 0;
+ this.startY = 0;
+ }
+
+ restartGame() {
+ this.setState({board: new GameBoard()});
+ }
+
+ handleTouchStart(event: Object) {
+ if (this.state.board.hasWon()) {
+ return;
+ }
+
+ this.startX = event.nativeEvent.pageX;
+ this.startY = event.nativeEvent.pageY;
+ }
+
+ handleTouchEnd(event: Object) {
+ if (this.state.board.hasWon()) {
+ return;
+ }
+
+ var deltaX = event.nativeEvent.pageX - this.startX;
+ var deltaY = event.nativeEvent.pageY - this.startY;
+
+ var direction = -1;
+ if (Math.abs(deltaX) > 3 * Math.abs(deltaY) && Math.abs(deltaX) > 30) {
+ direction = deltaX > 0 ? 2 : 0;
+ } else if (Math.abs(deltaY) > 3 * Math.abs(deltaX) && Math.abs(deltaY) > 30) {
+ direction = deltaY > 0 ? 3 : 1;
+ }
+
+ if (direction !== -1) {
+ this.setState({board: this.state.board.move(direction)});
+ }
+ }
+
+ render() {
+ var tiles = this.state.board.tiles
+ .filter((tile) => tile.value)
+ .map((tile) => );
+
+ return (
+ this.handleTouchStart(event)}
+ onTouchEnd={(event) => this.handleTouchEnd(event)}>
+
+ {tiles}
+
+ this.restartGame()} />
+
+ );
+ }
+}
+
+var styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ board: {
+ padding: BOARD_PADDING,
+ backgroundColor: '#bbaaaa',
+ borderRadius: 5,
+ },
+ overlay: {
+ position: 'absolute',
+ top: 0,
+ bottom: 0,
+ left: 0,
+ right: 0,
+ backgroundColor: 'rgba(221, 221, 221, 0.5)',
+ flex: 1,
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ overlayMessage: {
+ fontSize: 40,
+ marginBottom: 20,
+ },
+ tryAgain: {
+ backgroundColor: '#887761',
+ padding: 20,
+ borderRadius: 5,
+ },
+ tryAgainText: {
+ color: '#ffffff',
+ fontSize: 20,
+ fontWeight: '500',
+ },
+ cell: {
+ width: CELL_SIZE,
+ height: CELL_SIZE,
+ borderRadius: 5,
+ backgroundColor: '#ddccbb',
+ margin: CELL_MARGIN,
+ },
+ row: {
+ flexDirection: 'row',
+ },
+ tile: {
+ position: 'absolute',
+ width: CELL_SIZE,
+ height: CELL_SIZE,
+ backgroundColor: '#ddccbb',
+ borderRadius: 5,
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ value: {
+ fontSize: 24,
+ color: '#776666',
+ fontFamily: 'Verdana',
+ fontWeight: '500',
+ },
+ tile2: {
+ backgroundColor: '#eeeeee',
+ },
+ tile4: {
+ backgroundColor: '#eeeecc',
+ },
+ tile8: {
+ backgroundColor: '#ffbb87',
+ },
+ tile16: {
+ backgroundColor: '#ff9966',
+ },
+ tile32: {
+ backgroundColor: '#ff7755',
+ },
+ tile64: {
+ backgroundColor: '#ff5533',
+ },
+ tile128: {
+ backgroundColor: '#eecc77',
+ },
+ tile256: {
+ backgroundColor: '#eecc66',
+ },
+ tile512: {
+ backgroundColor: '#eecc55',
+ },
+ tile1024: {
+ backgroundColor: '#eecc33',
+ },
+ tile2048: {
+ backgroundColor: '#eecc22',
+ },
+ whiteText: {
+ color: '#ffffff',
+ },
+ threeDigits: {
+ fontSize: 20,
+ },
+ fourDigits: {
+ fontSize: 18,
+ },
+});
+
+AppRegistry.registerComponent('Game2048', () => Game2048);
+
+module.exports = Game2048;
diff --git a/examples/2048/GameBoard.js b/examples/2048/GameBoard.js
new file mode 100644
index 00000000..6ed92e6b
--- /dev/null
+++ b/examples/2048/GameBoard.js
@@ -0,0 +1,201 @@
+/**
+ * The examples provided by Facebook are for non-commercial testing and
+ * evaluation purposes only.
+ *
+ * Facebook reserves all rights not expressly granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
+ * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * @providesModule GameBoard
+ * @flow
+ */
+'use strict';
+
+// NB: Taken straight from: https://github.com/IvanVergiliev/2048-react/blob/master/src/board.js
+// with no modification except to format it for CommonJS and fix lint/flow errors
+
+var rotateLeft = function (matrix) {
+ var rows = matrix.length;
+ var columns = matrix[0].length;
+ var res = [];
+ for (var row = 0; row < rows; ++row) {
+ res.push([]);
+ for (var column = 0; column < columns; ++column) {
+ res[row][column] = matrix[column][columns - row - 1];
+ }
+ }
+ return res;
+};
+
+var Tile = function (value?: number, row?: number, column?: number) {
+ this.value = value || 0;
+ this.row = row || -1;
+
+ this.column = column || -1;
+ this.oldRow = -1;
+ this.oldColumn = -1;
+ this.markForDeletion = false;
+ this.mergedInto = null;
+ this.id = Tile.id++;
+};
+
+Tile.id = 0;
+
+Tile.prototype.moveTo = function (row, column) {
+ this.oldRow = this.row;
+ this.oldColumn = this.column;
+ this.row = row;
+ this.column = column;
+};
+
+Tile.prototype.isNew = function () {
+ return this.oldRow === -1 && !this.mergedInto;
+};
+
+Tile.prototype.hasMoved = function () {
+ return (this.fromRow() !== -1 && (this.fromRow() !== this.toRow() || this.fromColumn() !== this.toColumn())) ||
+ this.mergedInto;
+};
+
+Tile.prototype.fromRow = function () {
+ return this.mergedInto ? this.row : this.oldRow;
+};
+
+Tile.prototype.fromColumn = function () {
+ return this.mergedInto ? this.column : this.oldColumn;
+};
+
+Tile.prototype.toRow = function () {
+ return this.mergedInto ? this.mergedInto.row : this.row;
+};
+
+Tile.prototype.toColumn = function () {
+ return this.mergedInto ? this.mergedInto.column : this.column;
+};
+
+var Board = function () {
+ this.tiles = [];
+ this.cells = [];
+ for (var i = 0; i < Board.size; ++i) {
+ this.cells[i] = [this.addTile(), this.addTile(), this.addTile(), this.addTile()];
+ }
+ this.addRandomTile();
+ this.setPositions();
+ this.won = false;
+};
+
+Board.prototype.addTile = function () {
+ var res = new Tile();
+ Tile.apply(res, arguments);
+ this.tiles.push(res);
+ return res;
+};
+
+Board.size = 4;
+
+Board.prototype.moveLeft = function () {
+ var hasChanged = false;
+ for (var row = 0; row < Board.size; ++row) {
+ var currentRow = this.cells[row].filter(function (tile) { return tile.value !== 0; });
+ var resultRow = [];
+ for (var target = 0; target < Board.size; ++target) {
+ var targetTile = currentRow.length ? currentRow.shift() : this.addTile();
+ if (currentRow.length > 0 && currentRow[0].value === targetTile.value) {
+ var tile1 = targetTile;
+ targetTile = this.addTile(targetTile.value);
+ tile1.mergedInto = targetTile;
+ var tile2 = currentRow.shift();
+ tile2.mergedInto = targetTile;
+ targetTile.value += tile2.value;
+ }
+ resultRow[target] = targetTile;
+ this.won = this.won || (targetTile.value === 2048);
+ hasChanged = hasChanged || (targetTile.value !== this.cells[row][target].value);
+ }
+ this.cells[row] = resultRow;
+ }
+ return hasChanged;
+};
+
+Board.prototype.setPositions = function () {
+ this.cells.forEach(function (row, rowIndex) {
+ row.forEach(function (tile, columnIndex) {
+ tile.oldRow = tile.row;
+ tile.oldColumn = tile.column;
+ tile.row = rowIndex;
+ tile.column = columnIndex;
+ tile.markForDeletion = false;
+ });
+ });
+};
+
+Board.fourProbability = 0.1;
+
+Board.prototype.addRandomTile = function () {
+ var emptyCells = [];
+ for (var r = 0; r < Board.size; ++r) {
+ for (var c = 0; c < Board.size; ++c) {
+ if (this.cells[r][c].value === 0) {
+ emptyCells.push({r: r, c: c});
+ }
+ }
+ }
+ var index = Math.floor(Math.random() * emptyCells.length);
+ var cell = emptyCells[index];
+ var newValue = Math.random() < Board.fourProbability ? 4 : 2;
+ this.cells[cell.r][cell.c] = this.addTile(newValue);
+};
+
+Board.prototype.move = function (direction) {
+ // 0 -> left, 1 -> up, 2 -> right, 3 -> down
+ this.clearOldTiles();
+ for (var i = 0; i < direction; ++i) {
+ this.cells = rotateLeft(this.cells);
+ }
+ var hasChanged = this.moveLeft();
+ for (var i = direction; i < 4; ++i) {
+ this.cells = rotateLeft(this.cells);
+ }
+ if (hasChanged) {
+ this.addRandomTile();
+ }
+ this.setPositions();
+ return this;
+};
+
+Board.prototype.clearOldTiles = function () {
+ this.tiles = this.tiles.filter(function (tile) { return tile.markForDeletion === false; });
+ this.tiles.forEach(function (tile) { tile.markForDeletion = true; });
+};
+
+Board.prototype.hasWon = function () {
+ return this.won;
+};
+
+Board.deltaX = [-1, 0, 1, 0];
+Board.deltaY = [0, -1, 0, 1];
+
+Board.prototype.hasLost = function () {
+ var canMove = false;
+ for (var row = 0; row < Board.size; ++row) {
+ for (var column = 0; column < Board.size; ++column) {
+ canMove = canMove || (this.cells[row][column].value === 0);
+ for (var dir = 0; dir < 4; ++dir) {
+ var newRow = row + Board.deltaX[dir];
+ var newColumn = column + Board.deltaY[dir];
+ if (newRow < 0 || newRow >= Board.size || newColumn < 0 || newColumn >= Board.size) {
+ continue;
+ }
+ canMove = canMove || (this.cells[row][column].value === this.cells[newRow][newColumn].value);
+ }
+ }
+ }
+ return !canMove;
+};
+
+module.exports = Board;
diff --git a/examples/index.html b/examples/index.html
index 50b48f6e..6ee40eea 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -3,4 +3,4 @@
React Native for Web
-
+
diff --git a/examples/index.js b/examples/index.js
index da3cc22a..74af3f62 100644
--- a/examples/index.js
+++ b/examples/index.js
@@ -1,23 +1,7 @@
-import { MediaProvider, matchMedia } from 'react-media-queries'
-import App from './components/App'
-import createGetter from 'react-media-queries/lib/createMediaQueryGetter'
-import createListener from 'react-media-queries/lib/createMediaQueryListener'
import React, { AppRegistry } from 'react-native'
+import Game2048 from './2048/Game2048'
+import TicTacToeApp from './TicTacToe/TicTacToe'
-const mediaQueries = {
- small: '(min-width: 300px)',
- medium: '(min-width: 400px)',
- large: '(min-width: 500px)'
-}
-const ResponsiveApp = matchMedia()(App)
-const WrappedApp = () => (
-
-
-
-)
-
-AppRegistry.registerComponent('Example', () => WrappedApp)
-
-AppRegistry.runApplication('Example', {
+AppRegistry.runApplication('Game2048', {
rootTag: document.getElementById('react-root')
})