Poprawki błędów (1.3.5)

This commit is contained in:
2020-12-29 01:48:10 +01:00
parent ff17b791f0
commit 709eacc980
33 changed files with 7037 additions and 7039 deletions
+235 -235
View File
@@ -1,235 +1,235 @@
<template>
<div class="train-schedule" @click="click">
<div class="schedule-wrapper">
<div class="schedule-bar"></div>
<ul class="schedule-list">
<li
class="schedule-item"
v-for="(stop, i) in followingStops"
:key="i"
:class="{ confirmed: stop.confirmed, stopped: stop.stopped }"
>
<div class="progress-bar"></div>
<div
class="stop-line arrival"
v-if="
i > 0 && followingStops[i - 1].departureLine != stop.arrivalLine
"
>{{ stop.arrivalLine }}</div>
<span class="stop-info">
<div class="info-indicator"></div>
<span class="info-name" v-html="stop.stopName"></span>
<span class="info-date">
<span
class="date-arrival"
v-if="!stop.beginsHere"
:class="{
delayed: stop.arrivalDelay > 0,
preponed: stop.arrivalDelay < 0,
}"
>
p.
{{
stylizeTime(stop.arrivalRealTimeString, stop.arrivalDelay)
}}
</span>
<span
class="date-stop"
v-if="stop.stopTime"
:class="stop.stopType.replace(', ', '-')"
>{{ stop.stopTime }} {{ stop.stopType }}</span>
<span
class="date-departure"
v-if="!stop.terminatesHere && stop.stopTime != 0"
:class="{
delayed: stop.departureDelay > 0,
preponed: stop.departureDelay < 0,
}"
>
o.
{{
stylizeTime(stop.departureRealTimeString, stop.departureDelay)
}}
</span>
</span>
</span>
<div class="stop-line departure">{{ stop.departureLine }}</div>
</li>
</ul>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator";
import TrainStop from "@/scripts/interfaces/TrainStop";
@Component
export default class TrainSchedule extends Vue {
@Prop() readonly followingStops!: TrainStop[];
@Prop() readonly currentStationName!: string;
stylizeTime(timeString: string, delay: number) {
return (
timeString +
(delay != 0 ? " (" + (delay > 0 ? "+" : "") + delay.toString() + ")" : "")
);
}
click() {
this.$emit("click");
}
}
</script>
<style lang="scss" scoped>
@import "../../styles/responsive.scss";
.train-schedule {
max-height: 600px;
overflow: auto;
margin-top: 1rem;
font-size: 1em;
@include smallScreen() {
font-size: 0.8em;
}
}
.schedule-bar,
.progress-bar {
position: absolute;
z-index: 1;
width: 2px;
height: 100%;
top: 0;
left: 0;
background: white;
}
.progress-bar {
height: 100%;
top: 0;
left: calc(-0.5rem - 2px);
width: 2px;
}
.schedule-wrapper {
position: relative;
margin-top: 1rem;
margin-left: 0.5rem;
}
ul.schedule-list {
margin-left: 10px;
}
ul.schedule-list > li.schedule-item {
position: relative;
display: flex;
flex-direction: column;
padding: 0 0.5rem;
&.confirmed {
& > .progress-bar,
& > .stop-info > .info-indicator {
background: lime;
}
}
&.stopped {
& > .progress-bar {
background: lime;
height: 50%;
}
& > .stop-info > .info-indicator {
background: orangered;
}
}
}
li.schedule-item > .stop-info {
display: flex;
position: relative;
& > .info-indicator {
position: absolute;
z-index: 1;
top: 50%;
left: -1.5rem;
transform: translateY(-50%);
width: 15px;
height: 2px;
background: white;
}
& > .info-name {
background: rgb(0, 81, 187);
padding: 0.3rem 0.5rem;
}
& > .info-date {
display: flex;
align-items: center;
& > span {
background: #5c5c5c;
padding: 0.3rem 0.5rem;
}
& > .date-stop {
&.ph,
&.ph-pm {
background: #ce8d00;
}
&.pt,
&.pm,
&.pt-pm {
background: #252525;
}
}
& > .date-arrival,
& > .date-departure {
&.delayed {
background: rgb(250, 0, 0);
}
&.preponed {
background: rgb(0, 139, 0);
}
}
}
}
li.schedule-item > .stop-line {
font-size: 0.8em;
color: #bbb;
padding: 0.3em 0;
margin: 0.2em 0;
transform: translateX(-0.8rem);
}
</style>
<template>
<div class="train-schedule" @click="click">
<div class="schedule-wrapper">
<div class="schedule-bar"></div>
<ul class="schedule-list">
<li
class="schedule-item"
v-for="(stop, i) in followingStops"
:key="i"
:class="{ confirmed: stop.confirmed, stopped: stop.stopped }"
>
<div class="progress-bar"></div>
<div
class="stop-line arrival"
v-if="
i > 0 && followingStops[i - 1].departureLine != stop.arrivalLine
"
>{{ stop.arrivalLine }}</div>
<span class="stop-info">
<div class="info-indicator"></div>
<span class="info-name" v-html="stop.stopName"></span>
<span class="info-date">
<span
class="date-arrival"
v-if="!stop.beginsHere"
:class="{
delayed: stop.arrivalDelay > 0,
preponed: stop.arrivalDelay < 0,
}"
>
p.
{{
stylizeTime(stop.arrivalRealTimeString, stop.arrivalDelay)
}}
</span>
<span
class="date-stop"
v-if="stop.stopTime"
:class="stop.stopType.replace(', ', '-')"
>{{ stop.stopTime }} {{ stop.stopType }}</span>
<span
class="date-departure"
v-if="!stop.terminatesHere && stop.stopTime != 0"
:class="{
delayed: stop.departureDelay > 0,
preponed: stop.departureDelay < 0,
}"
>
o.
{{
stylizeTime(stop.departureRealTimeString, stop.departureDelay)
}}
</span>
</span>
</span>
<div class="stop-line departure">{{ stop.departureLine }}</div>
</li>
</ul>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator";
import TrainStop from "@/scripts/interfaces/TrainStop";
@Component
export default class TrainSchedule extends Vue {
@Prop() readonly followingStops!: TrainStop[];
@Prop() readonly currentStationName!: string;
stylizeTime(timeString: string, delay: number) {
return (
timeString +
(delay != 0 ? " (" + (delay > 0 ? "+" : "") + delay.toString() + ")" : "")
);
}
click() {
this.$emit("click");
}
}
</script>
<style lang="scss" scoped>
@import "../../styles/responsive.scss";
.train-schedule {
max-height: 600px;
overflow: auto;
margin-top: 1rem;
font-size: 1em;
@include smallScreen() {
font-size: 0.8em;
}
}
.schedule-bar,
.progress-bar {
position: absolute;
z-index: 1;
width: 2px;
height: 100%;
top: 0;
left: 0;
background: white;
}
.progress-bar {
height: 100%;
top: 0;
left: calc(-0.5rem - 2px);
width: 2px;
}
.schedule-wrapper {
position: relative;
margin-top: 1rem;
margin-left: 0.5rem;
}
ul.schedule-list {
margin-left: 10px;
}
ul.schedule-list > li.schedule-item {
position: relative;
display: flex;
flex-direction: column;
padding: 0 0.5rem;
&.confirmed {
& > .progress-bar,
& > .stop-info > .info-indicator {
background: lime;
}
}
&.stopped {
& > .progress-bar {
background: lime;
height: 50%;
}
& > .stop-info > .info-indicator {
background: orangered;
}
}
}
li.schedule-item > .stop-info {
display: flex;
position: relative;
& > .info-indicator {
position: absolute;
z-index: 1;
top: 50%;
left: -1.5rem;
transform: translateY(-50%);
width: 15px;
height: 2px;
background: white;
}
& > .info-name {
background: rgb(0, 81, 187);
padding: 0.3rem 0.5rem;
}
& > .info-date {
display: flex;
align-items: center;
& > span {
background: #5c5c5c;
padding: 0.3rem 0.5rem;
}
& > .date-stop {
&.ph,
&.ph-pm {
background: #ce8d00;
}
&.pt,
&.pm,
&.pt-pm {
background: #252525;
}
}
& > .date-arrival,
& > .date-departure {
&.delayed {
background: rgb(250, 0, 0);
}
&.preponed {
background: rgb(0, 139, 0);
}
}
}
}
li.schedule-item > .stop-line {
font-size: 0.8em;
color: #bbb;
padding: 0.3em 0;
margin: 0.2em 0;
transform: translateX(-0.8rem);
}
</style>
+122 -122
View File
@@ -1,123 +1,123 @@
<template>
<div class="train-search">
<span class="search train">
<div class="search-title title">Szukaj składu</div>
<div class="search-box">
<input class="search-input" v-model="searchedTrain" />
<img
class="search-exit"
:src="exitIcon"
alt="exit-icon"
@click="() => (searchedTrain = '')"
/>
</div>
</span>
<span class="search driver">
<div class="search-title title">Szukaj maszynisty</div>
<div class="search-box">
<input class="search-input" v-model="searchedDriver" />
<img
class="search-exit"
:src="exitIcon"
alt="exit-icon"
@click="() => (searchedDriver = '')"
/>
</div>
</span>
</div>
</template>
<script lang="ts">
import { Component, Vue, Watch, Prop } from "vue-property-decorator";
@Component
export default class extends Vue {
exitIcon = require("@/assets/icon-exit.svg");
searchedTrain = "";
searchedDriver = "";
@Prop() readonly passedSearchedTrain!: string;
@Prop() readonly focusedTrain!: string;
// @Prop() readonly passedSearchedDriver!: string;
@Watch("searchedTrain")
onSearchedTrainChanged(val: string, oldVal: string) {
this.$emit("changeSearchedTrain", val);
}
@Watch("searchedDriver")
onSearchedDriverChanged(val: string, oldVal: string) {
this.$emit("changeSearchedDriver", val);
}
@Watch("passedSearchedTrain")
onPassedSearchedTrainChanged(val: string, oldVal: string) {
if (val && val != "") {
this.searchedTrain = val;
this.searchedDriver = "";
}
}
@Watch("focusedTrain")
onFocusedTrainChanged(val: string, oldVal: string) {
console.log(val);
this.searchedTrain = val;
this.searchedDriver = "";
}
mounted() {
if (this.passedSearchedTrain && this.passedSearchedTrain != "") {
this.searchedTrain = this.passedSearchedTrain;
this.searchedDriver = "";
}
}
}
</script>
<style lang="scss" scoped>
@import "../../styles/responsive.scss";
.train-search {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin-bottom: 1rem;
}
.search {
padding-right: 1rem;
&-box {
position: relative;
background: #333;
border-radius: 0.5em;
min-width: 150px;
}
&-input {
border: none;
padding: 0.5rem 1rem;
margin: 0;
font-size: 1em;
min-width: 85%;
}
&-exit {
position: absolute;
cursor: pointer;
top: 50%;
right: 10px;
transform: translateY(-50%);
width: 1em;
}
}
<template>
<div class="train-search">
<span class="search train">
<div class="search-title title">Szukaj składu</div>
<div class="search-box">
<input class="search-input" v-model="searchedTrain" />
<img
class="search-exit"
:src="exitIcon"
alt="exit-icon"
@click="() => (searchedTrain = '')"
/>
</div>
</span>
<span class="search driver">
<div class="search-title title">Szukaj maszynisty</div>
<div class="search-box">
<input class="search-input" v-model="searchedDriver" />
<img
class="search-exit"
:src="exitIcon"
alt="exit-icon"
@click="() => (searchedDriver = '')"
/>
</div>
</span>
</div>
</template>
<script lang="ts">
import { Component, Vue, Watch, Prop } from "vue-property-decorator";
@Component
export default class extends Vue {
exitIcon = require("@/assets/icon-exit.svg");
searchedTrain = "";
searchedDriver = "";
@Prop() readonly passedSearchedTrain!: string;
@Prop() readonly focusedTrain!: string;
// @Prop() readonly passedSearchedDriver!: string;
@Watch("searchedTrain")
onSearchedTrainChanged(val: string, oldVal: string) {
this.$emit("changeSearchedTrain", val);
}
@Watch("searchedDriver")
onSearchedDriverChanged(val: string, oldVal: string) {
this.$emit("changeSearchedDriver", val);
}
@Watch("passedSearchedTrain")
onPassedSearchedTrainChanged(val: string, oldVal: string) {
if (val && val != "") {
this.searchedTrain = val;
this.searchedDriver = "";
}
}
@Watch("focusedTrain")
onFocusedTrainChanged(val: string, oldVal: string) {
console.log(val);
this.searchedTrain = val;
this.searchedDriver = "";
}
mounted() {
if (this.passedSearchedTrain && this.passedSearchedTrain != "") {
this.searchedTrain = this.passedSearchedTrain;
this.searchedDriver = "";
}
}
}
</script>
<style lang="scss" scoped>
@import "../../styles/responsive.scss";
.train-search {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin-bottom: 1rem;
}
.search {
padding-right: 1rem;
&-box {
position: relative;
background: #333;
border-radius: 0.5em;
min-width: 150px;
}
&-input {
border: none;
padding: 0.5rem 1rem;
margin: 0;
font-size: 1em;
min-width: 85%;
}
&-exit {
position: absolute;
cursor: pointer;
top: 50%;
right: 10px;
transform: translateY(-50%);
width: 1em;
}
}
</style>
+173 -173
View File
@@ -1,173 +1,173 @@
<template>
<div class="train-sorter">
<div class="sorter-wrapper">
<div class="sorter-box">
<div class="title">Sortuj według</div>
<div class="selected" @click="toggleOptionList">
<span>{{ sorterName }}</span>
<img :src="require('@/assets/icon-select.svg')" alt="icon-select" />
</div>
<div class="options-container">
<ul class="options-list" :class="{ open: listOpen }">
<li
class="option"
v-for="(option, i) in sortOptionList"
:key="i"
@click="() => chooseOption(option)"
>
<input type="radio" name="sort" :id="option.id" />
<label :for="option.id">{{ option.content }}</label>
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Vue, Component, Prop } from "vue-property-decorator";
const ascSVG = require("@/assets/icon-arrow-asc.svg");
const descSVG = require("@/assets/icon-arrow-desc.svg");
@Component
export default class TrainSorter extends Vue {
ascSVG = ascSVG;
descSVG = descSVG;
@Prop() trainList!: [];
listOpen: boolean = false;
sorterName: string = "numer pociągu";
sortOptionList: { id: string; content: string }[] = [
{
id: "mass",
content: "masa",
},
{
id: "speed",
content: "prędkość",
},
{
id: "length",
content: "długość",
},
{
id: "distance",
content: "kilometraż",
},
{
id: "timetable",
content: "numer pociągu",
},
];
toggleOptionList() {
this.listOpen = !this.listOpen;
}
closeOptionList() {
this.listOpen = false;
}
chooseOption(option: { id: string; content: string }) {
this.$emit("changeSorter", { id: option.id, dir: -1 });
this.sorterName = option.content;
this.closeOptionList();
}
get compTrainList() {
return this.trainList;
}
}
</script>
<style lang="scss" scoped>
.title {
padding-left: 0.3em;
}
.sorter-wrapper {
display: flex;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
}
.selected {
display: flex;
justify-content: space-between;
align-items: center;
span {
margin-right: 2rem;
}
img {
max-width: 0.75em;
}
}
.selected,
.options-list {
background: #333;
border-radius: 0.5em;
}
.selected {
padding: 0.5rem 1rem;
min-width: 150px;
cursor: pointer;
}
.options-container {
position: relative;
}
.options-list {
position: absolute;
top: 0;
left: 0;
z-index: 10;
width: 100%;
background-color: rgba(#222, 0.95);
overflow: hidden;
max-height: 0;
&.open {
max-height: 250px;
opacity: 1;
}
transition: all 150ms ease-in;
}
.option {
display: flex;
&:hover {
background-color: rgba(#868686, 0.85);
}
transition: background 150ms ease-in;
}
input {
display: none;
}
label {
padding: 0.5rem 1rem;
width: 100%;
cursor: pointer;
}
</style>
<template>
<div class="train-sorter">
<div class="sorter-wrapper">
<div class="sorter-box">
<div class="title">Sortuj według</div>
<div class="selected" @click="toggleOptionList">
<span>{{ sorterName }}</span>
<img :src="require('@/assets/icon-select.svg')" alt="icon-select" />
</div>
<div class="options-container">
<ul class="options-list" :class="{ open: listOpen }">
<li
class="option"
v-for="(option, i) in sortOptionList"
:key="i"
@click="() => chooseOption(option)"
>
<input type="radio" name="sort" :id="option.id" />
<label :for="option.id">{{ option.content }}</label>
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Vue, Component, Prop } from "vue-property-decorator";
const ascSVG = require("@/assets/icon-arrow-asc.svg");
const descSVG = require("@/assets/icon-arrow-desc.svg");
@Component
export default class TrainSorter extends Vue {
ascSVG = ascSVG;
descSVG = descSVG;
@Prop() trainList!: [];
listOpen: boolean = false;
sorterName: string = "numer pociągu";
sortOptionList: { id: string; content: string }[] = [
{
id: "mass",
content: "masa",
},
{
id: "speed",
content: "prędkość",
},
{
id: "length",
content: "długość",
},
{
id: "distance",
content: "kilometraż",
},
{
id: "timetable",
content: "numer pociągu",
},
];
toggleOptionList() {
this.listOpen = !this.listOpen;
}
closeOptionList() {
this.listOpen = false;
}
chooseOption(option: { id: string; content: string }) {
this.$emit("changeSorter", { id: option.id, dir: -1 });
this.sorterName = option.content;
this.closeOptionList();
}
get compTrainList() {
return this.trainList;
}
}
</script>
<style lang="scss" scoped>
.title {
padding-left: 0.3em;
}
.sorter-wrapper {
display: flex;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
}
.selected {
display: flex;
justify-content: space-between;
align-items: center;
span {
margin-right: 2rem;
}
img {
max-width: 0.75em;
}
}
.selected,
.options-list {
background: #333;
border-radius: 0.5em;
}
.selected {
padding: 0.5rem 1rem;
min-width: 150px;
cursor: pointer;
}
.options-container {
position: relative;
}
.options-list {
position: absolute;
top: 0;
left: 0;
z-index: 10;
width: 100%;
background-color: rgba(#222, 0.95);
overflow: hidden;
max-height: 0;
&.open {
max-height: 250px;
opacity: 1;
}
transition: all 150ms ease-in;
}
.option {
display: flex;
&:hover {
background-color: rgba(#868686, 0.85);
}
transition: background 150ms ease-in;
}
input {
display: none;
}
label {
padding: 0.5rem 1rem;
width: 100%;
cursor: pointer;
}
</style>
+321 -321
View File
@@ -1,322 +1,322 @@
<template>
<div class="train-stats">
<div class="btn-wrapper">
<button
class="stats-btn button"
@click="toggleStats"
v-if="trains.length > 0"
>
STATYSTYKI RUCHU
</button>
</div>
<transition name="stats-anim">
<div class="stats-body" v-if="statsOpen">
<h2 class="stats-header">STATYSTYKI RUCHU</h2>
<div class="stats-speed">
<div class="title stats-title">
PRĘDKOŚCI POCIĄGÓW (MIN | ŚR | MAX) [km/h]
</div>
<div class="stats-content">
{{ speedStats.min }} | {{ speedStats.avg }} | {{ speedStats.max }}
</div>
</div>
<div class="stats-length">
<div class="title stats-title">
DŁUGOŚCI ROZKŁADÓW (MIN | ŚR | MAX) [km]
</div>
<div class="stats-content">
{{ timetableStats.min }} | {{ timetableStats.avg }} |
{{ timetableStats.max }}
</div>
</div>
<div class="stats-categories">
<div class="title stats-title">KATEGORIE RJ</div>
<div class="category-list">
<span
class="category"
v-for="[key, value] of categoryList"
:key="key"
>
<span class="category-type">{{ key }}</span>
<span class="category-count">{{ value }}</span>
</span>
</div>
<div class="special-list">
<span class="special twr">
<span class="special-type">WYSOKIEGO RYZYKA</span>
<span class="special-count">{{ specialTrainCount[0] }}</span>
</span>
<span class="special skr">
<span class="special-type">PRZEKROCZONA SKRAJNIA</span>
<span class="special-count">{{ specialTrainCount[1] }}</span>
</span>
</div>
</div>
<div class="stats-locos">
<div class="title stats-title">NAJCZĘSTSZE JEDNOSTKI</div>
<div class="loco-list stats-content">
<div class="loco-item" v-for="(loco, i) in locoList" :key="i">
{{ loco[0] }} | {{ loco[1] }}
</div>
</div>
</div>
</div>
</transition>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator";
import Train from "@/scripts/interfaces/Train";
@Component
export default class TrainStats extends Vue {
@Prop() readonly trains!: Train[];
statsOpen: boolean = false;
toggleStats() {
this.statsOpen = !this.statsOpen;
}
get speedStats(): { avg: string; min: string; max: string } {
if (this.trains.length == 0) return { avg: "0", min: "0", max: "0" };
const avg = (
this.trains.reduce((acc, train) => acc + train.speed, 0) /
this.trains.length
).toFixed(2);
const minMax = this.trains.reduce((acc, train) => {
if (!train.timetableData) return acc;
acc[0] =
acc[0] === undefined || train.speed < acc[0] ? train.speed : acc[0];
acc[1] =
acc[1] === undefined || train.speed > acc[1] ? train.speed : acc[1];
return acc;
}, [] as any);
return { avg, min: minMax[0].toString(), max: minMax[1].toString() };
}
get timetableStats(): { avg: string; min: string; max: string } {
if (this.trains.length == 0) return { avg: "0", min: "0", max: "0" };
const avg = (
this.trains.reduce((acc, train) => train.timetableData ? acc + train.timetableData.routeDistance : acc, 0) /
this.trains.length
).toFixed(2);
const minMax = this.trains.reduce((acc, train) => {
if (!train.timetableData) return acc;
acc[0] =
acc[0] === undefined || train.timetableData.routeDistance < acc[0]
? train.timetableData.routeDistance
: acc[0];
acc[1] =
acc[1] === undefined || train.timetableData.routeDistance > acc[1]
? train.timetableData.routeDistance
: acc[1];
return acc;
}, [] as any);
return { avg, min: minMax[0].toString(), max: minMax[1].toString() };
}
get categoryList(): Map<string, number> {
const map = this.trains.reduce((acc, train) => {
if (!train.timetableData || !train.timetableData.category) return acc;
acc.set(
train.timetableData.category,
acc.get(train.timetableData.category) ? acc.get(train.timetableData.category) + 1 : 1
);
return acc;
}, new Map());
return new Map([...map.entries()].sort((a, b) => b[1] - a[1]));
}
get locoList(): any[] {
const map = this.trains.reduce((acc, train) => {
if (!train.timetableData || !train.locoType) return acc;
acc.set(
train.locoType,
acc.get(train.locoType) ? acc.get(train.locoType) + 1 : 1
);
return acc;
}, new Map());
const sorted = [...map.entries()]
.sort((a, b) => b[1] - a[1])
.filter((v, i) => i < 3);
return sorted;
}
get specialTrainCount(): [number, number] {
const twrList = this.trains.filter((train) => train.timetableData && train.timetableData.TWR);
const skrList = this.trains.filter((train) => train.timetableData && train.timetableData.SKR);
return [twrList.length, skrList.length];
}
}
</script>
<style lang="scss" scoped>
@import "../../styles/responsive";
@import "../../styles/variables";
.stats-anim {
&-enter-active,
&-leave-active {
transition: all 150ms ease-out;
}
&-enter,
&-leave-to {
opacity: 0;
transform: translateY(30px);
}
}
.train-stats {
padding: 0.3em 0;
font-size: 1.1em;
z-index: 5;
position: relative;
}
.stats {
&-btn {
font-size: 1em;
padding: 0.5em;
}
&-header {
margin-bottom: 1rem;
}
&-body {
position: absolute;
display: inline-block;
max-width: 700px;
background: rgba(black, 0.85);
border-radius: 0 1em 1em 1em;
padding: 1rem;
}
&-content {
font-size: 1.1em;
color: #ddd;
}
}
.category,
.special {
&-list {
display: flex;
flex-wrap: wrap;
font-size: 0.95em;
}
margin-right: 0.4em;
margin-bottom: 0.4em;
&-type,
&-count {
display: inline-block;
padding: 0.2em 0.4em;
}
&-type {
background: rgb(88, 88, 88);
font-weight: 600;
}
&-count {
background: #ffc014;
color: black;
}
}
.special {
&-list {
font-size: 0.85em;
}
&-count {
background: gray;
color: white;
}
&.twr > &-type {
background-color: $twr;
color: black;
}
&.skr > &-type {
background-color: $skr;
color: white;
}
}
.warning {
display: inline-block;
margin-right: 0.4em;
padding: 0.2em 0.3em;
color: black;
font-weight: bold;
font-size: 0.85em;
&.twr {
background-color: #ffc700;
}
&.skr {
background-color: #f00000;
}
}
@include smallScreen {
.button {
font-size: 0.85rem;
}
.stats-body {
display: block;
font-size: 0.9em;
width: 100%;
border-radius: 0 0 1em 1em;
}
.btn-wrapper {
display: flex;
justify-content: center;
margin-top: 1rem;
}
}
<template>
<div class="train-stats">
<div class="btn-wrapper">
<button
class="stats-btn button"
@click="toggleStats"
v-if="trains.length > 0"
>
STATYSTYKI RUCHU
</button>
</div>
<transition name="stats-anim">
<div class="stats-body" v-if="statsOpen">
<h2 class="stats-header">STATYSTYKI RUCHU</h2>
<div class="stats-speed">
<div class="title stats-title">
PRĘDKOŚCI POCIĄGÓW (MIN | ŚR | MAX) [km/h]
</div>
<div class="stats-content">
{{ speedStats.min }} | {{ speedStats.avg }} | {{ speedStats.max }}
</div>
</div>
<div class="stats-length">
<div class="title stats-title">
DŁUGOŚCI ROZKŁADÓW (MIN | ŚR | MAX) [km]
</div>
<div class="stats-content">
{{ timetableStats.min }} | {{ timetableStats.avg }} |
{{ timetableStats.max }}
</div>
</div>
<div class="stats-categories">
<div class="title stats-title">KATEGORIE RJ</div>
<div class="category-list">
<span
class="category"
v-for="[key, value] of categoryList"
:key="key"
>
<span class="category-type">{{ key }}</span>
<span class="category-count">{{ value }}</span>
</span>
</div>
<div class="special-list">
<span class="special twr">
<span class="special-type">WYSOKIEGO RYZYKA</span>
<span class="special-count">{{ specialTrainCount[0] }}</span>
</span>
<span class="special skr">
<span class="special-type">PRZEKROCZONA SKRAJNIA</span>
<span class="special-count">{{ specialTrainCount[1] }}</span>
</span>
</div>
</div>
<div class="stats-locos">
<div class="title stats-title">NAJCZĘSTSZE JEDNOSTKI</div>
<div class="loco-list stats-content">
<div class="loco-item" v-for="(loco, i) in locoList" :key="i">
{{ loco[0] }} | {{ loco[1] }}
</div>
</div>
</div>
</div>
</transition>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator";
import Train from "@/scripts/interfaces/Train";
@Component
export default class TrainStats extends Vue {
@Prop() readonly trains!: Train[];
statsOpen: boolean = false;
toggleStats() {
this.statsOpen = !this.statsOpen;
}
get speedStats(): { avg: string; min: string; max: string } {
if (this.trains.length == 0) return { avg: "0", min: "0", max: "0" };
const avg = (
this.trains.reduce((acc, train) => acc + train.speed, 0) /
this.trains.length
).toFixed(2);
const minMax = this.trains.reduce((acc, train) => {
if (!train.timetableData) return acc;
acc[0] =
acc[0] === undefined || train.speed < acc[0] ? train.speed : acc[0];
acc[1] =
acc[1] === undefined || train.speed > acc[1] ? train.speed : acc[1];
return acc;
}, [] as any);
return { avg, min: minMax[0].toString(), max: minMax[1].toString() };
}
get timetableStats(): { avg: string; min: string; max: string } {
if (this.trains.length == 0) return { avg: "0", min: "0", max: "0" };
const avg = (
this.trains.reduce((acc, train) => train.timetableData ? acc + train.timetableData.routeDistance : acc, 0) /
this.trains.length
).toFixed(2);
const minMax = this.trains.reduce((acc, train) => {
if (!train.timetableData) return acc;
acc[0] =
acc[0] === undefined || train.timetableData.routeDistance < acc[0]
? train.timetableData.routeDistance
: acc[0];
acc[1] =
acc[1] === undefined || train.timetableData.routeDistance > acc[1]
? train.timetableData.routeDistance
: acc[1];
return acc;
}, [] as any);
return { avg, min: minMax[0].toString(), max: minMax[1].toString() };
}
get categoryList(): Map<string, number> {
const map = this.trains.reduce((acc, train) => {
if (!train.timetableData || !train.timetableData.category) return acc;
acc.set(
train.timetableData.category,
acc.get(train.timetableData.category) ? acc.get(train.timetableData.category) + 1 : 1
);
return acc;
}, new Map());
return new Map([...map.entries()].sort((a, b) => b[1] - a[1]));
}
get locoList(): any[] {
const map = this.trains.reduce((acc, train) => {
if (!train.timetableData || !train.locoType) return acc;
acc.set(
train.locoType,
acc.get(train.locoType) ? acc.get(train.locoType) + 1 : 1
);
return acc;
}, new Map());
const sorted = [...map.entries()]
.sort((a, b) => b[1] - a[1])
.filter((v, i) => i < 3);
return sorted;
}
get specialTrainCount(): [number, number] {
const twrList = this.trains.filter((train) => train.timetableData && train.timetableData.TWR);
const skrList = this.trains.filter((train) => train.timetableData && train.timetableData.SKR);
return [twrList.length, skrList.length];
}
}
</script>
<style lang="scss" scoped>
@import "../../styles/responsive";
@import "../../styles/variables";
.stats-anim {
&-enter-active,
&-leave-active {
transition: all 150ms ease-out;
}
&-enter,
&-leave-to {
opacity: 0;
transform: translateY(30px);
}
}
.train-stats {
padding: 0.3em 0;
font-size: 1.1em;
z-index: 5;
position: relative;
}
.stats {
&-btn {
font-size: 1em;
padding: 0.5em;
}
&-header {
margin-bottom: 1rem;
}
&-body {
position: absolute;
display: inline-block;
max-width: 700px;
background: rgba(black, 0.85);
border-radius: 0 1em 1em 1em;
padding: 1rem;
}
&-content {
font-size: 1.1em;
color: #ddd;
}
}
.category,
.special {
&-list {
display: flex;
flex-wrap: wrap;
font-size: 0.95em;
}
margin-right: 0.4em;
margin-bottom: 0.4em;
&-type,
&-count {
display: inline-block;
padding: 0.2em 0.4em;
}
&-type {
background: rgb(88, 88, 88);
font-weight: 600;
}
&-count {
background: #ffc014;
color: black;
}
}
.special {
&-list {
font-size: 0.85em;
}
&-count {
background: gray;
color: white;
}
&.twr > &-type {
background-color: $twr;
color: black;
}
&.skr > &-type {
background-color: $skr;
color: white;
}
}
.warning {
display: inline-block;
margin-right: 0.4em;
padding: 0.2em 0.3em;
color: black;
font-weight: bold;
font-size: 0.85em;
&.twr {
background-color: #ffc700;
}
&.skr {
background-color: #f00000;
}
}
@include smallScreen {
.button {
font-size: 0.85rem;
}
.stats-body {
display: block;
font-size: 0.9em;
width: 100%;
border-radius: 0 0 1em 1em;
}
.btn-wrapper {
display: flex;
justify-content: center;
margin-top: 1rem;
}
}
</style>
+484 -484
View File
@@ -1,485 +1,485 @@
<template>
<div class="train-table">
<div class="no-trains" v-if="computedTrains.length == 0">
Ups! Brak pociągów do wyświetlenia :/
</div>
<ul class="train-list">
<li
class="train-row"
v-for="(train, i) in computedTrains"
:key="i"
:id="train.timetableData.timetableId"
>
<span class="wrapper">
<span
class="info"
@click="changeScheduleShowState(train.timetableData.timetableId)"
>
<div class="info-main">
<div class="info-category">
<div class="category-left">
<span class="warning twr" v-if="train.timetableData.TWR">
TWR
</span>
<span class="warning skr" v-if="train.timetableData.SKR">
SKR
</span>
<span>
<strong>{{ train.timetableData.category }}</strong>
{{ train.trainNo }} |
<span style="color: gold">
{{ train.timetableData.routeDistance }} km
</span>
</span>
</div>
<div class="category-right tooltip">
<img
:src="
showedSchedule === train.timetableData.timetableId
? ascSVG
: descSVG
"
alt="asc-arrow"
/>
<span>SRJP</span>
<span class="tooltip-text">
Szczegółowy rozkład jazdy pociągu {{ train.trainNo }}
</span>
</div>
</div>
<div class="info-route">
<span class="info-route-text">
<strong>
{{ train.timetableData.route.replace("|", " - ") }}
</strong>
</span>
</div>
<div class="info-stops">
<span v-if="train.timetableData.followingStops.length > 2">
Przez:
<span
v-html="
generateStopList(train.timetableData.followingStops)
"
></span>
</span>
</div>
</div>
<div class="info-bottom">
<span
class="info-label user-badge"
:class="train.stopStatus || 'disconnected'"
>
<span
class="tooltip"
:style="!train.online ? 'color: gray' : ''"
>
<span v-if="train.stopStatus">{{ train.stopLabel }}</span>
<span v-else-if="train.currentStationName">
Pociąg na złej stacji!
</span>
<span v-else>Sceneria offline!</span>
<span class="tooltip-text" v-if="!train.online">
Pociąg offline
</span>
</span>
</span>
</div>
</span>
<span class="driver">
<span class="driver-name">
<a
:href="'https://td2.info.pl/profile/?u=' + train.driverId"
target="_blank"
>
{{ train.driverName }}
</a>
</span>
<span class="driver-type">
{{ train.locoType }}
</span>
<span class="driver-loco">
<img :src="train.locoURL" @error="onImageError" />
</span>
</span>
<span class="stats">
<div class="stats-main">
<span class="mass">
<img :src="massIcon" alt="icon-mass" />
{{ train.mass / 1000 }}t
</span>
<span class="speed">
<img :src="speedIcon" alt="icon-speed" />
{{ train.speed }} km/h
</span>
<span class="length">
<img :src="lengthIcon" alt="icon-length" />
{{ train.length }}m
</span>
</div>
<div class="stats-position">
<span class="station">
<div class="stat-icon">
<img :src="sceneryIcon" alt="icon-scenery" />
</div>
{{ train.currentStationName || "---" }}
</span>
<span class="track">
<div class="stat-icon">
<img :src="routeIcon" alt="icon-scenery" />
</div>
{{ train.connectedTrack || "---" }}
</span>
<span class="signal">
<div class="stat-icon">
<img :src="signalIcon" alt="icon-scenery" />
</div>
{{ train.signal || "---" }}
</span>
<span class="distance">
<div class="stat-icon">
<img :src="distanceIcon" alt="icon-scenery" />
</div>
{{ train.distance || "0" }}m
</span>
</div>
</span>
</span>
<TrainSchedule
:followingStops="train.timetableData.followingStops"
:currentStationName="train.currentStationName"
@click="changeScheduleShowState(train.timetableData.timetableId)"
v-if="showedSchedule == train.timetableData.timetableId"
/>
</li>
</ul>
</div>
</template>
<script lang="ts">
import { Vue, Component, Prop, Watch } from "vue-property-decorator";
const unknownTrainImage = require("@/assets/unknown.png");
const ascSVG = require("@/assets/icon-arrow-asc.svg");
const descSVG = require("@/assets/icon-arrow-desc.svg");
import Train from "@/scripts/interfaces/Train";
import Station from "@/scripts/interfaces/Station";
import TrainSchedule from "@/components/TrainsView/TrainSchedule.vue";
import TrainStop from '@/scripts/interfaces/TrainStop';
@Component({
components: { TrainSchedule }
})
export default class TrainTable extends Vue {
@Prop() computedTrains!: Train[];
showedSchedule = 0;
ascSVG = ascSVG;
descSVG = descSVG;
speedIcon: string = require("@/assets/icon-speed.svg");
massIcon: string = require("@/assets/icon-mass.svg");
lengthIcon: string = require("@/assets/icon-length.svg");
distanceIcon: string = require("@/assets/icon-distance.svg");
sceneryIcon: string = require("@/assets/icon-scenery.svg");
signalIcon: string = require("@/assets/icon-signal.svg");
routeIcon: string = require("@/assets/icon-route.svg");
changeScheduleShowState(timetableId: number) {
this.showedSchedule = this.showedSchedule === timetableId ? 0 : timetableId;
}
onImageError(e: Event) {
(e.target as HTMLImageElement).src = unknownTrainImage;
}
generateStopList(stops: any): string | undefined {
if (!stops) return "";
return stops.reduce((acc, stop: TrainStop, i) => {
if (stop.stopType.includes("ph")) acc.push(`<strong style='color:${stop.confirmed ? "springgreen" : "white"}'>${stop.stopName}</strong>`);
else if (i > 0 && i < stops.length - 1 && !stop.stopNameRAW.includes("po."))
acc.push(`<span style='color:${stop.confirmed ? "springgreen" : "lightgray"}'>${stop.stopName}</span>`);
return acc;
}, []).join(" * ");
}
}
</script>
<style lang="scss" scoped>
@import "../../styles/responsive.scss";
@import "../../styles/variables.scss";
@import "../../styles/user_badge.scss";
.no-trains {
text-align: center;
font-size: 1.5em;
padding: 1rem;
margin: 1rem 0;
background: #333;
}
.train {
&-list {
@include smallScreen() {
width: 100%;
overflow: hidden;
}
}
&-row {
padding: 1rem;
margin-bottom: 1rem;
background-color: $primaryCol;
cursor: pointer;
& > .wrapper {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
font-size: calc(0.4rem + 0.5vw);
@include smallScreen() {
grid-template-columns: 1fr;
grid-template-rows: repeat(3, minmax(100px, 1fr));
font-size: 0.8rem;
gap: 0.4em 0;
}
@include bigScreen() {
font-size: 1.1rem;
}
}
}
}
.info {
display: flex;
flex-direction: column;
justify-content: space-between;
&-category {
font-size: 1.05em;
display: flex;
justify-content: space-between;
align-items: center;
div {
display: flex;
}
.category-right {
padding: 0.15em 0.5em;
background: #1085b3;
border-radius: 1em;
font-size: 0.9em;
-moz-user-select: none;
-webkit-user-select: none;
img {
vertical-align: middle;
width: 1.2em;
}
.tooltip-text {
font-size: 0.9em;
background-color: #1085b3;
&::after {
border-color: #1085b3 transparent transparent transparent;
}
}
}
}
&-route {
display: flex;
align-items: center;
font-size: 1.25em;
margin: 5px 0;
}
&-stops {
margin-bottom: 10px;
font-size: 0.75em;
}
&-online {
background-color: #ce0000;
padding: 0.2em 0.7em;
font-size: 0.85em;
border-radius: 1em;
&.online {
background-color: #009700;
}
}
&-bottom {
display: flex;
align-items: center;
button {
margin-left: 10px;
border-radius: 0.7em;
padding: 0.2em 0.5em;
font-size: 0.85em;
border: 1px solid white;
}
}
}
.driver {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
&-exp {
font-size: 1.4em;
padding: 0.3em 0.6em;
border-radius: 0.4em;
background-color: red;
}
&-name {
margin: 0 0.3em;
font-weight: bold;
}
&-type {
color: #bbb;
margin-left: 1em;
}
&-loco {
width: 100%;
text-align: center;
}
&-loco img {
width: 13em;
max-width: 190px;
}
}
.stats {
display: flex;
flex-direction: column;
justify-content: space-around;
&-main {
display: flex;
margin-bottom: 1.5em;
& > span {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
img {
margin: 0 0.3em;
width: 2em;
}
}
&-position {
display: flex;
margin-top: 1em;
text-align: center;
font-size: 0.9em;
p {
color: #00cff3;
}
& > span {
width: 100%;
img {
width: 2em;
}
}
}
}
.warning {
border-radius: 15px;
padding: 0.1em 1.2em;
margin-right: 0.5em;
display: flex;
align-items: center;
color: white;
font-weight: bold;
font-size: 0.7em;
&.twr {
border: 2px solid $twr;
}
&.skr {
border: 2px solid $skr;
}
}
@include bigScreen() {
.item {
font-size: 1rem;
}
}
@include smallScreen() {
.info-bottom {
text-align: center;
}
}
<template>
<div class="train-table">
<div class="no-trains" v-if="computedTrains.length == 0">
Ups! Brak pociągów do wyświetlenia :/
</div>
<ul class="train-list">
<li
class="train-row"
v-for="(train, i) in computedTrains"
:key="i"
:id="train.timetableData.timetableId"
>
<span class="wrapper">
<span
class="info"
@click="changeScheduleShowState(train.timetableData.timetableId)"
>
<div class="info-main">
<div class="info-category">
<div class="category-left">
<span class="warning twr" v-if="train.timetableData.TWR">
TWR
</span>
<span class="warning skr" v-if="train.timetableData.SKR">
SKR
</span>
<span>
<strong>{{ train.timetableData.category }}</strong>
{{ train.trainNo }} |
<span style="color: gold">
{{ train.timetableData.routeDistance }} km
</span>
</span>
</div>
<div class="category-right tooltip">
<img
:src="
showedSchedule === train.timetableData.timetableId
? ascSVG
: descSVG
"
alt="asc-arrow"
/>
<span>SRJP</span>
<span class="tooltip-text">
Szczegółowy rozkład jazdy pociągu {{ train.trainNo }}
</span>
</div>
</div>
<div class="info-route">
<span class="info-route-text">
<strong>
{{ train.timetableData.route.replace("|", " - ") }}
</strong>
</span>
</div>
<div class="info-stops">
<span v-if="train.timetableData.followingStops.length > 2">
Przez:
<span
v-html="
generateStopList(train.timetableData.followingStops)
"
></span>
</span>
</div>
</div>
<div class="info-bottom">
<span
class="info-label user-badge"
:class="train.stopStatus || 'disconnected'"
>
<span
class="tooltip"
:style="!train.online ? 'color: gray' : ''"
>
<span v-if="train.stopStatus">{{ train.stopLabel }}</span>
<span v-else-if="train.currentStationName">
Pociąg na złej stacji!
</span>
<span v-else>Sceneria offline!</span>
<span class="tooltip-text" v-if="!train.online">
Pociąg offline
</span>
</span>
</span>
</div>
</span>
<span class="driver">
<span class="driver-name">
<a
:href="'https://td2.info.pl/profile/?u=' + train.driverId"
target="_blank"
>
{{ train.driverName }}
</a>
</span>
<span class="driver-type">
{{ train.locoType }}
</span>
<span class="driver-loco">
<img :src="train.locoURL" @error="onImageError" />
</span>
</span>
<span class="stats">
<div class="stats-main">
<span class="mass">
<img :src="massIcon" alt="icon-mass" />
{{ train.mass / 1000 }}t
</span>
<span class="speed">
<img :src="speedIcon" alt="icon-speed" />
{{ train.speed }} km/h
</span>
<span class="length">
<img :src="lengthIcon" alt="icon-length" />
{{ train.length }}m
</span>
</div>
<div class="stats-position">
<span class="station">
<div class="stat-icon">
<img :src="sceneryIcon" alt="icon-scenery" />
</div>
{{ train.currentStationName || "---" }}
</span>
<span class="track">
<div class="stat-icon">
<img :src="routeIcon" alt="icon-scenery" />
</div>
{{ train.connectedTrack || "---" }}
</span>
<span class="signal">
<div class="stat-icon">
<img :src="signalIcon" alt="icon-scenery" />
</div>
{{ train.signal || "---" }}
</span>
<span class="distance">
<div class="stat-icon">
<img :src="distanceIcon" alt="icon-scenery" />
</div>
{{ train.distance || "0" }}m
</span>
</div>
</span>
</span>
<TrainSchedule
:followingStops="train.timetableData.followingStops"
:currentStationName="train.currentStationName"
@click="changeScheduleShowState(train.timetableData.timetableId)"
v-if="showedSchedule == train.timetableData.timetableId"
/>
</li>
</ul>
</div>
</template>
<script lang="ts">
import { Vue, Component, Prop, Watch } from "vue-property-decorator";
const unknownTrainImage = require("@/assets/unknown.png");
const ascSVG = require("@/assets/icon-arrow-asc.svg");
const descSVG = require("@/assets/icon-arrow-desc.svg");
import Train from "@/scripts/interfaces/Train";
import Station from "@/scripts/interfaces/Station";
import TrainSchedule from "@/components/TrainsView/TrainSchedule.vue";
import TrainStop from '@/scripts/interfaces/TrainStop';
@Component({
components: { TrainSchedule }
})
export default class TrainTable extends Vue {
@Prop() computedTrains!: Train[];
showedSchedule = 0;
ascSVG = ascSVG;
descSVG = descSVG;
speedIcon: string = require("@/assets/icon-speed.svg");
massIcon: string = require("@/assets/icon-mass.svg");
lengthIcon: string = require("@/assets/icon-length.svg");
distanceIcon: string = require("@/assets/icon-distance.svg");
sceneryIcon: string = require("@/assets/icon-scenery.svg");
signalIcon: string = require("@/assets/icon-signal.svg");
routeIcon: string = require("@/assets/icon-route.svg");
changeScheduleShowState(timetableId: number) {
this.showedSchedule = this.showedSchedule === timetableId ? 0 : timetableId;
}
onImageError(e: Event) {
(e.target as HTMLImageElement).src = unknownTrainImage;
}
generateStopList(stops: any): string | undefined {
if (!stops) return "";
return stops.reduce((acc, stop: TrainStop, i) => {
if (stop.stopType.includes("ph")) acc.push(`<strong style='color:${stop.confirmed ? "springgreen" : "white"}'>${stop.stopName}</strong>`);
else if (i > 0 && i < stops.length - 1 && !stop.stopNameRAW.includes("po."))
acc.push(`<span style='color:${stop.confirmed ? "springgreen" : "lightgray"}'>${stop.stopName}</span>`);
return acc;
}, []).join(" * ");
}
}
</script>
<style lang="scss" scoped>
@import "../../styles/responsive.scss";
@import "../../styles/variables.scss";
@import "../../styles/user_badge.scss";
.no-trains {
text-align: center;
font-size: 1.5em;
padding: 1rem;
margin: 1rem 0;
background: #333;
}
.train {
&-list {
@include smallScreen() {
width: 100%;
overflow: hidden;
}
}
&-row {
padding: 1rem;
margin-bottom: 1rem;
background-color: $primaryCol;
cursor: pointer;
& > .wrapper {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
font-size: calc(0.4rem + 0.5vw);
@include smallScreen() {
grid-template-columns: 1fr;
grid-template-rows: repeat(3, minmax(100px, 1fr));
font-size: 0.8rem;
gap: 0.4em 0;
}
@include bigScreen() {
font-size: 1.1rem;
}
}
}
}
.info {
display: flex;
flex-direction: column;
justify-content: space-between;
&-category {
font-size: 1.05em;
display: flex;
justify-content: space-between;
align-items: center;
div {
display: flex;
}
.category-right {
padding: 0.15em 0.5em;
background: #1085b3;
border-radius: 1em;
font-size: 0.9em;
-moz-user-select: none;
-webkit-user-select: none;
img {
vertical-align: middle;
width: 1.2em;
}
.tooltip-text {
font-size: 0.9em;
background-color: #1085b3;
&::after {
border-color: #1085b3 transparent transparent transparent;
}
}
}
}
&-route {
display: flex;
align-items: center;
font-size: 1.25em;
margin: 5px 0;
}
&-stops {
margin-bottom: 10px;
font-size: 0.75em;
}
&-online {
background-color: #ce0000;
padding: 0.2em 0.7em;
font-size: 0.85em;
border-radius: 1em;
&.online {
background-color: #009700;
}
}
&-bottom {
display: flex;
align-items: center;
button {
margin-left: 10px;
border-radius: 0.7em;
padding: 0.2em 0.5em;
font-size: 0.85em;
border: 1px solid white;
}
}
}
.driver {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
&-exp {
font-size: 1.4em;
padding: 0.3em 0.6em;
border-radius: 0.4em;
background-color: red;
}
&-name {
margin: 0 0.3em;
font-weight: bold;
}
&-type {
color: #bbb;
margin-left: 1em;
}
&-loco {
width: 100%;
text-align: center;
}
&-loco img {
width: 13em;
max-width: 190px;
}
}
.stats {
display: flex;
flex-direction: column;
justify-content: space-around;
&-main {
display: flex;
margin-bottom: 1.5em;
& > span {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
img {
margin: 0 0.3em;
width: 2em;
}
}
&-position {
display: flex;
margin-top: 1em;
text-align: center;
font-size: 0.9em;
p {
color: #00cff3;
}
& > span {
width: 100%;
img {
width: 2em;
}
}
}
}
.warning {
border-radius: 15px;
padding: 0.1em 1.2em;
margin-right: 0.5em;
display: flex;
align-items: center;
color: white;
font-weight: bold;
font-size: 0.7em;
&.twr {
border: 2px solid $twr;
}
&.skr {
border: 2px solid $skr;
}
}
@include bigScreen() {
.item {
font-size: 1rem;
}
}
@include smallScreen() {
.info-bottom {
text-align: center;
}
}
</style>