Filtry pociągów

This commit is contained in:
2022-04-14 00:21:22 +02:00
parent e3735c171f
commit 414fd5d5b9
8 changed files with 184 additions and 124 deletions
@@ -1,67 +0,0 @@
<template>
<div class="train-filters">
<span v-for="category in availableCategories" :key="category">
{{ category }}
</span>
</div>
</template>
<script lang="ts">
import Train from '@/scripts/interfaces/Train';
import { computed, defineComponent, inject, TrainFilter } from 'vue';
const defaultFilters: TrainFilter[] = [
{
id: "comments",
isActive: true
},
{
id: "twr",
isActive: true
},
{
id: "skr",
isActive: true
}
]
export default defineComponent({
props: {
trainList: {
type: Object as () => Train[],
required: true
}
},
setup(props) {
const filters = inject('filtersActive') as TrainFilter[];
const trainList = props.trainList;
// Setup default train filters
filters.push(...defaultFilters);
const availableCategories = computed(() => trainList.reduce((acc, train) => {
if(!train.timetableData) return acc;
if(acc.includes(train.timetableData.category)) return acc;
acc.push(train.timetableData.category);
return acc;
}, [] as string[]));
// Remove unavailable train categories
for(let filter of filters) {
if(availableCategories.value.includes(filter.id)) continue;
filters.slice(filters.indexOf(filter), -1);
}
return {
availableCategories
};
},
});
</script>
<style lang="scss" scoped></style>
+70 -5
View File
@@ -26,11 +26,37 @@
</div> </div>
</div> </div>
</div> </div>
<div class="filters">
<span
:class="{ active: filter.isActive }"
class="filter"
v-for="filter in filterList"
:key="filter.id"
tabindex="0"
@contextmenu="
(e) => {
e.preventDefault();
return false;
}
"
@click.left="toggleFilter(filter)"
@keydown.enter="toggleFilter(filter)"
@click.right="setFilterOnly(filter)"
@keydown.space="setFilterOnly(filter)"
>
{{ $t(`trains.filter-${filter.id}`) }}
</span>
<span class="filter reset-btn" @click="resetFilters" tabindex="0">
{{ $t('trains.filter-reset') }}
</span>
</div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, inject } from 'vue'; import { computed, defineComponent, inject, TrainFilter } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import SelectBox from '../Global/SelectBox.vue'; import SelectBox from '../Global/SelectBox.vue';
@@ -50,10 +76,6 @@ export default defineComponent({
id: 'distance', id: 'distance',
value: 'kilometraż', value: 'kilometraż',
}, },
{
id: 'comments',
value: 'komentarze',
},
{ {
id: 'progress', id: 'progress',
value: 'przebyta trasa', value: 'przebyta trasa',
@@ -76,6 +98,8 @@ export default defineComponent({
}, },
]; ];
let filterList = inject('filterList') as TrainFilter[];
const translatedSorterOptions = computed(() => const translatedSorterOptions = computed(() =>
sorterOptions.map(({ id }) => ({ sorterOptions.map(({ id }) => ({
id, id,
@@ -88,6 +112,7 @@ export default defineComponent({
searchedTrain: inject('searchedTrain') as string, searchedTrain: inject('searchedTrain') as string,
searchedDriver: inject('searchedDriver') as string, searchedDriver: inject('searchedDriver') as string,
sorterActive: inject('sorterActive') as { id: string | number; dir: number }, sorterActive: inject('sorterActive') as { id: string | number; dir: number },
filterList,
}; };
}, },
@@ -96,6 +121,18 @@ export default defineComponent({
this.sorterActive.id = item.id; this.sorterActive.id = item.id;
this.sorterActive.dir = -1; this.sorterActive.dir = -1;
}, },
toggleFilter(filter: TrainFilter) {
filter.isActive = !filter.isActive;
},
setFilterOnly(filter: TrainFilter) {
this.filterList.forEach((f) => (f.isActive = f.id == filter.id));
},
resetFilters() {
this.filterList.forEach((f) => (f.isActive = true));
},
}, },
}); });
</script> </script>
@@ -108,6 +145,7 @@ export default defineComponent({
width: 100%; width: 100%;
} }
} }
.options { .options {
&_wrapper { &_wrapper {
display: flex; display: flex;
@@ -178,4 +216,31 @@ export default defineComponent({
width: 1em; width: 1em;
} }
} }
.filters {
display: flex;
flex-wrap: wrap;
@include smallScreen() {
justify-content: center;
}
}
.filter {
background: #333;
padding: 0.2em 0.25em;
margin: 0.25em 0.25em 0 0;
font-weight: bold;
cursor: pointer;
color: gray;
&.active {
color: gold;
}
&.reset-btn {
color: salmon;
}
}
</style> </style>
+9
View File
@@ -145,6 +145,15 @@
"option-delay": "current delay", "option-delay": "current delay",
"option-comments": "comments", "option-comments": "comments",
"filter-comments": "comments",
"filter-twr": "TWR",
"filter-skr": "SKR",
"filter-passenger": "passenger",
"filter-freight": "freight",
"filter-other": "other",
"filter-noTimetable": "no timetable",
"filter-reset": "X RESET",
"sorter-prefix": "Sort: ", "sorter-prefix": "Sort: ",
"search-train": "Train no.", "search-train": "Train no.",
"search-driver": "Driver name", "search-driver": "Driver name",
+10
View File
@@ -146,6 +146,16 @@
"option-delay": "opóźnienie", "option-delay": "opóźnienie",
"option-comments": "uwagi ekspl.", "option-comments": "uwagi ekspl.",
"filter-comments": "uwagi ekspl.",
"filter-twr": "TWR",
"filter-skr": "SKR",
"filter-passenger": "pasażerskie",
"filter-freight": "towarowe",
"filter-other": "inne",
"filter-noTimetable": "bez RJ",
"filter-reset": "X RESETUJ",
"sorter-prefix": "Sortuj: ", "sorter-prefix": "Sortuj: ",
"search-train": "Numer pociągu", "search-train": "Numer pociągu",
"search-driver": "Nick maszynisty", "search-driver": "Nick maszynisty",
+9
View File
@@ -0,0 +1,9 @@
export const enum TrainFilterType {
comments = "comments",
twr = "twr",
skr = "skr",
passenger = "passenger",
freight = "freight",
other = "other",
noTimetable = "noTimetable"
}
+36 -32
View File
@@ -1,4 +1,5 @@
import { TrainFilter } from "vue"; import { TrainFilter } from "vue";
import { TrainFilterType } from "../enums/TrainFilterType";
import Train from "../interfaces/Train"; import Train from "../interfaces/Train";
import TrainStop from "../interfaces/TrainStop"; import TrainStop from "../interfaces/TrainStop";
@@ -19,11 +20,41 @@ function currentDelay(stops: TrainStop[] | undefined) {
}; };
function filterTrainList(trainList: Train[], searchedTrain: string, searchedDriver: string, filters: TrainFilter[]) { function filterTrainList(trainList: Train[], searchedTrain: string, searchedDriver: string, filters: TrainFilter[]) {
return trainList.filter( return trainList.filter(
(train) => (train) => {
(searchedTrain.length > 0 ? train.trainNo.toString().startsWith(searchedTrain) : true) && const isFiltered = filters.every(f => {
(searchedDriver.length > 0 ? train.driverName.toLowerCase().startsWith(searchedDriver.toLowerCase()) : true) if (f.isActive) return true;
if (!train.timetableData) return filters.find(filter => filter.id == TrainFilterType.noTimetable)!.isActive;
switch (f.id) {
case TrainFilterType.comments:
return !train.timetableData.followingStops.some(stop => stop.comments);
case TrainFilterType.twr:
return !train.timetableData.TWR;
case TrainFilterType.skr:
return !train.timetableData.SKR;
case TrainFilterType.passenger:
return !/^[AMRE]\D{2}$/.test(train.timetableData.category);
case TrainFilterType.freight:
return !train.timetableData.category.startsWith('T');
case TrainFilterType.other:
return !/^[PXZ]\D{2}$/.test(train.timetableData.category);
default:
return true;
}
})
return (searchedTrain.length > 0 ? train.trainNo.toString().startsWith(searchedTrain) : true) &&
(searchedDriver.length > 0 ? train.driverName.toLowerCase().startsWith(searchedDriver.toLowerCase()) : true) && isFiltered
}
); );
} }
@@ -78,34 +109,7 @@ export function filteredTrainList(
sorterActive: { id: string; dir: number }, sorterActive: { id: string; dir: number },
filters: TrainFilter[] filters: TrainFilter[]
) { ) {
let finalTrainList: Train[] = [];
const filtered = filterTrainList(trainList, searchedTrain, searchedDriver, filters); const filtered = filterTrainList(trainList, searchedTrain, searchedDriver, filters);
return [...sortTrainList(filtered, sorterActive)];
switch (sorterActive.id) {
case 'comments':
const trainsSortedByComments = filtered
.sort((a, b) => {
const commentsA = a.timetableData?.followingStops.some((s) => s.comments) ? 1 : 0;
const commentsB = b.timetableData?.followingStops.some((s) => s.comments) ? 1 : 0;
return commentsB - commentsA;
});
const trainsWithComments = trainsSortedByComments.filter((train) =>
train.timetableData?.followingStops.some((s) => s.comments)
);
const trainsWithoutComments = trainsSortedByComments.slice(trainsWithComments.length);
finalTrainList.push(...trainsWithComments);
finalTrainList.push(...sortTrainList(trainsWithoutComments, sorterActive));
break;
default:
finalTrainList.push(...sortTrainList(filtered, sorterActive));
break;
}
return finalTrainList;
}; };
+35 -6
View File
@@ -14,7 +14,7 @@
<script lang="ts"> <script lang="ts">
import { computed, ComputedRef, defineComponent, provide, reactive, ref, TrainFilter } from 'vue'; import { computed, ComputedRef, defineComponent, provide, reactive, ref, TrainFilter } from 'vue';
import { filteredTrainList } from "@/scripts/managers/trainFilterManager"; import { filteredTrainList } from '@/scripts/managers/trainFilterManager';
import Train from '@/scripts/interfaces/Train'; import Train from '@/scripts/interfaces/Train';
@@ -24,7 +24,7 @@ import TrainOptions from '@/components/TrainsView/TrainOptions.vue';
import { useStore } from '@/store'; import { useStore } from '@/store';
import { GETTERS } from '@/constants/storeConstants'; import { GETTERS } from '@/constants/storeConstants';
import { TrainFilterType } from '@/scripts/enums/TrainFilterType';
export default defineComponent({ export default defineComponent({
components: { components: {
@@ -45,10 +45,39 @@ export default defineComponent({
const trainList: ComputedRef<Train[]> = computed(() => store.getters[GETTERS.trainList]); const trainList: ComputedRef<Train[]> = computed(() => store.getters[GETTERS.trainList]);
// const timetableDataStatus: ComputedRef<DataStatus> = computed(() => store.getters[GETTERS.timetableDataStatus]); const initFilters: TrainFilter[] = [
{
id: TrainFilterType.twr,
isActive: true,
},
{
id: TrainFilterType.skr,
isActive: true,
},
{
id: TrainFilterType.passenger,
isActive: true,
},
{
id: TrainFilterType.freight,
isActive: true,
},
{
id: TrainFilterType.other,
isActive: true,
},
{
id: TrainFilterType.comments,
isActive: true,
},
{
id: TrainFilterType.noTimetable,
isActive: true,
},
];
const sorterActive = ref({ id: 'distance', dir: -1 }); const sorterActive = ref({ id: 'distance', dir: -1 });
const filtersActive = reactive([]) as TrainFilter[]; const filterList = reactive([...initFilters]) as TrainFilter[];
const searchedDriver = ref(''); const searchedDriver = ref('');
const searchedTrain = ref(''); const searchedTrain = ref('');
@@ -56,7 +85,7 @@ export default defineComponent({
provide('searchedTrain', searchedTrain); provide('searchedTrain', searchedTrain);
provide('searchedDriver', searchedDriver); provide('searchedDriver', searchedDriver);
provide('sorterActive', sorterActive); provide('sorterActive', sorterActive);
provide('filtersActive', filtersActive); provide('filterList', filterList);
const computedTrains: ComputedRef<Train[]> = computed(() => { const computedTrains: ComputedRef<Train[]> = computed(() => {
return filteredTrainList( return filteredTrainList(
@@ -64,7 +93,7 @@ export default defineComponent({
searchedTrain.value, searchedTrain.value,
searchedDriver.value, searchedDriver.value,
sorterActive.value, sorterActive.value,
filtersActive filterList
); );
}); });
+2 -1
View File
@@ -1,5 +1,6 @@
import { ComponentCustomProperties } from 'vue' import { ComponentCustomProperties } from 'vue'
import { Store } from 'vuex' import { Store } from 'vuex'
import { TrainFilterType } from './scripts/enums/TrainFilterType';
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
// declare your own store states // declare your own store states
@@ -14,7 +15,7 @@ declare module '@vue/runtime-core' {
// Train filter for TrainView // Train filter for TrainView
interface TrainFilter { interface TrainFilter {
id: string; id: TrainFilterType;
isActive: boolean; isActive: boolean;
} }
} }