front: basic ui mockup
- more components - more navigation [bugfix] proxy routing
This commit is contained in:
parent
375af0798e
commit
8056c38ad5
5026
webapp/package-lock.json
generated
5026
webapp/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -2,21 +2,6 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.App-header {
|
|
||||||
background-color: #282c34;
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: calc(10px + 2vmin);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-link {
|
|
||||||
color: #61dafb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Container {
|
.Container {
|
||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
}
|
}
|
@ -9,6 +9,7 @@ import {
|
|||||||
import Header from "./components/Header"
|
import Header from "./components/Header"
|
||||||
import Leaderboard from "./components/Leaderboard"
|
import Leaderboard from "./components/Leaderboard"
|
||||||
import Game from "./components/Game"
|
import Game from "./components/Game"
|
||||||
|
import GameProposal from './components/Game/Proposal'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
||||||
@ -19,6 +20,7 @@ function App() {
|
|||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/leaderboard" element={<Leaderboard/>} />
|
<Route path="/leaderboard" element={<Leaderboard/>} />
|
||||||
<Route path="/game" element={<Game/>} />
|
<Route path="/game" element={<Game/>} />
|
||||||
|
<Route path="/game/proposal" element={<GameProposal/>} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
31
webapp/src/components/Board/index.css
Normal file
31
webapp/src/components/Board/index.css
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
.tile {
|
||||||
|
border: 1px solid #e4e4e4;
|
||||||
|
float: left;
|
||||||
|
font-size: 200%;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 34px;
|
||||||
|
height: 34px;
|
||||||
|
width: 34px;
|
||||||
|
margin-right: -1px;
|
||||||
|
margin-top: -1px;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.black {
|
||||||
|
background: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
/* scale: 15%; */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.white:hover {
|
||||||
|
background-color:azure;
|
||||||
|
}
|
79
webapp/src/components/Board/index.jsx
Normal file
79
webapp/src/components/Board/index.jsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import './index.css';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { WhiteStone, BlackStone } from '../Stone'
|
||||||
|
|
||||||
|
export default function Board() {
|
||||||
|
|
||||||
|
return <div className='board'>
|
||||||
|
<div className='row'>
|
||||||
|
<BlackTile/> <WhiteTile id={0} stone={WhiteStone()} />
|
||||||
|
<BlackTile/> <WhiteTile id={1} stone={WhiteStone()} />
|
||||||
|
<BlackTile/> <WhiteTile id={2} stone={WhiteStone()} />
|
||||||
|
<BlackTile/> <WhiteTile id={4} stone={WhiteStone()} />
|
||||||
|
</div>
|
||||||
|
<div className='row'>
|
||||||
|
<WhiteTile id={5} stone={WhiteStone()} /> <BlackTile/>
|
||||||
|
<WhiteTile id={6} stone={WhiteStone()} /> <BlackTile/>
|
||||||
|
<WhiteTile id={7} stone={WhiteStone()} /> <BlackTile/>
|
||||||
|
<WhiteTile id={8} stone={WhiteStone()} /> <BlackTile/>
|
||||||
|
</div>
|
||||||
|
<div className='row'>
|
||||||
|
<BlackTile/> <WhiteTile id={ 9} stone={WhiteStone()} />
|
||||||
|
<BlackTile/> <WhiteTile id={10} stone={WhiteStone()} />
|
||||||
|
<BlackTile/> <WhiteTile id={11} stone={WhiteStone()} />
|
||||||
|
<BlackTile/> <WhiteTile id={12} stone={WhiteStone()} />
|
||||||
|
</div>
|
||||||
|
<div className='row'>
|
||||||
|
<WhiteTile id={13} stone={null} /> <BlackTile/>
|
||||||
|
<WhiteTile id={14} stone={null} /> <BlackTile/>
|
||||||
|
<WhiteTile id={15} stone={null} /> <BlackTile/>
|
||||||
|
<WhiteTile id={16} stone={null} /> <BlackTile/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='row'>
|
||||||
|
<BlackTile/> <WhiteTile id={17} stone={null} />
|
||||||
|
<BlackTile/> <WhiteTile id={18} stone={null} />
|
||||||
|
<BlackTile/> <WhiteTile id={19} stone={null} />
|
||||||
|
<BlackTile/> <WhiteTile id={20} stone={null} />
|
||||||
|
</div>
|
||||||
|
<div className='row'>
|
||||||
|
<WhiteTile id={21} stone={BlackStone()} /> <BlackTile/>
|
||||||
|
<WhiteTile id={22} stone={BlackStone()} /> <BlackTile/>
|
||||||
|
<WhiteTile id={23} stone={BlackStone()} /> <BlackTile/>
|
||||||
|
<WhiteTile id={24} stone={BlackStone()} /> <BlackTile/>
|
||||||
|
</div>
|
||||||
|
<div className='row'>
|
||||||
|
<BlackTile/> <WhiteTile id={25} stone={BlackStone()} />
|
||||||
|
<BlackTile/> <WhiteTile id={26} stone={BlackStone()} />
|
||||||
|
<BlackTile/> <WhiteTile id={27} stone={BlackStone()} />
|
||||||
|
<BlackTile/> <WhiteTile id={28} stone={BlackStone()} />
|
||||||
|
</div>
|
||||||
|
<div className='row'>
|
||||||
|
<WhiteTile id={29} stone={BlackStone()} /> <BlackTile/>
|
||||||
|
<WhiteTile id={30} stone={BlackStone()} /> <BlackTile/>
|
||||||
|
<WhiteTile id={31} stone={BlackStone()} /> <BlackTile/>
|
||||||
|
<WhiteTile id={32} stone={BlackStone()} /> <BlackTile/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
function WhiteTile({ id, stone }) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className='tile white'
|
||||||
|
onClick={() => handleClick(id)}
|
||||||
|
>
|
||||||
|
{stone}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function BlackTile() {
|
||||||
|
return <div className='tile black'/>
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClick(i) {
|
||||||
|
console.log("click", i)
|
||||||
|
}
|
0
webapp/src/components/Game/Proposal/index.css
Normal file
0
webapp/src/components/Game/Proposal/index.css
Normal file
22
webapp/src/components/Game/Proposal/index.jsx
Normal file
22
webapp/src/components/Game/Proposal/index.jsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import './index.css';
|
||||||
|
import React from 'react';
|
||||||
|
// import { NavLink } from "react-router-dom";
|
||||||
|
// import { AppData } from "../../../context/data"
|
||||||
|
import GameHeader from '../../GameHeader'
|
||||||
|
import GameSelector from '../../GameSelector'
|
||||||
|
import Board from '../../Board'
|
||||||
|
|
||||||
|
export default function Proposal() {
|
||||||
|
// const [data] = React.useContext(AppData)
|
||||||
|
|
||||||
|
return <div className="split">
|
||||||
|
<div className='split left'>
|
||||||
|
<GameHeader/>
|
||||||
|
<button>+ Create</button>
|
||||||
|
<GameSelector/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='split right'></div>
|
||||||
|
<Board/>
|
||||||
|
</div>
|
||||||
|
};
|
@ -1,47 +1,21 @@
|
|||||||
.GameProposal {
|
.split {
|
||||||
|
width: 100%;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split .left {
|
||||||
|
float: left;
|
||||||
|
width: 45%;
|
||||||
|
/* max-width: 400px; */
|
||||||
|
|
||||||
|
/* height: 100px; */
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.GameProposal .li {
|
.split .right {
|
||||||
width: 50%;
|
float: left;
|
||||||
border: 1px solid black;
|
width: 55%;
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GameProposal .li p {
|
|
||||||
margin: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GameProposal .li p q {
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GameProposal .li p i {
|
|
||||||
font-size: 70%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GameProposal .li button.action {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GameProposal .li:hover button.action {
|
|
||||||
display: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.separator {
|
|
||||||
width: 20%;
|
|
||||||
/* height: 20px; */
|
|
||||||
border-bottom: 1px dotted black;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 50%;
|
|
||||||
padding-left: 50%;
|
|
||||||
margin-bottom: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stone {
|
|
||||||
font-size: 140%;
|
|
||||||
vertical-align: -3px;
|
|
||||||
}
|
}
|
@ -1,83 +1,22 @@
|
|||||||
import './index.css';
|
import './index.css';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Accept} from './GameProposalAction';
|
import GameHeader from '../GameHeader'
|
||||||
import Reject from './Reject'
|
import GameSelector from '../GameSelector'
|
||||||
import Cancel from './GameProposalCancel'
|
import Board from '../Board'
|
||||||
|
|
||||||
import { AppData } from "../../context/data"
|
// import { AppData } from "../../context/data"
|
||||||
|
|
||||||
export default function Game() {
|
export default function Game() {
|
||||||
const [data] = React.useContext(AppData)
|
|
||||||
|
|
||||||
if (data.games == null)
|
return <div className="split">
|
||||||
return <div>Loading..</div>
|
|
||||||
|
<div className='split left'>
|
||||||
// for (const [key, value] of Object.entries(data.games))
|
<GameHeader/>
|
||||||
// console.log(key, value);
|
<GameSelector/>
|
||||||
|
|
||||||
const waitForYou = data.games
|
|
||||||
.filter(game => game.status === Status.WaitForYou)
|
|
||||||
.map(game => {
|
|
||||||
return <div className="li" key={game.uuid}>
|
|
||||||
<p>
|
|
||||||
You {Stone(game.myColor)} <i>vs</i> {game.opponentName} {Stone(oppositeColor(game.myColor))}
|
|
||||||
<br/>
|
|
||||||
<q>{game.message}</q>
|
|
||||||
<br/>
|
|
||||||
<Accept uuid={game.uuid}/>
|
|
||||||
<Reject uuid={game.uuid}/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
});
|
|
||||||
|
|
||||||
const WaitForOpponent = data.games
|
|
||||||
.filter(game => game.status === Status.WaitForOpponent)
|
|
||||||
.map(game => {
|
|
||||||
return <div className="li" key={game.uuid}>
|
|
||||||
<p>
|
|
||||||
You {Stone(game.myColor)} <i>vs</i> {game.opponentName} {Stone(oppositeColor(game.myColor))}
|
|
||||||
<br/>
|
|
||||||
<q>{game.message}</q>
|
|
||||||
<br/>
|
|
||||||
<Cancel uuid={game.uuid}/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
});
|
|
||||||
|
|
||||||
return <div className="GameProposal">
|
|
||||||
{waitForYou}
|
|
||||||
{WaitForOpponent.length > 0 &&
|
|
||||||
<div className="separator">
|
|
||||||
waiting for opponent ({WaitForOpponent.length})
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
{WaitForOpponent}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className='split right'></div>
|
||||||
|
<Board/>
|
||||||
|
</div>
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const Status = {
|
|
||||||
WaitForOpponent: "GAME_PROPOSAL_WAIT_FOR_OPPONENT",
|
|
||||||
WaitForYou: "GAME_PROPOSAL_WAIT_FOR_YOU",
|
|
||||||
}
|
|
||||||
|
|
||||||
function Stone(color) {
|
|
||||||
if (color === "WHITE")
|
|
||||||
return <span className="stone">⛀</span>
|
|
||||||
|
|
||||||
if (color === "BLACK")
|
|
||||||
return <span className="stone">⛂</span>
|
|
||||||
|
|
||||||
return <span className="stone">{color}</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
function oppositeColor(color) {
|
|
||||||
if (color === "WHITE")
|
|
||||||
return "BLACK"
|
|
||||||
|
|
||||||
if (color === "BLACK")
|
|
||||||
return "WHITE"
|
|
||||||
|
|
||||||
return color
|
|
||||||
}
|
|
||||||
|
31
webapp/src/components/GameHeader/index.css
Normal file
31
webapp/src/components/GameHeader/index.css
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
.game-header {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
background-color: lightgrey;
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-header a {
|
||||||
|
color:darkgrey;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: .25s ease;
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-header .active {
|
||||||
|
color: white;
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: cadetblue;
|
||||||
|
opacity: 80%;
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-header a:hover:not(.active) {
|
||||||
|
color: cadetblue;
|
||||||
|
|
||||||
|
/* box-shadow: 0 1.5px 0 0 currentColor; */
|
||||||
|
}
|
15
webapp/src/components/GameHeader/index.jsx
Normal file
15
webapp/src/components/GameHeader/index.jsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import './index.css';
|
||||||
|
import React from 'react';
|
||||||
|
import { NavLink } from "react-router-dom";
|
||||||
|
// import { AppData } from "../../context/data"
|
||||||
|
|
||||||
|
export default function GameHeader() {
|
||||||
|
// const [data] = React.useContext(AppData)
|
||||||
|
|
||||||
|
|
||||||
|
return <nav className="game-header">
|
||||||
|
<NavLink to="/game/proposal">Proposal</NavLink>
|
||||||
|
<NavLink to="/game/active">Active</NavLink>
|
||||||
|
<NavLink to="/game/archive">Archive</NavLink>
|
||||||
|
</nav>
|
||||||
|
};
|
47
webapp/src/components/GameSelector/index.css
Normal file
47
webapp/src/components/GameSelector/index.css
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
.Games {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.Games .li {
|
||||||
|
border: 1px solid black;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Games .li p {
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Games .li p q {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Games .li p i {
|
||||||
|
font-size: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Games .li button.action {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Games .li:hover button.action {
|
||||||
|
display: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
/* width: 20%; */
|
||||||
|
/* height: 20px; */
|
||||||
|
border-bottom: 1px dotted black;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 50%;
|
||||||
|
padding-left: 50%;
|
||||||
|
margin-bottom: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stone {
|
||||||
|
font-size: 140%;
|
||||||
|
vertical-align: -3px;
|
||||||
|
}
|
82
webapp/src/components/GameSelector/index.jsx
Normal file
82
webapp/src/components/GameSelector/index.jsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import './index.css';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { AppData } from "../../context/data"
|
||||||
|
|
||||||
|
export default function Game() {
|
||||||
|
const [data] = React.useContext(AppData)
|
||||||
|
|
||||||
|
if (!data?.games)
|
||||||
|
return <div>Loading..</div>
|
||||||
|
|
||||||
|
// for (const [key, value] of Object.entries(data.games))
|
||||||
|
// console.log(key, value);
|
||||||
|
|
||||||
|
console.log("data.games", data.games)
|
||||||
|
const waitForYou = data.games
|
||||||
|
.filter(game => game.status === Status.WaitForYou)
|
||||||
|
.map(game => {
|
||||||
|
return <div className="li" key={game.uuid}>
|
||||||
|
<p>
|
||||||
|
You {Stone(game.myColor)} <i>vs</i> {game.opponentName} {Stone(oppositeColor(game.myColor))}
|
||||||
|
<br/>
|
||||||
|
<q>{game.message}</q>
|
||||||
|
<br/>
|
||||||
|
{/* <Accept uuid={game.uuid}/>
|
||||||
|
<Reject uuid={game.uuid}/> */}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
});
|
||||||
|
|
||||||
|
const WaitForOpponent = data.games
|
||||||
|
.filter(game => game.status === Status.WaitForOpponent)
|
||||||
|
.map(game => {
|
||||||
|
return <div className="li" key={game.uuid}>
|
||||||
|
<p>
|
||||||
|
You {Stone(game.myColor)} <i>vs</i> {game.opponentName} {Stone(oppositeColor(game.myColor))}
|
||||||
|
<br/>
|
||||||
|
<q>{game.message}</q>
|
||||||
|
<br/>
|
||||||
|
{/* <Cancel uuid={game.uuid}/> */}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
});
|
||||||
|
|
||||||
|
return <div className="Container">
|
||||||
|
<div className="Games">
|
||||||
|
{waitForYou}
|
||||||
|
{WaitForOpponent.length > 0 &&
|
||||||
|
<div className="separator">
|
||||||
|
waiting for opponent ({WaitForOpponent.length})
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{WaitForOpponent}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const Status = {
|
||||||
|
WaitForOpponent: "GAME_PROPOSAL_WAIT_FOR_OPPONENT",
|
||||||
|
WaitForYou: "GAME_PROPOSAL_WAIT_FOR_YOU",
|
||||||
|
}
|
||||||
|
|
||||||
|
function Stone(color) {
|
||||||
|
if (color === "WHITE")
|
||||||
|
return <span className="stone">⛀</span>
|
||||||
|
|
||||||
|
if (color === "BLACK")
|
||||||
|
return <span className="stone">⛂</span>
|
||||||
|
|
||||||
|
return <span className="stone">{color}</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
function oppositeColor(color) {
|
||||||
|
if (color === "WHITE")
|
||||||
|
return "BLACK"
|
||||||
|
|
||||||
|
if (color === "BLACK")
|
||||||
|
return "WHITE"
|
||||||
|
|
||||||
|
return color
|
||||||
|
}
|
@ -8,7 +8,7 @@ h1 {
|
|||||||
height: 1px;
|
height: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
.app-header {
|
||||||
padding: auto;
|
padding: auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -16,18 +16,18 @@ nav {
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a {
|
.app-header a {
|
||||||
color: lightgray;
|
color: lightgray;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: .25s ease;
|
transition: .25s ease;
|
||||||
width: 100px;
|
width: fit-content;
|
||||||
|
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
padding: 0.25rem 1rem;
|
padding: 0.25rem 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav .active {
|
.app-header .active {
|
||||||
color: white;
|
color: white;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
background-color: cadetblue;
|
background-color: cadetblue;
|
||||||
@ -35,18 +35,17 @@ nav .active {
|
|||||||
padding: 0.25rem 1rem;
|
padding: 0.25rem 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a:hover:not(.active) {
|
.app-header a:hover:not(.active) {
|
||||||
color: cadetblue;
|
color: cadetblue;
|
||||||
|
|
||||||
box-shadow: 0 1.5px 0 0 currentcolor;
|
box-shadow: 0 1.5px 0 0 currentcolor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-darkreader-scheme="dark"] .app-header a {
|
||||||
[data-darkreader-scheme="dark"] nav a {
|
|
||||||
color: darkslategrey;
|
color: darkslategrey;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-darkreader-scheme="dark"] nav .active {
|
[data-darkreader-scheme="dark"] .app-header .active {
|
||||||
color: white;
|
color: white;
|
||||||
box-shadow: 0 1.5px 0 0 currentcolor;
|
box-shadow: 0 1.5px 0 0 currentcolor;
|
||||||
}
|
}
|
@ -2,7 +2,7 @@ import './index.css';
|
|||||||
import './wave.css'
|
import './wave.css'
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import { NavLink } from "react-router-dom";
|
import { NavLink } from "react-router-dom";
|
||||||
import OfflineToggle from '../OnlineTgl';
|
import OnlineToggle from '../OnlineTgl';
|
||||||
import { AppData } from "../../context/data"
|
import { AppData } from "../../context/data"
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
@ -10,9 +10,9 @@ export default function Header() {
|
|||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<h1>
|
<h1>
|
||||||
CordaCheckers<OfflineToggle />
|
CordaCheckers<OnlineToggle />
|
||||||
</h1>
|
</h1>
|
||||||
<nav>
|
<nav className='app-header'>
|
||||||
<NavLink to="/leaderboard" className={data.leaderboardFetching && "woble"}>
|
<NavLink to="/leaderboard" className={data.leaderboardFetching && "woble"}>
|
||||||
<span>L</span>
|
<span>L</span>
|
||||||
<span>e</span>
|
<span>e</span>
|
||||||
|
7
webapp/src/components/Stone/index.css
Normal file
7
webapp/src/components/Stone/index.css
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.white-stone {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.black-stone {
|
||||||
|
cursor: default;
|
||||||
|
}
|
41
webapp/src/components/Stone/index.jsx
Normal file
41
webapp/src/components/Stone/index.jsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import './index.css';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function Stone( color ) {
|
||||||
|
switch (color) {
|
||||||
|
case WHITE():
|
||||||
|
return WhiteStone()
|
||||||
|
|
||||||
|
case BLACK():
|
||||||
|
return BlackStone()
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.warn("Unknown color: ", color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function WhiteStone() {
|
||||||
|
return <span className="white-stone">⛀</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BlackStone() {
|
||||||
|
return <span className="black-stone">⛂</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function oppositeColor(color) {
|
||||||
|
if (color === WHITE())
|
||||||
|
return BLACK()
|
||||||
|
|
||||||
|
if (color === BLACK())
|
||||||
|
return WHITE()
|
||||||
|
|
||||||
|
return color
|
||||||
|
}
|
||||||
|
|
||||||
|
export function WHITE() {
|
||||||
|
return "WHITE"
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BLACK() {
|
||||||
|
return "BLACK"
|
||||||
|
}
|
@ -12,8 +12,8 @@ export const AppDataProvider = ({ children }) => {
|
|||||||
|
|
||||||
const [data, dispatchData] = React.useReducer(reducer, initialState)
|
const [data, dispatchData] = React.useReducer(reducer, initialState)
|
||||||
|
|
||||||
const [games, gamesFetching ] = Poll('api/gamestate' , 30, data.offlineMode)
|
const [games, gamesFetching ] = Poll('/api/gamestate' , 30, data.offlineMode)
|
||||||
const [leaderboard, leaderboardFetching ] = Poll('api/leaderboard', 60, data.offlineMode)
|
const [leaderboard, leaderboardFetching ] = Poll('/api/leaderboard', 60, data.offlineMode)
|
||||||
|
|
||||||
data.games = games
|
data.games = games
|
||||||
data.gamesFetching = gamesFetching
|
data.gamesFetching = gamesFetching
|
||||||
|
Loading…
Reference in New Issue
Block a user