Zmiana wyglądu filtrów, dokończenie pozostałych z nich

This commit is contained in:
2020-07-07 13:32:22 +02:00
parent 57bb48c030
commit 05ccb4c74c
13 changed files with 587 additions and 199 deletions
+39 -7
View File
@@ -7,10 +7,17 @@
<img src="@/assets/trainlogo.png" alt="trainlogo" />
<span>wnik</span>
</div>
<span class="online">Scenerie online: {{stationCount}} | Maszyniści online: {{ trainCount }}</span>
</header>
<div class="app-info">
<span>Scenerie online: {{stationCount}} | Maszyniści online: {{ trainCount }}</span>
<div class="app-bar">
<div class="bar-left">
<Options />
</div>
<div class="bar-right">
<!-- <div class="last-update">Ostatnie zmiany</div> -->
</div>
</div>
<main class="app-main">
@@ -31,11 +38,12 @@ import { mapGetters, mapActions } from "vuex";
import Error from "@/components/states/Error.vue";
import Loading from "@/components/states/Loading.vue";
import Options from "@/components/ui/Options.vue";
// import ListFilter from "@/components/utils/ListFilter.vue";
export default Vue.extend({
name: "App",
components: { Error, Loading },
components: { Error, Loading, Options },
computed: mapGetters({
stations: "getStations",
trainCount: "getTrainCount",
@@ -87,6 +95,22 @@ input {
font-family: "Lato", sans-serif;
}
input {
border: 1px solid white;
background: none;
color: white;
font-size: 1em;
padding: 0.15em;
margin: 0.2em;
max-width: 55px;
&::placeholder {
color: #bebebe;
}
}
*,
*::before,
*::after {
@@ -111,6 +135,11 @@ a {
}
}
ul {
padding: 0;
list-style: none;
}
.app {
color: white;
overflow: hidden;
@@ -131,6 +160,7 @@ a {
flex-direction: column;
background: #333;
padding: 0.4rem;
& > .brand-name {
font-size: calc(1rem + 3.5vw);
@@ -139,18 +169,20 @@ a {
width: calc(1rem + 2.3vw);
}
}
.online {
font-size: calc(0.6rem + 0.4vw);
}
}
&-info {
&-bar {
display: flex;
justify-content: center;
align-items: center;
justify-content: space-between;
font-size: calc(0.8rem + 0.2vw);
background: #222;
padding: 0.3rem;
}
}
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="18px" height="18px"><path d="M0 0h24v24H0z" fill="none"/><path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/></svg>

After

Width:  |  Height:  |  Size: 201 B

-2
View File
@@ -248,8 +248,6 @@ ul {
display: block;
max-height: 100vh;
white-space: nowrap;
border-collapse: collapse;
+26 -27
View File
@@ -5,14 +5,16 @@
class="button-filters"
:class="{'open': filtersOpen}"
@click="filtersOpen = !filtersOpen"
>FILTRY</button>
>
<img :src="require('@/assets/icon-filter2.svg')" alt="icon-filter" /> FILTRY
</button>
</div>
<div class="option-wrapper">
<transition name="slide">
<keep-alive>
<ListFilter v-if="filtersOpen" />
</keep-alive>
</div>
</transition>
</div>
</template>
@@ -36,58 +38,55 @@ export default Vue.extend({
@import "../../styles/variables.scss";
@import "../../styles/responsive.scss";
.slide-enter-active,
.slide-leave-active {
transition: all 0.3s ease;
}
.slide-enter,
.slide-leave-to {
transform: translateX(10px);
opacity: 0;
}
.options {
position: relative;
font-size: calc(0.7rem + 0.5vw);
display: flex;
padding: 1rem;
@include smallScreen() {
flex-direction: column;
padding: 1rem 0;
}
}
button {
color: #e0e0e0;
font-size: 1em;
display: flex;
align-items: center;
background: #333;
border: none;
outline: none;
padding: 0.5em;
padding: 0.2em;
cursor: pointer;
border-left: 3px solid white;
transition: all 0.3s;
@include smallScreen {
border-left: none;
border-top: 2px solid white;
img {
width: 1.3em;
margin: 0.2em;
}
&.open {
color: $accentCol;
border-radius: 1em 0 0 1em;
border: none;
@include smallScreen() {
border-radius: 1em 1em 0 0;
}
font-weight: bold;
}
&:hover {
color: #ffffff;
&:hover,
&:focus {
background: rgba(#e0e0e0, 0.1);
}
img {
width: 45px;
}
}
</style>
+156 -29
View File
@@ -1,19 +1,129 @@
<template>
<div class="list-filter">
<div class="filter-grid">
<div class="grid-row">
<div class="grid-column" v-for="(el, i) in gridElements" :key="i">
<div class="column-title">{{el.title}}</div>
<ul class="grid">
<li class="grid-row">
<div class="grid-col" v-for="(el, i) in gridElements" :key="i">
<div class="grid-item">
<div class="item-title">{{el.title}}</div>
<div class="column-content">
<div class="grid-item" v-for="(item, i) in el.items" :key="i">
<input :type="el.type" :id="item.id" :name="item.name" checked @change="handleChange" />
<label :for="item.id">{{ item.content }}</label>
<div class="item-content">
<div class="item-input" v-for="(item, i) in el.items" :key="i">
<input
:type="el.type"
:id="item.id"
:name="item.name"
checked
@change="handleChange"
/>
<label :for="item.id">{{ item.content }}</label>
</div>
</div>
</div>
</div>
</div>
</div>
</li>
<li class="grid-row">
<div class="grid-col">
<div class="grid-item">
<div class="item-title">Poziomy dyżurnego</div>
<div class="item-content centered">
<div class="item-input" style="text-align: center;">
<input
type="number"
name="level-from"
min="0"
max="25"
value="0"
@change="handleInput"
/>
<span>&nbsp;do&nbsp;</span>
<input
type="number"
name="level-to"
min="0"
max="20"
value="20"
@change="handleInput"
/>
</div>
</div>
</div>
</div>
</li>
<li class="grid-row">
<div class="grid-col">
<div class="grid-item">
<div class="item-title">
Szlaki jednotorowe
<div>(minimum)</div>
</div>
<div class="item-content">
<div class="item-input">
<input
type="number"
name="1track-e"
min="0"
max="5"
placeholder="0"
@change="handleInput"
/>
<span>&nbsp;Zelektryfikowane</span>
</div>
<div class="item-input">
<input
type="number"
name="1track-ne"
min="0"
max="5"
placeholder="0"
@change="handleInput"
/>
<span>&nbsp;Niezelektryfikowane</span>
</div>
</div>
</div>
</div>
<div class="grid-col">
<div class="grid-item">
<div class="item-title">
Szlaki dwutorowe
<div>(minimum)</div>
</div>
<div class="item-content">
<div class="item-input">
<input
type="number"
name="2track-e"
min="0"
max="5"
placeholder="0"
@change="handleInput"
/>
<span>&nbsp;Zelektryfikowane</span>
</div>
<div class="item-input">
<input
type="number"
name="2track-ne"
min="0"
max="5"
placeholder="0"
@change="handleInput"
/>
<span>&nbsp;Niezelektryfikowane</span>
</div>
</div>
</div>
</div>
</li>
</ul>
</div>
</template>
@@ -105,11 +215,15 @@ export default Vue.extend({
}
}),
methods: {
...mapActions(["addFilters", "removeFilters"]),
...mapActions(["setFilter"]),
handleChange(e: any) {
if (e.target.checked) {
this.removeFilters([e.target.name]);
} else this.addFilters([e.target.name]);
this.setFilter({ filterName: e.target.name, value: !e.target.checked });
},
handleInput(e: any) {
this.setFilter({
filterName: e.target.name,
value: parseInt(e.target.value)
});
}
}
});
@@ -117,39 +231,52 @@ export default Vue.extend({
<style lang="scss" scoped>
@import "../../styles/responsive";
@import "../../styles/variables";
.list-filter {
display: flex;
font-size: calc(0.6rem + 0.4vw);
position: absolute;
top: 100%;
left: 0;
padding: 0.3rem;
@include smallScreen() {
width: 100%;
width: 100vw;
}
}
.filter-grid {
background: #333;
border-radius: 0 1rem 1rem 1rem;
padding: 0.3rem;
white-space: nowrap;
font-size: calc(0.6rem + 0.4vw);
}
.grid {
background: rgba(black, 0.65);
padding: 0.5rem;
&-row {
display: flex;
justify-content: center;
flex-wrap: wrap;
@include smallScreen() {
flex-wrap: wrap;
}
}
&-column {
margin: 0.3rem;
padding: 0.2rem;
&-col {
padding: 0.3rem;
}
&-item {
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
}
}
.column-title {
.item-title {
text-align: center;
margin-bottom: 0.3rem;
margin-bottom: 0.5rem;
font-weight: bold;
color: $accentCol;
}
</style>
+134
View File
@@ -0,0 +1,134 @@
import Vue from 'vue'
import Vuex from 'vuex'
import data from '@/data/stations.json';
Vue.use(Vuex)
export default new Vuex.Store({
state: {
stations: [],
filteredStations: [],
filters: [],
trainCount: 0
},
actions: {
fetchStations: async ({ commit }) => {
let onlineStations, statusList, onlineTrains
try {
onlineStations = (await (await fetch('https://api.td2.info.pl:9640/?method=getStationsOnline')).json()).message
statusList = (await (await fetch('https://api.td2.info.pl:9640/?method=readFromSWDR&value=getDispatcherStatusList%3B1')).json()).message
onlineTrains = (await (await fetch('https://api.td2.info.pl:9640/?method=getTrainsOnline')).json()).message
} catch (error) {
throw Error(error.message);
}
commit('setTrainCount', onlineTrains.filter((train) => train.isOnline && train.region === 'eu').length);
const mappedStations = onlineStations
.filter((station) => station.region === 'eu')
.filter((station) => station.isOnline)
.map((
{ stationName = '', stationHash = '', maxUsers = 0, currentUsers = 0, spawnString = '',
dispatcherRate = 0, dispatcherName = '', dispatcherExp = 0, dispatcherId = 0 }) => {
const status = statusList.find((s) => s[0] === stationHash && s[1] === 'eu')
let occupiedTo = "---"
let occupiedTimestamp = 0
if (!status)
occupiedTo = "NIEZALOGOWANY";
else {
let occupiedCode = status[2];
occupiedTimestamp = status[3];
occupiedTo = "NIEDOSTĘPNY";
if (occupiedCode === 0) {
if (occupiedTimestamp - Date.now() > 21000000)
occupiedTo = "BEZ LIMITU";
else
occupiedTo = new Date(status[3])
.toLocaleTimeString('en-US',
{ hour12: false, hour: '2-digit', minute: '2-digit' });
}
if (occupiedCode === 1)
occupiedTo = "Z/W";
if (occupiedCode === 2 && occupiedTimestamp === 0)
occupiedTo = "KOŃCZY";
if (occupiedCode === 3)
occupiedTo = "BRAK MIEJSCA";
}
const trains = onlineTrains.filter((train) =>
train.region === 'eu' && train.isOnline === 1 && train.station.stationName === stationName)
const stationData = data.find((station) => station.stationName === stationName) || { stationName, stationURL: "" }
return {
...stationData,
stationHash,
maxUsers,
currentUsers,
spawnString: spawnString && spawnString.split(';').map(v => v.split(',')[6] ? v.split(',')[6] : v.split(',')[0]),
dispatcherName,
dispatcherRate,
dispatcherId,
dispatcherExp: dispatcherExp < 2 ? 'L' : dispatcherExp,
occupiedTo,
trains
}
})
commit('setStations', mappedStations);
commit('filterStations');
},
addFilters({ commit }, filterId) {
commit('addFilters', filterId);
commit('filterStations');
},
removeFilters({ commit }, filterId) {
commit('removeFilters', filterId);
commit('filterStations');
}
},
mutations: {
setStations: (state, stations) => state.stations = stations,
setTrainCount: (state, count) => state.trainCount = count,
addFilters(state, filters) {
state.filters.push(...filters);
},
removeFilters: (state, filters) => {
filters.forEach(filter => {
state.filters = state.filters.filter((id) => id !== filter);
})
},
filterStations(state) {
state.filteredStations = state.stations.filter((station) => {
if (station.default && state.filters.includes("default")) return false;
if ((!station.default) && state.filters.includes("notDefault")) return false;
if ((station.nonPublic || !station.reqLevel) && (state.filters.includes("nonPublic"))) return false;
if (state.filters.includes(station.controlType)) return false;
if (state.filters.includes(station.signalType)) return false;
if (station.controlType && state.filters.filter((f) => station.controlType.includes(f)).length > 0) return false;
return true;
})
}
},
getters: {
getStations: state => state.filteredStations,
getStationCount: state => state.stations.length,
getTrainCount: state => state.trainCount,
getFilters: state => state.filters,
}
})
+5 -129
View File
@@ -1,134 +1,10 @@
import Vue from 'vue'
import Vuex from 'vuex'
import data from '@/data/stations.json';
import Store from '@/store/modules/store'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
stations: [],
filteredStations: [],
filters: [],
trainCount: 0
},
actions: {
fetchStations: async ({ commit }) => {
let onlineStations, statusList: any, onlineTrains: any
try {
onlineStations = (await (await fetch('https://api.td2.info.pl:9640/?method=getStationsOnline')).json()).message
statusList = (await (await fetch('https://api.td2.info.pl:9640/?method=readFromSWDR&value=getDispatcherStatusList%3B1')).json()).message
onlineTrains = (await (await fetch('https://api.td2.info.pl:9640/?method=getTrainsOnline')).json()).message
} catch (error) {
throw Error(error.message);
}
commit('setTrainCount', onlineTrains.filter((train: any) => train.isOnline && train.region === 'eu').length);
const mappedStations = onlineStations
.filter((station: any) => station.region === 'eu')
.filter((station: any) => station.isOnline)
.map((
{ stationName = '', stationHash = '', maxUsers = 0, currentUsers = 0, spawnString = '',
dispatcherRate = 0, dispatcherName = '', dispatcherExp = 0, dispatcherId = 0 }) => {
const status = statusList.find((s: any) => s[0] === stationHash && s[1] === 'eu')
let occupiedTo: string = "---"
let occupiedTimestamp: number = 0
if (!status)
occupiedTo = "NIEZALOGOWANY";
else {
let occupiedCode: number = status[2];
occupiedTimestamp = status[3];
occupiedTo = "NIEDOSTĘPNY";
if (occupiedCode === 0) {
if (occupiedTimestamp - Date.now() > 21000000)
occupiedTo = "BEZ LIMITU";
else
occupiedTo = new Date(status[3])
.toLocaleTimeString('en-US',
{ hour12: false, hour: '2-digit', minute: '2-digit' });
}
if (occupiedCode === 1)
occupiedTo = "Z/W";
if (occupiedCode === 2 && occupiedTimestamp === 0)
occupiedTo = "KOŃCZY";
if (occupiedCode === 3)
occupiedTo = "BRAK MIEJSCA";
}
const trains: {} = onlineTrains.filter((train: any) =>
train.region === 'eu' && train.isOnline === 1 && train.station.stationName === stationName)
const stationData: {} = data.find((station: any) => station.stationName === stationName) || { stationName, stationURL: "" }
return {
...stationData,
stationHash,
maxUsers,
currentUsers,
spawnString: spawnString && spawnString.split(';').map(v => v.split(',')[6] ? v.split(',')[6] : v.split(',')[0]),
dispatcherName,
dispatcherRate,
dispatcherId,
dispatcherExp: dispatcherExp < 2 ? 'L' : dispatcherExp,
occupiedTo,
trains
}
})
commit('setStations', mappedStations);
commit('filterStations');
},
addFilters({ commit }, filterId) {
commit('addFilters', filterId);
commit('filterStations');
},
removeFilters({ commit }, filterId) {
commit('removeFilters', filterId);
commit('filterStations');
const store = new Vuex.Store({
modules: {
Store
}
},
mutations: {
setStations: (state, stations) => state.stations = stations,
setTrainCount: (state, count) => state.trainCount = count,
addFilters(state: any, filters: string[]) {
state.filters.push(...filters);
},
removeFilters: (state: { filters: string[] }, filters: string[]) => {
filters.forEach(filter => {
state.filters = state.filters.filter((id: string) => id !== filter);
})
},
filterStations(state: any) {
state.filteredStations = state.stations.filter((station: any) => {
if (station.default && state.filters.includes("default")) return false;
if ((!station.default) && state.filters.includes("notDefault")) return false;
if ((station.nonPublic || !station.reqLevel) && (state.filters.includes("nonPublic"))) return false;
if (state.filters.includes(station.controlType)) return false;
if (state.filters.includes(station.signalType)) return false;
if (station.controlType && state.filters.filter((f: string) => station.controlType.includes(f)).length > 0) return false;
return true;
})
}
},
getters: {
getStations: state => state.filteredStations,
getStationCount: state => state.stations.length,
getTrainCount: state => state.trainCount,
getFilters: (state: any) => state.filters,
}
})
export default store
+215
View File
@@ -0,0 +1,215 @@
import { VuexModule, Module, Mutation, Action } from 'vuex-module-decorators';
import data from '@/data/stations.json';
@Module
class Store extends VuexModule {
public trainCount: number = 0;
public stations: {
stationName: string;
stationHash: string;
maxUsers: number;
currentUsers: number;
spawnString: string;
dispatcherRate: number;
dispatcherName: string;
dispatcherExp: number;
dispatcherId: number;
stationLines: string;
stationProject: string;
reqLevel: string;
supportersOnly: string;
signalType: string;
controlType: string;
default: boolean;
nonPublic: boolean;
routes: { oneWay: { catenary: number; noCatenary: number; }, twoWay: { catenary: number; noCatenary: number; } };
}[] = [];
public filteredStations: {}[] = [];
public filters = {
"default": false,
"notDefault": false,
"nonPublic": false,
"SPK": false,
"SCS": false,
"ręczne": false,
"mechaniczne": false,
"współczesna": false,
"kształtowa": false,
"historyczna": false,
"mieszana": false,
"levelFrom": 0,
"levelTo": 20,
"1track-ne": 0,
"2track-ne": 0,
"1track-e": 0,
"2track-e": 0,
} as any;
get getStationCount(): number {
return this.stations.length;
}
get getTrainCount(): number {
return this.trainCount;
}
get getStations() {
return this.filteredStations;
}
get getFilters() {
return this.filters;
}
@Action
public setFilter(payload: { filterName: string, value: number | boolean }) {
this.context.commit('mutateFilter', payload);
this.context.commit('filterStations');
}
@Action
public async fetchStations() {
let onlineStations: {
stationName: string,
stationHash: string,
maxUsers: number,
currentUsers: number,
spawnString: string,
dispatcherRate: number,
dispatcherName: string,
dispatcherExp: number,
dispatcherId: number,
region: string,
isOnline: number
}[];
let statusList: [string, string, number, number][];
let onlineTrains: { isOnline: number, region: string, station: { stationName: string } }[];
try {
onlineStations = (await (await fetch('https://api.td2.info.pl:9640/?method=getStationsOnline')).json()).message
statusList = (await (await fetch('https://api.td2.info.pl:9640/?method=readFromSWDR&value=getDispatcherStatusList%3B1')).json()).message
onlineTrains = (await (await fetch('https://api.td2.info.pl:9640/?method=getTrainsOnline')).json()).message
} catch (error) {
throw Error(error.message);
}
this.context.commit('setTrainCount', onlineTrains.filter((train) => train.isOnline && train.region === 'eu').length);
const mappedStations = onlineStations
.filter((station) => station.region === 'eu')
.filter((station) => station.isOnline)
.map((
{ stationName = '', stationHash = '', maxUsers = 0, currentUsers = 0, spawnString = '',
dispatcherRate = 0, dispatcherName = '', dispatcherExp = 0, dispatcherId = 0 }) => {
const status = statusList.find((s) => s[0] === stationHash && s[1] === 'eu')
let occupiedTo = "---"
let occupiedTimestamp = 0
if (!status)
occupiedTo = "NIEZALOGOWANY";
else {
let occupiedCode = status[2];
occupiedTimestamp = status[3];
occupiedTo = "NIEDOSTĘPNY";
if (occupiedCode === 0) {
if (occupiedTimestamp - Date.now() > 21000000)
occupiedTo = "BEZ LIMITU";
else
occupiedTo = new Date(status[3])
.toLocaleTimeString('en-US',
{ hour12: false, hour: '2-digit', minute: '2-digit' });
}
if (occupiedCode === 1)
occupiedTo = "Z/W";
if (occupiedCode === 2 && occupiedTimestamp === 0)
occupiedTo = "KOŃCZY";
if (occupiedCode === 3)
occupiedTo = "BRAK MIEJSCA";
}
const trains = onlineTrains.filter((train) =>
train.region === 'eu' && train.isOnline && train.station.stationName === stationName)
const stationData = data.find((station) => station.stationName === stationName) || { stationName, stationURL: "" }
return {
...stationData,
stationHash,
maxUsers,
currentUsers,
spawnString: spawnString && spawnString.split(';').map(v => v.split(',')[6] ? v.split(',')[6] : v.split(',')[0]),
dispatcherName,
dispatcherRate,
dispatcherId,
dispatcherExp: dispatcherExp < 2 ? 'L' : dispatcherExp,
occupiedTo,
trains
}
})
this.context.commit('setStations', mappedStations);
this.context.commit('filterStations');
}
@Mutation
public filterStations() {
this.filteredStations = this.stations.filter(station => {
if ((station.nonPublic || !station.reqLevel) && this.filters['nonPublic']) return false;
if (!station.reqLevel) return true;
if (station.default && this.filters['default']) return false;
if (!station.default && this.filters['notDefault']) return false;
if (station.reqLevel < this.filters['level-from']) return false;
if (station.reqLevel > this.filters['level-to']) return false;
if (station.routes.oneWay.catenary < this.filters['1track-e']) return false;
if (station.routes.oneWay.noCatenary < this.filters['1track-ne']) return false;
if (station.routes.twoWay.catenary < this.filters['2track-e']) return false;
if (station.routes.twoWay.noCatenary < this.filters['2track-ne']) return false;
if (this.filters[station.controlType]) return false;
if (this.filters[station.signalType]) return false;
if (this.filters["SPK"] && station.controlType.includes("SPK")) return false;
if (this.filters["SCS"] && station.controlType.includes("SCS")) return false;
if (this.filters["mechaniczne"] && station.controlType.includes("mechaniczne")) return false;
if (this.filters["ręczne"] && station.controlType.includes("ręczne")) return false;
return true;
})
}
@Mutation
public setStations(stations: []) {
this.stations = stations;
}
@Mutation
public setTrainCount(count: number) {
this.trainCount = count;
}
@Mutation
public mutateFilter(payload: { filterName: string, value: number | boolean }) {
this.filters[payload.filterName] = payload.value;
}
}
export default Store;
+1 -1
View File
@@ -1,5 +1,5 @@
@mixin smallScreen() {
@media only screen and (max-width: 850px) {
@media only screen and (max-width: 650px) {
@content;
}
}
+1 -1
View File
@@ -6,5 +6,5 @@ $bgCol: #525252;
$errorCol: #ff1919;
$warningCol: #ff975b;
$accentCol: #ffbb00;
$accentCol: #ffc62b;
$accent2Col: #ff3d5d;
+2 -3
View File
@@ -1,6 +1,5 @@
<template>
<div class="home">
<Options />
<List />
</div>
</template>
@@ -8,11 +7,11 @@
<script lang="ts">
import { Vue, Component } from "vue-property-decorator";
import Options from "@/components/ui/Options.vue";
// import Options from "@/components/ui/Options.vue";
import List from "@/components/ui/List.vue";
@Component({
components: { Options, List }
components: { List }
})
export default class Home extends Vue {
mounted() {}