Переглянути джерело

Merge pull request #25 from NielsOverkamp/more-responsive-frontend

Lightly rework lobby and game page
ard66 5 роки тому
батько
коміт
136459d106

+ 1 - 1
frontend/keezen-frontend/src/App.js

@@ -10,7 +10,7 @@ import Hand from "./Hand";
 import StateRouter from "./StateRouter";
 
 function App() {
-    return <div className="container App">
+    return <div className="App">
         <StateRouter/>
         {/*<Hand cards={[*/}
         {/*    { suit: Suits.DIAMONDS, denom: Denoms.ACE },*/}

+ 16 - 14
frontend/keezen-frontend/src/Game.js

@@ -37,20 +37,22 @@ export default function Game({ message, swapCard, playCard, confirmPlay, undoPla
 
 
     return <Fragment>
-        <h1>{text}</h1>
-        {card &&
-        <Card value={card}/>
-        }
-        {card && play_card && state === SiteState.PLAY_CARD &&
-        <button className="btn btn-success" onClick={confirmPlay}>Volgende speler</button>
-        }
-        {card && (state === SiteState.PLAY_CARD || state === SiteState.SWAP_CARD) &&
-        <button className="btn btn-secondary" onClick={undoPlay}>Neem terug</button>
-        }
-        <br/>
-        {state === SiteState.PLAY_CARD &&
-        <button className="btn btn-primary" onClick={skipTurn}>Pas</button>
-        }
+        <div className="top-bar">{text}</div>
+            <div className="container">
+                {card &&
+                <Card value={card}/>
+                }
+                {card && play_card && state === SiteState.PLAY_CARD &&
+                <button className="btn btn-success" onClick={confirmPlay}>Volgende speler</button>
+                }
+                {card && (state === SiteState.PLAY_CARD || state === SiteState.SWAP_CARD) &&
+                <button className="btn btn-secondary" onClick={undoPlay}>Neem terug</button>
+                }
+                <br/>
+                {state === SiteState.PLAY_CARD &&
+                <button className="btn btn-primary" onClick={skipTurn}>Pas</button>
+                }
+            </div>
         <Hand cards={hand} play={play}/>
     </Fragment>;
 }

+ 10 - 8
frontend/keezen-frontend/src/Hand.js

@@ -4,14 +4,16 @@ import { Card } from "./Card";
 export default function Hand({ cards, play }) {
     const [selected, setSelected] = useState(null);
 
-    return <div className="hand">
-        <div className="row">
-        {cards.map((c, i) => <div className="col-2">
-            <Card value={c}
-                                   animate={play !== null}
-                                   play={play === null ? null : (() => play(c))}
-                                   select={selected === i ? () => setSelected(null) : () => setSelected(i)}
-                                                      selected={selected === i}/></div>)}
+    return <div className="bottom-bar hand">
+        <div className="container">
+            <div className="row m-auto">
+                {cards.map((c, i) => <div className="col-2">
+                    <Card value={c}
+                          animate={play !== null}
+                          play={play === null ? null : (() => play(c))}
+                          select={selected === i ? () => setSelected(null) : () => setSelected(i)}
+                          selected={selected === i}/></div>)}
+            </div>
         </div>
     </div>
 }

+ 25 - 23
frontend/keezen-frontend/src/LandingPage.js

@@ -21,28 +21,30 @@ export default function LandingPage({ message, newGame, joinGame }) {
         }
     }
 
-                                                                                                                                                    
-    return <div className="landing-page row h-100 align-content-center justify-content-center flex-md-column">
-        <button className="btn btn-primary mb-5" onClick={newGame}>Start een nieuw spel</button>
-        {showCodeInput ?
-            <form className="form-inline" onSubmit={(event) => {
-                event.preventDefault();
-                joinGame(code)
-            }}>
-                <input type="number"
-                       className="form-control mx-2"
-                       placeholder="1234"
-                       onChange={handleCodeChange}
-                       value={code}
-                       autoFocus/>
-                <button type="submit"
-                        className="btn btn-secondary"
-                        disabled={code === "" || code > 9999 || code < 1000}>
-                    <i className="fa fa-arrow-right"/>
-                </button>
-            </form> :
-            <button className="btn btn-secondary" onClick={() => setShowCodeInput(true)}>
-                Doe mee met ander spel
-            </button>}
+
+    return <div className="container h-100">
+        <div className="landing-page row h-100 align-content-center justify-content-center flex-md-column">
+            <button className="btn btn-primary mb-5" onClick={newGame}>Start een nieuw spel</button>
+            {showCodeInput ?
+                <form className="form-inline" onSubmit={(event) => {
+                    event.preventDefault();
+                    joinGame(code)
+                }}>
+                    <input type="number"
+                           className="form-control mx-2"
+                           placeholder="1234"
+                           onChange={handleCodeChange}
+                           value={code}
+                           autoFocus/>
+                    <button type="submit"
+                            className="btn btn-secondary"
+                            disabled={code === "" || code > 9999 || code < 1000}>
+                        <i className="fa fa-arrow-right"/>
+                    </button>
+                </form> :
+                <button className="btn btn-secondary" onClick={() => setShowCodeInput(true)}>
+                    Doe mee met ander spel
+                </button>}
+        </div>
     </div>;
 }

+ 53 - 22
frontend/keezen-frontend/src/Lobby.js

@@ -1,24 +1,44 @@
-import React from "react";
+import React, { Fragment, useRef } from "react";
 import "./css/lobby.css"
 import Colors from "./util/colors";
 import deck from './img/deck.svg';
 import { Commands } from "./StateRouter";
 
-export default function Lobby({ message, pickColor, deal }) {
-    const { color, options, state, game_code, others, name,...args } = message;
+export default function Lobby({ message, pickColor, deal, setName }) {
+    const { color, options, state, game_code, others, name, ...args } = message;
     const pickableColors = options.filter((o) => o.code === Commands.PICK_COLOR).map((o) => o.color);
-    const circle_args = { state, myColor: color, myName: name, others, pickableColors, pickColor };
+
+    const timeoutCode = useRef(null);
+
+    const scheduleSetName = (name) => {
+        if (timeoutCode.current !== null) {
+            window.clearTimeout(timeoutCode.current);
+        }
+        timeoutCode.current = window.setTimeout(() => {
+            timeoutCode.current = null;
+            setName(name);
+        }, 500);
+    }
+
+    const circle_args = { state, myColor: color, myName: name, setMyName: scheduleSetName, others, pickableColors, pickColor };
     const canDeal = options.find((o) => o.code === Commands.DEAL);
-    return <div className="mt-4">
-        <div className="row my-2">
-            <h1>Je kan meedoen door naar <a href={`/${game_code}`}>{window.location.host}/{game_code}</a> te gaan
-            </h1>
+
+    return <Fragment>
+        <div className="top-bar">
+            <span>Je kan meedoen door naar <a
+                href={`/${game_code}`}>{window.location.origin}/{game_code}</a> te gaan</span>
         </div>
-        <div className="row justify-content-center my-2">
-            <Circle color={Colors.RED} {...circle_args}/>
-            <Circle color={Colors.YELLOW} {...circle_args}/>
-            <Circle color={Colors.BLUE} {...circle_args}/>
-            <Circle color={Colors.GREEN} {...circle_args}/>
+        <div className="content">
+            <div className="container">
+                <div className="row justify-content-center my-2">
+                    <Pawn color={Colors.RED} {...circle_args}/>
+                    <Pawn color={Colors.GREEN} {...circle_args}/>
+                </div>
+                <div className="row justify-content-center my2">
+                    <Pawn color={Colors.BLUE} {...circle_args}/>
+                    <Pawn color={Colors.YELLOW} {...circle_args}/>
+                </div>
+            </div>
         </div>
         {
             canDeal &&
@@ -30,24 +50,35 @@ export default function Lobby({ message, pickColor, deal }) {
                 </div>
             </div>
         }
-    </div>
+    </Fragment>
 }
 
-function Circle({ color, myColor, myName, others, pickableColors, pickColor }) {
+function Pawn({ color, myColor, myName, setMyName, others, pickableColors, pickColor }) {
     const isMine = color === myColor;
     const playerInfo = isMine ?
-        {color, name: myName} :
+        { color, name: myName } :
         others.find((o) => o.color === color);
     const isSelectable = pickableColors.includes(color);
     const isPicked = !isSelectable;
-    const name = (playerInfo && playerInfo.name) || "";
-    return <div className="col-2 flex-column">
+    const name = (playerInfo && playerInfo.name) || "\u00a0";
+    return <div className="col col-lg-3 col-md-4">
         <div className={`color-pick 
                 ${color} ${isPicked ? "selected" : ""} 
                 ${isSelectable ? "selectable" : ""} 
                 ${isMine ? "mine" : ""}`}
-             onClick={() => isSelectable && pickColor(color)}
-        />
-        <h4>{name}</h4>
+             onClick={() => isSelectable && pickColor(color)}>
+        </div>
+        {isMine ?
+            <form className="color-pick-name">
+                <input className="form-control"
+                       type="text"
+                       defaultValue={myName}
+                       placeholder="Naam"
+                       onChange={(e) => {
+                           e.preventDefault();
+                           setMyName(e.target.value);
+                       }}/>
+            </form> :
+            <div className="color-pick-name">{name}</div>}
     </div>
-}
+}

+ 7 - 1
frontend/keezen-frontend/src/StateRouter.js

@@ -29,6 +29,7 @@ export const Commands = {
     NEW_GAME: "new_game",
     JOIN_GAME: "join_game",
     PICK_COLOR: "pick_color",
+    CHANGE_NAME: "change_name",
     DEAL: "deal",
     SWAP_CARD: "swap_card",
     PLAY_CARD: "play_card",
@@ -133,7 +134,12 @@ export default function StateRouter() {
                           deal={() => send({
                               code: Commands.DEAL,
                               text: "Deel", 
-                          })}/>
+                          })}
+            setName={(name) => send({
+                code: Commands.CHANGE_NAME,
+                text: "Verander naam",
+                user_name: name
+            })}/>
         case SiteState.SWAP_CARD:
         case SiteState.SWAP_CARD_OTHERS:
         case SiteState.PLAY_CARD:

+ 25 - 0
frontend/keezen-frontend/src/css/App.css

@@ -42,3 +42,28 @@
     animation: full-spin infinite 1500ms ease-out;
   }
 }
+
+.top-bar {
+  /*position: fixed;*/
+  top: 0;
+  width: 100%;
+  /*height: 100px;*/
+}
+
+.content {
+  /*position: fixed;*/
+  top: 100px;
+}
+
+.bottom-bar {
+  position: fixed;
+  bottom: 0;
+  width: 100%;
+  height: 120px;
+}
+
+.top-bar {
+  border-bottom: #1d2124 2px solid;
+  padding-top: 20px;
+  font-size: min(min(4.5vh, 2.5vw), 30pt);
+}

+ 1 - 2
frontend/keezen-frontend/src/css/Card.css

@@ -1,8 +1,7 @@
 .hand {
     display: flex;
-    position: fixed;
-    bottom: 0;
     height: 160px;
+    align-items: center;
 }
 
 .playing-card {

+ 3 - 1
frontend/keezen-frontend/src/css/SuitsFont.css

@@ -21,4 +21,6 @@
 .icon-clubs:before{content:'\0043';}
 .icon-diamonds:before{content:'\0044';}
 .icon-hearts:before{content:'\0048';}
-.icon-spades:before{content:'\0053';}
+.icon-pawn-filled:before{content:'\0050';}
+.icon-spades:before{content:'\0053';}
+.icon-pawn-outline:before{content:'\0070';}

+ 34 - 22
frontend/keezen-frontend/src/css/lobby.css

@@ -1,46 +1,58 @@
 .color-pick {
-    width: 50px;
-    height: 50px;
-    border-radius: 50%;
-    border: #1d2124 2px solid;
-    opacity: 0.5;
-    font-family: "Font Awesome 5 Free";
-    font-weight: 900;
-    font-size: 19pt;
-    padding: 3px;
+    font-family: "Suits";
+    font-size: min(20vh, 70pt);
+    padding: min(4vh, 20px);
     margin:auto;
+    line-height: 1;
+    opacity: 0.5;
 }
 
-.color-pick.mine {
-    opacity: 1;
-}
-
-.color-pick.mine:before,
-.color-pick.selected:before {
-    content: "\f00c";
+.color-pick:before {
+    content: "p";
 }
 
+.color-pick.mine,
 .color-pick.selectable:hover {
     opacity: 1;
 }
 
+.color-pick.mine:before,
+.color-pick.selected:before,
 .color-pick.selectable:hover:before {
-    content: "\f00c";
+    content: "P";
 }
 
 .color-pick.red {
-    background: #ff1d1d;
+    color: #ff1d1d;
 }
 
 .color-pick.blue {
-    background: #2b46e5;
+    color: #2b46e5;
 }
 
 .color-pick.yellow {
-    background: #ffff00;
+    color: #ffff00;
 }
 
 
 .color-pick.green {
-    background: #0ce20a;
-}
+    color: #0ce20a;
+}
+
+.color-pick-name {
+    font-size: min(5vh, 20pt);
+    text-align: center;
+    height: calc(1.5em + 0.75rem + 2px);
+}
+
+.color-pick-name input {
+    font-size: min(5vh, 20pt);
+    text-align: center;
+    border: none;
+    border-bottom: lightgray solid;
+    border-radius: 0;
+}
+
+.color-pick-name input:focus {
+    box-shadow: none;
+}

BIN
frontend/keezen-frontend/src/webfonts/Suits.eot


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
frontend/keezen-frontend/src/webfonts/Suits.svg


BIN
frontend/keezen-frontend/src/webfonts/Suits.ttf


BIN
frontend/keezen-frontend/src/webfonts/Suits.woff


Деякі файли не було показано, через те що забагато файлів було змінено