rozszerzony wybór regionów przez URL; poprawki headerów

This commit is contained in:
2023-11-02 17:40:31 +01:00
parent 1d7fc2955f
commit d9da49a867
5 changed files with 108 additions and 145 deletions
+5 -42
View File
@@ -39,9 +39,9 @@
<img src="/images/icon-train.svg" alt="icon train" />
</div>
<span class="info_region">
<SelectBox :itemList="computedRegions" :defaultItemIndex="0" @selected="changeRegion" />
</span>
<div class="info_region">
<RegionDropdown />
</div>
</span>
<span class="header_links">
@@ -69,10 +69,9 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { useStore } from '../../store/store';
import options from '../../data/options.json';
import SelectBox from '../Global/SelectBox.vue';
import StatusIndicator from './StatusIndicator.vue';
import Clock from './Clock.vue';
import RegionDropdown from '../Global/RegionDropdown.vue';
export default defineComponent({
emits: ['changeLang'],
@@ -90,10 +89,6 @@ export default defineComponent({
},
methods: {
changeRegion(region: { id: string; value: string }) {
this.store.changeRegion(region);
},
changeLang(lang: string) {
this.$emit('changeLang', lang);
}
@@ -112,26 +107,9 @@ export default defineComponent({
return this.onlineDispatchersCount == 0
? '-'
: (this.onlineTrainsCount / this.onlineDispatchersCount).toFixed(2);
},
computedRegions() {
return options.regions.map((region) => {
const regionStationCount =
this.store.apiData.stations?.filter(
(station) => station.region == region.id && station.isOnline
).length || 0;
const regionTrainCount =
this.store.apiData.trains?.filter((train) => train.region == region.id && train.online)
.length || 0;
return {
id: region.id,
value: `${region.value} <div class='text--grayed'>${regionStationCount} / ${regionTrainCount}</div>`,
selectedValue: region.value
};
});
}
},
components: { SelectBox, StatusIndicator, Clock }
components: { StatusIndicator, Clock, RegionDropdown }
});
</script>
<style lang="scss" scoped>
@@ -227,23 +205,8 @@ export default defineComponent({
}
}
// REGION SELECTION
.info_region {
color: white;
font-weight: bold;
display: flex;
justify-content: flex-end;
.select-box_content button {
background-color: transparent;
font-weight: bold;
padding: 0.1em 0.5em;
color: paleturquoise;
}
.options {
font-size: 0.9em;
}
}
</style>
@@ -1,28 +1,23 @@
<template>
<div class="select-box">
<div class="select-box_content">
<button class="selected" @click="toggleBox">
<span>{{ computedSelectedItem.selectedValue || computedSelectedItem.value }}</span>
<div class="region-dropdown" v-click-outside="clickedOutside">
<div class="content">
<button class="selected-region" @click="toggleBox">
<span>{{ selectedItem.name }}</span>
<div class="arrow">
<img :src="`/images/icon-arrow-${listOpen ? 'asc' : 'desc'}.svg`" alt="Arrow icon" />
</div>
<img :src="`/images/icon-arrow-${listOpen ? 'asc' : 'desc'}.svg`" alt="Arrow icon" />
</button>
<ul class="options" :ref="(el) => (listRef = el as Element)">
<li class="option" v-for="(item, i) in itemList" :key="item.id">
<ul class="options">
<li class="option" v-for="(item, i) in regionList" :key="item.id">
<transition
name="unfold"
:style="`
--delay-in: ${i * 55}ms;
--delay-out: ${(itemList.length - 1 - i) * 55}ms`"
--delay-out: ${(regionList.length - 1 - i) * 55}ms`"
>
<label :for="item.id" v-if="listOpen">
<input type="button" :id="item.id" name="select-box" @click="selectOption(item)" />
<span
:style="computedSelectedItem.id == item.id ? 'color: gold;' : ''"
v-html="item.value"
>
<span :style="selectedItem.id == item.id ? 'color: gold;' : ''" v-html="item.value">
</span>
</label>
</transition>
@@ -33,79 +28,69 @@
</template>
<script lang="ts">
import { defineComponent, Ref, ref, computed } from 'vue';
import { defineComponent, Ref, ref } from 'vue';
import { useStore } from '../../store/store';
import { regions as regionsJSON } from '../../data/options.json';
interface Item {
id: string;
value: string;
selectedValue?: string;
name: string;
}
export default defineComponent({
emits: ['selected'],
props: {
itemList: {
type: Array as () => Item[],
required: true
},
defaultItemIndex: {
type: Number,
default: 0
},
prefix: {
type: String,
default: ''
}
data() {
return {
store: useStore(),
selectedItemIndex: 0,
listOpen: false
};
},
setup(props) {
let listRef: Ref<Element | null> = ref(null);
setup() {
let buttonRef: Ref<HTMLButtonElement | null> = ref(null);
let activeEl: Ref<Element | null> = ref(document.activeElement);
let listOpen = ref(false);
let selectedItem: Ref<Item> = ref(props.itemList[props.defaultItemIndex]);
const computedSelectedItem = computed(() => {
return (
props.itemList.find((item) => item.id === selectedItem.value.id) ||
props.itemList[props.defaultItemIndex]
);
});
return {
computedSelectedItem,
listOpen,
selectedItem,
listRef,
buttonRef,
activeEl
buttonRef
};
},
watch: {
'$route.query': {
immediate: true,
handler(newVal) {
if (newVal.region) {
const item = this.itemList.find((it) => it.id == newVal.region);
if (item) this.selectedItem = item;
}
'store.region.id': {
handler(regionId) {
this.selectedItemIndex = this.regionList.findIndex((reg) => reg.id == regionId);
}
}
},
methods: {
selectOption(item: Item) {
this.selectedItem = item;
this.listOpen = false;
computed: {
selectedItem() {
return this.regionList[this.selectedItemIndex] || null;
},
regionList() {
return regionsJSON.map((region) => {
const regionStationCount =
this.store.apiData.stations?.filter(
(station) => station.region == region.id && station.isOnline
).length || 0;
this.$emit('selected', item);
const regionTrainCount =
this.store.apiData.trains?.filter((train) => train.region == region.id && train.online)
.length || 0;
return {
id: region.id,
value: `${region.value} <div class='text--grayed'>${regionStationCount} / ${regionTrainCount}</div>`,
name: region.name
};
});
}
},
methods: {
selectOption(selectedRegion: Item) {
this.store.region = selectedRegion;
this.listOpen = false;
},
toggleBox(e: Event) {
@@ -125,40 +110,20 @@ export default defineComponent({
<style lang="scss" scoped>
@import '../../styles/variables.scss';
.unfold {
&-enter-from,
&-leave-to {
opacity: 0;
transform: translateY(-10px) scale(0.85);
}
&-enter-active,
&-leave-active {
transition: all 110ms ease-out;
}
&-enter-active {
transition-delay: var(--delay-in);
}
&-leave-active {
transition-delay: var(--delay-out);
}
}
.select-box {
.region-dropdown {
display: flex;
align-items: center;
justify-content: space-between;
}
.arrow {
img {
vertical-align: middle;
width: 1.35em;
}
button img {
vertical-align: middle;
width: 1.35em;
}
button.selected {
button.selected-region {
display: flex;
justify-content: space-between;
color: paleturquoise;
font-weight: bold;
@@ -167,11 +132,16 @@ button.selected {
&:focus {
background-color: #262626;
}
span {
margin-right: 10px;
}
}
.select-box_content {
.content {
position: relative;
margin: 0 auto;
font-weight: bold;
height: 100%;
@@ -232,4 +202,25 @@ li.option {
cursor: pointer;
}
}
.unfold {
&-enter-from,
&-leave-to {
opacity: 0;
transform: translateY(-10px) scale(0.85);
}
&-enter-active,
&-leave-active {
transition: all 110ms ease-out;
}
&-enter-active {
transition-delay: var(--delay-in);
}
&-leave-active {
transition-delay: var(--delay-out);
}
}
</style>