Refactoring
This commit is contained in:
parent
83f187a7dc
commit
2ebaccfb2d
9 changed files with 231 additions and 118 deletions
|
@ -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<TargetPreviewType>({
|
||||
newX: 0,
|
||||
newY: 0,
|
||||
shouldShow: false,
|
||||
appearOK: false,
|
||||
eventReady: true,
|
||||
show: false,
|
||||
x: 0,
|
||||
y: 0
|
||||
})
|
||||
const count = 12;
|
||||
const [target, setTarget] = useState<TargetType>(initlialTarget);
|
||||
const [targetPreview, setTargetPreview] = useState<TargetPreviewType>(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) =>
|
||||
<div key={i} className='hit-svg' style={{'--x': obj.x, '--y': obj.y} as CSSProperties}>
|
||||
<FontAwesomeIcon icon={faBurst} />
|
||||
</div>);
|
||||
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';
|
||||
const autoTimer = setTimeout(() => {
|
||||
DispatchHits({type: 'fireMissle', payload: {hit: (x+y)%2 !== 0, x: x+2, y: y+2}});
|
||||
setX(x => x+1);
|
||||
}, 100);
|
||||
|
||||
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(
|
||||
<div
|
||||
key={y*(count+2)+x}
|
||||
className={classNames.join(' ')}
|
||||
style={{'--x': (x + 1), '--y': (y + 1)} as CSSProperties}
|
||||
onClick={() => {
|
||||
if (isGameTile)
|
||||
setTarget({ show: true, x, y });
|
||||
}}
|
||||
onMouseEnter={() => setTargetPreview(e => ({...e, newX: x, newY: y, shouldShow: isGameTile}))}
|
||||
></div>
|
||||
)
|
||||
}
|
||||
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(
|
||||
<div key={i} className={`ship s${i}`} style={{'--x': i+3} as CSSProperties}>
|
||||
<img src={`/svgs/${i}.svg`} alt={`${i}.svg`}/>
|
||||
</div>);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<div id="game-frame" style={{'--i': count} as CSSProperties}>
|
||||
{/* Bordes */}
|
||||
{ borderTiles }
|
||||
<BorderTiles count={count} targets={{setTarget, setTargetPreview}} />
|
||||
|
||||
{/* Collumn lettes and row numbers */}
|
||||
{elems2.map((obj, i) =>
|
||||
<span key={i} className={`${obj.field} r1`} style={{'--x': obj.x, '--y': obj.y} as CSSProperties}>{obj.field}</span>
|
||||
)}
|
||||
{ hitSVGs }
|
||||
<Labeling count={count} />
|
||||
|
||||
{/* Ships */}
|
||||
{/* { shipElems } */}
|
||||
{/* <Ships /> */}
|
||||
|
||||
<HitElems hits={hits} />
|
||||
|
||||
{/* Fog images */}
|
||||
{/* <img className='fog-left' src={`/fog/fog2.png`} alt={`fog1.png`} />
|
||||
<img className='fog-right' src={`/fog/fog2.png`} alt={`fog1.png`} />
|
||||
<img className='fog-middle' src={`/fog/fog4.png`} alt={`fog4.png`} /> */}
|
||||
{/* <FogImages /> */}
|
||||
<div className={`hit-svg target ${target.show ? 'show' : ''}`} style={{'--x': target.x+1, '--y': target.y+1} as CSSProperties}>
|
||||
<FontAwesomeIcon icon={faCrosshairs} />
|
||||
</div>
|
||||
|
@ -184,9 +106,9 @@ function App() {
|
|||
<FontAwesomeIcon icon={faCrosshairs} />
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
{/* <p>
|
||||
Edit <code>src/App.tsx</code> and save to reload.
|
||||
</p>
|
||||
</p> */}
|
||||
<a
|
||||
className="App-link"
|
||||
href="https://www.freepik.com/free-vector/militaristic-ships-set-navy-ammunition-warship-submarine-nuclear-battleship-float-cruiser-trawler-gunboat-frigate-ferry_10704121.htm"
|
||||
|
|
45
frontend/src/components/BorderTiles.tsx
Normal file
45
frontend/src/components/BorderTiles.tsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
import { CSSProperties } from 'react';
|
||||
import { borderCN, cornerCN, fieldIndex } from '../helpers';
|
||||
import { TargetPreviewType, TargetType } from '../interfaces';
|
||||
|
||||
function BorderTiles({count, targets: {setTarget, setTargetPreview}}: {count: number, targets: {setTarget: React.Dispatch<React.SetStateAction<TargetType>>, setTargetPreview: React.Dispatch<React.SetStateAction<TargetPreviewType>>}}) {
|
||||
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 <div
|
||||
key={key}
|
||||
className={classNameString}
|
||||
style={{'--x': (x + 1), '--y': (y + 1)} as CSSProperties}
|
||||
onClick={() => {
|
||||
if (isGameTile)
|
||||
setTarget({ show: true, x, y });
|
||||
}}
|
||||
onMouseEnter={() => setTargetPreview(e => ({...e, newX: x, newY: y, shouldShow: isGameTile}))}
|
||||
></div>
|
||||
})}
|
||||
</>
|
||||
}
|
||||
|
||||
export default BorderTiles;
|
9
frontend/src/components/FogImages.tsx
Normal file
9
frontend/src/components/FogImages.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
function FogImages() {
|
||||
return <>
|
||||
<img className='fog-left' src={`/fog/fog2.png`} alt={`fog1.png`} />
|
||||
<img className='fog-right' src={`/fog/fog2.png`} alt={`fog1.png`} />
|
||||
<img className='fog-middle' src={`/fog/fog4.png`} alt={`fog4.png`} />
|
||||
</>
|
||||
}
|
||||
|
||||
export default FogImages;
|
17
frontend/src/components/HitElems.tsx
Normal file
17
frontend/src/components/HitElems.tsx
Normal file
|
@ -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) =>
|
||||
<div key={i} className='hit-svg' style={{'--x': x, '--y': y} as CSSProperties}>
|
||||
<FontAwesomeIcon icon={hit ? faBurst : faXmark} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
|
||||
export default HitElems;
|
29
frontend/src/components/Labeling.tsx
Normal file
29
frontend/src/components/Labeling.tsx
Normal file
|
@ -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) =>
|
||||
<span key={i} className={`label ${orientation} ${field}`} style={{'--x': x, '--y': y} as CSSProperties}>{field}</span>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
|
||||
export default Labeling;
|
19
frontend/src/components/Ships.tsx
Normal file
19
frontend/src/components/Ships.tsx
Normal file
|
@ -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 =>
|
||||
<div key={i} className={`ship s${i}`} style={{'--x': i+3} as CSSProperties}>
|
||||
<img src={`/svgs/${i}.svg`} alt={`${i}.svg`}/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
|
||||
export default Ships;
|
52
frontend/src/helpers.ts
Normal file
52
frontend/src/helpers.ts
Normal file
|
@ -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
|
||||
};
|
|
@ -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
|
||||
}
|
||||
};
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue