Adding support for flag and reveal
This commit is contained in:
108
src/App.js
108
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 (
|
||||
<div className="app">
|
||||
<div className="canvas">
|
||||
<Header />
|
||||
<Header
|
||||
cols={cols}
|
||||
flags={flags}
|
||||
handleSetMines={handleSetMines}
|
||||
mines={mines}
|
||||
rows={rows}
|
||||
setCols={handleSetCols}
|
||||
setMines={setMines}
|
||||
setRows={handleSetRows}
|
||||
/>
|
||||
<Board
|
||||
cols={cols}
|
||||
dummyBoard={dummyBoard}
|
||||
handleCellClick={handleCellClick}
|
||||
handleCellContextMenu={handleCellContextMenu}
|
||||
rows={rows}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
7
src/assets/images/flag.svg
Normal file
7
src/assets/images/flag.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16.439" height="15.031" viewBox="0 0 16.439 15.031">
|
||||
<line id="Line_6" data-name="Line 6" y2="13" transform="translate(5.259 1.531)" fill="none" stroke="#000" stroke-linecap="round" stroke-width="1"/>
|
||||
<g id="Polygon_2" data-name="Polygon 2" transform="matrix(-0.657, 0.293, 0.752, 0.308, 11.211, -0.035)" fill="red">
|
||||
<path d="M 15.6063289642334 6.5 L 1.393670678138733 6.5 L 8.5 0.6477288007736206 L 15.6063289642334 6.5 Z" stroke="none"/>
|
||||
<path d="M 8.5 1.295455932617188 L 2.787339210510254 6 L 14.21266078948975 6 L 8.5 1.295455932617188 M 8.5 0 L 17 7 L 0 7 L 8.5 0 Z" stroke="none"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 662 B |
@@ -3,3 +3,5 @@ $gray-mid: #e3e3e3;
|
||||
$gray-dark: #333;
|
||||
$gray: #d1d1d1;
|
||||
$red: #ec433c;
|
||||
|
||||
$cell-size: 20px;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,8 +1,37 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import './styles.scss';
|
||||
|
||||
const Board = () => {
|
||||
return <div className="board">Board here</div>;
|
||||
const Board = ({ dummyBoard, handleCellClick, handleCellContextMenu }) => {
|
||||
return (
|
||||
<div className="board">
|
||||
{dummyBoard.map((cols, rowId) => (
|
||||
<div className="board-row" key={`row_${rowId}`}>
|
||||
{cols.map((col, colId) => (
|
||||
<div
|
||||
className="board-row-cell box"
|
||||
id={`cell_${rowId}_${colId}`}
|
||||
onClick={() => handleCellClick(rowId, colId)}
|
||||
onContextMenu={event =>
|
||||
handleCellContextMenu(event, rowId, colId)
|
||||
}
|
||||
key={`cell_${rowId}_${colId}`}>
|
||||
{' '}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +1,21 @@
|
||||
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 (
|
||||
<div className="header">
|
||||
<div className="flags led-panel box">{flags}</div>
|
||||
<div className="settings">
|
||||
{selectedDifficulty === 'custom' && (
|
||||
<div className="custom">
|
||||
<input
|
||||
className="box"
|
||||
@@ -74,22 +36,34 @@ const Header = () => {
|
||||
value={mines}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<select
|
||||
className="box"
|
||||
onChange={event => handleSetDifficulty(event)}
|
||||
value={selectedDifficulty}>
|
||||
<option value="custom">Custom</option>
|
||||
<option value="beginner">Beginner</option>
|
||||
<option value="intermediate">Intermediate</option>
|
||||
<option value="expert">Expert</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="reset box">{resetContent}</div>
|
||||
<div
|
||||
className="reset box"
|
||||
onMouseDown={event => {
|
||||
event.target.innerHTML = '😮';
|
||||
console.log('onmousedown');
|
||||
}}
|
||||
onMouseUp={event => {
|
||||
event.target.innerHTML = '🙂';
|
||||
console.log('onMouseUp');
|
||||
}}>
|
||||
🙂
|
||||
</div>
|
||||
<div className="timer led-panel box">000</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 5px;
|
||||
margin: 0 auto;
|
||||
|
||||
.led-panel {
|
||||
font-family: 'Space Mono', monospace;
|
||||
|
||||
Reference in New Issue
Block a user