more consistent theming
This commit is contained in:
		
							parent
							
								
									af2d6a1f16
								
							
						
					
					
						commit
						f7e20fc608
					
				
					 14 changed files with 127 additions and 93 deletions
				
			
		
							
								
								
									
										4
									
								
								tank-frontend/src/App.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								tank-frontend/src/App.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					.GadgetRows {
 | 
				
			||||||
 | 
					    justify-content: space-evenly;
 | 
				
			||||||
 | 
					    margin-top: 24px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										47
									
								
								tank-frontend/src/App.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								tank-frontend/src/App.tsx
									
										
									
									
									
										Normal 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>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@ kbd {
 | 
				
			||||||
    background: hsl(0, 0%, 96%);
 | 
					    background: hsl(0, 0%, 96%);
 | 
				
			||||||
    padding: 10px;
 | 
					    padding: 10px;
 | 
				
			||||||
    display: block;
 | 
					    display: block;
 | 
				
			||||||
    border-radius: 5px;
 | 
					
 | 
				
			||||||
    margin: 5px;
 | 
					    margin: 5px;
 | 
				
			||||||
    width: 1.6em;
 | 
					    width: 1.6em;
 | 
				
			||||||
    height: 1.3em;
 | 
					    height: 1.3em;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -62,12 +62,12 @@ export default function Controls({playerId, logout}: {
 | 
				
			||||||
    return <Column className="Controls">
 | 
					    return <Column className="Controls">
 | 
				
			||||||
        <div className="control">
 | 
					        <div className="control">
 | 
				
			||||||
            <div className="row">
 | 
					            <div className="row">
 | 
				
			||||||
                <kbd className="up">↑</kbd>
 | 
					                <kbd className="up">▲</kbd>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div className="row">
 | 
					            <div className="row">
 | 
				
			||||||
                <kbd>←</kbd>
 | 
					                <kbd>◄</kbd>
 | 
				
			||||||
                <kbd>↓</kbd>
 | 
					                <kbd>▼</kbd>
 | 
				
			||||||
                <kbd>→</kbd>
 | 
					                <kbd>►</kbd>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <h3>Move</h3>
 | 
					        <h3>Move</h3>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,13 @@
 | 
				
			||||||
.JoinForm {
 | 
					.JoinForm {
 | 
				
			||||||
    border: 2px solid rgb(76, 76, 76);
 | 
					    border: 4px solid rgb(76, 76, 76);
 | 
				
			||||||
    border-radius: 4px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
.JoinElems {
 | 
					    position: absolute;
 | 
				
			||||||
    padding: 8px 8px;
 | 
					    top: 50%;
 | 
				
			||||||
    margin: 8px 8px;
 | 
					    left: 50%;
 | 
				
			||||||
 | 
					    transform: translate(-50%, -50%);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    background: black;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    gap: 16px;
 | 
				
			||||||
 | 
					    padding: 16px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,8 @@ import './JoinForm.css';
 | 
				
			||||||
import {NameId, PlayerResponse, postPlayer} from './serverCalls';
 | 
					import {NameId, PlayerResponse, postPlayer} from './serverCalls';
 | 
				
			||||||
import {Guid} from './Guid.ts';
 | 
					import {Guid} from './Guid.ts';
 | 
				
			||||||
import Column from "./components/Column.tsx";
 | 
					import Column from "./components/Column.tsx";
 | 
				
			||||||
 | 
					import Button from "./components/Button.tsx";
 | 
				
			||||||
 | 
					import TextInput from "./components/TextInput.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function JoinForm({setNameId, clientId}: {
 | 
					export default function JoinForm({setNameId, clientId}: {
 | 
				
			||||||
    setNameId: (mutator: (oldState: NameId) => NameId) => void,
 | 
					    setNameId: (mutator: (oldState: NameId) => NameId) => void,
 | 
				
			||||||
| 
						 | 
					@ -31,25 +33,19 @@ export default function JoinForm({setNameId, clientId}: {
 | 
				
			||||||
    }, [clicked, setData, data, clientId, setClicked, setNameId]);
 | 
					    }, [clicked, setData, data, clientId, setClicked, setNameId]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const disableButtons = clicked || name.trim() === '';
 | 
					    const disableButtons = clicked || name.trim() === '';
 | 
				
			||||||
 | 
					    const setClickedTrue = () => setClicked(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return <Column className='JoinForm'>
 | 
					    return <Column className='JoinForm'>
 | 
				
			||||||
        <p className="JoinElems"> Enter your name to join the game! </p>
 | 
					        <h3> Enter your name to play </h3>
 | 
				
			||||||
        <input
 | 
					        <TextInput
 | 
				
			||||||
            className="JoinElems"
 | 
					 | 
				
			||||||
            type="text"
 | 
					 | 
				
			||||||
            value={name}
 | 
					            value={name}
 | 
				
			||||||
            placeholder="player name"
 | 
					            placeholder="player name"
 | 
				
			||||||
            onChange={e => setName(e.target.value)}
 | 
					            onChange={e => setName(e.target.value)}
 | 
				
			||||||
            onKeyUp={event => {
 | 
					            onEnter={setClickedTrue}
 | 
				
			||||||
                if (event.key === 'Enter')
 | 
					 | 
				
			||||||
                    setClicked(true);
 | 
					 | 
				
			||||||
            }}
 | 
					 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
        <button
 | 
					        <Button
 | 
				
			||||||
            className="JoinElems"
 | 
					            onClick={setClickedTrue}
 | 
				
			||||||
            onClick={() => setClicked(true)}
 | 
					 | 
				
			||||||
            disabled={disableButtons}
 | 
					            disabled={disableButtons}
 | 
				
			||||||
        >
 | 
					            text='INSERT COIN'/>
 | 
				
			||||||
            join
 | 
					 | 
				
			||||||
        </button>
 | 
					 | 
				
			||||||
    </Column>;
 | 
					    </Column>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,3 @@
 | 
				
			||||||
    margin: 8px 8px;
 | 
					    margin: 8px 8px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PlayerInfo-Reset {
 | 
					 | 
				
			||||||
    height: 4em;
 | 
					 | 
				
			||||||
    width: 4em;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,14 @@
 | 
				
			||||||
.Button {
 | 
					.Button {
 | 
				
			||||||
    border: solid 1px green;
 | 
					    border: solid 4px green;
 | 
				
			||||||
    border-radius: 12px;
 | 
					    padding: 8px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    color: green;
 | 
					    color: green;
 | 
				
			||||||
    background: transparent;
 | 
					    background: transparent;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.Button:hover, .Button:active {
 | 
				
			||||||
 | 
					    background-color: green;
 | 
				
			||||||
 | 
					    color: black;
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,16 @@
 | 
				
			||||||
import './Button.css';
 | 
					import './Button.css';
 | 
				
			||||||
import {MouseEventHandler} from "react";
 | 
					import {MouseEventHandler} from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Button({text, onClick, className}: {
 | 
					export default function Button({text, onClick, className, disabled}: {
 | 
				
			||||||
    text: string,
 | 
					    text: string,
 | 
				
			||||||
    onClick?: MouseEventHandler<HTMLButtonElement>,
 | 
					    onClick?: MouseEventHandler<HTMLButtonElement>,
 | 
				
			||||||
    className?: string
 | 
					    className?: string,
 | 
				
			||||||
 | 
					    disabled?: boolean
 | 
				
			||||||
}) {
 | 
					}) {
 | 
				
			||||||
    return <button
 | 
					    return <button
 | 
				
			||||||
        className={'Button ' + (className ?? '')}
 | 
					        className={'Button ' + (className ?? '')}
 | 
				
			||||||
        onClick={onClick}
 | 
					        onClick={onClick}
 | 
				
			||||||
 | 
					        disabled={disabled ?? false}
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
        {text}
 | 
					        {text}
 | 
				
			||||||
    </button>
 | 
					    </button>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								tank-frontend/src/components/TextInput.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								tank-frontend/src/components/TextInput.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					.TextInput {
 | 
				
			||||||
 | 
					    padding: 8px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								tank-frontend/src/components/TextInput.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tank-frontend/src/components/TextInput.tsx
									
										
									
									
									
										Normal 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();
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					    />;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,3 @@
 | 
				
			||||||
* {
 | 
					 | 
				
			||||||
    box-sizing: border-box;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@font-face {
 | 
					@font-face {
 | 
				
			||||||
    font-family: 'CCCBFont';
 | 
					    font-family: 'CCCBFont';
 | 
				
			||||||
    font-style: normal;
 | 
					    font-style: normal;
 | 
				
			||||||
| 
						 | 
					@ -10,10 +6,15 @@
 | 
				
			||||||
    src: url('/CCCBFont.ttf') format('ttf'), url('/CCCBFont.woff') format('woff');
 | 
					    src: url('/CCCBFont.ttf') format('ttf'), url('/CCCBFont.woff') format('woff');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* {
 | 
				
			||||||
 | 
					    box-sizing: border-box;
 | 
				
			||||||
 | 
					    font-family: CCCBFont, monospace;
 | 
				
			||||||
 | 
					    text-transform: uppercase;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
html, body {
 | 
					html, body {
 | 
				
			||||||
    height: 100%;
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    font-family: CCCBFont, monospace;
 | 
					 | 
				
			||||||
    margin: 0;
 | 
					    margin: 0;
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    align-items: center;
 | 
					    align-items: center;
 | 
				
			||||||
| 
						 | 
					@ -34,8 +35,3 @@ html, body {
 | 
				
			||||||
.grow {
 | 
					.grow {
 | 
				
			||||||
    flex-grow: 1;
 | 
					    flex-grow: 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
.GadgetRows {
 | 
					 | 
				
			||||||
    justify-content: space-evenly;
 | 
					 | 
				
			||||||
    margin-top: 24px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,51 +1,8 @@
 | 
				
			||||||
import React, {useCallback, useState} from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import './index.css';
 | 
					 | 
				
			||||||
import ClientScreen from './ClientScreen';
 | 
					 | 
				
			||||||
import Controls from './Controls.tsx';
 | 
					 | 
				
			||||||
import JoinForm from './JoinForm.tsx';
 | 
					 | 
				
			||||||
import {createRoot} from 'react-dom/client';
 | 
					import {createRoot} from 'react-dom/client';
 | 
				
			||||||
import PlayerInfo from './PlayerInfo.tsx';
 | 
					import './index.css';
 | 
				
			||||||
import {useStoredObjectState} from './useStoredState.ts';
 | 
					import App from "./App.tsx";
 | 
				
			||||||
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";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
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(
 | 
					createRoot(document.getElementById('root')!).render(
 | 
				
			||||||
    <React.StrictMode>
 | 
					    <React.StrictMode>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@ export function useStoredObjectState<T>(
 | 
				
			||||||
    const getInitialState = () => {
 | 
					    const getInitialState = () => {
 | 
				
			||||||
        const localStorageJson = localStorage.getItem(storageKey);
 | 
					        const localStorageJson = localStorage.getItem(storageKey);
 | 
				
			||||||
        if (localStorageJson !== null && localStorageJson !== '') {
 | 
					        if (localStorageJson !== null && localStorageJson !== '') {
 | 
				
			||||||
            return JSON.parse(localStorageJson);
 | 
					            return JSON.parse(localStorageJson) as T;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return initialState();
 | 
					        return initialState();
 | 
				
			||||||
| 
						 | 
					@ -27,8 +27,9 @@ export function useStoredObjectState<T>(
 | 
				
			||||||
    const [state, setState] = useState<T>(getInitialState);
 | 
					    const [state, setState] = useState<T>(getInitialState);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const setSavedState = (mut: (oldState: T) => T) => {
 | 
					    const setSavedState = (mut: (oldState: T) => T) => {
 | 
				
			||||||
        localStorage.setItem(storageKey, JSON.stringify(mut(state)));
 | 
					        const newState = mut(state);
 | 
				
			||||||
        setState(mut);
 | 
					        localStorage.setItem(storageKey, JSON.stringify(newState));
 | 
				
			||||||
 | 
					        setState(newState);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return [state, setSavedState];
 | 
					    return [state, setSavedState];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue