From c724c2c0cf77b6f59b16ddf6a6f48f0eed8b4355 Mon Sep 17 00:00:00 2001 From: Michel Wilhelm Date: Sat, 7 Nov 2020 15:17:24 -0300 Subject: [PATCH] Adding support for flag and reveal --- src/App.js | 108 +++++++++++++++++++++++- src/assets/images/flag.svg | 7 ++ src/assets/scss/_variables.scss | 2 + src/assets/scss/styles.scss | 5 +- src/components/Board/Board.js | 33 +++++++- src/components/Board/styles.scss | 26 ++++++ src/components/Header/Header.js | 136 ++++++++++++------------------ src/components/Header/styles.scss | 1 + 8 files changed, 230 insertions(+), 88 deletions(-) create mode 100644 src/assets/images/flag.svg diff --git a/src/App.js b/src/App.js index 6ca4350..8677d85 100644 --- a/src/App.js +++ b/src/App.js @@ -1,12 +1,114 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; -import { Header } from 'components'; +import { Board, Header } from 'components'; function App() { + const [rows, setRows] = useState(10); + const [cols, setCols] = useState(10); + const [mines, setMines] = useState(5); + const [flags, setFlags] = useState(5); + const [mounted, setMounted] = useState(false); + const [dummyBoard, setDummyBoard] = useState([]); + const [updateDummyBoard, setUpdateDummyBoard] = useState(true); + + const handleSetRows = value => { + // TODO can't be less than 3 + setRows(value); + setUpdateDummyBoard(true); + }; + + const handleSetCols = value => { + // TODO can't be less than 3 + setCols(value); + setUpdateDummyBoard(true); + }; + + const generateDummyBoard = () => { + const board = []; + + for (let row = 0; row < rows; row++) { + board.push( + Array(cols) + .join(0) + .split(0) + ); + } + + setDummyBoard(board); + }; + + const handleSetMines = event => { + // TODO can't be less than 1 + setMines(parseInt(event.target.value)); + setFlags(parseInt(event.target.value)); + generateDummyBoard(); + }; + + useEffect(() => { + function bootstrap() { + setMounted(true); + generateDummyBoard(); + } + + if (!mounted) { + bootstrap(); + } + + if (updateDummyBoard) { + setUpdateDummyBoard(false); + generateDummyBoard(); + } + }); + + const handleCellClick = (row, col) => { + const cell = document.getElementById(`cell_${row}_${col}`); + if (cell.classList.contains('flagged')) { + return; + } + + if (!cell.classList.contains('revealed')) { + cell.classList.add('revealed'); + } + }; + + const handleCellContextMenu = (event, row, col) => { + event.preventDefault(); + const cell = document.getElementById(`cell_${row}_${col}`); + if (cell.classList.contains('revealed')) { + return; + } + + if (cell.classList.contains('flagged')) { + cell.classList.remove('flagged'); + } else { + cell.classList.add('flagged'); + } + }; + + if (!mounted) { + return null; + } + return (
-
+
+
); diff --git a/src/assets/images/flag.svg b/src/assets/images/flag.svg new file mode 100644 index 0000000..955a5a0 --- /dev/null +++ b/src/assets/images/flag.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/scss/_variables.scss b/src/assets/scss/_variables.scss index 2aa1e95..7148243 100644 --- a/src/assets/scss/_variables.scss +++ b/src/assets/scss/_variables.scss @@ -3,3 +3,5 @@ $gray-mid: #e3e3e3; $gray-dark: #333; $gray: #d1d1d1; $red: #ec433c; + +$cell-size: 20px; diff --git a/src/assets/scss/styles.scss b/src/assets/scss/styles.scss index a9d5e5e..1a0fe73 100644 --- a/src/assets/scss/styles.scss +++ b/src/assets/scss/styles.scss @@ -4,18 +4,19 @@ body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-size: 14px; - background-color: $gray-light; + + background-color: $gray-mid; } .canvas { max-width: 1000px; width: 100%; min-width: 500px; - background-color: $gray-mid; margin: 0 auto; border-radius: 3px; display: flex; justify-content: center; + flex-direction: column; } .box { diff --git a/src/components/Board/Board.js b/src/components/Board/Board.js index 88e9e60..4298ef8 100644 --- a/src/components/Board/Board.js +++ b/src/components/Board/Board.js @@ -1,8 +1,37 @@ import React from 'react'; +import PropTypes from 'prop-types'; + import './styles.scss'; -const Board = () => { - return
Board here
; +const Board = ({ dummyBoard, handleCellClick, handleCellContextMenu }) => { + return ( +
+ {dummyBoard.map((cols, rowId) => ( +
+ {cols.map((col, colId) => ( +
handleCellClick(rowId, colId)} + onContextMenu={event => + handleCellContextMenu(event, rowId, colId) + } + key={`cell_${rowId}_${colId}`}> + {' '} +
+ ))} +
+ ))} +
+ ); +}; + +Board.propTypes = { + cols: PropTypes.number.isRequired, + dummyBoard: PropTypes.array.isRequired, + handleCellClick: PropTypes.func.isRequired, + rows: PropTypes.number.isRequired, + handleCellContextMenu: PropTypes.func.isRequired }; export default Board; diff --git a/src/components/Board/styles.scss b/src/components/Board/styles.scss index 04999c1..bbda6e0 100644 --- a/src/components/Board/styles.scss +++ b/src/components/Board/styles.scss @@ -1 +1,27 @@ @import '../../assets/scss/variables'; + +.board { + display: flex; + flex-direction: column; + &-row { + display: flex; + justify-content: center; + + &-cell.flagged { + background-image: url(../../assets/images/flag.svg); + background-repeat: no-repeat; + background-position: center center; + } + + &-cell.revealed { + margin: 1px; + border: 2px solid $gray-dark; + } + + &-cell { + width: $cell-size; + height: $cell-size; + line-height: $cell-size; + } + } +} diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js index b5f3af7..11d39e9 100644 --- a/src/components/Header/Header.js +++ b/src/components/Header/Header.js @@ -1,95 +1,69 @@ -import React, { useState } from 'react'; +import React from 'react'; +import PropTypes from 'prop-types'; + import './styles.scss'; -const difficulty = { - custom: { - rows: 10, - cols: 10, - mines: 5, - flags: 5 - }, - beginner: { - rows: 20, - cols: 20, - mines: 5, - flags: 5 - }, - intermediate: { - rows: 50, - cols: 50, - mines: 15, - flags: 15 - }, - expert: { - rows: 100, - cols: 100, - mines: 50, - flags: 50 - } -}; - -const Header = () => { - const [selectedDifficulty, setSelectedDifficulty] = useState('beginner'); - const [rows, setRows] = useState(difficulty.beginner.rows); - const [cols, setCols] = useState(difficulty.beginner.cols); - const [mines, setMines] = useState(difficulty.beginner.mines); - const [flags, setFlags] = useState(5); - const [resetContent] = useState('🙂'); - - const handleSetDifficulty = event => { - setSelectedDifficulty(event.target.value); - setRows(difficulty[event.target.value].rows); - setCols(difficulty[event.target.value].cols); - setMines(difficulty[event.target.value].mines); - setFlags(difficulty[event.target.value].flags); - }; - - const handleSetMines = event => { - setMines(parseInt(event.target.value)); - setFlags(parseInt(event.target.value)); - }; - +const Header = ({ + cols, + flags, + handleSetMines, + mines, + rows, + setCols, + setRows +}) => { return (
{flags}
- {selectedDifficulty === 'custom' && ( -
- setRows(parseInt(event.target.value))} - type="number" - value={rows} - /> - setCols(parseInt(event.target.value))} - type="number" - value={cols} - /> - handleSetMines(event)} - type="number" - value={mines} - /> -
- )} - +
+ setRows(parseInt(event.target.value))} + type="number" + value={rows} + /> + setCols(parseInt(event.target.value))} + type="number" + value={cols} + /> + handleSetMines(event)} + type="number" + value={mines} + /> +
-
{resetContent}
+
{ + event.target.innerHTML = '😮'; + console.log('onmousedown'); + }} + onMouseUp={event => { + event.target.innerHTML = '🙂'; + console.log('onMouseUp'); + }}> + 🙂 +
000
); }; +Header.propTypes = { + cols: PropTypes.number, + flags: PropTypes.number, + handleSetMines: PropTypes.func.isRequired, + mines: PropTypes.number, + rows: PropTypes.number, + setCols: PropTypes.func.isRequired, + setMines: PropTypes.func.isRequired, + setRows: PropTypes.func.isRequired +}; + export default Header; diff --git a/src/components/Header/styles.scss b/src/components/Header/styles.scss index 312ec79..9d645d3 100644 --- a/src/components/Header/styles.scss +++ b/src/components/Header/styles.scss @@ -6,6 +6,7 @@ display: flex; justify-content: space-between; padding: 5px; + margin: 0 auto; .led-panel { font-family: 'Space Mono', monospace;