From 6eeea185eab6ca5553aacfe4a58a5302232eb03a Mon Sep 17 00:00:00 2001 From: aronmal Date: Mon, 15 Aug 2022 23:16:31 +0200 Subject: [PATCH] Refactoring --- frontend/src/App.tsx | 154 ++++++------------------ frontend/src/components/BorderTiles.tsx | 45 +++++++ frontend/src/components/FogImages.tsx | 9 ++ frontend/src/components/HitElems.tsx | 17 +++ frontend/src/components/Labeling.tsx | 29 +++++ frontend/src/components/Ships.tsx | 19 +++ frontend/src/helpers.ts | 52 ++++++++ frontend/src/interfaces.ts | 21 +++- frontend/src/styles/App.scss | 3 +- 9 files changed, 231 insertions(+), 118 deletions(-) create mode 100644 frontend/src/components/BorderTiles.tsx create mode 100644 frontend/src/components/FogImages.tsx create mode 100644 frontend/src/components/HitElems.tsx create mode 100644 frontend/src/components/Labeling.tsx create mode 100644 frontend/src/components/Ships.tsx create mode 100644 frontend/src/helpers.ts diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 06dcf31..5533c3b 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,109 +1,41 @@ -import { faBurst, faCrosshairs, faXmark } from '@fortawesome/free-solid-svg-icons'; +import { faCrosshairs } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { CSSProperties, useEffect, useState } from 'react'; -import { TargetPreviewType } from './interfaces'; +import { CSSProperties, useEffect, useReducer, useState } from 'react'; +import BorderTiles from './components/BorderTiles'; +import FogImages from './components/FogImages'; +import HitElems from './components/HitElems'; +import Labeling from './components/Labeling'; +import Ships from './components/Ships'; +import { hitReducer, initlialTarget, initlialTargetPreview } from './helpers'; +import { HitType, TargetPreviewType, TargetType } from './interfaces'; import './styles/App.scss'; function App() { - const [target, setTarget] = useState({ - show: false, - x: 0, - y: 0 - }) - const [targetPreview, setTargetPreview] = useState({ - newX: 0, - newY: 0, - shouldShow: false, - appearOK: false, - eventReady: true, - show: false, - x: 0, - y: 0 - }) + const count = 12; + const [target, setTarget] = useState(initlialTarget); + const [targetPreview, setTargetPreview] = useState(initlialTargetPreview); + const [hits, DispatchHits] = useReducer(hitReducer, [] as HitType[]); + const [x, setX] = useState(0); + const [y, setY] = useState(0); - let borderTiles: JSX.Element[] = []; - let shipElems: JSX.Element[] = []; - let elems2: { - field: string, - x: number, - y: number, - }[] = []; - let hitElems: { - field: string, - x: number, - y: number, - }[] = [], - count = 12; - for (let y = 0; y < count; y++) { - elems2.push(...[ - // Up - { field: String.fromCharCode(65+y), x: y+2, y: 1 }, - // Left - { field: (y+1).toString(), x: 1, y: y+2 }, - // Bottom - { field: String.fromCharCode(65+y), x: y+2, y: count+2 }, - // Right - { field: (y+1).toString(), x: count+2, y: y+2 } - ]); - for (let x = 0; x < count; x++) { - hitElems.push({ field: String.fromCharCode(65+x)+(y), x: x+2, y: y+2 }) + useEffect(() => { + if (hits.length === count*count) + return; + if (x === count) { + setX(0); + setY(y => y+1); + return; } - } - const hitSVGs = hitElems.map((obj, i) => -
- -
); - const corner = (x: number, y: number, count: number) => {switch (true) { - case x === 0 && y === 0: - return 'left-top-corner'; - case x === count+1 && y === 0: - return 'right-top-corner'; - case x === 0 && y === count+1: - return 'left-bottom-corner'; - case x === count+1 && y === count+1: - return 'right-bottom-corner'; - - default: - return ''; - }}; - const border = (x: number, y: number, count: number) => {switch (true) { - case x === 0: - return 'left'; - case y === 0: - return 'top'; - case x === count+1: - return 'right'; - case y === count+1: - return 'bottom'; - - default: - return ''; - }}; - for (let y = 0; y < count+2; y++) { - for (let x = 0; x < count+2; x++) { - const cornerReslt = corner(x, y, count); - const borderType = cornerReslt ? cornerReslt : border(x, y, count); - const isGameTile = x > 0 && x < count+1 && y > 0 && y < count+1; - const classNames = ['border-tile']; - if (borderType) - classNames.push('edge', borderType); - if (isGameTile) - classNames.push('game-tile'); - borderTiles.push( -
{ - if (isGameTile) - setTarget({ show: true, x, y }); - }} - onMouseEnter={() => setTargetPreview(e => ({...e, newX: x, newY: y, shouldShow: isGameTile}))} - >
- ) + const autoTimer = setTimeout(() => { + DispatchHits({type: 'fireMissle', payload: {hit: (x+y)%2 !== 0, x: x+2, y: y+2}}); + setX(x => x+1); + }, 100); + + return () => { + clearTimeout(autoTimer); } - } + }, [x, y, hits.length]); // handle visibility and position change of targetPreview useEffect(() => { @@ -150,33 +82,23 @@ function App() { } }, [targetPreview.shouldShow, targetPreview.newX, targetPreview.newY]); - for (let i = 1; i <= 6; i++) { - shipElems.push( -
- {`${i}.svg`}/ -
); - } - return (
{/* Bordes */} - { borderTiles } + {/* Collumn lettes and row numbers */} - {elems2.map((obj, i) => - {obj.field} - )} - { hitSVGs } + {/* Ships */} - {/* { shipElems } */} + {/* */} + + {/* Fog images */} - {/* {`fog1.png`} - {`fog1.png`} - {`fog4.png`} */} + {/* */}
@@ -184,9 +106,9 @@ function App() {
-

+ {/*

Edit src/App.tsx and save to reload. -

+

*/} >, setTargetPreview: React.Dispatch>}}) { + let tilesProperties: { + key: number, + isGameTile: boolean, + classNameString: string, + x: number, + y: number + }[] = []; + + for (let y = 0; y < count+2; y++) { + for (let x = 0; x < count+2; x++) { + const key = fieldIndex(count, x, y); + const cornerReslt = cornerCN(count, x, y); + const borderType = cornerReslt ? cornerReslt : borderCN(count, x, y); + const isGameTile = x > 0 && x < count+1 && y > 0 && y < count+1; + const classNames = ['border-tile']; + if (borderType) + classNames.push('edge', borderType); + if (isGameTile) + classNames.push('game-tile'); + const classNameString = classNames.join(' ') + tilesProperties.push({key, classNameString, isGameTile, x, y}) + } + } + return <> + {tilesProperties.map(({key, classNameString, isGameTile, x, y}) => { + return
{ + if (isGameTile) + setTarget({ show: true, x, y }); + }} + onMouseEnter={() => setTargetPreview(e => ({...e, newX: x, newY: y, shouldShow: isGameTile}))} + >
+ })} + +} + +export default BorderTiles; diff --git a/frontend/src/components/FogImages.tsx b/frontend/src/components/FogImages.tsx new file mode 100644 index 0000000..ac3c230 --- /dev/null +++ b/frontend/src/components/FogImages.tsx @@ -0,0 +1,9 @@ +function FogImages() { + return <> + {`fog1.png`} + {`fog1.png`} + {`fog4.png`} + +} + +export default FogImages; diff --git a/frontend/src/components/HitElems.tsx b/frontend/src/components/HitElems.tsx new file mode 100644 index 0000000..e304b6e --- /dev/null +++ b/frontend/src/components/HitElems.tsx @@ -0,0 +1,17 @@ +import { faBurst, faXmark } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { CSSProperties } from 'react'; +import { HitType } from '../interfaces'; + +function HitElems({hits}: {hits: HitType[]}) { + + return <> + {hits.map(({hit, x, y}, i) => +
+ +
+ )} + +} + +export default HitElems; diff --git a/frontend/src/components/Labeling.tsx b/frontend/src/components/Labeling.tsx new file mode 100644 index 0000000..b3d0c9a --- /dev/null +++ b/frontend/src/components/Labeling.tsx @@ -0,0 +1,29 @@ +import { CSSProperties } from 'react' +import { fieldIndex } from '../helpers'; +import { FieldType } from '../interfaces'; + +function Labeling({count}: {count: number}) { + let elems: (FieldType & { + orientation: string + })[] = []; + for (let x = 0; x < count; x++) { + elems.push( + // Up + {field: String.fromCharCode(65+x), x: x+2, y: 1, orientation: 'up'}, + // Left + {field: (x+1).toString(), x: 1, y: x+2, orientation: 'left'}, + // Bottom + {field: String.fromCharCode(65+x), x: x+2, y: count+2, orientation: 'bottom'}, + // Right + {field: (x+1).toString(), x: count+2, y: x+2, orientation: 'right'} + ); + } + elems = elems.sort((a, b) => fieldIndex(count, a.x, a.y)-fieldIndex(count, b.x, b.y)); + return <> + {elems.map(({field, x, y, orientation}, i) => + {field} + )} + +} + +export default Labeling; diff --git a/frontend/src/components/Ships.tsx b/frontend/src/components/Ships.tsx new file mode 100644 index 0000000..11b2fa5 --- /dev/null +++ b/frontend/src/components/Ships.tsx @@ -0,0 +1,19 @@ +import { CSSProperties } from 'react' + +function Ships() { + let shipIndexes: number[] = []; + + for (let i = 1; i <= 6; i++) { + shipIndexes.push(i); + } + + return <> + {shipIndexes.map(i => +
+ {`${i}.svg`}/ +
+ )} + +} + +export default Ships; diff --git a/frontend/src/helpers.ts b/frontend/src/helpers.ts new file mode 100644 index 0000000..da0fcf4 --- /dev/null +++ b/frontend/src/helpers.ts @@ -0,0 +1,52 @@ +import { HitType, HitDispatchType } from "./interfaces"; + +export const hitReducer = (formObject: HitType[], action: HitDispatchType) => { + switch (action.type) { + + case 'fireMissle': { + const result = [...formObject, action.payload]; + return result; + } + + default: + return formObject; + } +} +export const fieldIndex = (count: number, x: number, y: number) => y*(count+2)+x; +export const cornerCN = (count: number, x: number, y: number) => { + if (x === 0 && y === 0) + return 'left-top-corner'; + if (x === count+1 && y === 0) + return 'right-top-corner'; + if (x === 0 && y === count+1) + return 'left-bottom-corner'; + if (x === count+1 && y === count+1) + return 'right-bottom-corner'; + return ''; +}; +export const borderCN = (count: number, x: number, y: number) => { + if (x === 0) + return 'left'; + if (y === 0) + return 'top'; + if (x === count+1) + return 'right'; + if (y === count+1) + return 'bottom'; + return ''; +}; +export const initlialTarget = { + show: false, + x: 0, + y: 0 +}; +export const initlialTargetPreview = { + newX: 0, + newY: 0, + shouldShow: false, + appearOK: false, + eventReady: true, + show: false, + x: 0, + y: 0 +}; \ No newline at end of file diff --git a/frontend/src/interfaces.ts b/frontend/src/interfaces.ts index 3b45114..61f9cb2 100644 --- a/frontend/src/interfaces.ts +++ b/frontend/src/interfaces.ts @@ -1,3 +1,8 @@ +export interface TargetType { + show: boolean, + x: number, + y: number +}; export interface TargetPreviewType { newX: number, newY: number, @@ -7,4 +12,18 @@ export interface TargetPreviewType { show: boolean, x: number, y: number -} \ No newline at end of file +}; +export interface FieldType { + field: string, + x: number, + y: number, +}; +export interface HitType { + hit: boolean, + x: number, + y: number, +}; + +interface fireMissle { type: 'fireMissle', payload: { x: number, y: number, hit: boolean } }; + +export type HitDispatchType = fireMissle; diff --git a/frontend/src/styles/App.scss b/frontend/src/styles/App.scss index d7b0d9c..210303e 100644 --- a/frontend/src/styles/App.scss +++ b/frontend/src/styles/App.scss @@ -48,7 +48,7 @@ grid-template-columns: .75fr repeat(12, 1fr) .75fr; // grid-gap: 1px solid blue; - > .r1 { + > .label { grid-column: var(--x); grid-row: var(--y); } @@ -83,6 +83,7 @@ // justify-content: center; border: 1px solid yellow; grid-row: var(--x); + pointer-events: none; img { position: absolute;