├── .prettierrc ├── .gitignore ├── src ├── pages │ ├── index.js │ └── Home.jsx ├── assets │ ├── img │ │ ├── waves.gif │ │ ├── linkedin.svg │ │ ├── twitter.svg │ │ └── github.svg │ └── styles │ │ ├── fonts │ │ ├── Pacifico-Regular.woff │ │ ├── ClearSans-Bold-webfont.eot │ │ ├── ClearSans-Bold-webfont.woff │ │ ├── ClearSans-Light-webfont.eot │ │ ├── ClearSans-Light-webfont.woff │ │ ├── ClearSans-Regular-webfont.eot │ │ ├── ClearSans-Regular-webfont.woff │ │ └── font.scss │ │ └── app.scss ├── hoc │ ├── index.js │ └── keyboardManager │ │ └── index.js ├── utils │ ├── transition.js │ ├── index.js │ ├── elevation.js │ ├── portal.js │ ├── color.js │ ├── keyframes.js │ └── game.js ├── components │ ├── index.js │ ├── Header │ │ └── index.jsx │ ├── Score │ │ └── index.js │ ├── Details │ │ └── index.js │ ├── Cell │ │ └── index.js │ ├── Grid │ │ └── index.js │ ├── Instructions │ │ └── index.jsx │ └── Game │ │ └── index.jsx ├── elements │ ├── index.js │ ├── Text.js │ ├── Modal.js │ ├── Button.js │ ├── Icon.js │ ├── GameFactory.js │ └── Container.js └── App.jsx ├── screenshots ├── 1.png ├── 2.png ├── game.gif └── game-logo.png ├── dist ├── waves.0ff540f5.gif ├── icon-72x72.64199dc3.png ├── icon-96x96.f16e8aa9.png ├── favicon-16x16.48376abf.png ├── favicon-32x32.7038d02b.png ├── favicon-96x96.b441a26b.png ├── icon-128x128.bdc5b571.png ├── icon-144x144.357081de.png ├── icon-152x152.bc4c1001.png ├── icon-192x192.52b953b7.png ├── icon-384x384.b9e62e98.png ├── icon-512x512.e3a8497e.png ├── apple-icon-57x57.c7df9d0f.png ├── apple-icon-60x60.1c9552b0.png ├── apple-icon-72x72.e98825e6.png ├── apple-icon-76x76.dd21d491.png ├── ms-icon-144x144.9c1d6372.png ├── Pacifico-Regular.c0c32478.woff ├── apple-icon-114x114.12757dc0.png ├── apple-icon-120x120.c7598235.png ├── apple-icon-144x144.9c1d6372.png ├── apple-icon-152x152.c259a454.png ├── apple-icon-180x180.ce6023f9.png ├── android-icon-192x192.cf567ee1.png ├── ClearSans-Bold-webfont.6a6f5f3f.eot ├── ClearSans-Bold-webfont.ae49b717.woff ├── ClearSans-Light-webfont.fe3992bc.eot ├── ClearSans-Light-webfont.fb98e0ac.woff ├── ClearSans-Regular-webfont.662fe67c.eot ├── ClearSans-Regular-webfont.7edfc781.woff ├── manifest.1a627359.webmanifest ├── 15Puzzle.b3ebf31c.css └── index.html ├── public └── images │ └── icons │ ├── favicon.ico │ ├── apple-icon.png │ ├── icon-72x72.png │ ├── icon-96x96.png │ ├── icon-128x128.png │ ├── icon-144x144.png │ ├── icon-152x152.png │ ├── icon-192x192.png │ ├── icon-384x384.png │ ├── icon-512x512.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── ms-icon-144x144.png │ ├── ms-icon-150x150.png │ ├── ms-icon-310x310.png │ ├── ms-icon-70x70.png │ ├── apple-icon-57x57.png │ ├── apple-icon-60x60.png │ ├── apple-icon-72x72.png │ ├── apple-icon-76x76.png │ ├── android-icon-144x144.png │ ├── android-icon-192x192.png │ ├── android-icon-36x36.png │ ├── android-icon-48x48.png │ ├── android-icon-72x72.png │ ├── android-icon-96x96.png │ ├── apple-icon-114x114.png │ ├── apple-icon-120x120.png │ ├── apple-icon-144x144.png │ ├── apple-icon-152x152.png │ ├── apple-icon-180x180.png │ └── apple-icon-precomposed.png ├── index.js ├── .babelrc ├── package.json ├── LICENSE ├── manifest.webmanifest ├── README.md └── index.html /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .cache 3 | *-lock.json 4 | .lock -------------------------------------------------------------------------------- /src/pages/index.js: -------------------------------------------------------------------------------- 1 | import Home from './Home'; 2 | export { Home }; 3 | -------------------------------------------------------------------------------- /screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/screenshots/1.png -------------------------------------------------------------------------------- /screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/screenshots/2.png -------------------------------------------------------------------------------- /screenshots/game.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/screenshots/game.gif -------------------------------------------------------------------------------- /dist/waves.0ff540f5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/waves.0ff540f5.gif -------------------------------------------------------------------------------- /src/assets/img/waves.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/src/assets/img/waves.gif -------------------------------------------------------------------------------- /src/hoc/index.js: -------------------------------------------------------------------------------- 1 | import KeyBoardManagar from './keyboardManager'; 2 | 3 | export { KeyBoardManagar }; 4 | -------------------------------------------------------------------------------- /screenshots/game-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/screenshots/game-logo.png -------------------------------------------------------------------------------- /dist/icon-72x72.64199dc3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/icon-72x72.64199dc3.png -------------------------------------------------------------------------------- /dist/icon-96x96.f16e8aa9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/icon-96x96.f16e8aa9.png -------------------------------------------------------------------------------- /dist/favicon-16x16.48376abf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/favicon-16x16.48376abf.png -------------------------------------------------------------------------------- /dist/favicon-32x32.7038d02b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/favicon-32x32.7038d02b.png -------------------------------------------------------------------------------- /dist/favicon-96x96.b441a26b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/favicon-96x96.b441a26b.png -------------------------------------------------------------------------------- /dist/icon-128x128.bdc5b571.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/icon-128x128.bdc5b571.png -------------------------------------------------------------------------------- /dist/icon-144x144.357081de.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/icon-144x144.357081de.png -------------------------------------------------------------------------------- /dist/icon-152x152.bc4c1001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/icon-152x152.bc4c1001.png -------------------------------------------------------------------------------- /dist/icon-192x192.52b953b7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/icon-192x192.52b953b7.png -------------------------------------------------------------------------------- /dist/icon-384x384.b9e62e98.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/icon-384x384.b9e62e98.png -------------------------------------------------------------------------------- /dist/icon-512x512.e3a8497e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/icon-512x512.e3a8497e.png -------------------------------------------------------------------------------- /public/images/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/favicon.ico -------------------------------------------------------------------------------- /dist/apple-icon-57x57.c7df9d0f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/apple-icon-57x57.c7df9d0f.png -------------------------------------------------------------------------------- /dist/apple-icon-60x60.1c9552b0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/apple-icon-60x60.1c9552b0.png -------------------------------------------------------------------------------- /dist/apple-icon-72x72.e98825e6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/apple-icon-72x72.e98825e6.png -------------------------------------------------------------------------------- /dist/apple-icon-76x76.dd21d491.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/apple-icon-76x76.dd21d491.png -------------------------------------------------------------------------------- /dist/ms-icon-144x144.9c1d6372.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/ms-icon-144x144.9c1d6372.png -------------------------------------------------------------------------------- /public/images/icons/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/apple-icon.png -------------------------------------------------------------------------------- /public/images/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/icon-72x72.png -------------------------------------------------------------------------------- /public/images/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/icon-96x96.png -------------------------------------------------------------------------------- /dist/Pacifico-Regular.c0c32478.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/Pacifico-Regular.c0c32478.woff -------------------------------------------------------------------------------- /dist/apple-icon-114x114.12757dc0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/apple-icon-114x114.12757dc0.png -------------------------------------------------------------------------------- /dist/apple-icon-120x120.c7598235.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/apple-icon-120x120.c7598235.png -------------------------------------------------------------------------------- /dist/apple-icon-144x144.9c1d6372.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/apple-icon-144x144.9c1d6372.png -------------------------------------------------------------------------------- /dist/apple-icon-152x152.c259a454.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/apple-icon-152x152.c259a454.png -------------------------------------------------------------------------------- /dist/apple-icon-180x180.ce6023f9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/apple-icon-180x180.ce6023f9.png -------------------------------------------------------------------------------- /public/images/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/icon-128x128.png -------------------------------------------------------------------------------- /public/images/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/icon-144x144.png -------------------------------------------------------------------------------- /public/images/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/icon-152x152.png -------------------------------------------------------------------------------- /public/images/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/icon-192x192.png -------------------------------------------------------------------------------- /public/images/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/icon-384x384.png -------------------------------------------------------------------------------- /public/images/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/icon-512x512.png -------------------------------------------------------------------------------- /dist/android-icon-192x192.cf567ee1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/android-icon-192x192.cf567ee1.png -------------------------------------------------------------------------------- /public/images/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/images/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/images/icons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/favicon-96x96.png -------------------------------------------------------------------------------- /public/images/icons/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/ms-icon-144x144.png -------------------------------------------------------------------------------- /public/images/icons/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/ms-icon-150x150.png -------------------------------------------------------------------------------- /public/images/icons/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/ms-icon-310x310.png -------------------------------------------------------------------------------- /public/images/icons/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/ms-icon-70x70.png -------------------------------------------------------------------------------- /dist/ClearSans-Bold-webfont.6a6f5f3f.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/ClearSans-Bold-webfont.6a6f5f3f.eot -------------------------------------------------------------------------------- /dist/ClearSans-Bold-webfont.ae49b717.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/ClearSans-Bold-webfont.ae49b717.woff -------------------------------------------------------------------------------- /dist/ClearSans-Light-webfont.fe3992bc.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/ClearSans-Light-webfont.fe3992bc.eot -------------------------------------------------------------------------------- /public/images/icons/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/apple-icon-57x57.png -------------------------------------------------------------------------------- /public/images/icons/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/apple-icon-60x60.png -------------------------------------------------------------------------------- /public/images/icons/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/apple-icon-72x72.png -------------------------------------------------------------------------------- /public/images/icons/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/apple-icon-76x76.png -------------------------------------------------------------------------------- /dist/ClearSans-Light-webfont.fb98e0ac.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/ClearSans-Light-webfont.fb98e0ac.woff -------------------------------------------------------------------------------- /dist/ClearSans-Regular-webfont.662fe67c.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/ClearSans-Regular-webfont.662fe67c.eot -------------------------------------------------------------------------------- /dist/ClearSans-Regular-webfont.7edfc781.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/dist/ClearSans-Regular-webfont.7edfc781.woff -------------------------------------------------------------------------------- /public/images/icons/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/android-icon-144x144.png -------------------------------------------------------------------------------- /public/images/icons/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/android-icon-192x192.png -------------------------------------------------------------------------------- /public/images/icons/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/android-icon-36x36.png -------------------------------------------------------------------------------- /public/images/icons/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/android-icon-48x48.png -------------------------------------------------------------------------------- /public/images/icons/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/android-icon-72x72.png -------------------------------------------------------------------------------- /public/images/icons/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/android-icon-96x96.png -------------------------------------------------------------------------------- /public/images/icons/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/apple-icon-114x114.png -------------------------------------------------------------------------------- /public/images/icons/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/apple-icon-120x120.png -------------------------------------------------------------------------------- /public/images/icons/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/apple-icon-144x144.png -------------------------------------------------------------------------------- /public/images/icons/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/apple-icon-152x152.png -------------------------------------------------------------------------------- /public/images/icons/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/apple-icon-180x180.png -------------------------------------------------------------------------------- /public/images/icons/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/public/images/icons/apple-icon-precomposed.png -------------------------------------------------------------------------------- /src/assets/styles/fonts/Pacifico-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/src/assets/styles/fonts/Pacifico-Regular.woff -------------------------------------------------------------------------------- /src/assets/styles/fonts/ClearSans-Bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/src/assets/styles/fonts/ClearSans-Bold-webfont.eot -------------------------------------------------------------------------------- /src/assets/styles/fonts/ClearSans-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/src/assets/styles/fonts/ClearSans-Bold-webfont.woff -------------------------------------------------------------------------------- /src/assets/styles/fonts/ClearSans-Light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/src/assets/styles/fonts/ClearSans-Light-webfont.eot -------------------------------------------------------------------------------- /src/utils/transition.js: -------------------------------------------------------------------------------- 1 | export default ({ property = 'all', length = '0.3s', ease = 'ease' }) => ` 2 | transition: ${property} ${length} ${ease}; 3 | `; 4 | -------------------------------------------------------------------------------- /src/assets/styles/fonts/ClearSans-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/src/assets/styles/fonts/ClearSans-Light-webfont.woff -------------------------------------------------------------------------------- /src/assets/styles/fonts/ClearSans-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/src/assets/styles/fonts/ClearSans-Regular-webfont.eot -------------------------------------------------------------------------------- /src/assets/styles/fonts/ClearSans-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imshubhamsingh/15-puzzle/HEAD/src/assets/styles/fonts/ClearSans-Regular-webfont.woff -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | import Game from './Game'; 2 | import Instruction from './Instructions'; 3 | import Header from './Header'; 4 | import Details from './Details'; 5 | 6 | export { Game, Instruction, Header, Details }; 7 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | 4 | import App from '@Src'; 5 | 6 | const rootEl = document.getElementById('root'); 7 | 8 | const renderComponent = Component => { 9 | render(, rootEl); 10 | }; 11 | 12 | renderComponent(App); 13 | -------------------------------------------------------------------------------- /src/elements/index.js: -------------------------------------------------------------------------------- 1 | import GameFactory from './GameFactory'; 2 | import Modal from './Modal'; 3 | 4 | export * from './Button'; 5 | export * from './Container'; 6 | export * from './Text'; 7 | export * from './Icon'; 8 | export * from './GameFactory'; 9 | 10 | export { GameFactory, Modal }; 11 | -------------------------------------------------------------------------------- /src/components/Header/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { HeaderText } from '@Elements'; 3 | 4 | export default class Header extends Component { 5 | render() { 6 | return ( 7 | 8 | 15 Puzzle 9 | 10 | ); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | import color from './color'; 2 | import transition from './transition'; 3 | import Portal from './portal'; 4 | import elevation from './elevation'; 5 | 6 | export * from './color'; 7 | export * from './game'; 8 | export * from './keyframes'; 9 | 10 | export { color, transition, Portal, elevation }; 11 | -------------------------------------------------------------------------------- /src/utils/elevation.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | 'box-shadow: inset 0 7px 9px -7px rgba(0,0,0.7);', 3 | 'box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);', 4 | 'box-shadow: 0 3px 6px rgba(0,0,0,0.10), 0 6px 6px rgba(0,0,0,0.10);', 5 | 'box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);', 6 | 'box-shadow: 0 19px 38px rgba(0,0,0,0.30), 0 15px 12px rgba(0,0,0,0.22);' 7 | ]; 8 | -------------------------------------------------------------------------------- /src/utils/portal.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | import { createPortal } from 'react-dom'; 3 | 4 | const portalRoot = document.getElementById('portal'); 5 | 6 | export default class Portal extends Component { 7 | constructor() { 8 | super(); 9 | this.el = document.createElement('div'); 10 | } 11 | 12 | componentDidMount() { 13 | portalRoot.appendChild(this.el); 14 | } 15 | 16 | componentWillUnmount() { 17 | portalRoot.removeChild(this.el); 18 | } 19 | 20 | render() { 21 | const { children } = this.props; 22 | return createPortal(children, this.el); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/assets/img/linkedin.svg: -------------------------------------------------------------------------------- 1 | LinkedIn icon -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { BrowserRouter, Route, Redirect } from 'react-router-dom'; 3 | 4 | import '@Styles/App.scss'; 5 | 6 | import { Home } from '@Pages'; 7 | import { GameFactory } from '@Elements'; 8 | 9 | export default class App extends PureComponent { 10 | render() { 11 | return ( 12 | 13 | 14 |
15 | 16 | } /> 17 |
18 |
19 |
20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "babel-plugin-styled-components", 4 | "@babel/plugin-proposal-class-properties", 5 | "@babel/plugin-transform-runtime", 6 | [ 7 | "module-resolver", 8 | { 9 | "root": "./src", 10 | "alias": { 11 | "@Src": "./src/App.jsx", 12 | "@Components": "./src/components", 13 | "@Pages": "./src/pages", 14 | "@Elements": "./src/elements", 15 | "@Styles": "./src/assets/styles", 16 | "@Utils": "./src/utils", 17 | "@HOC": "./src/hoc", 18 | "@Image": "./src/assets/img" 19 | } 20 | } 21 | ] 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /src/utils/color.js: -------------------------------------------------------------------------------- 1 | export const gridTileColor = '#3d2963'; 2 | export const backgroundColor = '#57407c'; 3 | export const primaryFontColor = (opacity = 1) => 4 | `rgba(255, 255, 255, ${opacity})`; 5 | export const buttonHoverColor = '#d3386a'; 6 | export const modalTextColor = '#534648'; 7 | export const modalBackgroundColor = '#FFFFFF'; 8 | export const overlayBackgroundColor = '#000000'; 9 | export const gridOverlayBackground = '#412667b5'; 10 | 11 | export default { 12 | backgroundColor, 13 | gridTileColor, 14 | primaryFontColor, 15 | buttonHoverColor, 16 | modalTextColor, 17 | modalBackgroundColor, 18 | overlayBackgroundColor, 19 | gridOverlayBackground 20 | }; 21 | -------------------------------------------------------------------------------- /dist/manifest.1a627359.webmanifest: -------------------------------------------------------------------------------- 1 | {"theme_color":"#57407c","background_color":"#57407c","display":"standalone","orientation":"portrait","Scope":"/","start_url":"/","icons":[{"src":"icon-72x72.64199dc3.png","sizes":"72x72","type":"image/png"},{"src":"icon-96x96.f16e8aa9.png","sizes":"96x96","type":"image/png"},{"src":"icon-128x128.bdc5b571.png","sizes":"128x128","type":"image/png"},{"src":"icon-144x144.357081de.png","sizes":"144x144","type":"image/png"},{"src":"icon-152x152.bc4c1001.png","sizes":"152x152","type":"image/png"},{"src":"icon-192x192.52b953b7.png","sizes":"192x192","type":"image/png"},{"src":"icon-384x384.b9e62e98.png","sizes":"384x384","type":"image/png"},{"src":"icon-512x512.e3a8497e.png","sizes":"512x512","type":"image/png"}],"splash_pages":null} -------------------------------------------------------------------------------- /src/assets/img/twitter.svg: -------------------------------------------------------------------------------- 1 | Twitter icon -------------------------------------------------------------------------------- /src/components/Score/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ScoreContainer } from '@Elements'; 3 | 4 | const Score = ({ moves, seconds }) => { 5 | return ( 6 | 7 |
8 |
Time
9 |
{seconds}s
10 |
11 |
12 |
Moves
13 |
{moves}
14 |
15 | {/*
16 |
best
17 |
123s 10Moves
18 |
*/} 19 |
20 | ); 21 | }; 22 | 23 | export default Score; 24 | -------------------------------------------------------------------------------- /src/assets/styles/app.scss: -------------------------------------------------------------------------------- 1 | @import url(./fonts/font.scss); 2 | 3 | $break-small: 520px; 4 | 5 | * { 6 | box-sizing: border-box; 7 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 8 | -webkit-touch-callout: none; 9 | -webkit-text-size-adjust: none; 10 | } 11 | 12 | *:focus, 13 | textarea:focus, 14 | input:focus, 15 | button:focus { 16 | outline: none; 17 | } 18 | 19 | body { 20 | padding: 0; 21 | margin: 0; 22 | font-family: 'Clear Sans', Arial, sans-serif; 23 | background: #57407c; 24 | color: white; 25 | @media screen and (max-width: $break-small) { 26 | margin: 20px 0; 27 | padding: 0 20px; 28 | font-size: 12px; 29 | } 30 | } 31 | .app { 32 | width: 100%; 33 | margin: 0; 34 | padding: 0; 35 | top: 0; 36 | left: 0; 37 | & #home { 38 | padding: 15px; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/assets/img/github.svg: -------------------------------------------------------------------------------- 1 | GitHub icon -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "15puzzle", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "parcel index.html", 8 | "build": "parcel build index.html --public-url ./" 9 | }, 10 | "author": "Shubham", 11 | "license": "ISC", 12 | "dependencies": { 13 | "parcel-bundler": "^1.10.3", 14 | "react": "^16.7.0-alpha.2", 15 | "react-dom": "^16.7.0-alpha.2", 16 | "react-router-dom": "^4.4.0-beta.6", 17 | "styled-components": "^4.1.1" 18 | }, 19 | "devDependencies": { 20 | "@babel/core": "^7.1.6", 21 | "@babel/plugin-proposal-class-properties": "^7.1.0", 22 | "@babel/plugin-transform-runtime": "^7.1.0", 23 | "babel-plugin-module-resolver": "^3.1.1", 24 | "babel-plugin-styled-components": "^1.8.0", 25 | "react-hot-loader": "^4.3.12", 26 | "sass": "^1.15.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Details/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import { Footer, Profile, Icon } from '@Elements'; 4 | 5 | const Details = ({ name, githubURL, linkedinURL, twitterURL, projectURL }) => ( 6 | 27 | ); 28 | 29 | export default Details; 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Shubham Singh 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. -------------------------------------------------------------------------------- /src/components/Cell/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component, useState, useEffect } from 'react'; 2 | 3 | import { CellContainer, NumberCellContainer } from '@Elements'; 4 | const moveKey = ({ x, y }) => { 5 | const [moveX, handleX] = useState(0); 6 | const [moveY, handleY] = useState(0); 7 | 8 | useEffect( 9 | () => { 10 | handleX(currentX => currentX + x); 11 | handleY(currentY => currentY + y); 12 | }, 13 | [moveX, moveY] 14 | ); 15 | 16 | return { x, y }; 17 | }; 18 | 19 | export default class Cell extends Component { 20 | render() { 21 | //const { x, y } = moveKey(this.props.x, this.props.y); 22 | const { number, index } = this.props; 23 | return ( 24 | 25 | { 29 | this.props.clickMove(index); 30 | }} 31 | > 32 |
33 |
34 |
{number}
35 |
{number}
36 | 37 | 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/elements/Text.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { color } from '@Utils'; 3 | 4 | import Waves from '@Image/waves.gif'; 5 | 6 | export const GameInstruction = styled.span` 7 | display: block; 8 | float: right; 9 | margin-top: 18px; 10 | font-size: 22px; 11 | line-height: 30px; 12 | color: ${color.primaryFontColor(0.5)}; 13 | & strong { 14 | color: ${color.primaryFontColor()}; 15 | font-size: inherit; 16 | } 17 | @media screen and (max-width: 520px) { 18 | margin-top: 10px; 19 | font-size: 18px; 20 | text-align: center; 21 | float: none; 22 | line-height: 24px; 23 | } 24 | `; 25 | 26 | export const HeaderText = styled.div` 27 | font-family: 'Pacifico', cursive; 28 | font-size: 60px; 29 | padding-left: 7px; 30 | margin-left: 2px; 31 | text-align: center; 32 | span { 33 | font-size: 85px; 34 | } 35 | 36 | background: url(${Waves}) repeat 0 0; 37 | width: 100%; 38 | text-align: center; 39 | color: ${color.primaryFontColor()}; 40 | margin-top: 27px; 41 | @media screen and (max-width: 520px) { 42 | font-size: 44px; 43 | margin-left: -3px; 44 | margin-top: 5px; 45 | span { 46 | font-size: 58px; 47 | } 48 | } 49 | `; 50 | -------------------------------------------------------------------------------- /src/components/Grid/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { 3 | GridContainer, 4 | GameFactoryConsumer, 5 | GridOverlay, 6 | Icon 7 | } from '@Elements'; 8 | import { color, gameState } from '@Utils'; 9 | 10 | import Cell from '../Cell'; 11 | 12 | export default class Grid extends Component { 13 | cellRender(number = [], clickMove) { 14 | return number.map((i, _) => ( 15 | 16 | )); 17 | } 18 | render() { 19 | return ( 20 | 21 | {({ values, methods }) => ( 22 | 23 | {this.cellRender(values.numbers, methods.clickMove)} 24 | {values.gameState === gameState.GAME_PAUSED && ( 25 | 26 |
27 | 35 |
36 |
37 | )} 38 |
39 | )} 40 |
41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /manifest.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "theme_color": "#57407c", 3 | "background_color": "#57407c", 4 | "display": "standalone", 5 | "orientation": "portrait", 6 | "Scope": "/", 7 | "start_url": "/", 8 | "icons": [ 9 | { 10 | "src": "public/images/icons/icon-72x72.png", 11 | "sizes": "72x72", 12 | "type": "image/png" 13 | }, 14 | { 15 | "src": "public/images/icons/icon-96x96.png", 16 | "sizes": "96x96", 17 | "type": "image/png" 18 | }, 19 | { 20 | "src": "public/images/icons/icon-128x128.png", 21 | "sizes": "128x128", 22 | "type": "image/png" 23 | }, 24 | { 25 | "src": "public/images/icons/icon-144x144.png", 26 | "sizes": "144x144", 27 | "type": "image/png" 28 | }, 29 | { 30 | "src": "public/images/icons/icon-152x152.png", 31 | "sizes": "152x152", 32 | "type": "image/png" 33 | }, 34 | { 35 | "src": "public/images/icons/icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image/png" 38 | }, 39 | { 40 | "src": "public/images/icons/icon-384x384.png", 41 | "sizes": "384x384", 42 | "type": "image/png" 43 | }, 44 | { 45 | "src": "public/images/icons/icon-512x512.png", 46 | "sizes": "512x512", 47 | "type": "image/png" 48 | } 49 | ], 50 | "splash_pages": null 51 | } 52 | -------------------------------------------------------------------------------- /src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import { KeyBoardManagar } from '@HOC'; 3 | 4 | import { Instruction, Header, Game, Details } from '@Components'; 5 | import { Container, Wave, GameFactoryConsumer } from '@Elements'; 6 | 7 | import Waves from '@Image/waves.gif'; 8 | 9 | const Home = ({ eventType }) => { 10 | return ( 11 | 12 | 13 | {({ values, methods }) => { 14 | return ( 15 | 16 |
17 |
18 | 19 |
20 | 21 |
22 | 23 |
24 |
25 |
32 | 33 | ); 34 | }} 35 | 36 | 37 | ); 38 | }; 39 | 40 | export default KeyBoardManagar(Home); 41 | -------------------------------------------------------------------------------- /src/utils/keyframes.js: -------------------------------------------------------------------------------- 1 | import { keyframes } from 'styled-components'; 2 | 3 | export const bounceIn = keyframes` 4 | from, 5 | 20%, 6 | 40%, 7 | 60%, 8 | 80%, 9 | to { 10 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 11 | } 12 | 13 | 0% { 14 | opacity: 0; 15 | transform: scale3d(0, 0, 0); 16 | } 17 | 18 | 20% { 19 | transform: scale3d(1.1, 1.1, 1.1); 20 | } 21 | 22 | 40% { 23 | transform: scale3d(0.9, 0.9, 0.9); 24 | } 25 | 26 | 60% { 27 | opacity: 1; 28 | transform: scale3d(1.03, 1.03, 1.03); 29 | } 30 | 31 | 80% { 32 | transform: scale3d(0.97, 0.97, 0.97); 33 | } 34 | 35 | to { 36 | opacity: 1; 37 | transform: scale3d(1, 1, 1); 38 | } 39 | `; 40 | 41 | export const bounceInUp = keyframes` 42 | from, 43 | 60%, 44 | 75%, 45 | 90%, 46 | to { 47 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 48 | } 49 | 50 | from { 51 | opacity: 0; 52 | transform: translate3d(0, 3000px, 0); 53 | } 54 | 55 | 60% { 56 | opacity: 1; 57 | transform: translate3d(0, -20px, 0); 58 | } 59 | 60 | 75% { 61 | transform: translate3d(0, 10px, 0); 62 | } 63 | 64 | 90% { 65 | transform: translate3d(0, -5px, 0); 66 | } 67 | 68 | to { 69 | transform: translate3d(0, 0, 0); 70 | } 71 | `; 72 | 73 | export const fadeIn = keyframes` 74 | from{ 75 | opacity: 0; 76 | } 77 | tp{ 78 | opacity:1; 79 | } 80 | `; 81 | -------------------------------------------------------------------------------- /src/elements/Modal.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import { Portal, elevation, bounceInUp, fadeIn, color } from '@Utils'; 5 | 6 | export default class Modal extends Component { 7 | render() { 8 | const { children, on } = this.props; 9 | return ( 10 | 11 | {on && ( 12 | 13 | {children} 14 | 15 | 16 | )} 17 | 18 | ); 19 | } 20 | } 21 | 22 | const ModalWrapper = styled.div` 23 | position: absolute; 24 | top: 0; 25 | left: 0; 26 | width: 100%; 27 | height: 100%; 28 | display: flex; 29 | justify-content: center; 30 | align-items: center; 31 | `; 32 | 33 | const ModalCard = styled.div` 34 | color: ${color.modalTextColor}; 35 | position: relative; 36 | z-index: 10000; 37 | animation-duration: 0.75s; 38 | animation-name: ${bounceInUp}; 39 | 40 | border-radius: 10px; 41 | padding: 15px; 42 | min-width: 320px; 43 | background-color: ${color.modalBackgroundColor}; 44 | height: 200px; 45 | ${elevation} 46 | @media screen and (max-width: 520px) { 47 | min-width: 284px; 48 | } 49 | `; 50 | 51 | const Background = styled.div` 52 | position: absolute; 53 | top: 0; 54 | left: 0; 55 | width: 100%; 56 | z-index: 1000; 57 | animation-duration: 0.75s; 58 | animation-name: ${fadeIn}; 59 | height: 100%; 60 | opacity: 0.3; 61 | background: ${color.overlayBackgroundColor}; 62 | `; 63 | -------------------------------------------------------------------------------- /src/elements/Button.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { color, transition } from '@Utils'; 3 | 4 | export const Button = styled.button` 5 | font-size: 22px; 6 | width: ${props => (props.type === 'big' ? '100%' : '150px')}; 7 | display: block; 8 | border-radius: 8px; 9 | border: none; 10 | cursor: pointer; 11 | font-weight: bold; 12 | outline: none; 13 | text-decoration: none; 14 | line-height: 60px; 15 | text-align: center; 16 | background-color: ${color.gridTileColor}; 17 | color: ${props => 18 | props.textColor ? props.textColor : color.primaryFontColor(0.5)}; 19 | ${transition({ property: 'color' })}; 20 | ${transition({ property: 'background-color' })}; 21 | ${transition({ property: 'opacity' })}; 22 | &:active { 23 | transform: scale(0.99); 24 | } 25 | &:hover { 26 | background-color: ${color.buttonHoverColor}; 27 | color: ${color.primaryFontColor()}; 28 | } 29 | 30 | &:disabled { 31 | opacity: 0.5; 32 | cursor: not-allowed; 33 | &:hover { 34 | background-color: ${color.gridTileColor}; 35 | color: ${color.primaryFontColor(0.5)}; 36 | } 37 | } 38 | 39 | @media (hover: none), (hover: on-demand) { 40 | &:hover { 41 | background-color: ${color.gridTileColor}; 42 | color: ${color.primaryFontColor(0.5)}; 43 | } 44 | } 45 | @media only screen and (max-width: 520px) { 46 | font-size: 16px; 47 | width: ${props => (props.type === 'big' ? '100%' : '112px')}; 48 | height: 100%; 49 | border-radius: 5px; 50 | padding: 0 5px; 51 | line-height: 42px; 52 | } 53 | `; 54 | -------------------------------------------------------------------------------- /dist/15Puzzle.b3ebf31c.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:Clear Sans;src:url(ClearSans-Light-webfont.fe3992bc.eot);src:url(ClearSans-Light-webfont.fe3992bc.eot?#iefix) format("embedded-opentype"),url(ClearSans-Light-webfont.95af1afc.svg#clear_sans_lightregular) format("svg"),url(ClearSans-Light-webfont.fb98e0ac.woff) format("woff");font-weight:200;font-style:normal}@font-face{font-family:Clear Sans;src:url(ClearSans-Regular-webfont.662fe67c.eot);src:url(ClearSans-Regular-webfont.662fe67c.eot?#iefix) format("embedded-opentype"),url(ClearSans-Regular-webfont.3abd45a8.svg#clear_sansregular) format("svg"),url(ClearSans-Regular-webfont.7edfc781.woff) format("woff");font-weight:400;font-style:normal}@font-face{font-family:Clear Sans;src:url(ClearSans-Bold-webfont.6a6f5f3f.eot);src:url(ClearSans-Bold-webfont.6a6f5f3f.eot?#iefix) format("embedded-opentype"),url(ClearSans-Bold-webfont.be59597a.svg#clear_sansbold) format("svg"),url(ClearSans-Bold-webfont.ae49b717.woff) format("woff");font-weight:700;font-style:normal}@font-face{font-family:Pacifico;font-style:normal;font-weight:400;src:url(Pacifico-Regular.c0c32478.woff) format("woff2");unicode-range:U+00??,U+0131,U+0152-0153,U+02bb-02bc,U+02c6,U+02da,U+02dc,U+2000-206f,U+2074,U+20ac,U+2122,U+2191,U+2193,U+2212,U+2215,U+feff,U+fffd}*{box-sizing:border-box;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-touch-callout:none;-webkit-text-size-adjust:none}:focus,button:focus,input:focus,textarea:focus{outline:none}body{padding:0;margin:0;font-family:Clear Sans,Arial,sans-serif;background:#57407c;color:#fff}@media screen and (max-width:520px){body{margin:20px 0;padding:0 20px;font-size:12px}}.app{width:100%;margin:0;padding:0;top:0;left:0}.app #home{padding:15px} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 | 15Puzzle

5 | 15 Puzzle 6 | 7 |

8 |

My first Game using React.js

9 |

10 |

11 | 12 | > This is a classic _15_-_puzzle_. To play you first scramble the tiles and then try to put them back in order. To move a tile you simply click on it 13 | 14 |

15 | 15Puzzle 16 |

17 | 18 | 19 | ## Gameplay 20 | 21 |

22 | 23 |

24 | 25 | >The app uses React.js alpha API likes hooks etc.. 26 | ## Development 27 | 28 | ### Run app 29 | 30 | 1. Clone repo ```git@github.com:imshubhamsingh/15-puzzle.git``` 31 | 2. ```cd 15-puzzle``` 32 | 3. ```npm install && npm start ``` 33 | 34 | 35 |

or

36 | 37 | View a live demo of this project on [netlify](https://15puzzle.netlify.com) 38 | 39 | 40 | 41 | ## License [![License](https://img.shields.io/github/license/hyperium/hyper.svg)](https://github.com/imshubhamsingh/15-puzzle/blob/master/LICENSE) 42 | 43 | Copyright (c) 2018 Shubham Singh, This software is licensed under the [MIT License](https://github.com/imshubhamsingh/15-puzzle/blob/master/LICENSE). 44 | -------------------------------------------------------------------------------- /src/components/Instructions/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { 3 | GameInstruction, 4 | Icon, 5 | Keys, 6 | GameInstructionContainer 7 | } from '@Elements'; 8 | import { color } from '@Utils'; 9 | 10 | const KeyButton = ({ currentKey, index }) => { 11 | return ( 12 | 28 | ); 29 | }; 30 | const Instructions = () => { 31 | const [key, moveKey] = useState(null); 32 | 33 | useEffect(() => { 34 | let timer1 = index => setTimeout(() => moveKey(index), index * 350); 35 | for (let i = 1; i <= 5; i++) { 36 | timer1(i); 37 | } 38 | return () => { 39 | clearTimeout(timer1); 40 | }; 41 | }, []); 42 | 43 | return ( 44 | 45 | 46 | 47 |
48 | 49 | 50 | 51 | 52 | 53 |
54 | 55 | Move tiles in grid
to order them from 1 to 15. 56 |
57 |
58 | ); 59 | }; 60 | 61 | export default Instructions; 62 | -------------------------------------------------------------------------------- /src/assets/styles/fonts/font.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Clear Sans Webfont 3 | // 4 | // Webfont conversion of the Clear Sans typeface, designed by the 5 | // Intel Open Source Technology Center 6 | // 7 | // Original font file released under the Apache 2.0 License 8 | // 9 | // 10 | // Webfont version by Resi Respati 11 | // Released under the MIT License. 12 | // 13 | 14 | @font-face { 15 | font-family: 'Clear Sans'; 16 | src: url('ClearSans-Light-webfont.eot'); 17 | src: url('ClearSans-Light-webfont.eot?#iefix') format('embedded-opentype'), 18 | url('ClearSans-Light-webfont.svg#clear_sans_lightregular') format('svg'), 19 | url('ClearSans-Light-webfont.woff') format('woff'); 20 | font-weight: 200; 21 | font-style: normal; 22 | } 23 | 24 | @font-face { 25 | font-family: 'Clear Sans'; 26 | src: url('ClearSans-Regular-webfont.eot'); 27 | src: url('ClearSans-Regular-webfont.eot?#iefix') format('embedded-opentype'), 28 | url('ClearSans-Regular-webfont.svg#clear_sansregular') format('svg'), 29 | url('ClearSans-Regular-webfont.woff') format('woff'); 30 | font-weight: normal; 31 | font-style: normal; 32 | } 33 | 34 | @font-face { 35 | font-family: 'Clear Sans'; 36 | src: url('ClearSans-Bold-webfont.eot'); 37 | src: url('ClearSans-Bold-webfont.eot?#iefix') format('embedded-opentype'), 38 | url('ClearSans-Bold-webfont.svg#clear_sansbold') format('svg'), 39 | url('ClearSans-Bold-webfont.woff') format('woff'); 40 | font-weight: 700; 41 | font-style: normal; 42 | } 43 | 44 | @font-face { 45 | font-family: 'Pacifico'; 46 | font-style: normal; 47 | font-weight: 400; 48 | src: url('Pacifico-Regular.woff') format('woff2'); 49 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, 50 | U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, 51 | U+FEFF, U+FFFD; 52 | } 53 | -------------------------------------------------------------------------------- /src/components/Game/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { 3 | GameScore, 4 | Button, 5 | PlayPauseContainer, 6 | Modal, 7 | ModalContainer 8 | } from '@Elements'; 9 | import Score from '../Score'; 10 | import Grid from '../Grid'; 11 | 12 | import { gameState } from '@Utils'; 13 | export default class Game extends Component { 14 | componentDidUpdate(prevProps, prevState) { 15 | if (prevProps.eventType !== this.props.eventType) { 16 | const [_, move] = this.props.eventType || [null, null]; 17 | const [row, col, location] = this.props.gettingEmptyBoxLocation(); 18 | if ( 19 | this.props.gameState === gameState.GAME_IDLE || 20 | this.props.gameState === gameState.GAME_STARTED 21 | ) { 22 | this.props.moveCell(location, row, col, move); 23 | } 24 | } 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 45 | 46 | 47 | 48 |
Excellent!
49 |
50 | It took you {this.props.moves} moves 51 |
52 |
53 | 60 |
61 |
62 |
63 |
64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/utils/game.js: -------------------------------------------------------------------------------- 1 | export const gameState = { 2 | GAME_IDLE: '__game_idle__', 3 | GAME_STARTED: '__game_started__', 4 | GAME_OVER: '__game_over__', 5 | GAME_PAUSED: '__game_paused__' 6 | }; 7 | 8 | export const swap = (arr, from, to) => { 9 | arr.splice(from, 1, arr.splice(to, 1, arr[from])[0]); 10 | return arr; 11 | }; 12 | 13 | export const isNeighbour = (to, from) => { 14 | let emptyColumn = Math.floor(to % 4); 15 | let emptyRow = Math.floor(to / 4); 16 | let clickedColumn = Math.floor(from % 4); 17 | let clickedRow = Math.floor(from / 4); 18 | 19 | const sameRow = emptyRow === clickedRow; 20 | const sameColumn = emptyColumn === clickedColumn; 21 | const columnDiff = emptyColumn - clickedColumn; 22 | const rowDiff = emptyRow - clickedRow; 23 | const diffColumn = Math.abs(columnDiff) === 1; 24 | const diffRow = Math.abs(rowDiff) === 1; 25 | const sameRowDiffColumn = sameRow && diffColumn; 26 | const sameColumnDiffRow = sameColumn && diffRow; 27 | if (sameRowDiffColumn || sameColumnDiffRow) { 28 | return true; 29 | } else { 30 | return false; 31 | } 32 | }; 33 | 34 | export const swapSpace = (arr, from, row, col, move) => { 35 | let yMove = move === 0 ? 1 : move === 2 ? -1 : 0; 36 | let xMove = move === 3 ? 1 : move === 1 ? -1 : 0; 37 | let newRow = row + yMove; 38 | let newCol = col + xMove; 39 | if (newRow <= -1 || newCol <= -1 || newRow >= 4 || newCol >= 4) { 40 | return [false, arr]; 41 | } 42 | let to = newRow * 4 + newCol; 43 | return [true, swap(arr, from, to)]; 44 | }; 45 | 46 | export const shuffle = array_elements => { 47 | let i = array_elements.length, 48 | randomNumIndex, 49 | randomNum; 50 | while (--i > 0) { 51 | randomNumIndex = Math.floor(Math.random() * (i + 1)); 52 | randomNum = array_elements[randomNumIndex]; 53 | array_elements[randomNumIndex] = array_elements[i]; 54 | array_elements[i] = randomNum; 55 | } 56 | return array_elements; 57 | }; 58 | 59 | export const checkArray = arr => { 60 | let decision = true; 61 | arr.forEach((i, index) => { 62 | if (i !== index + 1 && i != 0) { 63 | decision = false; 64 | } 65 | }); 66 | return decision; 67 | }; 68 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 15 Puzzle
-------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 15 Puzzle 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 47 |
48 |
49 | 50 | 51 | -------------------------------------------------------------------------------- /src/elements/Icon.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | export const Icon = props => { 4 | const { size, color } = props; 5 | switch (props.name) { 6 | case 'key': { 7 | const { rotate, fillColor, move } = props; 8 | return ( 9 | 15 | 24 | 29 | 37 | 38 | ); 39 | } 40 | case 'play': { 41 | return ( 42 | 48 | 52 | 53 | ); 54 | } 55 | case 'github': { 56 | return ( 57 | 58 | 59 | 60 | ); 61 | } 62 | case 'twitter': { 63 | return ( 64 | 65 | 66 | 67 | ); 68 | } 69 | case 'linkedin': { 70 | return ( 71 | 72 | 73 | 74 | ); 75 | } 76 | } 77 | }; 78 | -------------------------------------------------------------------------------- /src/hoc/keyboardManager/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | const useMovement = () => { 4 | const [event, handleEvent] = useState(null); 5 | 6 | const emit = (event, data) => { 7 | handleEvent([event, data]); 8 | }; 9 | 10 | const map = { 11 | 38: 0, // Up 12 | 39: 1, // Right 13 | 40: 2, // Down 14 | 37: 3, // Left 15 | 75: 0, // Vim up 16 | 76: 1, // Vim right 17 | 74: 2, // Vim down 18 | 72: 3, // Vim left 19 | 87: 0, // W 20 | 68: 1, // D 21 | 83: 2, // S 22 | 65: 3 // A 23 | }; 24 | 25 | // Respond to direction keys 26 | const handleKey = event => { 27 | const modifiers = 28 | event.altKey || event.ctrlKey || event.metaKey || event.shiftKey; 29 | const mapped = map[event.which]; 30 | if (!modifiers) { 31 | if (mapped !== undefined) { 32 | event.preventDefault(); 33 | emit('move', mapped); 34 | } 35 | } 36 | 37 | // R key restarts the game 38 | if (!modifiers && event.which === 82) { 39 | restart.call(self, event); 40 | } 41 | }; 42 | 43 | const listenKey = () => { 44 | window.document.addEventListener('keydown', handleKey); 45 | }; 46 | 47 | const restart = event => { 48 | event.preventDefault(); 49 | emit('restart'); 50 | }; 51 | 52 | const keepPlaying = event => { 53 | event.preventDefault(); 54 | emit('keepPlaying'); 55 | }; 56 | 57 | let eventTouchstart = null, 58 | eventTouchmove = null, 59 | eventTouchend = null, 60 | touchStartClientX = null, 61 | touchStartClientY = null; 62 | 63 | if (window.navigator.msPointerEnabled) { 64 | //Internet Explorer 10 style 65 | eventTouchstart = 'MSPointerDown'; 66 | eventTouchmove = 'MSPointerMove'; 67 | eventTouchend = 'MSPointerUp'; 68 | } else { 69 | eventTouchstart = 'touchstart'; 70 | eventTouchmove = 'touchmove'; 71 | eventTouchend = 'touchend'; 72 | } 73 | 74 | const eventTouchStartListner = event => { 75 | if ( 76 | (!window.navigator.msPointerEnabled && event.touches.length > 1) || 77 | event.targetTouches > 1 78 | ) { 79 | return; // Ignore if touching with more than 1 finger 80 | } 81 | 82 | if (window.navigator.msPointerEnabled) { 83 | touchStartClientX = event.pageX; 84 | touchStartClientY = event.pageY; 85 | } else { 86 | touchStartClientX = event.touches[0].clientX; 87 | touchStartClientY = event.touches[0].clientY; 88 | } 89 | 90 | //event.preventDefault(); 91 | }; 92 | 93 | const eventTouchmoveListner = event => { 94 | // event.preventDefault(); 95 | }; 96 | 97 | const eventTouchendListner = event => { 98 | if ( 99 | (!window.navigator.msPointerEnabled && event.touches.length > 0) || 100 | event.targetTouches > 0 101 | ) { 102 | return; // Ignore if still touching with one or more fingers 103 | } 104 | 105 | let touchEndClientX; 106 | let touchEndClientY; 107 | 108 | if (window.navigator.msPointerEnabled) { 109 | touchEndClientX = event.pageX; 110 | touchEndClientY = event.pageY; 111 | } else { 112 | touchEndClientX = event.changedTouches[0].clientX; 113 | touchEndClientY = event.changedTouches[0].clientY; 114 | } 115 | 116 | const dx = touchEndClientX - touchStartClientX; 117 | const absDx = Math.abs(dx); 118 | 119 | const dy = touchEndClientY - touchStartClientY; 120 | const absDy = Math.abs(dy); 121 | 122 | if (Math.max(absDx, absDy) > 10) { 123 | // (right : left) : (down : up) 124 | emit('move', absDx > absDy ? (dx > 0 ? 1 : 3) : dy > 0 ? 2 : 0); 125 | } 126 | }; 127 | 128 | const listenSwipe = () => { 129 | window.document.addEventListener(eventTouchstart, eventTouchStartListner); 130 | window.document.addEventListener(eventTouchmove, eventTouchmoveListner); 131 | window.document.addEventListener(eventTouchend, eventTouchendListner); 132 | }; 133 | 134 | const removeEventListeners = () => { 135 | window.document.removeEventListener('keydown', handleKey); 136 | window.document.removeEventListener( 137 | eventTouchstart, 138 | eventTouchStartListner 139 | ); 140 | window.document.removeEventListener(eventTouchmove, eventTouchmoveListner); 141 | window.document.removeEventListener(eventTouchend, eventTouchendListner); 142 | }; 143 | 144 | useEffect(() => { 145 | listenKey(); 146 | listenSwipe(); 147 | return () => removeEventListeners(); 148 | }); 149 | 150 | return event; 151 | }; 152 | 153 | const KeyBoardManager = PassedComponent => props => { 154 | const eventType = useMovement(); 155 | return ; 156 | }; 157 | 158 | export default KeyBoardManager; 159 | -------------------------------------------------------------------------------- /src/elements/GameFactory.js: -------------------------------------------------------------------------------- 1 | import React, { Component, createContext } from 'react'; 2 | 3 | import { 4 | swap, 5 | isNeighbour, 6 | swapSpace, 7 | shuffle, 8 | checkArray, 9 | gameState 10 | } from '@Utils'; 11 | 12 | const NEW_GAME = '__new_game__'; 13 | const RESET_GAME = '__reset_game__'; 14 | 15 | // [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16] 16 | const genrateArray = (num, add) => { 17 | let puzzle = [...Array(num)].map((_, i) => i + add); 18 | puzzle.push(0); 19 | return puzzle; 20 | }; 21 | 22 | const ValuesContext = createContext({}); 23 | const SetValueContext = createContext(() => {}); 24 | 25 | const isSolvable = puzzle => { 26 | let parity = 0; 27 | let gridWidth = 4; 28 | let row = 0; 29 | let blankRow = 0; 30 | for (let i = 0; i < puzzle.length; i++) { 31 | if (i % gridWidth == 0) { 32 | // advance to next row 33 | row++; 34 | } 35 | if (puzzle[i] == 0) { 36 | blankRow = row; 37 | continue; 38 | } 39 | for (var j = i + 1; j < puzzle.length; j++) { 40 | if (puzzle[i] > puzzle[j] && puzzle[j] != 0) { 41 | parity++; 42 | } 43 | } 44 | } 45 | 46 | if (gridWidth % 2 == 0) { 47 | if (blankRow % 2 == 0) { 48 | return parity % 2 == 0; 49 | } else { 50 | return parity % 2 != 0; 51 | } 52 | } else { 53 | return parity % 2 == 0; 54 | } 55 | }; 56 | 57 | const genratePuzzle = (arr, event) => { 58 | if (event === NEW_GAME) { 59 | if (isSolvable(arr)) { 60 | return arr; 61 | } else { 62 | return genratePuzzle(shuffle(genrateArray(15, 1)), NEW_GAME); 63 | } 64 | } else { 65 | return arr; 66 | } 67 | }; 68 | 69 | class GameFactory extends Component { 70 | defaultState = (_event, num) => ({ 71 | numbers: 72 | _event === NEW_GAME 73 | ? genratePuzzle(shuffle(genrateArray(15, num)), _event) 74 | : shuffle(genrateArray(15, num)), 75 | moves: 0, 76 | seconds: 0, 77 | gameState: gameState.GAME_IDLE 78 | }); 79 | 80 | state = this.defaultState(NEW_GAME, 1); 81 | 82 | timerId = null; 83 | 84 | reset = () => { 85 | this.setState(this.defaultState(RESET_GAME)); 86 | setTimeout(() => { 87 | this.setState(this.defaultState(NEW_GAME, 1)); 88 | if (this.timerId) { 89 | clearInterval(this.timerId); 90 | } 91 | }, 100); 92 | }; 93 | 94 | gettingEmptyBoxLocation = () => { 95 | let location = this.state.numbers.indexOf(0); 96 | let column = Math.floor(location % 4); 97 | let row = Math.floor(location / 4); 98 | return [row, column, location]; 99 | }; 100 | 101 | move = (from, row, col, moveType) => { 102 | this.setState(prevState => { 103 | let newState = null; 104 | const [updated, newNumList] = swapSpace( 105 | prevState.numbers, 106 | from, 107 | row, 108 | col, 109 | moveType 110 | ); 111 | if (updated) { 112 | newState = { 113 | number: newNumList, 114 | moves: prevState.moves + 1 115 | }; 116 | if (prevState.moves === 0) { 117 | this.setTimer(); 118 | newState = { 119 | ...newState, 120 | gameState: gameState.GAME_STARTED 121 | }; 122 | } 123 | if (checkArray(this.state.numbers)) { 124 | clearInterval(this.timerId); 125 | newState = { 126 | ...newState, 127 | gameState: gameState.GAME_OVER 128 | }; 129 | } 130 | } 131 | return newState; 132 | }); 133 | }; 134 | 135 | addTimer = () => { 136 | this.setState(prevState => { 137 | return { seconds: prevState.seconds + 1 }; 138 | }); 139 | }; 140 | 141 | setTimer = () => { 142 | this.timerId = setInterval(() => { 143 | this.addTimer(); 144 | }, 1000); 145 | }; 146 | 147 | clickMove = from => { 148 | this.setState(prevState => { 149 | let newState = null; 150 | let to = prevState.numbers.indexOf(0); 151 | if (isNeighbour(to, from)) { 152 | const newNumList = swap(prevState.numbers, to, from); 153 | newState = { 154 | number: newNumList, 155 | moves: prevState.moves + 1 156 | }; 157 | if (prevState.moves === 0) { 158 | this.setTimer(); 159 | newState = { 160 | ...newState, 161 | gameState: gameState.GAME_STARTED 162 | }; 163 | } 164 | if (checkArray(this.state.numbers)) { 165 | clearInterval(this.timerId); 166 | newState = { 167 | ...newState, 168 | gameState: gameState.GAME_OVER 169 | }; 170 | } 171 | } 172 | return newState; 173 | }); 174 | }; 175 | 176 | onPauseClick = () => { 177 | this.setState(prevState => { 178 | let newGameState = null; 179 | 180 | if (prevState.gameState === gameState.GAME_STARTED) { 181 | clearInterval(this.timerId); 182 | newGameState = gameState.GAME_PAUSED; 183 | } else { 184 | this.setTimer(); 185 | newGameState = gameState.GAME_STARTED; 186 | } 187 | 188 | return { 189 | gameState: newGameState 190 | }; 191 | }); 192 | }; 193 | 194 | render() { 195 | return ( 196 | 197 | 207 | {this.props.children} 208 | 209 | 210 | ); 211 | } 212 | } 213 | 214 | export const GameFactoryConsumer = ({ children }) => { 215 | return ( 216 | 217 | {values => ( 218 | 219 | {methods => children({ values, methods })} 220 | 221 | )} 222 | 223 | ); 224 | }; 225 | 226 | export default GameFactory; 227 | -------------------------------------------------------------------------------- /src/elements/Container.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { color, transition, bounceIn, fadeIn } from '@Utils'; 3 | 4 | export const Container = styled.div` 5 | width: 500px; 6 | margin: 0 auto; 7 | @media screen and (max-width: 520px) { 8 | width: 291px; 9 | margin: 0 auto; 10 | } 11 | `; 12 | 13 | export const GridContainer = styled.div` 14 | margin: 5px 0; 15 | grid-template-columns: auto auto auto auto; 16 | display: grid; 17 | position: relative; 18 | padding: 15px; 19 | cursor: default; 20 | touch-action: none; 21 | background: ${color.backgroundColor}; 22 | border-radius: 12px; 23 | width: 500px; 24 | height: 500px; 25 | .grid-row { 26 | margin-bottom: 15px; 27 | display: flex; 28 | } 29 | @media screen and (max-width: 520px) { 30 | width: 292px; 31 | height: 292px; 32 | .grid-row { 33 | margin-bottom: 10px; 34 | } 35 | } 36 | `; 37 | 38 | export const GridOverlay = styled.div` 39 | display: flex; 40 | justify-content: center; 41 | align-items: center; 42 | position: absolute; 43 | padding: 15px; 44 | z-index: 100; 45 | background-color: ${color.gridOverlayBackground}; 46 | float: left; 47 | margin-top: 3px; 48 | margin-left: 2px; 49 | border-radius: 10px; 50 | width: 499px; 51 | height: 487px; 52 | animation: ${fadeIn} 250ms; 53 | @media screen and (max-width: 520px) { 54 | width: 280px; 55 | height: 275px; 56 | margin-left: 6px; 57 | margin-top: 5px; 58 | } 59 | `; 60 | 61 | export const PlayPauseContainer = styled.div` 62 | padding: 0 15px; 63 | `; 64 | 65 | export const CellContainer = styled.div` 66 | width: 106.25px; 67 | height: 106.25px; 68 | margin-right: 15px; 69 | border-radius: 10px; 70 | background-color: ${color.gridTileColor}; 71 | @media screen and (max-width: 520px) { 72 | width: 57.5px; 73 | height: 57.5px; 74 | margin-right: 10px; 75 | } 76 | &:last-child { 77 | margin-right: 0; 78 | } 79 | `; 80 | 81 | // 121px; 82 | // 67px; 83 | export const NumberCellContainer = styled.div` 84 | display: ${props => 85 | props.number > 0 && props.number < 16 ? 'flex' : 'none'}; 86 | border-radius: 10px; 87 | background: ${props => 88 | props.index === props.number ? '#E88A45' : '#6ac6b8'}; 89 | cursor: pointer; 90 | position: relative; 91 | user-select: none; 92 | justify-content: center; 93 | align-items: center; 94 | font-family: 'Pacifico', cursive; 95 | text-align: center; 96 | font-weight: bold; 97 | z-index: 10; 98 | font-size: 75px; 99 | width: 107px; 100 | ${transition({ property: 'transform' })}; 101 | 102 | animation-duration: 0.75s; 103 | animation-name: ${bounceIn}; 104 | 105 | height: 107px; 106 | transform: ${({ x = 0, y = 0 }) => `translate3d(${x}px, ${y}px, 0)`}; 107 | .ball-1, 108 | .ball-2 { 109 | position: absolute; 110 | background-color: ${props => 111 | props.index === props.number ? ' #CD583A' : '#499591'}; 112 | opacity: 0.2; 113 | border-radius: 50%; 114 | } 115 | .ball-1 { 116 | height: 30px; 117 | width: 30px; 118 | top: 21px; 119 | left: 19px; 120 | } 121 | 122 | .ball-2 { 123 | height: 60px; 124 | width: 60px; 125 | bottom: 9px; 126 | right: 10px; 127 | } 128 | .shadow { 129 | color: ${props => (props.index === props.number ? ' #CD583A' : '#499591')}; 130 | font-size: 90px; 131 | margin-left: ${props => 132 | props.number.toString().length == 2 133 | ? props.number === 11 134 | ? -16 135 | : -6 136 | : props.number === 1 137 | ? -10 138 | : 0}px; 139 | margin-top: ${props => (props.number.toString().length == 2 ? -21 : -19)}px; 140 | } 141 | .number { 142 | color: white; 143 | z-index: 99; 144 | position: absolute; 145 | top: -22px; 146 | left: ${props => (props.number.toString().length == 2 ? 15 : 32)}px; 147 | } 148 | 149 | @media screen and (max-width: 520px) { 150 | width: 58px; 151 | height: 58px; 152 | line-height: 67.5px; 153 | font-size: 45px; 154 | padding-top: 9px; 155 | padding-left: ${props => (props.number === 1 ? 3 : 0)}px; 156 | border-radius: 5px; 157 | .ball-1 { 158 | height: 15px; 159 | width: 15px; 160 | top: 12px; 161 | left: 9px; 162 | } 163 | 164 | .ball-2 { 165 | height: 30px; 166 | width: 30px; 167 | bottom: 4px; 168 | right: 7px; 169 | } 170 | .shadow { 171 | font-size: 53px; 172 | margin-left: ${props => 173 | props.number.toString().length == 2 174 | ? props.number === 11 175 | ? -6 176 | : -1 177 | : props.number === 1 178 | ? -7 179 | : 0}px; 180 | margin-top: ${props => 181 | props.number.toString().length == 2 ? -20 : -19}px; 182 | } 183 | .number { 184 | top: -14px; 185 | left: ${props => (props.number.toString().length == 2 ? 7 : 16)}px; 186 | } 187 | } 188 | `; 189 | 190 | export const Keys = styled.span` 191 | width: 150px; 192 | float: left; 193 | height: 105px; 194 | display: flex; 195 | flex-direction: column; 196 | align-items: center; 197 | & span.bottom-keys { 198 | margin-top: -16px; 199 | } 200 | & rect { 201 | ${transition({ property: 'fill' })}; 202 | } 203 | & path { 204 | ${transition({ property: 'transform' })}; 205 | } 206 | @media screen and (max-width: 520px) { 207 | display: none; 208 | } 209 | `; 210 | 211 | export const GameScore = styled.div` 212 | padding: 0 15px; 213 | display: flex; 214 | justify-content: space-between; 215 | @media screen and (max-width: 520px) { 216 | padding: 20px 15px 0 15px; 217 | } 218 | `; 219 | 220 | export const ScoreContainer = styled.div` 221 | background: ${color.gridTileColor}; 222 | display: flex; 223 | margin-left: 10px; 224 | padding-left: 15px; 225 | border-radius: 8px; 226 | 227 | .move, 228 | .best, 229 | .time { 230 | position: relative; 231 | display: inline-block; 232 | text-align: right; 233 | width: 97px; 234 | padding: 13px 20px 0 10px; 235 | float: right; 236 | } 237 | .score-title { 238 | text-transform: uppercase; 239 | font-size: 12px; 240 | line-height: 12px; 241 | color: ${color.primaryFontColor()}; 242 | } 243 | 244 | .move-container, 245 | .best-container, 246 | .time-container { 247 | font-size: 21px; 248 | line-height: 25px; 249 | font-weight: bold; 250 | color: ${color.primaryFontColor()}; 251 | } 252 | 253 | .best { 254 | opacity: 0.5; 255 | } 256 | @media screen and (max-width: 520px) { 257 | padding-left: 10px; 258 | border-radius: 5px; 259 | 260 | .move-container, 261 | .best-container, 262 | .time-container { 263 | font-size: 16px; 264 | line-height: 14px; 265 | font-weight: bold; 266 | } 267 | 268 | .move, 269 | .best, 270 | .time { 271 | position: relative; 272 | display: inline-block; 273 | text-align: right; 274 | padding: 8px 15px 0 10px; 275 | float: right; 276 | width: 65px; 277 | } 278 | .time { 279 | width: 80px; 280 | padding-right: 6px; 281 | } 282 | .best { 283 | display: none; 284 | } 285 | } 286 | `; 287 | 288 | export const GameInstructionContainer = styled.div` 289 | display: flex; 290 | padding: 0 17px 0 6px; 291 | justify-content: space-between; 292 | @media screen and (max-width: 520px) { 293 | display: block; 294 | } 295 | `; 296 | 297 | export const Wave = styled.img` 298 | margin-top: -30px; 299 | @media screen and (max-width: 520px) { 300 | max-width: 270px; 301 | height: auto; 302 | margin-left: 9px; 303 | } 304 | `; 305 | 306 | export const ModalContainer = styled.div` 307 | display: flex; 308 | flex-direction: column; 309 | align-content: space-around; 310 | width: 100%; 311 | height: 100%; 312 | text-align: center; 313 | justify-content: space-between; 314 | .text-1 { 315 | font-family: 'Pacifico', cursive; 316 | text-align: center; 317 | font-weight: bold; 318 | font-size: 37px; 319 | color: ${color.backgroundColor}; 320 | } 321 | `; 322 | 323 | export const Footer = styled.div` 324 | width: 100%; 325 | height: 22px; 326 | background: ${color.backgroundColor}; 327 | z-index: 10; 328 | display: flex; 329 | justify-content: center; 330 | flex-direction: column; 331 | margin-bottom: 9px; 332 | & .text { 333 | color: ${color.primaryFontColor()}; 334 | font-family: 'Clear Sans', Arial, sans-serif; 335 | text-align: center; 336 | bottom: 0px; 337 | margin: 11px auto 0px; 338 | a { 339 | text-decoration: none; 340 | color: white; 341 | font-weight: bold; 342 | } 343 | } 344 | 345 | & .logos { 346 | display: flex; 347 | justify-content: center; 348 | align-items: center; 349 | width: 100%; 350 | margin-top: 29px; 351 | } 352 | 353 | @media (max-width: 520px) { 354 | height: 22px; 355 | margin-top: -10px; 356 | padding: 10px 4px; 357 | & .text { 358 | margin: 26px auto 0px; 359 | } 360 | & .logos { 361 | margin-top: 20px; 362 | } 363 | } 364 | `; 365 | 366 | export const Profile = styled.a` 367 | fill: ${color.gridTileColor}; 368 | svg, 369 | path { 370 | ${transition({})}; 371 | margin-right: 17px; 372 | height: 32px; 373 | width: 32px; 374 | } 375 | &:hover svg path, 376 | &:hover { 377 | fill: ${color.buttonHoverColor}; 378 | color: ${color.buttonHoverColor}; 379 | } 380 | 381 | @media (max-width: 520px) { 382 | svg, 383 | path { 384 | ${transition({})}; 385 | margin-right: 7px; 386 | height: 22px; 387 | width: 22px; 388 | } 389 | } 390 | `; 391 | --------------------------------------------------------------------------------