more consistent theming

This commit is contained in:
Vinzenz Schroeter 2024-04-14 11:16:34 +02:00
parent af2d6a1f16
commit f7e20fc608
14 changed files with 127 additions and 93 deletions

View file

@ -0,0 +1,4 @@
.GadgetRows {
justify-content: space-evenly;
margin-top: 24px;
}

47
tank-frontend/src/App.tsx Normal file
View file

@ -0,0 +1,47 @@
import {useCallback, useState} from 'react';
import ClientScreen from './ClientScreen';
import Controls from './Controls.tsx';
import JoinForm from './JoinForm.tsx';
import PlayerInfo from './PlayerInfo.tsx';
import {useStoredObjectState} from './useStoredState.ts';
import {NameId, postPlayer} from './serverCalls.tsx';
import Column from "./components/Column.tsx";
import Row from "./components/Row.tsx";
import Scoreboard from "./Scoreboard.tsx";
import Button from "./components/Button.tsx";
import './App.css';
const getNewNameId = () => ({
id: crypto.randomUUID(),
name: ''
});
export default function App() {
const [nameId, setNameId] = useStoredObjectState<NameId>('access', getNewNameId);
const [isLoggedIn, setLoggedIn] = useState<boolean>(false);
const logout = () => setLoggedIn(false);
useCallback(async () => {
if (isLoggedIn)
return;
const result = await postPlayer(nameId);
setLoggedIn(result !== null);
}, [nameId, isLoggedIn])();
return <Column className='grow'>
<Row>
<h1 className='grow'>Tanks!</h1>
{nameId.name !== '' &&
<Button className='PlayerInfo-Reset' onClick={() => setNameId(getNewNameId)} text='logout'/>}
</Row>
<ClientScreen logout={logout}/>
{nameId.name === '' && <JoinForm setNameId={setNameId} clientId={nameId.id}/>}
{isLoggedIn && <Row className='GadgetRows'>
<Controls playerId={nameId.id} logout={logout}/>
<PlayerInfo playerId={nameId.id} logout={logout}/>
<Scoreboard/>
</Row>
}
</Column>;
}

View file

@ -6,7 +6,7 @@ kbd {
background: hsl(0, 0%, 96%);
padding: 10px;
display: block;
border-radius: 5px;
margin: 5px;
width: 1.6em;
height: 1.3em;

View file

@ -62,12 +62,12 @@ export default function Controls({playerId, logout}: {
return <Column className="Controls">
<div className="control">
<div className="row">
<kbd className="up"></kbd>
<kbd className="up"></kbd>
</div>
<div className="row">
<kbd></kbd>
<kbd></kbd>
<kbd></kbd>
<kbd></kbd>
<kbd></kbd>
<kbd></kbd>
</div>
</div>
<h3>Move</h3>

View file

@ -1,9 +1,13 @@
.JoinForm {
border: 2px solid rgb(76, 76, 76);
border-radius: 4px;
}
border: 4px solid rgb(76, 76, 76);
.JoinElems {
padding: 8px 8px;
margin: 8px 8px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: black;
gap: 16px;
padding: 16px;
}

View file

@ -3,6 +3,8 @@ import './JoinForm.css';
import {NameId, PlayerResponse, postPlayer} from './serverCalls';
import {Guid} from './Guid.ts';
import Column from "./components/Column.tsx";
import Button from "./components/Button.tsx";
import TextInput from "./components/TextInput.tsx";
export default function JoinForm({setNameId, clientId}: {
setNameId: (mutator: (oldState: NameId) => NameId) => void,
@ -31,25 +33,19 @@ export default function JoinForm({setNameId, clientId}: {
}, [clicked, setData, data, clientId, setClicked, setNameId]);
const disableButtons = clicked || name.trim() === '';
const setClickedTrue = () => setClicked(true);
return <Column className='JoinForm'>
<p className="JoinElems"> Enter your name to join the game! </p>
<input
className="JoinElems"
type="text"
<h3> Enter your name to play </h3>
<TextInput
value={name}
placeholder="player name"
onChange={e => setName(e.target.value)}
onKeyUp={event => {
if (event.key === 'Enter')
setClicked(true);
}}
onEnter={setClickedTrue}
/>
<button
className="JoinElems"
onClick={() => setClicked(true)}
<Button
onClick={setClickedTrue}
disabled={disableButtons}
>
join
</button>
text='INSERT COIN'/>
</Column>;
}

View file

@ -1,9 +1,5 @@
.Elems {
.Elems {
padding: 8px 8px;
margin: 8px 8px;
}
.PlayerInfo-Reset {
height: 4em;
width: 4em;
}

View file

@ -1,7 +1,14 @@
.Button {
border: solid 1px green;
border-radius: 12px;
border: solid 4px green;
padding: 8px;
color: green;
background: transparent;
}
.Button:hover, .Button:active {
background-color: green;
color: black;
cursor: pointer;
}

View file

@ -1,14 +1,16 @@
import './Button.css';
import {MouseEventHandler} from "react";
export default function Button({text, onClick, className}: {
export default function Button({text, onClick, className, disabled}: {
text: string,
onClick?: MouseEventHandler<HTMLButtonElement>,
className?: string
className?: string,
disabled?: boolean
}) {
return <button
className={'Button ' + (className ?? '')}
onClick={onClick}
disabled={disabled ?? false}
>
{text}
</button>

View file

@ -0,0 +1,3 @@
.TextInput {
padding: 8px;
}

View file

@ -0,0 +1,21 @@
import {ChangeEventHandler} from "react";
import './TextInput.css';
export default function TextInput(props: {
onChange?: ChangeEventHandler<HTMLInputElement> | undefined;
className?: string;
value: string;
placeholder: string;
onEnter?: () => void;
}) {
return <input
{...props}
type="text"
className={'TextInput ' + (props.className?? '')}
onKeyUp={event => {
if (props.onEnter && event.key === 'Enter')
props.onEnter();
}}
/>;
}

View file

@ -1,7 +1,3 @@
* {
box-sizing: border-box;
}
@font-face {
font-family: 'CCCBFont';
font-style: normal;
@ -10,10 +6,15 @@
src: url('/CCCBFont.ttf') format('ttf'), url('/CCCBFont.woff') format('woff');
}
* {
box-sizing: border-box;
font-family: CCCBFont, monospace;
text-transform: uppercase;
}
html, body {
height: 100%;
font-family: CCCBFont, monospace;
margin: 0;
display: flex;
align-items: center;
@ -34,8 +35,3 @@ html, body {
.grow {
flex-grow: 1;
}
.GadgetRows {
justify-content: space-evenly;
margin-top: 24px;
}

View file

@ -1,51 +1,8 @@
import React, {useCallback, useState} from 'react';
import './index.css';
import ClientScreen from './ClientScreen';
import Controls from './Controls.tsx';
import JoinForm from './JoinForm.tsx';
import React from 'react';
import {createRoot} from 'react-dom/client';
import PlayerInfo from './PlayerInfo.tsx';
import {useStoredObjectState} from './useStoredState.ts';
import {NameId, postPlayer} from './serverCalls.tsx';
import Column from "./components/Column.tsx";
import Row from "./components/Row.tsx";
import Scoreboard from "./Scoreboard.tsx";
import Button from "./components/Button.tsx";
import './index.css';
import App from "./App.tsx";
const getNewNameId = () => ({
id: crypto.randomUUID(),
name: ''
});
function App() {
const [nameId, setNameId] = useStoredObjectState<NameId>('access', getNewNameId);
const [isLoggedIn, setLoggedIn] = useState<boolean>(false);
const logout = () => setLoggedIn(false);
useCallback(async () => {
if (isLoggedIn)
return;
const result = await postPlayer(nameId);
setLoggedIn(result !== null);
}, [nameId, isLoggedIn])();
return <Column className='grow'>
<Row>
<h1 className='grow'>Tanks!</h1>
{nameId.name !== '' &&
<Button className='PlayerInfo-Reset' onClick={() => setNameId(getNewNameId)} text='x'/>}
</Row>
<ClientScreen logout={logout}/>
{nameId.name === '' && <JoinForm setNameId={setNameId} clientId={nameId.id}/>}
{isLoggedIn && <Row className='GadgetRows'>
<Controls playerId={nameId.id} logout={logout}/>
<PlayerInfo playerId={nameId.id} logout={logout}/>
<Scoreboard/>
</Row>
}
</Column>;
}
createRoot(document.getElementById('root')!).render(
<React.StrictMode>

View file

@ -18,7 +18,7 @@ export function useStoredObjectState<T>(
const getInitialState = () => {
const localStorageJson = localStorage.getItem(storageKey);
if (localStorageJson !== null && localStorageJson !== '') {
return JSON.parse(localStorageJson);
return JSON.parse(localStorageJson) as T;
}
return initialState();
@ -27,8 +27,9 @@ export function useStoredObjectState<T>(
const [state, setState] = useState<T>(getInitialState);
const setSavedState = (mut: (oldState: T) => T) => {
localStorage.setItem(storageKey, JSON.stringify(mut(state)));
setState(mut);
const newState = mut(state);
localStorage.setItem(storageKey, JSON.stringify(newState));
setState(newState);
};
return [state, setSavedState];