chore(filters): added filtering stations by external and internal route groups; changed sliders to min & max range

This commit is contained in:
2026-03-11 18:17:12 +01:00
parent 7073c0687c
commit 1a39c9054b
4 changed files with 310 additions and 129 deletions
@@ -0,0 +1,140 @@
<template>
<div class="filter-slider">
<input
v-for="slider in sliderOptionsList[sliderGroup]"
type="range"
:name="slider.id"
:id="slider.id"
:min="slider.minRange"
:max="slider.maxRange"
:step="slider.step"
v-model="filters[slider.id]"
/>
</div>
</template>
<script lang="ts" setup>
import { inject, PropType } from 'vue';
import { SliderGroup, sliderOptionsList } from '../../managers/stationFilterManager';
const filters = inject('StationsView_filters') as Record<string, any>;
const props = defineProps({
sliderGroup: {
type: Object as PropType<SliderGroup>,
required: true
}
});
</script>
<style lang="scss" scoped>
@use '../../styles/responsive';
.filter-slider {
position: relative;
padding: 0.5em;
}
.filter-slider > input {
position: absolute;
width: 100%;
top: 50%;
transform: translateY(-50%);
-webkit-appearance: none;
appearance: none;
background: none;
border: none;
outline: none;
pointer-events: none;
&:focus-visible ~ * {
color: gold;
}
&::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
pointer-events: all;
z-index: 100;
height: 20px;
width: 20px;
margin-top: -7px;
border-radius: 50%;
background-color: var(--clr-primary);
@include responsive.smallScreen {
width: 15px;
height: 15px;
margin-top: -5px;
}
}
&::-moz-range-thumb {
pointer-events: all;
position: relative;
z-index: 10;
height: 1em;
width: 1em;
border-radius: 50%;
background-color: #333;
border: 3px solid var(--clr-primary);
cursor: pointer;
@include responsive.smallScreen {
width: 1em;
height: 1em;
}
}
&::-webkit-slider-runnable-track {
position: relative;
z-index: 1;
width: 100%;
height: 5px;
cursor: pointer;
border-radius: 1em;
}
&:first-child::-webkit-slider-runnable-track {
background: var(--clr-primary);
}
&::-moz-range-track {
position: relative;
z-index: -1;
width: 100%;
height: 5px;
cursor: pointer;
background: none;
border-radius: 1em;
}
&:first-child::-moz-range-track {
background: var(--clr-primary);
}
&::-ms-track {
width: 100%;
height: 5px;
cursor: pointer;
background: none;
border-radius: 1em;
}
&:first-child::-ms-track {
background: white;
}
}
</style>
@@ -137,20 +137,16 @@
</section> </section>
<section class="card_sliders"> <section class="card_sliders">
<div class="slider" v-for="(slider, i) in sliderStates" :key="i"> <div class="slider-box" v-for="(sliderGroup, i) in sliderGroups" :key="i">
<input <FilterSlider :sliderGroup="sliderGroup" />
class="slider-input"
type="range" <span class="slider-value">
:name="slider.id" {{ filters[sliderOptionsList[sliderGroup][0].id] }} -
:id="slider.id" {{ filters[sliderOptionsList[sliderGroup][1].id] }}
:min="slider.minRange" </span>
:max="slider.maxRange"
:step="slider.step"
v-model.number="filters[slider.id]"
/>
<span class="slider-value">{{ filters[slider.id] }}</span>
<div class="slider-content"> <div class="slider-content">
{{ $t(`filters.sliders.${slider.id}`) }} {{ $t(`filters.sliders.${sliderGroups[i]}`) }}
</div> </div>
</div> </div>
</section> </section>
@@ -190,13 +186,15 @@ import routerMixin from '../../mixins/routerMixin';
import { useMainStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
import FilterOption from './FilterOption.vue'; import FilterOption from './FilterOption.vue';
import FilterSlider from './FilterSlider.vue';
import StorageManager from '../../managers/storageManager'; import StorageManager from '../../managers/storageManager';
import { import {
filtersSections, filtersSections,
sliderStates,
initFilters, initFilters,
getChangedFilters sliderGroups,
getChangedFilters,
sliderOptionsList
} from '../../managers/stationFilterManager'; } from '../../managers/stationFilterManager';
import { StationFilterSection } from '../../managers/stationFilterManager'; import { StationFilterSection } from '../../managers/stationFilterManager';
@@ -206,14 +204,15 @@ import { watch } from 'vue';
const STORAGE_KEY = 'options_saved'; const STORAGE_KEY = 'options_saved';
export default defineComponent({ export default defineComponent({
components: { FilterOption }, components: { FilterOption, FilterSlider },
mixins: [keyMixin, routerMixin], mixins: [keyMixin, routerMixin],
data: () => ({ data: () => ({
saveOptions: false, saveOptions: false,
filtersSections, filtersSections,
sliderStates, sliderGroups,
sliderOptionsList,
minimumHours: 0, minimumHours: 0,
@@ -516,7 +515,7 @@ h3.hours-section-header {
.section-filters { .section-filters {
display: grid; display: grid;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
gap: 0.5em; gap: 0.5em;
margin: 1em 0; margin: 1em 0;
} }
@@ -528,9 +527,11 @@ h3.hours-section-header {
-moz-user-select: none; -moz-user-select: none;
span { span {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
cursor: pointer; cursor: pointer;
display: inline-block;
width: 100%;
text-align: center; text-align: center;
padding: 0.25em; padding: 0.25em;
font-weight: bold; font-weight: bold;
@@ -588,10 +589,14 @@ h3.hours-section-header {
} }
} }
.slider { .card_sliders {
margin-top: 1em;
}
.slider-box {
display: grid; display: grid;
grid-template-columns: 1fr 50px 1fr;
align-items: center; align-items: center;
grid-template-columns: 250px 100px 1fr;
gap: 0.25em; gap: 0.25em;
margin-bottom: 1em; margin-bottom: 1em;
@@ -601,88 +606,14 @@ h3.hours-section-header {
padding: 0.1em 0.2em; padding: 0.1em 0.2em;
text-align: center; text-align: center;
} }
&-input {
-webkit-appearance: none;
appearance: none;
background: none;
border: none;
outline: none;
min-width: 25%;
&:focus-visible ~ * {
color: gold;
} }
&::-webkit-slider-thumb { .slider-value {
-webkit-appearance: none; text-align: center;
appearance: none;
height: 20px;
width: 20px;
margin-top: -7px;
border-radius: 50%;
background: white;
border: 3px solid var(--clr-primary);
background-color: #333;
@include responsive.smallScreen {
width: 15px;
height: 15px;
margin-top: -5px;
border: 3px solid var(--clr-primary);
}
}
&::-moz-range-thumb {
height: 1em;
width: 1em;
border-radius: 50%;
background: white;
border: 4px solid var(--clr-primary);
cursor: pointer;
@include responsive.smallScreen {
width: 1em;
height: 1em;
border: 3px solid var(--clr-primary);
}
}
&::-webkit-slider-runnable-track {
width: 100%;
height: 5px;
cursor: pointer;
background: #ffffff;
border-radius: 1em;
}
&::-moz-range-track {
width: 100%;
height: 5px;
cursor: pointer;
background: #ffffff;
border-radius: 1em;
}
&::-ms-track {
width: 100%;
height: 5px;
cursor: pointer;
background: #ffffff;
border-radius: 1em;
}
}
} }
@include responsive.smallScreen { @include responsive.smallScreen {
.slider { .slider-box {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: center; justify-content: center;
+23 -13
View File
@@ -247,7 +247,9 @@
"blockades": "BLOKADY LINIOWE", "blockades": "BLOKADY LINIOWE",
"status": "STATUS ONLINE", "status": "STATUS ONLINE",
"timetables": "AKTYWNE ROZKŁADY JAZDY", "timetables": "AKTYWNE ROZKŁADY JAZDY",
"spawns": "OTWARTE SPAWNY" "spawns": "OTWARTE SPAWNY",
"externalRoutes": "SZLAKI ZEWNĘTRZNE",
"internalRoutes": "SZLAKI WEWNĘTRZNE"
}, },
"changed-filters-count": "Zmienione filtry:", "changed-filters-count": "Zmienione filtry:",
"no-changed-filters": "Brak zmienionych filtrów", "no-changed-filters": "Brak zmienionych filtrów",
@@ -291,19 +293,27 @@
"withoutActiveTimetables": "BEZ AKTYWNYCH", "withoutActiveTimetables": "BEZ AKTYWNYCH",
"junction": "WĘZŁOWE", "junction": "WĘZŁOWE",
"nonJunction": "INNE", "nonJunction": "INNE",
"oneWay": "JEDNOTOROWE NIEZELEKTRYFIKOWANE",
"oneWayCatenary": "JEDNOTOROWE ZELEKTRYFIKOWANE",
"twoWayCatenary": "DWUTOROWE ZELEKTRYFIKOWANE",
"twoWay": "DWUTOROWE NIEZELEKTRYFIKOWANE",
"oneWayCatenaryInt": "JEDNOTOROWE ZELEKTRYFIKOWANE",
"oneWayInt": "JEDNOTOROWE NIEZELEKTRYFIKOWANE",
"twoWayCatenaryInt": "DWUTOROWE ZELEKTRYFIKOWANE",
"twoWayInt": "DWUTOROWE NIEZELEKTRYFIKOWANE",
"sliders": { "sliders": {
"minLevel": "MIN. WYMAGANY POZIOM DYŻURNEGO", "vMax": "PRĘDKOŚĆ SZLAKOWA",
"maxLevel": "MAKS. WYMAGANY POZIOM DYŻURNEGO", "level": "WYMAGANY POZIOM DYŻURNEGO",
"minVmax": "MIN. PRĘDKOŚĆ SZLAKOWA", "routeOneWay": "SZLAKI JEDNOTOROWE NIEZELEKTR.",
"maxVmax": "MAKS. PRĘDKOŚĆ SZLAKOWA", "routeOneWayCatenary": "SZLAKI JEDNOTOROWE ZELEKTR.",
"minOneWayCatenary": "SZLAKI JEDNOTOROWE ZELEKTR. (MINIMUM)", "routeTwoWayCatenary": "SZLAKI DWUTOROWE ZELEKTR.",
"minOneWay": "SZLAKI JEDNOTOROWE NIEZELEKTR. (MINIMUM)", "routeTwoWay": "SZLAKI DWUTOROWE NIEZELEKTR.",
"minTwoWayCatenary": "SZLAKI DWUTOROWE ZELEKTR. (MINIMUM)", "routeOneWayInternalCatenary": "SZLAKI JEDNOTOROWE ZELEKTR. WEWNĘTRZNE",
"minTwoWay": "SZLAKI DWUTOROWE NIEZELEKTR. (MINIMUM)", "routeOneWayInternal": "SZLAKI JEDNOTOROWE NIEZELEKTR. WEWNĘTRZNE",
"minOneWayCatenaryInt": "SZLAKI JEDNOTOROWE ZELEKTR. WEWNĘTRZNE (MINIMUM)", "routeTwoWayInternalCatenary": "SZLAKI DWUTOROWE ZELEKTR. WEWNĘTRZNE",
"minOneWayInt": "SZLAKI JEDNOTOROWE NIEZELEKTR. WEWNĘTRZNE (MINIMUM)", "routeTwoWayInternal": "SZLAKI DWUTOROWE NIEZELEKTR. WEWNĘTRZNE"
"minTwoWayCatenaryInt": "SZLAKI DWUTOROWE ZELEKTR. WEWNĘTRZNE (MINIMUM)",
"minTwoWayInt": "SZLAKI DWUTOROWE NIEZELEKTR. WEWNĘTRZNE (MINIMUM)"
}, },
"sceneries-placeholder": "Wyszukaj scenerię", "sceneries-placeholder": "Wyszukaj scenerię",
"line-numbers-placeholder": "Numery linii (oddzielone przecinkami)", "line-numbers-placeholder": "Numery linii (oddzielone przecinkami)",
+116 -16
View File
@@ -1,5 +1,24 @@
import StorageManager from './storageManager'; import StorageManager from './storageManager';
export type SliderGroup =
| 'vMax'
| 'level'
| 'routeOneWay'
| 'routeOneWayCatenary'
| 'routeOneWayInternal'
| 'routeOneWayInternalCatenary'
| 'routeTwoWay'
| 'routeTwoWayCatenary'
| 'routeTwoWayInternal'
| 'routeTwoWayInternalCatenary';
export interface SliderOptions {
id: string;
minRange: number;
maxRange: number;
step: number;
}
export const sections = [ export const sections = [
'status', 'status',
'timetables', 'timetables',
@@ -10,7 +29,9 @@ export const sections = [
'control', 'control',
'blockades', 'blockades',
'signals', 'signals',
'addons' 'addons',
'externalRoutes',
'internalRoutes'
] as const; ] as const;
export const initFilters = { export const initFilters = {
@@ -60,34 +81,111 @@ export const initFilters = {
onlineFromHours: 0, onlineFromHours: 0,
minLevel: 0, minLevel: 0,
maxLevel: 20, maxLevel: 20,
oneWay: false,
oneWayCatenary: false,
twoWay: false,
twoWayCatenary: false,
oneWayCatenaryInt: false,
oneWayInt: false,
twoWayInt: false,
twoWayCatenaryInt: false,
minOneWay: 0, minOneWay: 0,
minOneWayCatenary: 0, minOneWayCatenary: 0,
minOneWayInt: 0,
minOneWayCatenaryInt: 0, minOneWayCatenaryInt: 0,
minOneWayInt: 0,
minTwoWay: 0, minTwoWay: 0,
minTwoWayCatenary: 0, minTwoWayCatenary: 0,
minTwoWayInt: 0, minTwoWayInt: 0,
minTwoWayCatenaryInt: 0, minTwoWayCatenaryInt: 0,
maxOneWay: 5,
maxOneWayCatenary: 5,
maxOneWayInt: 5,
maxOneWayCatenaryInt: 5,
maxTwoWay: 5,
maxTwoWayCatenary: 5,
maxTwoWayInt: 5,
maxTwoWayCatenaryInt: 5,
authors: '', authors: '',
projects: '', projects: '',
lines: '' lines: ''
}; };
export const sliderStates = [ export const sliderGroups: SliderGroup[] = [
{ id: 'maxVmax', minRange: 0, maxRange: 200, step: 10 }, 'vMax',
{ id: 'minVmax', minRange: 0, maxRange: 200, step: 10 }, 'level',
{ id: 'minLevel', minRange: 0, maxRange: 20, step: 1 }, 'routeOneWay',
{ id: 'maxLevel', minRange: 0, maxRange: 20, step: 1 }, 'routeOneWayCatenary',
{ id: 'minOneWay', minRange: 0, maxRange: 5, step: 1 }, 'routeOneWayInternal',
{ id: 'minOneWayCatenary', minRange: 0, maxRange: 5, step: 1 }, 'routeOneWayInternalCatenary',
{ id: 'minOneWayInt', minRange: 0, maxRange: 5, step: 1 }, 'routeTwoWay',
{ id: 'minOneWayCatenaryInt', minRange: 0, maxRange: 5, step: 1 }, 'routeTwoWayCatenary',
{ id: 'minTwoWay', minRange: 0, maxRange: 5, step: 1 }, 'routeTwoWayInternal',
{ id: 'minTwoWayCatenary', minRange: 0, maxRange: 5, step: 1 }, 'routeTwoWayInternalCatenary'
{ id: 'minTwoWayInt', minRange: 0, maxRange: 5, step: 1 },
{ id: 'minTwoWayCatenaryInt', minRange: 0, maxRange: 5, step: 1 }
]; ];
export const sliderOptionsList: Record<SliderGroup, SliderOptions[]> = {
vMax: [
{ id: 'minVmax', minRange: 0, maxRange: 200, step: 10 },
{ id: 'maxVmax', minRange: 0, maxRange: 200, step: 10 }
],
level: [
{ id: 'minLevel', minRange: 0, maxRange: 20, step: 1 },
{ id: 'maxLevel', minRange: 0, maxRange: 20, step: 1 }
],
routeOneWay: [
{ id: 'minOneWay', minRange: 0, maxRange: 5, step: 1 },
{ id: 'maxOneWay', minRange: 0, maxRange: 5, step: 1 }
],
routeOneWayCatenary: [
{ id: 'minOneWayCatenary', minRange: 0, maxRange: 5, step: 1 },
{ id: 'maxOneWayCatenary', minRange: 0, maxRange: 5, step: 1 }
],
routeOneWayInternal: [
{ id: 'minOneWayInt', minRange: 0, maxRange: 5, step: 1 },
{ id: 'maxOneWayInt', minRange: 0, maxRange: 5, step: 1 }
],
routeOneWayInternalCatenary: [
{
id: 'minOneWayCatenaryInt',
minRange: 0,
maxRange: 5,
step: 1
},
{
id: 'maxOneWayCatenaryInt',
minRange: 0,
maxRange: 5,
step: 1
}
],
routeTwoWay: [
{ id: 'minTwoWay', minRange: 0, maxRange: 5, step: 1 },
{ id: 'maxTwoWay', minRange: 0, maxRange: 5, step: 1 }
],
routeTwoWayCatenary: [
{ id: 'minTwoWayCatenary', minRange: 0, maxRange: 5, step: 1 },
{ id: 'maxTwoWayCatenary', minRange: 0, maxRange: 5, step: 1 }
],
routeTwoWayInternal: [
{ id: 'minTwoWayInt', minRange: 0, maxRange: 5, step: 1 },
{ id: 'maxTwoWayInt', minRange: 0, maxRange: 5, step: 1 }
],
routeTwoWayInternalCatenary: [
{
id: 'minTwoWayCatenaryInt',
minRange: 0,
maxRange: 5,
step: 1
},
{
id: 'maxTwoWayCatenaryInt',
minRange: 0,
maxRange: 5,
step: 1
}
]
};
export type StationFilter = keyof typeof initFilters; export type StationFilter = keyof typeof initFilters;
export type StationFilterSection = (typeof sections)[number]; export type StationFilterSection = (typeof sections)[number];
@@ -112,7 +210,9 @@ export const filtersSections: Record<StationFilterSection, StationFilter[]> = {
'manual' 'manual'
], ],
blockades: ['SBL', 'PBL'], blockades: ['SBL', 'PBL'],
signals: ['modern', 'semaphores', 'mixed', 'historical'] signals: ['modern', 'semaphores', 'mixed', 'historical'],
externalRoutes: ['oneWay', 'oneWayCatenary', 'twoWay', 'twoWayCatenary'],
internalRoutes: ['oneWayInt', 'oneWayCatenaryInt', 'twoWayInt', 'twoWayCatenaryInt']
}; };
export function setupFilters(currentFilters: Record<string, any>) { export function setupFilters(currentFilters: Record<string, any>) {