From 85e5b5c1e6192a134fa69182e8c077605fdb969f Mon Sep 17 00:00:00 2001 From: Benjamin Auder <benjamin.auder@somewhere> Date: Thu, 31 Jan 2019 19:32:09 +0100 Subject: [PATCH] Styled layout. TODO: variants page, and then index --- client/next_src/components/challengeList.js | 33 ----- client/next_src/components/gameList.js | 29 ---- client/public/index.html | 6 + client/src/App.vue | 145 ++++++++++++++------ client/src/components/ChallengeList.vue | 36 +++++ client/src/components/GameList.vue | 29 ++++ client/src/data/nbPlayers.js | 4 +- client/src/main.js | 1 + client/src/stylesheets/layout.sass | 20 --- client/src/translations/fr.js | 8 +- client/src/views/Home.vue | 108 ++++++++------- client/src/views/Variants.vue | 58 ++++---- 12 files changed, 269 insertions(+), 208 deletions(-) delete mode 100644 client/next_src/components/challengeList.js delete mode 100644 client/next_src/components/gameList.js create mode 100644 client/src/components/ChallengeList.vue create mode 100644 client/src/components/GameList.vue diff --git a/client/next_src/components/challengeList.js b/client/next_src/components/challengeList.js deleted file mode 100644 index 2c997b7e..00000000 --- a/client/next_src/components/challengeList.js +++ /dev/null @@ -1,33 +0,0 @@ -Vue.component("my-challenge-list", { - props: ["challenges"], - computed: { - showVariant: function() { - this.challenges.length > 0 && !!this.challenges[0].variant; - }, - showNbPlayers: function() { - this.challenges.length > 0 && !!this.challenges[0].nbPlayers; - }, - }, - template: ` - <table> - <tr> - <th v-if="showVariant">Variant</th> - <th>From</th> - <th>To</th> - <th>Cadence</th> - <th v-if="showNbPlayers">Number of players</th> - </tr> - <tr v-for="c in challenges" @click="$emit('click-challenge',c)"> - <td v-if="showVariant">{{ c.variant }}</td> - <td>{{ c.from.name }}</td> - <td> - <span v-for="p in c.to">{{ p.name }}</span> - </td> - <td>{{ c.mainTime }} + {{ c.increment }}</td> - <td v-if="showNbPlayers">{{ c.nbPlayers }}</td> - </tr> - </table> - `, -}); - -// TODO: challenge format from/to ou uid/players ............ diff --git a/client/next_src/components/gameList.js b/client/next_src/components/gameList.js deleted file mode 100644 index 9b669826..00000000 --- a/client/next_src/components/gameList.js +++ /dev/null @@ -1,29 +0,0 @@ -Vue.component("my-game-list", { - props: ["games"], - computed: { - showVariant: function() { - return this.games.length > 0 && !!this.games[0].vname; - }, - showResult: function() { - return this.games.length > 0 && this.games[0].score != "*"; - }, - }, - template: ` - <table> - <tr> - <th v-if="showVariant">Variant</th> - <th>Players names</th> - <th>Cadence</th> - <th v-if="showResult">Result</th> - </tr> - <tr v-for="g in games" @click="$emit('show-game',g)"> - <td v-if="showVariant">{{ g.vname }}</td> - <td> - <span v-for="p in g.players">{{ p.name }}</span> - </td> - <td>{{ g.mainTime }} + {{ g.increment }}</td> - <td v-if="showResult">{{ g.score }}</td> - </tr> - </table> - `, -}); diff --git a/client/public/index.html b/client/public/index.html index 6549d7a7..af506cdf 100644 --- a/client/public/index.html +++ b/client/public/index.html @@ -10,6 +10,12 @@ href="//cdnjs.cloudflare.com/ajax/libs/mini.css/3.0.0/mini-default.min.css"> <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Open+Sans:400,700"> + <style> + body { + --fore-color: #2c3e50; + --back-color: #f2f2f2; + } + </style> </head> <body> <div id="app"></div> diff --git a/client/src/App.vue b/client/src/App.vue index 2b131276..dd45b343 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -8,48 +8,46 @@ UpsertUser .container .row(v-show="$route.path == '/'") - // Header (on index only ?!) - header - .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 + .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 + // Header (on index only) + header img(src="./assets/images/index/unicorn.svg") .info-container p vchess.club img(src="./assets/images/index/wildebeest.svg") .row - // Menu (top of page): - // shared: Home + flags, userMenu - // variant: hall, problems, rules, my games + settings - nav - label.drawer-toggle(for="drawerControl") - input#drawerControl.drawer(type="checkbox") - #menuBar - label.drawer-close(for="drawerControl") - router-link(to="/") - // select options all variants + filter possible (as in problems) - | Home - router-link(to="/myGames") - | {{ st.tr["My games"] }} - router-link(to="/rules") - // Boxes OK for rules/Atomic/ ...etc - | {{ st.tr["Rules"] }} - router-link(to="/problems") - | {{ st.tr["Problems"] }} - #userMenu.clickable.right-menu(onClick="doClick('modalUser')") - .info-container - p - span {{ !st.user.id ? "Login" : "Update" }} - span.icon-user - #flagMenu.clickable.right-menu(onClick="doClick('modalLang')") - img(src="/images/flags/" + lang + ".svg") - #settings.clickable(onClick="doClick('modalSettings')") - | Settings - i(data-feather="settings") + .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 + // Menu (top of page): + // Left: home, variants, mygames, problems + // Right: usermenu, settings, flag + nav + label.drawer-toggle(for="drawerControl") + input#drawerControl.drawer(type="checkbox") + #menuBar + label.drawer-close(for="drawerControl") + #leftMenu + router-link(to="/") + | {{ st.tr["Home"] }} + router-link(to="/variants") + | {{ st.tr["Variants"] }} + router-link(to="/mygames") + | {{ st.tr["My games"] }} + router-link(to="/problems") + | {{ st.tr["Problems"] }} + #rightMenu + .clickable(onClick="doClick('modalUser')") + | {{ !st.user.id ? "Login" : "Update" }} + .clickable(onClick="doClick('modalSettings')") + | {{ st.tr["Settings"] }} + .clickable(onClick="doClick('modalLang')") + img(v-if="!!st.lang" + :src="require(`@/assets/images/flags/${st.lang}.svg`)") .row router-view .row - footer - .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2.text-center - a(href="https://github.com/yagu0/vchess") Source code + .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 + footer + a(href="https://github.com/yagu0/vchess") {{ st.tr["Source code"] }} p.clickable(onClick="doClick('modalContact')") | {{ st.tr["Contact form"] }} //my-game(:game-ref="gameRef" :mode="mode" :settings="settings" @game-over="archiveGame") @@ -84,14 +82,75 @@ export default { font-family: "Avenir", Helvetica, Arial, sans-serif -webkit-font-smoothing: antialiased -moz-osx-font-smoothing: grayscale - text-align: center - color: #2c3e50 -#nav - padding: 30px - a - font-weight: bold - color: #2c3e50 - &.router-link-exact-active - color: #42b983 +.container + @media screen and (max-width: 767px) + padding: 0 + +header + width: 100% + display: flex + align-items: center + justify-content: center + margin: 0 auto + & > img + width: 30px + height: 30px + +.clickable + cursor: pointer + +nav + width: 100% + padding: 0 + & > #menuBar + width: 100% + padding: 0 + & > #leftMenu + padding: 0 + width: 50% + display: inline-flex + align-items: center + justify-content: flex-start + & > a + display: inline-block + color: #2c3e50 + &.router-link-exact-active + color: #42b983 + & > #rightMenu + padding: 0 + width: 50% + display: inline-flex + align-items: center + justify-content: flex-end + & > div + display: inline-block + & > img + padding: 0 + width: 30px + height: 30px + +// TODO: drawer, until 600px wide OK (seemingly) +// After, zone where left and right just go on top of another +// Then, on narrow screen put everything on one line +[type="checkbox"].drawer+* + right: -767px + +footer + //background-color: #000033 + font-size: 1rem + width: 100% + display: inline-flex + align-items: center + justify-content: center + & > a + display: inline-block + margin: 0 10px 0 0 + &:link + color: #2c3e50 + &:hover + text-decoration: none + & > p + display: inline-block + margin: 0 0 0 10px </style> diff --git a/client/src/components/ChallengeList.vue b/client/src/components/ChallengeList.vue new file mode 100644 index 00000000..a52e0cae --- /dev/null +++ b/client/src/components/ChallengeList.vue @@ -0,0 +1,36 @@ +<template lang="pug"> +table + tr + th(v-if="showVariant") Variant + th From + th To + th Cadence + th(v-if="showNbPlayers") Number of players + tr(v-for="c in challenges" @click="$emit('click-challenge',c)") + td(v-if="showVariant") {{ c.variant }} + td {{ c.from.name }} + td + span(v-for="p in c.to") {{ p.name }} + td {{ c.mainTime }} + {{ c.increment }} + td(v-if="showNbPlayers") {{ c.nbPlayers }} +</template> + +<script> +export default { + name: "my-challenge-list", + props: ["challenges"], + computed: { + showVariant: function() { + this.challenges.length > 0 && !!this.challenges[0].variant; + }, + showNbPlayers: function() { + this.challenges.length > 0 && !!this.challenges[0].nbPlayers; + }, + }, +}; +// TODO: challenge format from/to ou uid/players ............ +</script> + +<style lang="sass"> +// TODO: affichage bizarre sur petits écrans <=767px +</style> diff --git a/client/src/components/GameList.vue b/client/src/components/GameList.vue new file mode 100644 index 00000000..c7f661ca --- /dev/null +++ b/client/src/components/GameList.vue @@ -0,0 +1,29 @@ +<template lang="pug"> +table + tr + th(v-if="showVariant") Variant + th Players names + th Cadence + th(v-if="showResult") Result + tr(v-for="g in games" @click="$emit('show-game',g)") + td(v-if="showVariant") {{ g.vname }} + td + span(v-for="p in g.players") {{ p.name }} + td {{ g.mainTime }} + {{ g.increment }} + td(v-if="showResult") {{ g.score }} +</template> + +<script> +export default { + name: "my-game-list", + props: ["games"], + computed: { + showVariant: function() { + return this.games.length > 0 && !!this.games[0].vname; + }, + showResult: function() { + return this.games.length > 0 && this.games[0].score != "*"; + }, + }, +}; +</script> diff --git a/client/src/data/nbPlayers.js b/client/src/data/nbPlayers.js index 8c0cc864..2f58d168 100644 --- a/client/src/data/nbPlayers.js +++ b/client/src/data/nbPlayers.js @@ -1,4 +1,4 @@ -const NbPlayers = +export const NbPlayers = { "Alice": [2,3,4], "Antiking": [2,3,4], @@ -19,5 +19,3 @@ const NbPlayers = "Wildebeest": [2], "Zen": [2,3,4], }; - -try { module.exports = NbPlayers; } catch (e) { } //for server diff --git a/client/src/main.js b/client/src/main.js index 97bad754..703ff742 100644 --- a/client/src/main.js +++ b/client/src/main.js @@ -38,6 +38,7 @@ new Vue({ // mounted: function() { // feather.replace(); // }, + // "mounted" and not "created", because modalWelcome must be filled mounted: function() { store.initialize(); }, diff --git a/client/src/stylesheets/layout.sass b/client/src/stylesheets/layout.sass index 5639c7b2..b7446750 100644 --- a/client/src/stylesheets/layout.sass +++ b/client/src/stylesheets/layout.sass @@ -55,26 +55,6 @@ a.right-menu color: blue display: none -footer - height: 77px - background-color: #000033 - div - line-height: 77px - a - margin: 0 10px 0 0 - display: inline-block - &:visited, &:link - color: white - p - margin: 0 0 0 10px - display: inline-block - color: white - text-decoration: underline - @media screen and (max-width: 767px) - height: 43px - div - line-height: 43px - a text-decoration: underline diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index ee65449d..4d242c2c 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -1,5 +1,12 @@ export const translations = { + "Home": "Accueil", + "Variants": "Variantes", + "My games": "Mes parties", + "Problems": "Problèmes", + "Contact form": "Formulaire de contact", + "Source code": "Code source", + "Language": "Langue", // Index page: @@ -31,7 +38,6 @@ export const translations = "Waiting for opponent...": "En attente d'un adversaire...", "Rules": "Règles", "Play": "Jouer", - "Problems": "Problèmes", "White win": "Les blancs gagnent", "Black win": "Les noirs gagnent", "Draw": "Match nul", diff --git a/client/src/views/Home.vue b/client/src/views/Home.vue index c2c5b442..f0f6a5c7 100644 --- a/client/src/views/Home.vue +++ b/client/src/views/Home.vue @@ -7,7 +7,7 @@ div fieldset label(for="selectVariant") {{ st.tr["Variant"] }} select#selectVariant(v-model="newgameInfo.vid") - option(v-for="v in variants" :value="v.id") {{ v.name }} + option(v-for="v in st.variants" :value="v.id") {{ v.name }} fieldset label(for="selectNbPlayers") {{ st.tr["Number of players"] }} select#selectNbPlayers(v-model="newgameInfo.nbPlayers") @@ -31,14 +31,14 @@ div v-model="newgameInfo.players[2].name") fieldset label(for="inputFen") - {{ st.tr["FEN (ignored if players fields are blank)"] }} + | {{ st.tr["FEN (ignored if players fields are blank)"] }} input#inputFen(type="text" v-model="newgameInfo.fen") button(@click="newGame") Launch game p TODO: cadence, adversaire (pre-filled if click on name) p cadence 2m+12s ou 7d+1d (m,s ou d,d) --> main, increment p Note: leave FEN blank for random; FEN only for targeted challenge div - my-challenge-list(:challenges="challenges" @click-challenge="clickChallenge") + ChallengeList(:challenges="challenges" @click-challenge="clickChallenge") div(style="border:1px solid black") h3 Online players div(v-for="p in players" @click="challenge(p)") {{ p.name }} @@ -47,9 +47,9 @@ div .button-group button(@click="gdisplay='live'") Live games button(@click="gdisplay='corr'") Correspondance games - my-game-list(v-show="gdisplay=='live'" :games="liveGames" + GameList(v-show="gdisplay=='live'" :games="liveGames" @show-game="showGame") - my-game-list(v-show="gdisplay=='corr'" :games="corrGames" + GameList(v-show="gdisplay=='corr'" :games="corrGames" @show-game="showGame") </template> @@ -65,6 +65,7 @@ fin de partie corr: supprimer partie du serveur au bout de 7 jours (arbitraire) // TODO: au moins l'échange des coups en P2P ? et game chat ? // TODO: objet game, objet challenge ? et player ? import { store } from "@/store"; +import { NbPlayers } from "@/data/nbPlayers"; import GameList from "@/components/GameList.vue"; import ChallengeList from "@/components/ChallengeList.vue"; export default { @@ -92,46 +93,48 @@ export default { }, }; }, - created: function() { - // TODO: ask server for current corr games (all but mines: names, ID, time control) - const socketMessageListener = msg => { - const data = JSON.parse(msg.data); - switch (data.code) - { - case "newgame": - // TODO: new game just started: data contain all informations - // (id, players, time control, fenStart ...) - break; - // TODO: also receive live games summaries (update) - // (just players names, time control, and ID + player ID) - case "acceptchallenge": - // oppid: opponent socket ID (or DB id if registered) - if (true) //TODO: if challenge is full - this.newGame(data.challenge, data.user); //user.id et user.name - break; - case "withdrawchallenge": - // TODO - break; - case "cancelchallenge": - // TODO - break; - // TODO: distinguish these (dis)connect events from their analogs in game.js - case "connect": - this.players.push({name:data.name, id:data.uid}); - break; - case "disconnect": - const pIdx = this.players.findIndex(p => p.id == data.uid); - this.players.splice(pIdx); - break; - } - }; - const socketCloseListener = () => { - this.st.conn.addEventListener('message', socketMessageListener); - this.st.conn.addEventListener('close', socketCloseListener); - }; - this.st.conn.onmessage = socketMessageListener; - this.st.conn.onclose = socketCloseListener; - }, + watch: { + "st.conn": function() { + // TODO: ask server for current corr games (all but mines: names, ID, time control) + const socketMessageListener = msg => { + const data = JSON.parse(msg.data); + switch (data.code) + { + case "newgame": + // TODO: new game just started: data contain all informations + // (id, players, time control, fenStart ...) + break; + // TODO: also receive live games summaries (update) + // (just players names, time control, and ID + player ID) + case "acceptchallenge": + // oppid: opponent socket ID (or DB id if registered) + if (true) //TODO: if challenge is full + this.newGame(data.challenge, data.user); //user.id et user.name + break; + case "withdrawchallenge": + // TODO + break; + case "cancelchallenge": + // TODO + break; + // TODO: distinguish these (dis)connect events from their analogs in game.js + case "connect": + this.players.push({name:data.name, id:data.uid}); + break; + case "disconnect": + const pIdx = this.players.findIndex(p => p.id == data.uid); + this.players.splice(pIdx); + break; + } + }; + const socketCloseListener = () => { + this.st.conn.addEventListener('message', socketMessageListener); + this.st.conn.addEventListener('close', socketCloseListener); + }; + this.st.conn.onmessage = socketMessageListener; + this.st.conn.onclose = socketCloseListener; + }, + }, methods: { showGame: function(game) { // NOTE: if we are an observer, the game will be found in main games list @@ -207,8 +210,8 @@ export default { uid: user.id, added: Date.now(), vname: vname, - }, - this.challenges.push(response.challenge); + }); + this.challenges.push(chall); } ); // TODO: else, if live game: send infos (socket), and... @@ -233,10 +236,15 @@ export default { possibleNbplayers: function(nbp) { if (this.newgameInfo.vid == 0) return false; + const variants = this.st.variants; const idxInVariants = - variantArray.findIndex(v => v.id == this.newgameInfo.vid); - return NbPlayers[variantArray[idxInVariants].name].includes(nbp); + variants.findIndex(v => v.id == this.newgameInfo.vid); + return NbPlayers[variants[idxInVariants].name].includes(nbp); }, }, -}); +}; </script> + +<style lang="sass"> +// TODO +</style> diff --git a/client/src/views/Variants.vue b/client/src/views/Variants.vue index 03903572..f5214296 100644 --- a/client/src/views/Variants.vue +++ b/client/src/views/Variants.vue @@ -1,15 +1,15 @@ <template lang="pug"> div - .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 - label(for="prefixFilter") Type first letters... - input#prefixFilter(v-model="curPrefix") - .variant.col-sm-12.col-md-5.col-lg-4( - v-for="(v,idx) in filteredVariants" - :class="{'col-md-offset-1': idx%2==0, 'col-lg-offset-2': idx%2==0}" - ) - router-link(:to="getLink(v.name)") - h4.boxtitle.text-center {{ v.name }} - p.description.text-center {{ st.tr(v.desc) }} + .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 + label(for="prefixFilter") Type first letters... + input#prefixFilter(v-model="curPrefix") + .variant.col-sm-12.col-md-5.col-lg-4( + v-for="(v,idx) in filteredVariants" + :class="{'col-md-offset-1': idx%2==0, 'col-lg-offset-2': idx%2==0}" + ) + router-link(:to="getLink(v.name)") + h4.boxtitle.text-center {{ v.name }} + p.description.text-center {{ st.tr[v.desc] }} </template> <script> @@ -18,29 +18,29 @@ export default { name: "variants", data: function() { return { - curPrefix: "", + curPrefix: "", st: store.state, }; - }, - computed: { - filteredVariants: function () { - const capitalizedPrefix = this.curPrefix.replace(/^\w/, c => c.toUpperCase()); - const variants = this.st.variants - .filter( v => { - return v.name.startsWith(capitalizedPrefix); - }) - .map( v => { - return { - name: v.name, - desc: v.description, - }; - }) + }, + computed: { + filteredVariants: function () { + const capitalizedPrefix = this.curPrefix.replace(/^\w/, c => c.toUpperCase()); + const variants = this.st.variants + .filter( v => { + return v.name.startsWith(capitalizedPrefix); + }) + .map( v => { + return { + name: v.name, + desc: v.description, + }; + }) .sort((a,b) => { - return a.name.localeCompare(b.name); - }); + return a.name.localeCompare(b.name); + }); return variants; - }, - }, + }, + }, methods: { getLink: function(vname) { return "/variants/" + vname; -- 2.44.0