separated night order reference sheet into its own modal and hotkey (fixes #36)

This commit is contained in:
Steffen 2020-07-01 10:44:12 +02:00
parent 300066a551
commit 036f61fed0
No known key found for this signature in database
GPG Key ID: 764D74E98267DFC6
7 changed files with 909 additions and 1341 deletions

1725
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "townsquare",
"version": "1.2.1",
"version": "1.3.0",
"description": "Blood on the Clocktower Town Square",
"author": "Steffen Baumgart",
"scripts": {
@ -10,23 +10,23 @@
},
"main": "App.vue",
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.28",
"@fortawesome/free-brands-svg-icons": "^5.13.0",
"@fortawesome/free-solid-svg-icons": "^5.13.0",
"@fortawesome/vue-fontawesome": "^0.1.9",
"@vue/cli-service": "^4.3.1",
"sass": "^1.26.3",
"@fortawesome/fontawesome-svg-core": "^1.2.29",
"@fortawesome/free-brands-svg-icons": "^5.13.1",
"@fortawesome/free-solid-svg-icons": "^5.13.1",
"@fortawesome/vue-fontawesome": "^0.1.10",
"@vue/cli-service": "^4.4.6",
"sass": "^1.26.9",
"sass-loader": "^8.0.2",
"vue": "^2.3.3",
"vue-template-compiler": "^2.6.11",
"vuex": "^3.3.0",
"vuex": "^3.5.1",
"ws": "^7.3.0"
},
"devDependencies": {
"@vue/cli-plugin-eslint": "^4.3.1",
"@vue/cli-plugin-eslint": "^4.4.6",
"@vue/eslint-config-prettier": "^6.0.0",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-vue": "^6.2.2",
"prettier": "^1.19.1"
},

View File

@ -20,6 +20,7 @@
<EditionModal />
<RolesModal />
<ReferenceModal />
<NightOrderModal />
<Gradients />
<span id="version">v{{ version }}</span>
</div>
@ -37,9 +38,11 @@ import Intro from "./components/Intro";
import ReferenceModal from "./components/modals/ReferenceModal";
import Vote from "./components/Vote";
import Gradients from "./components/Gradients";
import NightOrderModal from "./components/modals/NightOrderModal";
export default {
components: {
NightOrderModal,
Vote,
ReferenceModal,
Intro,
@ -81,6 +84,9 @@ export default {
case "r":
this.$store.commit("toggleModal", "reference");
break;
case "n":
this.$store.commit("toggleModal", "nightOrder");
break;
case "e":
if (this.session.isSpectator) return;
this.$store.commit("toggleModal", "edition");

View File

@ -138,6 +138,10 @@
Reference Sheet
<em>[R]</em>
</li>
<li @click="toggleModal('nightOrder')">
Night Order Sheet
<em>[N]</em>
</li>
<li>
<a href="https://discord.gg/tkWDny6" target="_blank">
Join Discord

View File

@ -0,0 +1,287 @@
<template>
<Modal
class="characters"
v-show="modals.nightOrder"
@close="toggleModal('nightOrder')"
v-if="roles.size"
>
<font-awesome-icon
@click="toggleModal('reference')"
icon="address-card"
class="toggle"
title="Show Character Reference"
/>
<h3>
Night Order
<font-awesome-icon icon="cloud-moon" />
{{ editionName }}
</h3>
<div class="night">
<ul class="first">
<li class="headline">First Night</li>
<li v-for="role in rolesFirstNight" :key="role.id" :class="[role.team]">
<span class="name">
{{ role.name }}
</span>
<span
class="icon"
v-if="role.id"
v-bind:style="{
backgroundImage: `url(${role.image ||
require('../../assets/icons/' + role.id + '.png')})`
}"
></span>
</li>
</ul>
<ul class="other">
<li class="headline">Other Nights</li>
<li v-for="role in rolesOtherNight" :key="role.id" :class="[role.team]">
<span
class="icon"
v-if="role.id"
v-bind:style="{
backgroundImage: `url(${role.image ||
require('../../assets/icons/' + role.id + '.png')})`
}"
></span>
<span class="name">
{{ role.name }}
</span>
</li>
</ul>
</div>
</Modal>
</template>
<script>
import Modal from "./Modal";
import editionJSON from "./../../editions.json";
import { mapMutations, mapState } from "vuex";
export default {
components: {
Modal
},
data: function() {
return {
roleSelection: {}
};
},
computed: {
editionName: function() {
const edition = editionJSON.find(({ id }) => id === this.edition);
return edition ? edition.name : "Custom Script";
},
rolesFirstNight: function() {
const rolesFirstNight = [];
this.roles.forEach(role => {
if (role.firstNight) {
rolesFirstNight.push(role);
}
});
rolesFirstNight.sort((a, b) => a.firstNight - b.firstNight);
return rolesFirstNight;
},
rolesOtherNight: function() {
const rolesOtherNight = [];
this.roles.forEach(role => {
if (role.otherNight) {
rolesOtherNight.push(role);
}
});
rolesOtherNight.sort((a, b) => a.otherNight - b.otherNight);
return rolesOtherNight;
},
...mapState(["roles", "modals", "edition"]),
...mapState("players", ["players"])
},
methods: {
...mapMutations(["toggleModal"])
}
};
</script>
<style lang="scss" scoped>
@import "../../vars.scss";
.toggle {
position: absolute;
left: 20px;
top: 20px;
cursor: pointer;
&:hover {
color: red;
}
}
h3 {
margin: 0 40px;
svg {
vertical-align: middle;
}
}
h4 {
text-transform: capitalize;
display: flex;
align-items: center;
height: 20px;
&:before,
&:after {
content: " ";
width: 100%;
height: 1px;
border-radius: 2px;
}
&:before {
margin-right: 15px;
}
&:after {
margin-left: 15px;
}
}
.townsfolk {
.name,
.player,
h4 {
color: $townsfolk;
&:before,
&:after {
background-color: $townsfolk;
}
}
}
.outsider {
.name,
.player,
h4 {
color: $outsider;
&:before,
&:after {
background-color: $outsider;
}
}
}
.minion {
.name,
.player,
h4 {
color: $minion;
&:before,
&:after {
background-color: $minion;
}
}
}
.demon {
.name,
.player,
h4 {
color: $demon;
&:before,
&:after {
background-color: $demon;
}
}
}
ul {
li {
display: flex;
width: 100%;
align-items: center;
align-content: center;
/*background: linear-gradient(0deg, #ffffff0f, transparent);*/
border-radius: 10px;
.icon {
width: 6vh;
background-size: cover;
background-position: 0 -5px;
flex-grow: 0;
flex-shrink: 0;
margin: 0 10px;
text-align: center;
border-left: 1px solid #ffffff1f;
border-right: 1px solid #ffffff1f;
&:after {
content: " ";
display: block;
padding-top: 66%;
}
}
.name {
flex-grow: 0;
flex-shrink: 0;
width: 15%;
font-weight: bold;
text-align: right;
font-family: "Papyrus", sans-serif;
font-size: 110%;
}
.player {
flex-grow: 0;
flex-shrink: 1;
text-align: right;
margin: 0 10px;
}
.ability {
flex-grow: 1;
}
}
&.legend {
font-weight: bold;
height: 20px;
margin-top: 10px;
li span {
background: none;
height: auto;
font-family: inherit;
font-size: inherit;
}
.icon:after {
padding-top: 0;
}
}
}
.night {
display: flex;
align-items: flex-start;
justify-content: center;
> *:first-child {
margin-right: 2vh;
}
> * {
flex-grow: 0;
flex-wrap: nowrap;
flex-direction: column;
}
.headline {
display: block;
font-weight: bold;
border-bottom: 1px solid white;
padding: 5px 10px;
border-radius: 0;
text-align: center;
}
.icon {
border-color: white;
}
.name {
flex-grow: 1;
}
.first {
.icon {
border-right: 0;
}
}
.other {
li .name {
text-align: left;
}
.icon {
border-left: 0;
}
}
}
</style>

View File

@ -5,110 +5,47 @@
@close="toggleModal('reference')"
v-if="roles.size"
>
<template v-if="!isNightOrder">
<font-awesome-icon
@click="isNightOrder = !isNightOrder"
icon="cloud-moon"
class="toggle"
title="Show Night Order"
/>
<h3>
Character Reference
<font-awesome-icon icon="address-card" />
{{ editionName }}
</h3>
<ul class="legend">
<li>
<span class="name">Name</span>
<span class="icon">Icon</span>
<span class="ability">Ability</span>
<span class="player" v-if="Object.keys(playersByRole).length">
Player
</span>
<font-awesome-icon
@click="toggleModal('nightOrder')"
icon="cloud-moon"
class="toggle"
title="Show Night Order"
/>
<h3>
Character Reference
<font-awesome-icon icon="address-card" />
{{ editionName }}
</h3>
<ul class="legend">
<li>
<span class="name">Name</span>
<span class="icon">Icon</span>
<span class="ability">Ability</span>
<span class="player" v-if="Object.keys(playersByRole).length">
Player
</span>
</li>
</ul>
<div v-for="(teamRoles, team) in rolesGrouped" :key="team" :class="[team]">
<h4>{{ team }}</h4>
<ul>
<li v-for="role in teamRoles" :class="[team]" :key="role.id">
<span class="name">{{ role.name }}</span>
<span
class="icon"
v-if="role.id"
v-bind:style="{
backgroundImage: `url(${role.image ||
require('../../assets/icons/' + role.id + '.png')})`
}"
></span>
<span class="ability">{{ role.ability }}</span>
<span class="player" v-if="Object.keys(playersByRole).length">{{
playersByRole[role.id] ? playersByRole[role.id].join(", ") : ""
}}</span>
</li>
</ul>
<div
v-for="(teamRoles, team) in rolesGrouped"
:key="team"
:class="[team]"
>
<h4>{{ team }}</h4>
<ul>
<li v-for="role in teamRoles" :class="[team]" :key="role.id">
<span class="name">{{ role.name }}</span>
<span
class="icon"
v-if="role.id"
v-bind:style="{
backgroundImage: `url(${role.image ||
require('../../assets/icons/' + role.id + '.png')})`
}"
></span>
<span class="ability">{{ role.ability }}</span>
<span class="player" v-if="Object.keys(playersByRole).length">{{
playersByRole[role.id] ? playersByRole[role.id].join(", ") : ""
}}</span>
</li>
</ul>
</div>
</template>
<template v-else>
<font-awesome-icon
@click="isNightOrder = !isNightOrder"
icon="address-card"
class="toggle"
title="Show Character Reference"
/>
<h3>
Night Order
<font-awesome-icon icon="cloud-moon" />
{{ editionName }}
</h3>
<div class="night">
<ul class="first">
<li class="headline">First Night</li>
<li
v-for="role in rolesFirstNight"
:key="role.id"
:class="[role.team]"
>
<span class="name">
{{ role.name }}
</span>
<span
class="icon"
v-if="role.id"
v-bind:style="{
backgroundImage: `url(${require('../../assets/icons/' +
role.id +
'.png')})`
}"
></span>
</li>
</ul>
<ul class="other">
<li class="headline">Other Nights</li>
<li
v-for="role in rolesOtherNight"
:key="role.id"
:class="[role.team]"
>
<span
class="icon"
v-if="role.id"
v-bind:style="{
backgroundImage: `url(${require('../../assets/icons/' +
role.id +
'.png')})`
}"
></span>
<span class="name">
{{ role.name }}
</span>
</li>
</ul>
</div>
</template>
</div>
</Modal>
</template>
@ -123,8 +60,7 @@ export default {
},
data: function() {
return {
roleSelection: {},
isNightOrder: false
roleSelection: {}
};
},
computed: {
@ -155,26 +91,6 @@ export default {
});
return players;
},
rolesFirstNight: function() {
const rolesFirstNight = [];
this.roles.forEach(role => {
if (role.firstNight) {
rolesFirstNight.push(role);
}
});
rolesFirstNight.sort((a, b) => a.firstNight - b.firstNight);
return rolesFirstNight;
},
rolesOtherNight: function() {
const rolesOtherNight = [];
this.roles.forEach(role => {
if (role.otherNight) {
rolesOtherNight.push(role);
}
});
rolesOtherNight.sort((a, b) => a.otherNight - b.otherNight);
return rolesOtherNight;
},
...mapState(["roles", "modals", "edition"]),
...mapState("players", ["players"])
},
@ -326,45 +242,4 @@ ul {
}
}
}
.night {
display: flex;
align-items: flex-start;
justify-content: center;
> *:first-child {
margin-right: 2vh;
}
> * {
flex-grow: 0;
flex-wrap: nowrap;
flex-direction: column;
}
.headline {
display: block;
font-weight: bold;
border-bottom: 1px solid white;
padding: 5px 10px;
border-radius: 0;
text-align: center;
}
.icon {
border-color: white;
}
.name {
flex-grow: 1;
}
.first {
.icon {
border-right: 0;
}
}
.other {
li .name {
text-align: left;
}
.icon {
border-left: 0;
}
}
}
</style>

View File

@ -45,7 +45,8 @@ export default new Vuex.Store({
edition: false,
roles: false,
role: false,
reminder: false
reminder: false,
nightOrder: false
},
edition: "tb",
roles: getRolesByEdition()