mirror of
https://github.com/Spythere/pojazdownik.git
synced 2026-05-04 12:08:12 +00:00
section improvements; hotfixes
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||||
<link rel="manifest" href="/site.webmanifest" />
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
||||||
|
|
||||||
<meta name="msapplication-TileColor" content="#da532c" />
|
<meta name="msapplication-TileColor" content="#da532c" />
|
||||||
<meta name="theme-color" content="#e4c428" />
|
<meta name="theme-color" content="#e4c428" />
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
@@ -54,10 +54,4 @@ h2 {
|
|||||||
|
|
||||||
color: #d1d1d1;
|
color: #d1d1d1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $breakpointMd) {
|
|
||||||
#app {
|
|
||||||
font-size: calc(0.7rem + 0.75vw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ main {
|
|||||||
gap: 1em;
|
gap: 1em;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 1500px;
|
max-width: 1300px;
|
||||||
min-height: 75vh;
|
min-height: 75vh;
|
||||||
|
|
||||||
grid-template-columns: 1fr 2fr;
|
grid-template-columns: 1fr 2fr;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="inputs-section">
|
<section class="inputs-section">
|
||||||
<div class="input_container">
|
<div class="input_container">
|
||||||
<h2 class="input_header">{{ $t("inputs.title") }}</h2>
|
<h2 class="input_header">{{ $t('inputs.title') }}</h2>
|
||||||
|
|
||||||
<div class="input_list type">
|
<div class="input_list type">
|
||||||
<div class="vehicle-types locos">
|
<div class="vehicle-types locos">
|
||||||
@@ -25,11 +25,9 @@
|
|||||||
@keydown.backspace="removeVehicle"
|
@keydown.backspace="removeVehicle"
|
||||||
>
|
>
|
||||||
<option :value="null" disabled>
|
<option :value="null" disabled>
|
||||||
{{ $t("inputs.input-vehicle") }}
|
{{ $t('inputs.input-vehicle') }}
|
||||||
</option>
|
|
||||||
<option v-for="loco in locoOptions" :value="loco" :key="loco.type">
|
|
||||||
{{ loco.type }}<b v-if="loco.supportersOnly">*</b>
|
|
||||||
</option>
|
</option>
|
||||||
|
<option v-for="loco in locoOptions" :value="loco" :key="loco.type">{{ loco.type }}<b v-if="loco.isSponsorsOnly">*</b></option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -55,23 +53,19 @@
|
|||||||
@keydown.backspace="removeVehicle"
|
@keydown.backspace="removeVehicle"
|
||||||
>
|
>
|
||||||
<option :value="null" disabled>
|
<option :value="null" disabled>
|
||||||
{{ $t("inputs.input-carwagon") }}
|
{{ $t('inputs.input-carwagon') }}
|
||||||
</option>
|
</option>
|
||||||
|
|
||||||
<option v-for="car in carOptions" :value="car" :key="car.type">
|
<option v-for="car in carOptions" :value="car" :key="car.type">{{ car.type }}<b v-if="car.isSponsorsOnly">*</b></option>
|
||||||
{{ car.type }}<b v-if="car.supportersOnly">*</b>
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input_list cargo">
|
<div class="input_list cargo">
|
||||||
<label for="cargo-select">{{ $t("inputs.cargo-title") }}</label>
|
<label for="cargo-select">{{ $t('inputs.cargo-title') }}</label>
|
||||||
<select
|
<select
|
||||||
id="cargo-select"
|
id="cargo-select"
|
||||||
:disabled="
|
:disabled="
|
||||||
(store.chosenCar && !store.chosenCar.loadable) ||
|
(store.chosenCar && !store.chosenCar.loadable) || (store.chosenCar && store.chosenCar.useType == 'car-passenger') || !store.chosenCar
|
||||||
(store.chosenCar && store.chosenCar.useType == 'car-passenger') ||
|
|
||||||
!store.chosenCar
|
|
||||||
"
|
"
|
||||||
data-select="cargo"
|
data-select="cargo"
|
||||||
data-ignore-outside="1"
|
data-ignore-outside="1"
|
||||||
@@ -81,49 +75,30 @@
|
|||||||
@keydown.enter.prevent="addOrSwitchVehicle"
|
@keydown.enter.prevent="addOrSwitchVehicle"
|
||||||
@keydown.backspace="removeVehicle"
|
@keydown.backspace="removeVehicle"
|
||||||
>
|
>
|
||||||
<option
|
<option :value="null" v-if="!store.chosenCar || !store.chosenCar.loadable">
|
||||||
:value="null"
|
{{ $t('inputs.no-cargo-available') }}
|
||||||
v-if="!store.chosenCar || !store.chosenCar.loadable"
|
|
||||||
>
|
|
||||||
{{ $t("inputs.no-cargo-available") }}
|
|
||||||
</option>
|
</option>
|
||||||
<option :value="null" v-else>{{ $t("inputs.cargo-empty") }}</option>
|
<option :value="null" v-else>{{ $t('inputs.cargo-empty') }}</option>
|
||||||
|
|
||||||
<option
|
<option v-for="cargo in store.chosenCar?.cargoList" :value="cargo" :key="cargo.id">
|
||||||
v-for="cargo in store.chosenCar?.cargoList"
|
|
||||||
:value="cargo"
|
|
||||||
:key="cargo.id"
|
|
||||||
>
|
|
||||||
{{ cargo.id }}
|
{{ cargo.id }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input_actions">
|
<div class="input_actions">
|
||||||
<button
|
<button class="btn" @click="addVehicle(store.chosenVehicle, store.chosenCargo)">
|
||||||
class="btn"
|
{{ $t('inputs.action-add') }}
|
||||||
@click="addVehicle(store.chosenVehicle, store.chosenCargo)"
|
|
||||||
>
|
|
||||||
{{ $t("inputs.action-add") }}
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button class="btn" @click="switchVehicles" :disabled="store.chosenStockListIndex == -1" :data-disabled="store.chosenStockListIndex == -1">
|
||||||
class="btn"
|
{{ $t('inputs.action-swap') }}
|
||||||
@click="switchVehicles"
|
|
||||||
:disabled="store.chosenStockListIndex == -1"
|
|
||||||
:data-disabled="store.chosenStockListIndex == -1"
|
|
||||||
>
|
|
||||||
{{ $t("inputs.action-swap") }}
|
|
||||||
<b class="text--accent">
|
<b class="text--accent">
|
||||||
{{
|
{{ store.chosenStockListIndex == -1 ? '' : `${store.chosenStockListIndex + 1}.` }}
|
||||||
store.chosenStockListIndex == -1
|
|
||||||
? ""
|
|
||||||
: `${store.chosenStockListIndex + 1}.`
|
|
||||||
}}
|
|
||||||
</b>
|
</b>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button class="btn" @click="store.isRealStockListCardOpen = true">
|
<button class="btn" @click="store.isRealStockListCardOpen = true">
|
||||||
<b>{{ $t("inputs.real-stock") }}</b>
|
<b>{{ $t('inputs.real-stock') }}</b>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -131,54 +106,63 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
import imageMixin from "../../mixins/imageMixin";
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
import { useStore } from "../../store";
|
import { useStore } from '../../store';
|
||||||
import stockPreviewMixin from "../../mixins/stockPreviewMixin";
|
import stockPreviewMixin from '../../mixins/stockPreviewMixin';
|
||||||
import stockMixin from "../../mixins/stockMixin";
|
import stockMixin from '../../mixins/stockMixin';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
mixins: [imageMixin, stockPreviewMixin, stockMixin],
|
mixins: [imageMixin, stockPreviewMixin, stockMixin],
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
|
store: useStore(),
|
||||||
locomotiveTypeList: [
|
locomotiveTypeList: [
|
||||||
{
|
{
|
||||||
id: "loco-e",
|
id: 'loco-e',
|
||||||
desc: "ELEKTRYCZNE",
|
desc: 'ELEKTRYCZNE',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "loco-s",
|
id: 'loco-s',
|
||||||
desc: "SPALINOWE",
|
desc: 'SPALINOWE',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "loco-ezt",
|
id: 'loco-ezt',
|
||||||
desc: "ELEKTR. ZESPOŁY TRAKCYJNE",
|
desc: 'ELEKTR. ZESPOŁY TRAKCYJNE',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "loco-szt",
|
id: 'loco-szt',
|
||||||
desc: "SPAL. ZESPOŁY TRAKCYJNE",
|
desc: 'SPAL. ZESPOŁY TRAKCYJNE',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
carTypeList: [
|
carTypeList: [
|
||||||
{
|
{
|
||||||
id: "car-passenger",
|
id: 'car-passenger',
|
||||||
desc: "PASAŻERSKIE",
|
desc: 'PASAŻERSKIE',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "car-cargo",
|
id: 'car-cargo',
|
||||||
desc: "TOWAROWE",
|
desc: 'TOWAROWE',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
setup() {
|
computed: {
|
||||||
const store = useStore();
|
locoOptions() {
|
||||||
|
return this.store.locoDataList
|
||||||
|
.slice()
|
||||||
|
.sort((a, b) => (a.type > b.type ? 1 : -1))
|
||||||
|
.filter((loco) => loco.power == this.store.chosenLocoPower);
|
||||||
|
},
|
||||||
|
|
||||||
return {
|
carOptions() {
|
||||||
store,
|
return this.store.carDataList
|
||||||
};
|
.slice()
|
||||||
|
.sort((a, b) => (a.type > b.type ? 1 : -1))
|
||||||
|
.filter((car) => car.useType == this.store.chosenCarUseType);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@@ -189,8 +173,7 @@ export default defineComponent({
|
|||||||
addOrSwitchVehicle() {
|
addOrSwitchVehicle() {
|
||||||
if (!this.store.chosenVehicle) return;
|
if (!this.store.chosenVehicle) return;
|
||||||
|
|
||||||
if (this.store.chosenStockListIndex == -1)
|
if (this.store.chosenStockListIndex == -1) this.addVehicle(this.store.chosenVehicle, this.store.chosenCargo);
|
||||||
this.addVehicle(this.store.chosenVehicle, this.store.chosenCargo);
|
|
||||||
else this.switchVehicles();
|
else this.switchVehicles();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -213,12 +196,35 @@ export default defineComponent({
|
|||||||
const stockObject = this.getStockObject(vehicle, this.store.chosenCargo);
|
const stockObject = this.getStockObject(vehicle, this.store.chosenCargo);
|
||||||
this.store.stockList[this.store.chosenStockListIndex] = stockObject;
|
this.store.stockList[this.store.chosenStockListIndex] = stockObject;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
selectLocoType(locoTypeId: string) {
|
||||||
|
this.store.chosenLocoPower = locoTypeId;
|
||||||
|
this.store.chosenVehicle = this.locoOptions[0];
|
||||||
|
this.store.chosenLoco = this.locoOptions[0];
|
||||||
|
},
|
||||||
|
|
||||||
|
selectCarWagonType(carWagonTypeId: string) {
|
||||||
|
this.store.chosenCarUseType = carWagonTypeId;
|
||||||
|
this.store.chosenVehicle = this.carOptions[0];
|
||||||
|
this.store.chosenCar = this.carOptions[0];
|
||||||
|
this.store.chosenCargo = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
previewVehicleByType(type: 'loco' | 'car' | 'cargo') {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (!this.store.chosenLoco && !this.store.chosenCar) return;
|
||||||
|
|
||||||
|
this.store.chosenVehicle = type == 'loco' ? this.store.chosenLoco : this.store.chosenCar;
|
||||||
|
|
||||||
|
this.store.chosenCargo = this.store.chosenCar?.cargoList.find((cargo) => cargo.id == this.store.chosenCargo?.id) || null;
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "../../styles/global";
|
@import '../../styles/global';
|
||||||
|
|
||||||
.inputs-section {
|
.inputs-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -228,6 +234,11 @@ export default defineComponent({
|
|||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input_container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 380px;
|
||||||
|
}
|
||||||
|
|
||||||
.input_header {
|
.input_header {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
@@ -236,7 +247,7 @@ button.btn--choice {
|
|||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
padding: 0.3em 0.6em;
|
padding: 0.3em 0.6em;
|
||||||
|
|
||||||
&[data-selected="true"] {
|
&[data-selected='true'] {
|
||||||
background-color: $accentColor;
|
background-color: $accentColor;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
@@ -247,6 +258,10 @@ button.btn--choice {
|
|||||||
.input_list {
|
.input_list {
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
|
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
|
|||||||
@@ -1,89 +1,66 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="train-image-section">
|
<section class="train-image-section">
|
||||||
<div class="train-image__wrapper">
|
<div class="train-image__content" :class="{ sponsor: store.chosenVehicle?.isSponsorsOnly }">
|
||||||
<div
|
<img
|
||||||
class="train-image__content"
|
v-if="store.chosenVehicle"
|
||||||
:class="{ supporter: store.chosenVehicle?.supportersOnly }"
|
tabindex="0"
|
||||||
>
|
:src="getThumbnailURL(store.chosenVehicle.type, 'small')"
|
||||||
<transition name="img-message-anim">
|
@click="onImageClick"
|
||||||
<div
|
@keydown.enter="onImageClick"
|
||||||
class="empty-message"
|
@error="onImageError"
|
||||||
v-if="store.imageLoading && store.chosenVehicle?.imageSrc"
|
type="image/jpeg"
|
||||||
>
|
/>
|
||||||
{{ $t("preview.loading") }}
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
|
|
||||||
<div class="no-img" v-if="!store.chosenVehicle">
|
<img v-else src="images/placeholder.jpg" alt="placeholder" />
|
||||||
{{ $t("preview.title") }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<img
|
|
||||||
v-if="store.chosenVehicle"
|
|
||||||
:src="getThumbnailURL(store.chosenVehicle.type, 'small')"
|
|
||||||
:alt="store.chosenVehicle.type"
|
|
||||||
@load="onImageLoad"
|
|
||||||
@click="onImageClick"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- <div class="empty-message" v-if="store.chosenVehicle && !store.chosenVehicle.imageSrc">Ten pojazd nie ma jeszcze podglądu!</div> -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="train-image__info" v-if="store.chosenVehicle">
|
|
||||||
<b class="text--accent">{{ store.chosenVehicle.type }}</b> •
|
|
||||||
<b style="color: #ccc">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
`preview.${
|
|
||||||
isLocomotive(store.chosenVehicle)
|
|
||||||
? store.chosenVehicle.power
|
|
||||||
: store.chosenVehicle.useType
|
|
||||||
}`,
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</b>
|
|
||||||
|
|
||||||
<div style="color: #ccc">
|
|
||||||
<div>
|
|
||||||
{{ store.chosenVehicle.length }}m | {{ store.chosenVehicle.mass }}t
|
|
||||||
| {{ store.chosenVehicle.maxSpeed }} km/h
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="isLocomotive(store.chosenVehicle)">
|
|
||||||
{{ $t("preview.cabin") }} {{ store.chosenVehicle.cabinType }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else>
|
|
||||||
{{
|
|
||||||
store.chosenVehicle.useType == "car-cargo" // ? store.stockData?.usage[store.chosenVehicle.constructionType]
|
|
||||||
? $t(`usage.${store.chosenVehicle.constructionType}`)
|
|
||||||
: `${$t("preview.construction")} ${
|
|
||||||
store.chosenVehicle.constructionType
|
|
||||||
}`
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<b style="color: salmon" v-if="store.chosenVehicle.supportersOnly">{{
|
|
||||||
$t("preview.sponsor-only")
|
|
||||||
}}</b>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="train-image__info" v-else>{{ $t("preview.desc") }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="train-image__info" v-if="store.chosenVehicle">
|
||||||
|
<b class="text--accent">{{ store.chosenVehicle.type }}</b> •
|
||||||
|
<b style="color: #ccc">
|
||||||
|
{{ $t(`preview.${isLocomotive(store.chosenVehicle) ? store.chosenVehicle.power : store.chosenVehicle.useType}`) }}
|
||||||
|
</b>
|
||||||
|
|
||||||
|
<div style="color: #ccc">
|
||||||
|
<div>{{ store.chosenVehicle.length }}m | {{ store.chosenVehicle.mass }}t | {{ store.chosenVehicle.maxSpeed }} km/h</div>
|
||||||
|
|
||||||
|
<div v-if="isLocomotive(store.chosenVehicle)">{{ $t('preview.cabin') }} {{ store.chosenVehicle.cabinType }}</div>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
{{
|
||||||
|
store.chosenVehicle.useType == 'car-cargo'
|
||||||
|
? $t(`usage.${store.chosenVehicle.constructionType}`)
|
||||||
|
: `${$t('preview.construction')} ${store.chosenVehicle.constructionType}`
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<b style="color: salmon" v-if="store.chosenVehicle.isSponsorsOnly">{{
|
||||||
|
$t('preview.sponsor-only', [
|
||||||
|
new Date(store.chosenVehicle.sponsorsOnlyTimestamp).toLocaleDateString($i18n.locale == 'pl' ? 'pl-PL' : 'en-GB'),
|
||||||
|
])
|
||||||
|
}}</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="train-image__info" v-else>{{ $t('preview.desc') }}</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent } from "vue";
|
import { computed, defineComponent } from 'vue';
|
||||||
import { useStore } from "../../store";
|
import { useStore } from '../../store';
|
||||||
import { isLocomotive } from "../../utils/vehicleUtils";
|
import { isLocomotive } from '../../utils/vehicleUtils';
|
||||||
import { ILocomotive, Vehicle } from "../../types";
|
import { ILocomotive, Vehicle } from '../../types';
|
||||||
import imageMixin from "../../mixins/imageMixin";
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
mixins: [imageMixin],
|
mixins: [imageMixin],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
noImageAvailable: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
|
||||||
@@ -106,88 +83,74 @@ export default defineComponent({
|
|||||||
this.store.imageLoading = false;
|
this.store.imageLoading = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onImageError(e: Event) {
|
||||||
|
const el = e.target as HTMLImageElement;
|
||||||
|
if (el.src == '/images/placeholder.jpg') return;
|
||||||
|
|
||||||
|
el.src = '/images/placeholder.jpg';
|
||||||
|
},
|
||||||
|
|
||||||
isLocomotive(vehicle: Vehicle): vehicle is ILocomotive {
|
isLocomotive(vehicle: Vehicle): vehicle is ILocomotive {
|
||||||
return isLocomotive(vehicle);
|
return isLocomotive(vehicle);
|
||||||
},
|
},
|
||||||
|
|
||||||
onImageClick() {
|
onImageClick(e: Event) {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
|
||||||
const chosenVehicle = this.store.chosenVehicle;
|
const chosenVehicle = this.store.chosenVehicle;
|
||||||
|
|
||||||
if (!chosenVehicle) return;
|
if (!chosenVehicle) return;
|
||||||
|
|
||||||
this.store.vehiclePreviewSrc = this.getThumbnailURL(
|
this.store.lastFocusedElement = target;
|
||||||
chosenVehicle.type,
|
this.store.vehiclePreviewSrc = this.getThumbnailURL(chosenVehicle.type, 'large');
|
||||||
"large",
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "../../styles/global.scss";
|
@import '../../styles/global.scss';
|
||||||
|
|
||||||
.train-image-section {
|
.train-image-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
grid-row: 3;
|
grid-row: 3;
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
|
|
||||||
margin-top: 2em;
|
margin-top: 1em;
|
||||||
height: 22em;
|
height: 22em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.train-image {
|
.train-image {
|
||||||
&__wrapper {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
border: 1px solid white;
|
&.sponsor img {
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
max-width: 22em;
|
|
||||||
height: 13em;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
&.supporter {
|
|
||||||
border: 1px solid salmon;
|
border: 1px solid salmon;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
|
max-width: 380px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
border: 1px solid white;
|
||||||
|
|
||||||
cursor: zoom-in;
|
cursor: zoom-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-message,
|
|
||||||
.no-img {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
|
|
||||||
padding: 0.3em 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-message {
|
|
||||||
background: rgba(#000, 0.75);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.train-image__info {
|
.train-image__info {
|
||||||
margin: 1em 0;
|
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
padding: 0 1em;
|
padding: 0.5em;
|
||||||
|
margin: 0.5em auto;
|
||||||
|
line-height: 1.35;
|
||||||
|
|
||||||
b {
|
width: 100%;
|
||||||
font-size: 1.1em;
|
max-width: 380px;
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
background-color: $secondaryColor;
|
||||||
margin: 0.25em 0;
|
font-weight: bold;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transition animations
|
// Transition animations
|
||||||
|
|||||||
@@ -1,176 +1,111 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="stock-list-tab">
|
<section class="stock-list-tab">
|
||||||
<div class="tab_header">
|
<div class="tab_header">
|
||||||
<h2>{{ $t("stocklist.title") }}</h2>
|
<h2>{{ $t('stocklist.title') }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stock_actions">
|
<div class="stock_actions">
|
||||||
<label class="file-label">
|
<button class="btn btn--image" @click="clickFileInput">
|
||||||
<div class="btn btn--image">
|
<input type="file" @change="uploadStock" ref="conFile" accept=".con,.txt" />
|
||||||
<img src="/images/icon-upload.svg" alt="" />
|
<img src="/images/icon-upload.svg" alt="upload icon" />
|
||||||
{{ $t("stocklist.action-upload") }}
|
{{ $t('stocklist.action-upload') }}
|
||||||
</div>
|
</button>
|
||||||
|
|
||||||
<input
|
<button class="btn btn--image" :data-disabled="stockIsEmpty" :disabled="stockIsEmpty" @click="downloadStock">
|
||||||
type="file"
|
|
||||||
@change="uploadStock"
|
|
||||||
ref="conFile"
|
|
||||||
accept=".con,.txt"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<button
|
|
||||||
class="btn btn--image"
|
|
||||||
:data-disabled="stockIsEmpty"
|
|
||||||
:disabled="stockIsEmpty"
|
|
||||||
@click="downloadStock"
|
|
||||||
>
|
|
||||||
<img src="/images/icon-download.svg" alt="download icon" />
|
<img src="/images/icon-download.svg" alt="download icon" />
|
||||||
{{ $t("stocklist.action-download") }}
|
{{ $t('stocklist.action-download') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button class="btn btn--image" :data-disabled="stockIsEmpty" :disabled="stockIsEmpty" @click="copyToClipboard">
|
||||||
class="btn btn--image"
|
|
||||||
:data-disabled="stockIsEmpty"
|
|
||||||
:disabled="stockIsEmpty"
|
|
||||||
@click="copyToClipboard"
|
|
||||||
>
|
|
||||||
<img src="/images/icon-copy.svg" alt="copy icon" />
|
<img src="/images/icon-copy.svg" alt="copy icon" />
|
||||||
{{ $t("stocklist.action-copy") }}
|
{{ $t('stocklist.action-copy') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button class="btn btn--image" :data-disabled="stockIsEmpty" :disabled="stockIsEmpty" @click="resetStock">
|
||||||
class="btn btn--image"
|
|
||||||
:data-disabled="stockIsEmpty"
|
|
||||||
:disabled="stockIsEmpty"
|
|
||||||
@click="resetStock"
|
|
||||||
>
|
|
||||||
<img src="/images/icon-reset.svg" alt="reset icon" />
|
<img src="/images/icon-reset.svg" alt="reset icon" />
|
||||||
{{ $t("stocklist.action-reset") }}
|
{{ $t('stocklist.action-reset') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button class="btn btn--image" :data-disabled="stockIsEmpty" :disabled="stockIsEmpty" @click="shuffleCars">
|
||||||
class="btn btn--image"
|
|
||||||
:data-disabled="stockIsEmpty"
|
|
||||||
:disabled="stockIsEmpty"
|
|
||||||
@click="shuffleCars"
|
|
||||||
>
|
|
||||||
<img src="/images/icon-shuffle.svg" alt="shuffle icon" />
|
<img src="/images/icon-shuffle.svg" alt="shuffle icon" />
|
||||||
{{ $t("stocklist.action-shuffle") }}
|
{{ $t('stocklist.action-shuffle') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div class="stock_controls" :data-disabled="store.chosenStockListIndex == -1">
|
||||||
class="stock_controls"
|
|
||||||
:data-disabled="store.chosenStockListIndex == -1"
|
|
||||||
>
|
|
||||||
<b v-if="store.chosenStockListIndex >= 0">
|
<b v-if="store.chosenStockListIndex >= 0">
|
||||||
{{ $t("stocklist.vehicle-no") }}
|
{{ $t('stocklist.vehicle-no') }}
|
||||||
<span class="text--accent">{{ store.chosenStockListIndex + 1 }}</span>
|
<span class="text--accent">{{ store.chosenStockListIndex + 1 }}</span>
|
||||||
|
|
||||||
</b>
|
</b>
|
||||||
|
|
||||||
<b v-else>
|
<b v-else>
|
||||||
{{ $t("stocklist.no-vehicle-chosen") }}
|
{{ $t('stocklist.no-vehicle-chosen') }}
|
||||||
</b>
|
</b>
|
||||||
|
|
||||||
<button
|
<button class="btn" :tabindex="store.chosenStockListIndex == -1 ? -1 : 0" @click="moveUpStock(store.chosenStockListIndex)">
|
||||||
class="btn"
|
|
||||||
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
|
|
||||||
@click="moveUpStock(store.chosenStockListIndex)"
|
|
||||||
>
|
|
||||||
<img :src="getIconURL('higher')" alt="move up vehicle" />
|
<img :src="getIconURL('higher')" alt="move up vehicle" />
|
||||||
{{ $t("stocklist.action-move-up") }}
|
{{ $t('stocklist.action-move-up') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button class="btn" :tabindex="store.chosenStockListIndex == -1 ? -1 : 0" @click="moveDownStock(store.chosenStockListIndex)">
|
||||||
class="btn"
|
|
||||||
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
|
|
||||||
@click="moveDownStock(store.chosenStockListIndex)"
|
|
||||||
>
|
|
||||||
<img :src="getIconURL('lower')" alt="move down vehicle" />
|
<img :src="getIconURL('lower')" alt="move down vehicle" />
|
||||||
{{ $t("stocklist.action-move-down") }}
|
{{ $t('stocklist.action-move-down') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button class="btn" :tabindex="store.chosenStockListIndex == -1 ? -1 : 0" @click="removeStock(store.chosenStockListIndex)">
|
||||||
class="btn"
|
|
||||||
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
|
|
||||||
@click="removeStock(store.chosenStockListIndex)"
|
|
||||||
>
|
|
||||||
<img :src="getIconURL('remove')" alt="remove vehicle" />
|
<img :src="getIconURL('remove')" alt="remove vehicle" />
|
||||||
{{ $t("stocklist.action-remove") }}
|
{{ $t('stocklist.action-remove') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stock_specs">
|
<div class="stock_specs">
|
||||||
<b class="real-stock-info" v-if="store.chosenRealStock">
|
<b class="real-stock-info" v-if="store.chosenRealStock">
|
||||||
<span class="text--accent">
|
<span class="text--accent">
|
||||||
<img
|
<img :src="getIconURL(store.chosenRealStock.type)" :alt="store.chosenRealStock.type" />
|
||||||
:src="getIconURL(store.chosenRealStock.type)"
|
|
||||||
:alt="store.chosenRealStock.type"
|
|
||||||
/>
|
|
||||||
{{ store.chosenRealStock.number }} {{ store.chosenRealStock.name }}
|
{{ store.chosenRealStock.number }} {{ store.chosenRealStock.name }}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
|
||||||
</b>
|
</b>
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
{{ $t("stocklist.mass") }}
|
{{ $t('stocklist.mass') }}
|
||||||
<span class="text--accent">{{ store.totalMass }}t</span> ({{
|
<span class="text--accent">{{ store.totalMass }}t</span> ({{ $t('stocklist.mass-accepted') }}:
|
||||||
$t("stocklist.mass-accepted")
|
<span class="text--accent">{{ store.acceptableMass ? store.acceptableMass + 't' : '-' }}</span
|
||||||
}}:
|
>) - {{ $t('stocklist.length') }}:
|
||||||
<span class="text--accent">{{
|
|
||||||
store.acceptableMass ? store.acceptableMass + "t" : "-"
|
|
||||||
}}</span
|
|
||||||
>) - {{ $t("stocklist.length") }}:
|
|
||||||
<span class="text--accent">{{ store.totalLength }}m</span>
|
<span class="text--accent">{{ store.totalLength }}m</span>
|
||||||
- {{ $t("stocklist.vmax") }}:
|
- {{ $t('stocklist.vmax') }}:
|
||||||
<span class="text--accent">{{ store.maxStockSpeed }} km/h</span>
|
<span class="text--accent">{{ store.maxStockSpeed }} km/h</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stock_cold-start">
|
<div class="stock_cold-start">
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input type="checkbox" v-model="store.isColdStart" :disabled="!locoSupportsColdStart(store.stockList[0]?.constructionType || '')" />
|
||||||
type="checkbox"
|
{{ $t('stocklist.coldstart-info') }}
|
||||||
v-model="store.isColdStart"
|
|
||||||
:disabled="
|
|
||||||
!locoSupportsColdStart(store.stockList[0]?.constructionType || '')
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
{{ $t("stocklist.coldstart-info") }}
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stock_warnings" v-if="stockHasWarnings">
|
<div class="stock_warnings" v-if="stockHasWarnings">
|
||||||
<div class="warning" v-if="locoNotSuitable">
|
<div class="warning" v-if="locoNotSuitable">(!) {{ $t('stocklist.warning-not-suitable') }}</div>
|
||||||
(!) {{ $t("stocklist.warning-not-suitable") }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="warning" v-if="trainTooLong && store.isTrainPassenger">
|
<div class="warning" v-if="trainTooLong && store.isTrainPassenger">(!) {{ $t('stocklist.warning-passenger-too-long') }}</div>
|
||||||
(!) {{ $t("stocklist.warning-passenger-too-long") }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="warning" v-if="trainTooLong && !store.isTrainPassenger">
|
<div class="warning" v-if="trainTooLong && !store.isTrainPassenger">(!) {{ $t('stocklist.warning-freight-too-long') }}</div>
|
||||||
(!) {{ $t("stocklist.warning-freight-too-long") }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="warning" v-if="trainTooHeavy">
|
<div class="warning" v-if="trainTooHeavy">
|
||||||
(!)
|
(!)
|
||||||
<i18n-t keypath="stocklist.warning-too-heavy">
|
<i18n-t keypath="stocklist.warning-too-heavy">
|
||||||
<template #href>
|
<template #href>
|
||||||
<a
|
<a target="_blank" href="https://docs.google.com/spreadsheets/d/1bFXUsHsAu4youmNz-46Q1HslZaaoklvfoBDS553TnNk/edit">
|
||||||
target="_blank"
|
{{ $t('stocklist.acceptable-mass-docs') }}
|
||||||
href="https://docs.google.com/spreadsheets/d/1bFXUsHsAu4youmNz-46Q1HslZaaoklvfoBDS553TnNk/edit"
|
|
||||||
>
|
|
||||||
{{ $t("stocklist.acceptable-mass-docs") }}
|
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="warning" v-if="tooManyLocomotives">
|
<div class="warning" v-if="tooManyLocomotives">
|
||||||
{{ $t("stocklist.warning-too-many-locos") }}
|
{{ $t('stocklist.warning-too-many-locos') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -179,7 +114,7 @@
|
|||||||
<!-- Stock list -->
|
<!-- Stock list -->
|
||||||
<ul ref="stock_list">
|
<ul ref="stock_list">
|
||||||
<li v-if="stockIsEmpty" class="list-empty">
|
<li v-if="stockIsEmpty" class="list-empty">
|
||||||
<div class="stock-info">{{ $t("stocklist.list-empty") }}</div>
|
<div class="stock-info">{{ $t('stocklist.list-empty') }}</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<TransitionGroup name="stock-list-anim">
|
<TransitionGroup name="stock-list-anim">
|
||||||
@@ -195,25 +130,13 @@
|
|||||||
@keydown.backspace="removeStock(i)"
|
@keydown.backspace="removeStock(i)"
|
||||||
ref="itemRefs"
|
ref="itemRefs"
|
||||||
>
|
>
|
||||||
<div
|
<div class="stock-info" @dragstart="onDragStart(i)" @drop="onDrop($event, i)" @dragover="allowDrop" draggable="true">
|
||||||
class="stock-info"
|
<span class="stock-info__no" :data-selected="i == store.chosenStockListIndex">
|
||||||
@dragstart="onDragStart(i)"
|
|
||||||
@drop="onDrop($event, i)"
|
|
||||||
@dragover="allowDrop"
|
|
||||||
draggable="true"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="stock-info__no"
|
|
||||||
:data-selected="i == store.chosenStockListIndex"
|
|
||||||
>
|
|
||||||
<span v-if="i == store.chosenStockListIndex">• </span>
|
<span v-if="i == store.chosenStockListIndex">• </span>
|
||||||
{{ i + 1 }}.
|
{{ i + 1 }}.
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span
|
<span class="stock-info__type" :class="{ sponsor: stock.isSponsorsOnly }">
|
||||||
class="stock-info__type"
|
|
||||||
:class="{ supporter: stock.supportersOnly }"
|
|
||||||
>
|
|
||||||
{{ stock.isLoco ? stock.type : getCarSpecFromType(stock.type) }}
|
{{ stock.isLoco ? stock.type : getCarSpecFromType(stock.type) }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -221,9 +144,7 @@
|
|||||||
{{ stock.cargo.id }}
|
{{ stock.cargo.id }}
|
||||||
</span>
|
</span>
|
||||||
<span class="stock-info__length"> {{ stock.length }}m </span>
|
<span class="stock-info__length"> {{ stock.length }}m </span>
|
||||||
<span class="stock-info__mass"
|
<span class="stock-info__mass">{{ stock.cargo ? stock.cargo.totalMass : stock.mass }}t </span>
|
||||||
>{{ stock.cargo ? stock.cargo.totalMass : stock.mass }}t
|
|
||||||
</span>
|
|
||||||
<span class="stock-info__speed"> {{ stock.maxSpeed }}km/h </span>
|
<span class="stock-info__speed"> {{ stock.maxSpeed }}km/h </span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@@ -233,19 +154,19 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
import { useStore } from "../../store";
|
import { useStore } from '../../store';
|
||||||
|
|
||||||
import { locoSupportsColdStart } from "../../utils/locoUtils";
|
import { locoSupportsColdStart } from '../../utils/locoUtils';
|
||||||
import warningsMixin from "../../mixins/warningsMixin";
|
import warningsMixin from '../../mixins/warningsMixin';
|
||||||
import imageMixin from "../../mixins/imageMixin";
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
import stockPreviewMixin from "../../mixins/stockPreviewMixin";
|
import stockPreviewMixin from '../../mixins/stockPreviewMixin';
|
||||||
import StockThumbnails from "../utils/StockThumbnails.vue";
|
import StockThumbnails from '../utils/StockThumbnails.vue';
|
||||||
import stockMixin from "../../mixins/stockMixin";
|
import stockMixin from '../../mixins/stockMixin';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "stock-list",
|
name: 'stock-list',
|
||||||
components: { StockThumbnails },
|
components: { StockThumbnails },
|
||||||
|
|
||||||
mixins: [warningsMixin, imageMixin, stockMixin, stockPreviewMixin],
|
mixins: [warningsMixin, imageMixin, stockMixin, stockPreviewMixin],
|
||||||
@@ -269,20 +190,12 @@ export default defineComponent({
|
|||||||
stockString() {
|
stockString() {
|
||||||
return this.store.stockList
|
return this.store.stockList
|
||||||
.map((stock, i) => {
|
.map((stock, i) => {
|
||||||
let stockTypeStr =
|
let stockTypeStr = stock.isLoco || !stock.cargo ? stock.type : `${stock.type}:${stock.cargo.id}`;
|
||||||
stock.isLoco || !stock.cargo
|
let coldStart = i == 0 && this.store.isColdStart && locoSupportsColdStart(stock.constructionType || '') ? ',c' : '';
|
||||||
? stock.type
|
|
||||||
: `${stock.type}:${stock.cargo.id}`;
|
|
||||||
let coldStart =
|
|
||||||
i == 0 &&
|
|
||||||
this.store.isColdStart &&
|
|
||||||
locoSupportsColdStart(stock.constructionType || "")
|
|
||||||
? ",c"
|
|
||||||
: "";
|
|
||||||
|
|
||||||
return stockTypeStr + coldStart;
|
return stockTypeStr + coldStart;
|
||||||
})
|
})
|
||||||
.join(";");
|
.join(';');
|
||||||
},
|
},
|
||||||
|
|
||||||
stockIsEmpty() {
|
stockIsEmpty() {
|
||||||
@@ -290,18 +203,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
chosenStockVehicle() {
|
chosenStockVehicle() {
|
||||||
return this.store.chosenStockListIndex == -1
|
return this.store.chosenStockListIndex == -1 ? undefined : this.store.stockList[this.store.chosenStockListIndex];
|
||||||
? undefined
|
|
||||||
: this.store.stockList[this.store.chosenStockListIndex];
|
|
||||||
},
|
},
|
||||||
|
|
||||||
stockHasWarnings() {
|
stockHasWarnings() {
|
||||||
return (
|
return this.tooManyLocomotives || this.trainTooHeavy || this.trainTooLong || this.locoNotSuitable;
|
||||||
this.tooManyLocomotives ||
|
|
||||||
this.trainTooHeavy ||
|
|
||||||
this.trainTooLong ||
|
|
||||||
this.locoNotSuitable
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -312,18 +218,18 @@ export default defineComponent({
|
|||||||
navigator.clipboard.writeText(this.stockString);
|
navigator.clipboard.writeText(this.stockString);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
alert(this.$t("stocklist.alert-copied"));
|
alert(this.$t('stocklist.alert-copied'));
|
||||||
}, 20);
|
}, 20);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
clickFileInput() {
|
||||||
|
(this.$refs['conFile'] as HTMLInputElement).click();
|
||||||
|
},
|
||||||
|
|
||||||
onListItemClick(stockID: number) {
|
onListItemClick(stockID: number) {
|
||||||
const stock = this.store.stockList[stockID];
|
const stock = this.store.stockList[stockID];
|
||||||
|
|
||||||
this.store.chosenStockListIndex =
|
this.store.chosenStockListIndex = this.store.chosenStockListIndex == stockID && this.store.chosenVehicle?.type == stock.type ? -1 : stockID;
|
||||||
this.store.chosenStockListIndex == stockID &&
|
|
||||||
this.store.chosenVehicle?.type == stock.type
|
|
||||||
? -1
|
|
||||||
: stockID;
|
|
||||||
|
|
||||||
if (this.store.chosenStockListIndex == -1) {
|
if (this.store.chosenStockListIndex == -1) {
|
||||||
this.store.chosenVehicle = null;
|
this.store.chosenVehicle = null;
|
||||||
@@ -336,13 +242,12 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
getCarSpecFromType(typeStr: string) {
|
getCarSpecFromType(typeStr: string) {
|
||||||
const specArray = typeStr.split("_");
|
const specArray = typeStr.split('_');
|
||||||
|
|
||||||
if (specArray.length == 0) return null;
|
if (specArray.length == 0) return null;
|
||||||
|
|
||||||
/* 111a_Grafitti_1 */
|
/* 111a_Grafitti_1 */
|
||||||
if (specArray.length == 3)
|
if (specArray.length == 3) return `${specArray[0]} ${specArray[1]}-${specArray[2]}`;
|
||||||
return `${specArray[0]} ${specArray[1]}-${specArray[2]}`;
|
|
||||||
|
|
||||||
/* 111a_PKP_Bnouz_01 */
|
/* 111a_PKP_Bnouz_01 */
|
||||||
return `${specArray[0]} ${specArray[2]}-${specArray[3]} (${specArray[1]})`;
|
return `${specArray[0]} ${specArray[2]}-${specArray[3]} (${specArray[1]})`;
|
||||||
@@ -370,12 +275,9 @@ export default defineComponent({
|
|||||||
removeStock(index: number) {
|
removeStock(index: number) {
|
||||||
if (index == -1) return;
|
if (index == -1) return;
|
||||||
|
|
||||||
this.store.stockList = this.store.stockList.filter(
|
this.store.stockList = this.store.stockList.filter((stock, i) => i != index);
|
||||||
(stock, i) => i != index,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.store.stockList.length < index + 1)
|
if (this.store.stockList.length < index + 1) this.store.chosenStockListIndex = -1;
|
||||||
this.store.chosenStockListIndex = -1;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
moveUpStock(index: number) {
|
moveUpStock(index: number) {
|
||||||
@@ -413,8 +315,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
availableIndexes.splice(i, -1);
|
availableIndexes.splice(i, -1);
|
||||||
|
|
||||||
const randAvailableIndex =
|
const randAvailableIndex = availableIndexes[Math.floor(Math.random() * availableIndexes.length)];
|
||||||
availableIndexes[Math.floor(Math.random() * availableIndexes.length)];
|
|
||||||
const tempSwap = this.store.stockList[randAvailableIndex];
|
const tempSwap = this.store.stockList[randAvailableIndex];
|
||||||
|
|
||||||
this.store.stockList[randAvailableIndex] = this.store.stockList[i];
|
this.store.stockList[randAvailableIndex] = this.store.stockList[i];
|
||||||
@@ -423,33 +324,30 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
downloadStock() {
|
downloadStock() {
|
||||||
if (this.store.stockList.length == 0)
|
if (this.store.stockList.length == 0) return alert(this.$t('stocklist.alert-empty'));
|
||||||
return alert(this.$t("stocklist.alert-empty"));
|
|
||||||
|
|
||||||
const defaultName = `${
|
const defaultName = `${this.store.chosenRealStockName || this.store.stockList[0].type} ${this.store.totalMass}t; ${
|
||||||
this.store.chosenRealStockName || this.store.stockList[0].type
|
this.store.totalLength
|
||||||
} ${this.store.totalMass}t; ${this.store.totalLength}m; vmax ${
|
}m; vmax ${this.store.maxStockSpeed}`;
|
||||||
this.store.maxStockSpeed
|
|
||||||
}`;
|
|
||||||
|
|
||||||
const fileName = prompt(this.$t("stocklist.prompt-file"), defaultName);
|
const fileName = prompt(this.$t('stocklist.prompt-file'), defaultName);
|
||||||
|
|
||||||
if (!fileName) return;
|
if (!fileName) return;
|
||||||
|
|
||||||
const blob = new Blob([this.stockString]);
|
const blob = new Blob([this.stockString]);
|
||||||
const file = fileName + ".con";
|
const file = fileName + '.con';
|
||||||
|
|
||||||
var e = document.createEvent("MouseEvents"),
|
var e = document.createEvent('MouseEvents'),
|
||||||
a = document.createElement("a");
|
a = document.createElement('a');
|
||||||
a.download = file;
|
a.download = file;
|
||||||
a.href = window.URL.createObjectURL(blob);
|
a.href = window.URL.createObjectURL(blob);
|
||||||
a.dataset.downloadurl = ["", a.download, a.href].join(":");
|
a.dataset.downloadurl = ['', a.download, a.href].join(':');
|
||||||
e.initEvent("click", true, false);
|
e.initEvent('click', true, false);
|
||||||
a.dispatchEvent(e);
|
a.dispatchEvent(e);
|
||||||
},
|
},
|
||||||
|
|
||||||
uploadStock() {
|
uploadStock() {
|
||||||
const inputEl = this.$refs["conFile"] as HTMLInputElement;
|
const inputEl = this.$refs['conFile'] as HTMLInputElement;
|
||||||
const files = inputEl.files;
|
const files = inputEl.files;
|
||||||
|
|
||||||
if (files?.length != 1) return;
|
if (files?.length != 1) return;
|
||||||
@@ -461,14 +359,14 @@ export default defineComponent({
|
|||||||
reader.onload = (res) => {
|
reader.onload = (res) => {
|
||||||
const stockString = res.target?.result;
|
const stockString = res.target?.result;
|
||||||
|
|
||||||
if (!stockString || typeof stockString !== "string") return;
|
if (!stockString || typeof stockString !== 'string') return;
|
||||||
|
|
||||||
this.loadStockFromString(stockString);
|
this.loadStockFromString(stockString);
|
||||||
};
|
};
|
||||||
|
|
||||||
reader.onerror = (err) => console.log(err);
|
reader.onerror = (err) => console.log(err);
|
||||||
|
|
||||||
inputEl.value = "";
|
inputEl.value = '';
|
||||||
},
|
},
|
||||||
|
|
||||||
onDragStart(vehicleIndex: number) {
|
onDragStart(vehicleIndex: number) {
|
||||||
@@ -478,14 +376,13 @@ export default defineComponent({
|
|||||||
onDrop(e: DragEvent, vehicleIndex: number) {
|
onDrop(e: DragEvent, vehicleIndex: number) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
let targetEl = (this.$refs["itemRefs"] as Element[])[vehicleIndex];
|
let targetEl = (this.$refs['itemRefs'] as Element[])[vehicleIndex];
|
||||||
|
|
||||||
if (!targetEl) return;
|
if (!targetEl) return;
|
||||||
|
|
||||||
const tempVehicle = this.store.stockList[vehicleIndex];
|
const tempVehicle = this.store.stockList[vehicleIndex];
|
||||||
|
|
||||||
this.store.stockList[vehicleIndex] =
|
this.store.stockList[vehicleIndex] = this.store.stockList[this.draggedVehicleID];
|
||||||
this.store.stockList[this.draggedVehicleID];
|
|
||||||
this.store.stockList[this.draggedVehicleID] = tempVehicle;
|
this.store.stockList[this.draggedVehicleID] = tempVehicle;
|
||||||
|
|
||||||
this.store.chosenStockListIndex = vehicleIndex;
|
this.store.chosenStockListIndex = vehicleIndex;
|
||||||
@@ -499,8 +396,8 @@ export default defineComponent({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "../../styles/global";
|
@import '../../styles/global';
|
||||||
@import "../../styles/tab.scss";
|
@import '../../styles/tab.scss';
|
||||||
|
|
||||||
.stock-list-tab {
|
.stock-list-tab {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -532,7 +429,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
background-color: #353a57;
|
background-color: #353a57;
|
||||||
|
|
||||||
&[data-disabled="true"] {
|
&[data-disabled='true'] {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@@ -564,12 +461,13 @@ export default defineComponent({
|
|||||||
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
|
||||||
label.file-label {
|
button {
|
||||||
text-align: center;
|
width: 100%;
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
input {
|
input {
|
||||||
display: none;
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -626,7 +524,7 @@ li > .stock-info {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.supporter {
|
.sponsor {
|
||||||
color: salmon;
|
color: salmon;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,7 +542,7 @@ li > .stock-info {
|
|||||||
min-width: 3.5em;
|
min-width: 3.5em;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|
||||||
&[data-selected="true"] {
|
&[data-selected='true'] {
|
||||||
color: $accentColor;
|
color: $accentColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
<div class="tab_content">
|
<div class="tab_content">
|
||||||
<div class="actions-panel">
|
<div class="actions-panel">
|
||||||
<div class="actions-panel_vehicles">
|
<div class="actions-panel_vehicles">
|
||||||
<button class="btn" :data-chosen="filters.tractions" @click="toggleFilter('tractions')">
|
<button class="btn" :data-chosen="currentFilterMode == 'tractions'" @click="toggleFilter('tractions')">
|
||||||
{{ $t('wiki.action-vehicles') }}
|
{{ $t('wiki.action-vehicles') }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn" :data-chosen="filters.carriages" @click="toggleFilter('carriages')">
|
<button class="btn" :data-chosen="currentFilterMode == 'carriages'" @click="toggleFilter('carriages')">
|
||||||
{{ $t('wiki.action-carriages') }}
|
{{ $t('wiki.action-carriages') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -34,12 +34,16 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<!-- @click="previewLocomotive(vehicle)"
|
|
||||||
@keydown.enter="previewLocomotive(vehicle)"
|
|
||||||
@dblclick="addLocomotive(vehicle)"
|
|
||||||
-->
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="vehicle in computedVehicleList" v-show="vehicle.show" :key="vehicle.type" tabindex="0">
|
<tr
|
||||||
|
v-for="{ vehicle, show } in computedTableData"
|
||||||
|
tabindex="0"
|
||||||
|
v-show="show"
|
||||||
|
:key="vehicle.type"
|
||||||
|
@click="previewVehicle(vehicle)"
|
||||||
|
@keydown.enter="previewVehicle(vehicle)"
|
||||||
|
@dblclick="addVehicle(vehicle)"
|
||||||
|
>
|
||||||
<td style="width: 120px">
|
<td style="width: 120px">
|
||||||
<img
|
<img
|
||||||
width="120"
|
width="120"
|
||||||
@@ -50,16 +54,18 @@
|
|||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>{{ vehicle.type }}</td>
|
<td :data-sponsoronly="vehicle.isSponsorsOnly">{{ vehicle.type }}</td>
|
||||||
<!-- <td>{{ $t(`wiki.${vehicle.power || vehicle.}`) }}</td> -->
|
|
||||||
<td v-if="isLocomotive(vehicle)">{{ $t(`wiki.${vehicle.power}`) }}</td>
|
<td v-if="isLocomotive(vehicle)">{{ $t(`wiki.${vehicle.power}`) }}</td>
|
||||||
<td v-else>{{ $t(`wiki.${vehicle.useType}`) }}</td>
|
<td v-else>{{ $t(`wiki.${vehicle.useType}`) }}</td>
|
||||||
|
|
||||||
<td>{{ vehicle.constructionType }}</td>
|
<td>{{ vehicle.constructionType }}</td>
|
||||||
<td>{{ vehicle.length }}m</td>
|
<td>{{ vehicle.length }}m</td>
|
||||||
<td>{{ vehicle.mass }}t</td>
|
<td>{{ vehicle.mass }}t</td>
|
||||||
<td>{{ vehicle.maxSpeed }}km/h</td>
|
<td>{{ vehicle.maxSpeed }}km/h</td>
|
||||||
<td v-if="!filters.tractions && filters.carriages">{{ !isLocomotive(vehicle) ? vehicle.cargoList.length ?? '---' : 'niedost.' }}</td>
|
|
||||||
<td v-if="filters.tractions && !filters.carriages">
|
<td v-if="currentFilterMode == 'carriages'">{{ !isLocomotive(vehicle) ? vehicle.cargoList.length : '---' }}</td>
|
||||||
|
<td v-if="currentFilterMode == 'tractions'">
|
||||||
{{ isLocomotive(vehicle) ? (locoSupportsColdStart(vehicle.constructionType) ? `✓` : '✗') : '---' }}
|
{{ isLocomotive(vehicle) ? (locoSupportsColdStart(vehicle.constructionType) ? `✓` : '✗') : '---' }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -82,18 +88,23 @@ import stockMixin from '../../mixins/stockMixin';
|
|||||||
import imageMixin from '../../mixins/imageMixin';
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
import { locoSupportsColdStart } from '../../utils/locoUtils';
|
import { locoSupportsColdStart } from '../../utils/locoUtils';
|
||||||
|
|
||||||
type SorterID = 'type' | 'constructionType' | 'image' | 'length' | 'mass' | 'maxSpeed' | 'cargoCount' | 'power' | 'coldStart';
|
type SorterID = 'type' | 'constructionType' | 'image' | 'length' | 'mass' | 'maxSpeed' | 'cargoCount' | 'group' | 'coldStart';
|
||||||
|
|
||||||
interface WikiHeader {
|
interface IWikiHeader {
|
||||||
id: SorterID;
|
id: SorterID;
|
||||||
sortable: boolean;
|
sortable: boolean;
|
||||||
for: 'all' | 'carriages' | 'tractions';
|
for: 'all' | 'carriages' | 'tractions';
|
||||||
}
|
}
|
||||||
|
|
||||||
const headers: WikiHeader[] = [
|
interface IWikiRow {
|
||||||
|
vehicle: Vehicle;
|
||||||
|
show: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers: IWikiHeader[] = [
|
||||||
{ id: 'image', sortable: false, for: 'all' },
|
{ id: 'image', sortable: false, for: 'all' },
|
||||||
{ id: 'type', sortable: true, for: 'all' },
|
{ id: 'type', sortable: true, for: 'all' },
|
||||||
{ id: 'power', sortable: true, for: 'all' },
|
{ id: 'group', sortable: true, for: 'all' },
|
||||||
{ id: 'constructionType', sortable: true, for: 'all' },
|
{ id: 'constructionType', sortable: true, for: 'all' },
|
||||||
{ id: 'length', sortable: true, for: 'all' },
|
{ id: 'length', sortable: true, for: 'all' },
|
||||||
{ id: 'mass', sortable: true, for: 'all' },
|
{ id: 'mass', sortable: true, for: 'all' },
|
||||||
@@ -102,16 +113,6 @@ const headers: WikiHeader[] = [
|
|||||||
{ id: 'cargoCount', sortable: true, for: 'carriages' },
|
{ id: 'cargoCount', sortable: true, for: 'carriages' },
|
||||||
];
|
];
|
||||||
|
|
||||||
// const carHeaders: WikiHeader[] = [
|
|
||||||
// { id: 'image', sortable: false },
|
|
||||||
// { id: 'type', sortable: true },
|
|
||||||
// { id: 'constructionType', sortable: true },
|
|
||||||
// { id: 'length', sortable: true },
|
|
||||||
// { id: 'mass', sortable: true },
|
|
||||||
// { id: 'maxSpeed', sortable: true },
|
|
||||||
// { id: 'cargoCount', sortable: true },
|
|
||||||
// ];
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
mixins: [stockPreviewMixin, stockMixin, imageMixin],
|
mixins: [stockPreviewMixin, stockMixin, imageMixin],
|
||||||
|
|
||||||
@@ -120,8 +121,7 @@ export default defineComponent({
|
|||||||
store: useStore(),
|
store: useStore(),
|
||||||
headers,
|
headers,
|
||||||
|
|
||||||
locosScrollTop: 0,
|
scrollTop: 0,
|
||||||
carsScrollTop: 0,
|
|
||||||
|
|
||||||
searchedVehicleTypeName: '',
|
searchedVehicleTypeName: '',
|
||||||
|
|
||||||
@@ -130,95 +130,95 @@ export default defineComponent({
|
|||||||
direction: 1,
|
direction: 1,
|
||||||
},
|
},
|
||||||
|
|
||||||
filters: {
|
currentFilterMode: 'all' as 'all' | 'tractions' | 'carriages',
|
||||||
tractions: true,
|
|
||||||
carriages: true,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
activated() {
|
activated() {
|
||||||
const tableWrapperRef = this.$refs['table-wrapper'] as HTMLElement;
|
const tableWrapperRef = this.$refs['table-wrapper'] as HTMLElement;
|
||||||
|
|
||||||
// tableWrapperRef.scrollTo({
|
tableWrapperRef.scrollTo({
|
||||||
// top: this.wikiMode == 'locomotives' ? this.locosScrollTop : this.carsScrollTop,
|
top: this.scrollTop,
|
||||||
// });
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
locoSupportsColdStart,
|
locoSupportsColdStart,
|
||||||
isLocomotive,
|
isLocomotive,
|
||||||
|
|
||||||
toggleFilter(name: keyof typeof this.filters) {
|
toggleFilter(name: typeof this.currentFilterMode) {
|
||||||
this.filters[name] = !this.filters[name];
|
this.currentFilterMode = this.currentFilterMode == name ? 'all' : name;
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleSorter(header: WikiHeader) {
|
toggleSorter(header: IWikiHeader) {
|
||||||
if (!header.sortable) return;
|
if (!header.sortable) return;
|
||||||
|
|
||||||
if (header.id == this.currentSorter.id) this.currentSorter.direction *= -1;
|
if (header.id == this.currentSorter.id) this.currentSorter.direction *= -1;
|
||||||
this.currentSorter.id = header.id;
|
this.currentSorter.id = header.id;
|
||||||
},
|
},
|
||||||
|
|
||||||
sortVehicles(vA: Vehicle, vB: Vehicle) {
|
sortTableRows(row1: IWikiRow, row2: IWikiRow) {
|
||||||
|
if (!row1.show) return 0;
|
||||||
|
|
||||||
const { id, direction } = this.currentSorter;
|
const { id, direction } = this.currentSorter;
|
||||||
const vehiclesAreLocos = isLocomotive(vA) && isLocomotive(vB);
|
|
||||||
const vehiclesAreCars = !isLocomotive(vA) && !isLocomotive(vB);
|
|
||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case 'type':
|
case 'type':
|
||||||
case 'constructionType':
|
case 'constructionType':
|
||||||
return direction == 1 ? vA[id].localeCompare(vB[id]) : vB[id].localeCompare(vA[id]);
|
case 'group':
|
||||||
|
return direction == 1 ? row1.vehicle[id].localeCompare(row2.vehicle[id]) : row2.vehicle[id].localeCompare(row1.vehicle[id]);
|
||||||
|
|
||||||
case 'mass':
|
case 'mass':
|
||||||
case 'length':
|
case 'length':
|
||||||
case 'maxSpeed':
|
case 'maxSpeed':
|
||||||
return Math.sign(vA[id] - vB[id]) * direction;
|
return Math.sign(row1.vehicle[id] - row2.vehicle[id]) * direction;
|
||||||
|
|
||||||
case 'cargoCount':
|
case 'cargoCount':
|
||||||
if (vehiclesAreCars) return Math.sign((vA.cargoList.length || -1) - (vB.cargoList.length || -1)) * direction;
|
return (
|
||||||
break;
|
(!isLocomotive(row1.vehicle) ? Math.sign(row1.vehicle.cargoList.length || -1) : -1) -
|
||||||
|
(!isLocomotive(row2.vehicle) ? (row2.vehicle.cargoList.length || -1) * direction : -1)
|
||||||
|
);
|
||||||
|
|
||||||
case 'coldStart':
|
case 'coldStart':
|
||||||
if (vehiclesAreLocos) return (locoSupportsColdStart(vA.constructionType) > locoSupportsColdStart(vB.constructionType) ? 1 : -1) * direction;
|
return (locoSupportsColdStart(row1.vehicle.constructionType) > locoSupportsColdStart(row2.vehicle.constructionType) ? 1 : -1) * direction;
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return direction == 1 ? vA.type.localeCompare(vB.type) : vB.type.localeCompare(vA.type);
|
return direction == 1 ? row1.vehicle.type.localeCompare(row2.vehicle.type) : row2.vehicle.type.localeCompare(row1.vehicle.type);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
computedVehicleList() {
|
computedTableData(): IWikiRow[] {
|
||||||
return this.store.vehicleDataList
|
return this.store.vehicleDataList
|
||||||
.map((vehicle) => ({
|
.map((vehicle) => ({
|
||||||
...vehicle,
|
vehicle,
|
||||||
show:
|
show:
|
||||||
new RegExp(`${this.searchedVehicleTypeName.trim()}`, 'i').test(vehicle.type) &&
|
new RegExp(`${this.searchedVehicleTypeName.trim()}`, 'i').test(vehicle.type) &&
|
||||||
((this.filters.tractions && isLocomotive(vehicle)) || (this.filters.carriages && !isLocomotive(vehicle))),
|
(this.currentFilterMode == 'all' ||
|
||||||
|
(this.currentFilterMode == 'tractions' && isLocomotive(vehicle)) ||
|
||||||
|
(this.currentFilterMode == 'carriages' && !isLocomotive(vehicle))),
|
||||||
|
|
||||||
|
// ((this.filters.tractions && isLocomotive(vehicle)) || (this.filters.carriages && !isLocomotive(vehicle))),
|
||||||
}))
|
}))
|
||||||
.sort(this.sortVehicles);
|
.sort((a, b) => this.sortTableRows(a, b));
|
||||||
},
|
},
|
||||||
|
|
||||||
visibleHeaders() {
|
visibleHeaders() {
|
||||||
const filtersActive =
|
const filtersActive = this.currentFilterMode;
|
||||||
this.filters.carriages && this.filters.tractions ? 'all' : this.filters.carriages ? 'carriages' : this.filters.tractions ? 'tractions' : null;
|
|
||||||
|
|
||||||
console.log(filtersActive);
|
|
||||||
|
|
||||||
return this.headers.filter((header) => header.for == 'all' || header.for == filtersActive);
|
return this.headers.filter((header) => header.for == 'all' || header.for == filtersActive);
|
||||||
},
|
},
|
||||||
|
|
||||||
// computedCarList() {
|
areTractionVehiclesShown() {
|
||||||
// const trimmedSearchValue = this.searchedVehicleTypeName.trim();
|
return this.currentFilterMode == 'all' || this.currentFilterMode == 'tractions';
|
||||||
|
},
|
||||||
|
|
||||||
// return this.store.carDataList.map((car) =>({
|
areCarriagesShown() {
|
||||||
|
return this.currentFilterMode == 'all' || this.currentFilterMode == 'carriages';
|
||||||
// })).sort(this.sortVehicles);
|
},
|
||||||
// },
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -291,6 +291,10 @@ export default defineComponent({
|
|||||||
td {
|
td {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
height: 70px;
|
height: 70px;
|
||||||
|
|
||||||
|
&[data-sponsoronly='true'] {
|
||||||
|
color: salmon;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
@dragover="allowDrop"
|
@dragover="allowDrop"
|
||||||
>
|
>
|
||||||
<span @click="onListItemClick(stockIndex)" :key="stock.id">
|
<span @click="onListItemClick(stockIndex)" :key="stock.id">
|
||||||
<b :class="{ supporter: stock.supportersOnly }">
|
<b :class="{ sponsor: stock.isSponsorsOnly }">
|
||||||
{{ stock.type }}
|
{{ stock.type }}
|
||||||
</b>
|
</b>
|
||||||
|
|
||||||
@@ -29,18 +29,18 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Ref, computed, nextTick, ref, watch } from "vue";
|
import { Ref, computed, nextTick, ref, watch } from 'vue';
|
||||||
import { useStore } from "../../store";
|
import { useStore } from '../../store';
|
||||||
import { IStock } from "../../types";
|
import { IStock } from '../../types';
|
||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
const emit = defineEmits(["listItemClick"]);
|
const emit = defineEmits(['listItemClick']);
|
||||||
|
|
||||||
const thumbnailsRef = ref() as Ref<HTMLElement>;
|
const thumbnailsRef = ref() as Ref<HTMLElement>;
|
||||||
const draggedIndex = ref(-1);
|
const draggedIndex = ref(-1);
|
||||||
|
|
||||||
const onListItemClick = (index: number) => {
|
const onListItemClick = (index: number) => {
|
||||||
emit("listItemClick", index);
|
emit('listItemClick', index);
|
||||||
};
|
};
|
||||||
|
|
||||||
const stockImageError = (e: Event, stock: IStock) => {
|
const stockImageError = (e: Event, stock: IStock) => {
|
||||||
@@ -53,15 +53,13 @@ watch(
|
|||||||
if (index < 0) return;
|
if (index < 0) return;
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
(thumbnailsRef.value as HTMLElement)
|
(thumbnailsRef.value as HTMLElement).querySelector(`div:nth-child(${index + 1})`)?.scrollIntoView({
|
||||||
.querySelector(`div:nth-child(${index + 1})`)
|
block: 'nearest',
|
||||||
?.scrollIntoView({
|
inline: 'start',
|
||||||
block: "nearest",
|
behavior: 'smooth',
|
||||||
inline: "start",
|
});
|
||||||
behavior: "smooth",
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Dragging images
|
// Dragging images
|
||||||
@@ -72,9 +70,7 @@ const onDragStart = (vehicleIndex: number) => {
|
|||||||
const onDrop = (e: DragEvent, vehicleIndex: number) => {
|
const onDrop = (e: DragEvent, vehicleIndex: number) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
let targetEl = thumbnailsRef.value.querySelector(
|
let targetEl = thumbnailsRef.value.querySelector(`div:nth-child(${vehicleIndex + 1})`);
|
||||||
`div:nth-child(${vehicleIndex + 1})`,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!targetEl && draggedIndex.value != -1) return;
|
if (!targetEl && draggedIndex.value != -1) return;
|
||||||
|
|
||||||
@@ -102,7 +98,7 @@ const allowDrop = (e: DragEvent) => {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
|
|
||||||
&[data-selected="true"] {
|
&[data-selected='true'] {
|
||||||
background-color: rebeccapurple;
|
background-color: rebeccapurple;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +121,7 @@ const allowDrop = (e: DragEvent) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.supporter {
|
.sponsor {
|
||||||
color: salmon;
|
color: salmon;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ import axios from "axios";
|
|||||||
|
|
||||||
const http = axios.create({
|
const http = axios.create({
|
||||||
baseURL:
|
baseURL:
|
||||||
import.meta.env.VITE_API_DEV === "1"
|
import.meta.env.VITE_API_DEV === "1" && import.meta.env.DEV
|
||||||
? "http://localhost:5500"
|
? "http://localhost:5500"
|
||||||
: "https://spythere.github.io/api",
|
: "https://spythere.github.io/api",
|
||||||
});
|
});
|
||||||
|
|||||||
+2
-2
@@ -29,7 +29,7 @@
|
|||||||
"title": "RAILWAY VEHICLE PREVIEW",
|
"title": "RAILWAY VEHICLE PREVIEW",
|
||||||
"loading": "IMAGE LOADING...",
|
"loading": "IMAGE LOADING...",
|
||||||
"desc": "Choose a railway vehicle above to see its preview",
|
"desc": "Choose a railway vehicle above to see its preview",
|
||||||
"sponsor-only": "* SPONSORS ONLY",
|
"sponsor-only": "* SPONSORS ONLY UNTIL {0}",
|
||||||
"loco-e": "ELECTRIC LOCO",
|
"loco-e": "ELECTRIC LOCO",
|
||||||
"loco-s": "DIESEL LOCO",
|
"loco-s": "DIESEL LOCO",
|
||||||
"loco-ezt": "ELECTRIC M.U.",
|
"loco-ezt": "ELECTRIC M.U.",
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
"header": {
|
"header": {
|
||||||
"image": "Image",
|
"image": "Image",
|
||||||
"type": "Name",
|
"type": "Name",
|
||||||
"power": "Type",
|
"group": "Type group",
|
||||||
"constructionType": "Construction",
|
"constructionType": "Construction",
|
||||||
"coldStart": "Cold start",
|
"coldStart": "Cold start",
|
||||||
"length": "Length",
|
"length": "Length",
|
||||||
|
|||||||
+2
-2
@@ -29,7 +29,7 @@
|
|||||||
"title": "PODGLĄD WYBRANEGO POJAZDU",
|
"title": "PODGLĄD WYBRANEGO POJAZDU",
|
||||||
"loading": "ŁADOWANIE OBRAZU...",
|
"loading": "ŁADOWANIE OBRAZU...",
|
||||||
"desc": "Wybierz pojazd lub wagon, aby zobaczyć jego podgląd powyżej",
|
"desc": "Wybierz pojazd lub wagon, aby zobaczyć jego podgląd powyżej",
|
||||||
"sponsor-only": "* TYLKO DLA SPONSORÓW",
|
"sponsor-only": "* TYLKO DLA SPONSORÓW DO {0}",
|
||||||
"loco-e": "ELEKTROWÓZ",
|
"loco-e": "ELEKTROWÓZ",
|
||||||
"loco-s": "SPALINOWÓZ",
|
"loco-s": "SPALINOWÓZ",
|
||||||
"loco-ezt": "EZT",
|
"loco-ezt": "EZT",
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
"header": {
|
"header": {
|
||||||
"image": "Zdjęcie",
|
"image": "Zdjęcie",
|
||||||
"type": "Nazwa",
|
"type": "Nazwa",
|
||||||
"power": "Rodzaj",
|
"group": "Rodzaj",
|
||||||
"constructionType": "Konstrukcja",
|
"constructionType": "Konstrukcja",
|
||||||
"coldStart": "Zimny start",
|
"coldStart": "Zimny start",
|
||||||
"length": "Długość",
|
"length": "Długość",
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export default defineComponent({
|
|||||||
count,
|
count,
|
||||||
imgSrc: vehicle.imageSrc,
|
imgSrc: vehicle.imageSrc,
|
||||||
useType: isLoco ? vehicle.power : vehicle.useType,
|
useType: isLoco ? vehicle.power : vehicle.useType,
|
||||||
supportersOnly: vehicle.supportersOnly,
|
isSponsorsOnly: vehicle.isSponsorsOnly,
|
||||||
constructionType: vehicle.constructionType,
|
constructionType: vehicle.constructionType,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { defineComponent } from "vue";
|
import { defineComponent } from 'vue';
|
||||||
import { useStore } from "../store";
|
import { useStore } from '../store';
|
||||||
import { ICarWagon, ILocomotive, IStock } from "../types";
|
import { ICarWagon, ILocomotive, IStock, Vehicle } from '../types';
|
||||||
|
import { isLocomotive } from '../utils/vehicleUtils';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
@@ -9,64 +10,20 @@ export default defineComponent({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {},
|
||||||
locoOptions() {
|
|
||||||
return this.store.locoDataList
|
|
||||||
.slice()
|
|
||||||
.sort((a, b) => (a.type > b.type ? 1 : -1))
|
|
||||||
.filter((loco) => loco.power == this.store.chosenLocoPower);
|
|
||||||
},
|
|
||||||
|
|
||||||
carOptions() {
|
|
||||||
return this.store.carDataList
|
|
||||||
.slice()
|
|
||||||
.sort((a, b) => (a.type > b.type ? 1 : -1))
|
|
||||||
.filter((car) => car.useType == this.store.chosenCarUseType);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
selectLocoType(locoTypeId: string) {
|
|
||||||
this.store.chosenLocoPower = locoTypeId;
|
|
||||||
this.store.chosenVehicle = this.locoOptions[0];
|
|
||||||
this.store.chosenLoco = this.locoOptions[0];
|
|
||||||
},
|
|
||||||
|
|
||||||
selectCarWagonType(carWagonTypeId: string) {
|
|
||||||
this.store.chosenCarUseType = carWagonTypeId;
|
|
||||||
this.store.chosenVehicle = this.carOptions[0];
|
|
||||||
this.store.chosenCar = this.carOptions[0];
|
|
||||||
this.store.chosenCargo = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
previewVehicleByType(type: "loco" | "car" | "cargo") {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (!this.store.chosenLoco && !this.store.chosenCar) return;
|
|
||||||
|
|
||||||
this.store.chosenVehicle =
|
|
||||||
type == "loco" ? this.store.chosenLoco : this.store.chosenCar;
|
|
||||||
|
|
||||||
this.store.chosenCargo =
|
|
||||||
this.store.chosenCar?.cargoList.find(
|
|
||||||
(cargo) => cargo.id == this.store.chosenCargo?.id,
|
|
||||||
) || null;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
previewStock(stock: IStock) {
|
previewStock(stock: IStock) {
|
||||||
if (this.store.chosenVehicle?.imageSrc != stock.imgSrc)
|
if (this.store.chosenVehicle?.imageSrc != stock.imgSrc) this.store.imageLoading = true;
|
||||||
this.store.imageLoading = true;
|
|
||||||
|
|
||||||
if (stock.isLoco) {
|
if (stock.isLoco) {
|
||||||
const chosenLoco =
|
const chosenLoco = this.store.locoDataList.find((v) => v.type == stock.type) || null;
|
||||||
this.store.locoDataList.find((v) => v.type == stock.type) || null;
|
|
||||||
this.store.chosenVehicle = chosenLoco;
|
this.store.chosenVehicle = chosenLoco;
|
||||||
this.store.chosenLoco = chosenLoco;
|
this.store.chosenLoco = chosenLoco;
|
||||||
this.store.chosenCargo = null;
|
this.store.chosenCargo = null;
|
||||||
this.store.chosenLocoPower = stock.useType;
|
this.store.chosenLocoPower = stock.useType;
|
||||||
} else {
|
} else {
|
||||||
const chosenCar =
|
const chosenCar = this.store.carDataList.find((v) => v.type == stock.type) || null;
|
||||||
this.store.carDataList.find((v) => v.type == stock.type) || null;
|
|
||||||
this.store.chosenVehicle = chosenCar;
|
this.store.chosenVehicle = chosenCar;
|
||||||
this.store.chosenCar = chosenCar;
|
this.store.chosenCar = chosenCar;
|
||||||
|
|
||||||
@@ -89,6 +46,11 @@ export default defineComponent({
|
|||||||
this.store.chosenCargo = null;
|
this.store.chosenCargo = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
previewVehicle(vehicle: Vehicle) {
|
||||||
|
if (isLocomotive(vehicle)) this.previewLocomotive(vehicle);
|
||||||
|
else this.previewCarWagon(vehicle);
|
||||||
|
},
|
||||||
|
|
||||||
resetPreview() {
|
resetPreview() {
|
||||||
this.store.chosenVehicle = null;
|
this.store.chosenVehicle = null;
|
||||||
this.store.chosenCar = null;
|
this.store.chosenCar = null;
|
||||||
|
|||||||
+19
-18
@@ -1,5 +1,5 @@
|
|||||||
import { IStockData, IStore } from "./types";
|
import { IStockData, IStore } from './types';
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from 'pinia';
|
||||||
import {
|
import {
|
||||||
acceptableMass,
|
acceptableMass,
|
||||||
carDataList,
|
carDataList,
|
||||||
@@ -9,11 +9,11 @@ import {
|
|||||||
maxStockSpeed,
|
maxStockSpeed,
|
||||||
totalLength,
|
totalLength,
|
||||||
totalMass,
|
totalMass,
|
||||||
} from "./utils/vehicleUtils";
|
} from './utils/vehicleUtils';
|
||||||
import http from "./http";
|
import http from './http';
|
||||||
|
|
||||||
export const useStore = defineStore({
|
export const useStore = defineStore({
|
||||||
id: "store",
|
id: 'store',
|
||||||
state: () =>
|
state: () =>
|
||||||
({
|
({
|
||||||
chosenCar: null,
|
chosenCar: null,
|
||||||
@@ -26,8 +26,8 @@ export const useStore = defineStore({
|
|||||||
showSupporter: false,
|
showSupporter: false,
|
||||||
imageLoading: false,
|
imageLoading: false,
|
||||||
|
|
||||||
chosenLocoPower: "loco-e",
|
chosenLocoPower: 'loco-e',
|
||||||
chosenCarUseType: "car-passenger",
|
chosenCarUseType: 'car-passenger',
|
||||||
|
|
||||||
stockList: [],
|
stockList: [],
|
||||||
cargoOptions: [],
|
cargoOptions: [],
|
||||||
@@ -39,20 +39,22 @@ export const useStore = defineStore({
|
|||||||
chosenStockListIndex: -1,
|
chosenStockListIndex: -1,
|
||||||
chosenRealStockName: undefined,
|
chosenRealStockName: undefined,
|
||||||
|
|
||||||
vehiclePreviewSrc: "",
|
vehiclePreviewSrc: '',
|
||||||
|
|
||||||
stockSectionMode: "stock-list",
|
stockSectionMode: 'stock-list',
|
||||||
|
|
||||||
isRandomizerCardOpen: false,
|
isRandomizerCardOpen: false,
|
||||||
isRealStockListCardOpen: false,
|
isRealStockListCardOpen: false,
|
||||||
|
|
||||||
stockData: undefined,
|
stockData: undefined,
|
||||||
|
|
||||||
|
lastFocusedElement: null,
|
||||||
}) as IStore,
|
}) as IStore,
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
locoDataList: (state) => locoDataList(state),
|
locoDataList: (state) => locoDataList(state),
|
||||||
carDataList: (state) => carDataList(state),
|
carDataList: (state) => carDataList(state),
|
||||||
vehicleDataList: (state) => ([...locoDataList(state), ...carDataList(state)]),
|
vehicleDataList: (state) => [...locoDataList(state), ...carDataList(state)],
|
||||||
totalMass: (state) => totalMass(state),
|
totalMass: (state) => totalMass(state),
|
||||||
totalLength: (state) => totalLength(state),
|
totalLength: (state) => totalLength(state),
|
||||||
maxStockSpeed: (state) => maxStockSpeed(state),
|
maxStockSpeed: (state) => maxStockSpeed(state),
|
||||||
@@ -63,21 +65,20 @@ export const useStore = defineStore({
|
|||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
async fetchStockInfoData() {
|
async fetchStockInfoData() {
|
||||||
const stockData = (await http.get<IStockData>("td2/data/stockInfo.json"))
|
const stockData = (await http.get<IStockData>('td2/data/stockInfo.json')).data;
|
||||||
.data;
|
|
||||||
this.stockData = stockData;
|
this.stockData = stockData;
|
||||||
},
|
},
|
||||||
|
|
||||||
handleRouting() {
|
handleRouting() {
|
||||||
switch (window.location.pathname) {
|
switch (window.location.pathname) {
|
||||||
case "/numgnr":
|
case '/numgnr':
|
||||||
this.stockSectionMode = "number-generator";
|
this.stockSectionMode = 'number-generator';
|
||||||
break;
|
break;
|
||||||
case "/stockgnr":
|
case '/stockgnr':
|
||||||
this.stockSectionMode = "stock-generator";
|
this.stockSectionMode = 'stock-generator';
|
||||||
break;
|
break;
|
||||||
case "/vehicles":
|
case '/vehicles':
|
||||||
this.stockSectionMode = "wiki-list";
|
this.stockSectionMode = 'wiki-list';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
+33
-5
@@ -1,5 +1,3 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@400;700;900&display=swap');
|
|
||||||
|
|
||||||
$breakpointMd: 960px;
|
$breakpointMd: 960px;
|
||||||
$breakpointSm: 550px;
|
$breakpointSm: 550px;
|
||||||
|
|
||||||
@@ -8,6 +6,36 @@ $textColor: #fff;
|
|||||||
$secondaryColor: #222;
|
$secondaryColor: #222;
|
||||||
$accentColor: #e4c428;
|
$accentColor: #e4c428;
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Lato';
|
||||||
|
src:
|
||||||
|
url('$fonts/Lato-Light.woff2') format('woff2'),
|
||||||
|
url('$fonts/Lato-Light.woff') format('woff');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Lato';
|
||||||
|
src:
|
||||||
|
url('$fonts/Lato-Bold.woff2') format('woff2'),
|
||||||
|
url('$fonts/Lato-Bold.woff') format('woff');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Lato';
|
||||||
|
src:
|
||||||
|
url('$fonts/Lato-Regular.woff2') format('woff2'),
|
||||||
|
url('$fonts/Lato-Regular.woff') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 7px;
|
width: 7px;
|
||||||
height: 7px;
|
height: 7px;
|
||||||
@@ -32,7 +60,7 @@ html {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
font-family: 'Lato', sans-serif;
|
font-family: Lato, sans-serif;
|
||||||
|
|
||||||
background-color: $bgColor;
|
background-color: $bgColor;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
@@ -64,7 +92,7 @@ select,
|
|||||||
option,
|
option,
|
||||||
input,
|
input,
|
||||||
button {
|
button {
|
||||||
font-family: 'Lato', sans-serif;
|
font-family: Lato, sans-serif;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +185,7 @@ button {
|
|||||||
select,
|
select,
|
||||||
input[type='text'],
|
input[type='text'],
|
||||||
input[type='number'] {
|
input[type='number'] {
|
||||||
background: none;
|
background: $bgColor;
|
||||||
border: 2px solid #aaa;
|
border: 2px solid #aaa;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|
||||||
|
|||||||
+22
-24
@@ -1,5 +1,5 @@
|
|||||||
export type Vehicle = ILocomotive | ICarWagon;
|
export type Vehicle = ILocomotive | ICarWagon;
|
||||||
export type StockSectionMode = "STOCK_LIST" | "STOCK_GENERATOR";
|
export type StockSectionMode = 'STOCK_LIST' | 'STOCK_GENERATOR';
|
||||||
|
|
||||||
export interface IStore {
|
export interface IStore {
|
||||||
chosenCar: ICarWagon | null;
|
chosenCar: ICarWagon | null;
|
||||||
@@ -28,21 +28,14 @@ export interface IStore {
|
|||||||
isRandomizerCardOpen: boolean;
|
isRandomizerCardOpen: boolean;
|
||||||
isRealStockListCardOpen: boolean;
|
isRealStockListCardOpen: boolean;
|
||||||
|
|
||||||
stockSectionMode:
|
stockSectionMode: 'stock-list' | 'stock-generator' | 'number-generator' | 'wiki-list';
|
||||||
| "stock-list"
|
|
||||||
| "stock-generator"
|
|
||||||
| "number-generator"
|
|
||||||
| "wiki-list";
|
|
||||||
stockData?: IStockData;
|
stockData?: IStockData;
|
||||||
|
|
||||||
|
lastFocusedElement: HTMLElement | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TStockInfoKey =
|
export type TLocoGroup = 'loco-e' | 'loco-s' | 'loco-ezt' | 'loco-szt';
|
||||||
| "loco-e"
|
export type TCarWagonGroup = 'car-passenger' | 'car-cargo';
|
||||||
| "loco-s"
|
|
||||||
| "loco-ezt"
|
|
||||||
| "loco-szt"
|
|
||||||
| "car-passenger"
|
|
||||||
| "car-cargo";
|
|
||||||
|
|
||||||
export interface IStockProps {
|
export interface IStockProps {
|
||||||
type: string;
|
type: string;
|
||||||
@@ -62,12 +55,12 @@ export interface IStockData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
info: {
|
info: {
|
||||||
"car-cargo": [string, string, boolean, boolean, string][];
|
'car-cargo': [string, string, boolean, number | null, string][];
|
||||||
"car-passenger": [string, string, boolean, boolean, string][];
|
'car-passenger': [string, string, boolean, number | null, string][];
|
||||||
"loco-e": [string, string, string, string, boolean][];
|
'loco-e': [string, string, string, string, number | null][];
|
||||||
"loco-s": [string, string, string, string, boolean][];
|
'loco-s': [string, string, string, string, number | null][];
|
||||||
"loco-szt": [string, string, string, string, boolean][];
|
'loco-szt': [string, string, string, string, number | null][];
|
||||||
"loco-ezt": [string, string, string, string, boolean][];
|
'loco-ezt': [string, string, string, string, number | null][];
|
||||||
};
|
};
|
||||||
|
|
||||||
props: IStockProps[];
|
props: IStockProps[];
|
||||||
@@ -77,11 +70,13 @@ export interface IStockData {
|
|||||||
|
|
||||||
export interface ILocomotive {
|
export interface ILocomotive {
|
||||||
type: string;
|
type: string;
|
||||||
power: string;
|
power: TLocoGroup;
|
||||||
|
group: TLocoGroup;
|
||||||
constructionType: string;
|
constructionType: string;
|
||||||
cabinType: string;
|
cabinType: string;
|
||||||
maxSpeed: number;
|
maxSpeed: number;
|
||||||
supportersOnly: boolean;
|
isSponsorsOnly: boolean;
|
||||||
|
sponsorsOnlyTimestamp: number;
|
||||||
imageSrc: string;
|
imageSrc: string;
|
||||||
|
|
||||||
mass: number;
|
mass: number;
|
||||||
@@ -90,10 +85,12 @@ export interface ILocomotive {
|
|||||||
|
|
||||||
export interface ICarWagon {
|
export interface ICarWagon {
|
||||||
type: string;
|
type: string;
|
||||||
useType: "car-passenger" | "car-cargo";
|
useType: TCarWagonGroup;
|
||||||
|
group: TCarWagonGroup;
|
||||||
constructionType: string;
|
constructionType: string;
|
||||||
loadable: boolean;
|
loadable: boolean;
|
||||||
supportersOnly: boolean;
|
isSponsorsOnly: boolean;
|
||||||
|
sponsorsOnlyTimestamp: number;
|
||||||
maxSpeed: number;
|
maxSpeed: number;
|
||||||
imageSrc: string;
|
imageSrc: string;
|
||||||
|
|
||||||
@@ -117,7 +114,8 @@ export interface IStock {
|
|||||||
maxSpeed: number;
|
maxSpeed: number;
|
||||||
cargo?: { id: string; totalMass: number };
|
cargo?: { id: string; totalMass: number };
|
||||||
isLoco: boolean;
|
isLoco: boolean;
|
||||||
supportersOnly: boolean;
|
isSponsorsOnly: boolean;
|
||||||
|
sponsorsOnlyTimestamp: number;
|
||||||
count: number;
|
count: number;
|
||||||
imgSrc?: string;
|
imgSrc?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-76
@@ -1,10 +1,8 @@
|
|||||||
import { EVehicleUseType } from "../enums/EVehicleUseType";
|
import { EVehicleUseType } from '../enums/EVehicleUseType';
|
||||||
import { ICarWagon, ILocomotive, IStore } from "../types";
|
import { ICarWagon, ILocomotive, IStore, TCarWagonGroup, TLocoGroup } from '../types';
|
||||||
import { LocoType, calculateSpeedLimit } from "./speedLimitUtils";
|
import { LocoType, calculateSpeedLimit } from './speedLimitUtils';
|
||||||
|
|
||||||
export function isLocomotive(
|
export function isLocomotive(vehicle: ILocomotive | ICarWagon): vehicle is ILocomotive {
|
||||||
vehicle: ILocomotive | ICarWagon,
|
|
||||||
): vehicle is ILocomotive {
|
|
||||||
return (vehicle as ILocomotive).power !== undefined;
|
return (vehicle as ILocomotive).power !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,39 +12,29 @@ export function locoDataList(state: IStore) {
|
|||||||
const stockData = state.stockData;
|
const stockData = state.stockData;
|
||||||
|
|
||||||
return Object.keys(stockData.info).reduce((acc, vehiclePower) => {
|
return Object.keys(stockData.info).reduce((acc, vehiclePower) => {
|
||||||
if (!vehiclePower.startsWith("loco")) return acc;
|
if (!vehiclePower.startsWith('loco')) return acc;
|
||||||
|
|
||||||
const locoVehiclesData =
|
const locoVehiclesData = stockData.info[vehiclePower as TLocoGroup];
|
||||||
stockData.info[
|
|
||||||
vehiclePower as "loco-e" | "loco-s" | "loco-ezt" | "loco-szt"
|
|
||||||
];
|
|
||||||
|
|
||||||
locoVehiclesData.forEach((loco) => {
|
locoVehiclesData.forEach((loco) => {
|
||||||
if (state.showSupporter && !loco[4]) return;
|
if (state.showSupporter && !loco[4]) return;
|
||||||
|
|
||||||
const [type, constructionType, cabinType, maxSpeed, supportersOnly] =
|
const [type, constructionType, cabinType, maxSpeed, sponsorsTimestamp] = loco;
|
||||||
loco;
|
const locoProps = stockData.props.find((prop) => constructionType == prop.type);
|
||||||
const locoProps = stockData.props.find(
|
|
||||||
(prop) => constructionType == prop.type,
|
|
||||||
);
|
|
||||||
|
|
||||||
acc.push({
|
acc.push({
|
||||||
power: vehiclePower,
|
power: vehiclePower as TLocoGroup,
|
||||||
|
group: vehiclePower as TLocoGroup,
|
||||||
type,
|
type,
|
||||||
constructionType,
|
constructionType,
|
||||||
cabinType,
|
cabinType,
|
||||||
maxSpeed: Number(maxSpeed),
|
maxSpeed: Number(maxSpeed),
|
||||||
supportersOnly,
|
isSponsorsOnly: Number(sponsorsTimestamp) > Date.now(),
|
||||||
imageSrc: "",
|
sponsorsOnlyTimestamp: Number(sponsorsTimestamp),
|
||||||
|
imageSrc: '',
|
||||||
|
|
||||||
length:
|
length: locoProps?.length && type.startsWith('2EN') ? locoProps.length * 2 : locoProps?.length || 0,
|
||||||
locoProps?.length && type.startsWith("2EN")
|
mass: locoProps?.mass && type.startsWith('2EN') ? 253 : locoProps?.mass || 0,
|
||||||
? locoProps.length * 2
|
|
||||||
: locoProps?.length || 0,
|
|
||||||
mass:
|
|
||||||
locoProps?.mass && type.startsWith("2EN")
|
|
||||||
? 253
|
|
||||||
: locoProps?.mass || 0,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -60,32 +48,33 @@ export function carDataList(state: IStore) {
|
|||||||
const stockData = state.stockData;
|
const stockData = state.stockData;
|
||||||
|
|
||||||
return Object.keys(stockData.info).reduce((acc, vehicleUseType) => {
|
return Object.keys(stockData.info).reduce((acc, vehicleUseType) => {
|
||||||
if (!vehicleUseType.startsWith("car")) return acc;
|
if (!vehicleUseType.startsWith('car')) return acc;
|
||||||
|
|
||||||
const carVehiclesData =
|
const carVehiclesData = stockData.info[vehicleUseType as TCarWagonGroup];
|
||||||
stockData.info[vehicleUseType as "car-passenger" | "car-cargo"];
|
|
||||||
|
|
||||||
carVehiclesData.forEach((car) => {
|
carVehiclesData.forEach((car) => {
|
||||||
if (state.showSupporter && !car[3]) return;
|
const [type, constructionType, loadable, sponsorsOnlyTimestamp, maxSpeed] = car;
|
||||||
|
|
||||||
const carPropsData = stockData.props.find((v) =>
|
if (state.showSupporter && Number(sponsorsOnlyTimestamp) <= Date.now()) return;
|
||||||
car[0].toString().startsWith(v.type),
|
|
||||||
);
|
const carPropsData = stockData.props.find((v) => type.toString().startsWith(v.type));
|
||||||
|
|
||||||
acc.push({
|
acc.push({
|
||||||
useType: vehicleUseType as "car-passenger" | "car-cargo",
|
useType: vehicleUseType as TCarWagonGroup,
|
||||||
type: car[0],
|
group: vehicleUseType as TCarWagonGroup,
|
||||||
constructionType: car[1],
|
type,
|
||||||
loadable: car[2],
|
constructionType,
|
||||||
supportersOnly: car[3],
|
loadable,
|
||||||
maxSpeed: Number(car[4]),
|
isSponsorsOnly: Number(sponsorsOnlyTimestamp) > Date.now(),
|
||||||
imageSrc: "",
|
sponsorsOnlyTimestamp: Number(sponsorsOnlyTimestamp),
|
||||||
|
maxSpeed: Number(maxSpeed),
|
||||||
|
imageSrc: '',
|
||||||
cargoList:
|
cargoList:
|
||||||
!carPropsData || carPropsData.cargo === null
|
!carPropsData || carPropsData.cargo === null
|
||||||
? []
|
? []
|
||||||
: carPropsData.cargo.split(";").map((cargo) => ({
|
: carPropsData.cargo.split(';').map((cargo) => ({
|
||||||
id: cargo.split(":")[0],
|
id: cargo.split(':')[0],
|
||||||
totalMass: Number(cargo.split(":")[1]),
|
totalMass: Number(cargo.split(':')[1]),
|
||||||
})),
|
})),
|
||||||
|
|
||||||
mass: carPropsData?.mass || 0,
|
mass: carPropsData?.mass || 0,
|
||||||
@@ -98,46 +87,28 @@ export function carDataList(state: IStore) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function totalMass(state: IStore) {
|
export function totalMass(state: IStore) {
|
||||||
return ~~state.stockList.reduce(
|
return ~~state.stockList.reduce((acc, stock) => acc + (stock.cargo ? stock.cargo.totalMass : stock.mass) * stock.count, 0);
|
||||||
(acc, stock) =>
|
|
||||||
acc + (stock.cargo ? stock.cargo.totalMass : stock.mass) * stock.count,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function totalLength(state: IStore) {
|
export function totalLength(state: IStore) {
|
||||||
return state.stockList.reduce(
|
return state.stockList.reduce((acc, stock) => acc + stock.length * stock.count, 0);
|
||||||
(acc, stock) => acc + stock.length * stock.count,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function maxStockSpeed(state: IStore) {
|
export function maxStockSpeed(state: IStore) {
|
||||||
const stockSpeedLimit = state.stockList.reduce(
|
const stockSpeedLimit = state.stockList.reduce((acc, stock) => (stock.maxSpeed < acc || acc == 0 ? stock.maxSpeed : acc), 0);
|
||||||
(acc, stock) => (stock.maxSpeed < acc || acc == 0 ? stock.maxSpeed : acc),
|
const headingLoco = state.stockList[0]?.isLoco ? state.stockList[0] : undefined;
|
||||||
0,
|
|
||||||
);
|
|
||||||
const headingLoco = state.stockList[0]?.isLoco
|
|
||||||
? state.stockList[0]
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
if (!headingLoco) return stockSpeedLimit;
|
if (!headingLoco) return stockSpeedLimit;
|
||||||
|
|
||||||
const locoType = headingLoco.type.split("-")[0];
|
const locoType = headingLoco.type.split('-')[0];
|
||||||
|
|
||||||
if (/^(EN|2EN|SN)/.test(locoType)) return stockSpeedLimit;
|
if (/^(EN|2EN|SN)/.test(locoType)) return stockSpeedLimit;
|
||||||
|
|
||||||
const stockMass = totalMass(state);
|
const stockMass = totalMass(state);
|
||||||
|
|
||||||
const speedLimitByMass = calculateSpeedLimit(
|
const speedLimitByMass = calculateSpeedLimit(locoType as LocoType, stockMass, isTrainPassenger(state));
|
||||||
locoType as LocoType,
|
|
||||||
stockMass,
|
|
||||||
isTrainPassenger(state),
|
|
||||||
);
|
|
||||||
|
|
||||||
return speedLimitByMass
|
return speedLimitByMass ? Math.min(stockSpeedLimit, speedLimitByMass) : stockSpeedLimit;
|
||||||
? Math.min(stockSpeedLimit, speedLimitByMass)
|
|
||||||
: stockSpeedLimit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function acceptableMass(state: IStore) {
|
export function acceptableMass(state: IStore) {
|
||||||
@@ -168,9 +139,7 @@ export function isTrainPassenger(state: IStore) {
|
|||||||
if (state.stockList.length == 0) return false;
|
if (state.stockList.length == 0) return false;
|
||||||
if (state.stockList.every((stock) => stock.isLoco)) return false;
|
if (state.stockList.every((stock) => stock.isLoco)) return false;
|
||||||
|
|
||||||
return state.stockList
|
return state.stockList.filter((stock) => !stock.isLoco).every((stock) => stock.useType === EVehicleUseType.CAR_PASSENGER);
|
||||||
.filter((stock) => !stock.isLoco)
|
|
||||||
.every((stock) => stock.useType === EVehicleUseType.CAR_PASSENGER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function chosenRealStock(state: IStore) {
|
export function chosenRealStock(state: IStore) {
|
||||||
@@ -179,11 +148,9 @@ export function chosenRealStock(state: IStore) {
|
|||||||
for (let i = 0; i < stock.count; i++) acc.push(stock.type);
|
for (let i = 0; i < stock.count; i++) acc.push(stock.type);
|
||||||
return acc;
|
return acc;
|
||||||
}, [] as string[])
|
}, [] as string[])
|
||||||
.join(";");
|
.join(';');
|
||||||
|
|
||||||
const realStockObj = state.readyStockList.find(
|
const realStockObj = state.readyStockList.find((readyStock) => readyStock.stockString == currentStockString);
|
||||||
(readyStock) => readyStock.stockString == currentStockString,
|
|
||||||
);
|
|
||||||
|
|
||||||
state.chosenRealStockName = realStockObj?.stockId ?? undefined;
|
state.chosenRealStockName = realStockObj?.stockId ?? undefined;
|
||||||
|
|
||||||
|
|||||||
+23
-5
@@ -2,19 +2,26 @@ import { defineConfig } from 'vite';
|
|||||||
import vue from '@vitejs/plugin-vue';
|
import vue from '@vitejs/plugin-vue';
|
||||||
|
|
||||||
import { VitePWA } from 'vite-plugin-pwa';
|
import { VitePWA } from 'vite-plugin-pwa';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
server: {
|
server: {
|
||||||
port: 2137,
|
port: 2137,
|
||||||
},
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
$fonts: resolve('/fonts'),
|
||||||
|
$images: resolve('/images'),
|
||||||
|
},
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
VitePWA({
|
VitePWA({
|
||||||
registerType: 'autoUpdate',
|
registerType: 'autoUpdate',
|
||||||
|
|
||||||
workbox: {
|
workbox: {
|
||||||
// globPatterns: ['**/*.{js,css,html,png,svg,img}'],
|
globPatterns: ['**/*.{js,css,html,png,svg,img,woff,woff2}'],
|
||||||
|
|
||||||
runtimeCaching: [
|
runtimeCaching: [
|
||||||
{
|
{
|
||||||
@@ -24,10 +31,23 @@ export default defineConfig({
|
|||||||
cacheName: 'swdr-images-cache',
|
cacheName: 'swdr-images-cache',
|
||||||
expiration: {
|
expiration: {
|
||||||
maxEntries: 50,
|
maxEntries: 50,
|
||||||
maxAgeSeconds: 60 * 60 * 24 * 7, // <== 7 days
|
maxAgeSeconds: 60 * 60 * 24, // <== 1 day
|
||||||
},
|
},
|
||||||
cacheableResponse: {
|
cacheableResponse: {
|
||||||
statuses: [404],
|
statuses: [0, 200, 404],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
urlPattern: /^https:\/\/spythere.github.io\/api\/td2\/.*/i,
|
||||||
|
handler: 'CacheFirst',
|
||||||
|
options: {
|
||||||
|
cacheName: 'spythere-api-cache',
|
||||||
|
expiration: {
|
||||||
|
maxAgeSeconds: 60 * 60 * 24, // <== 1 day
|
||||||
|
},
|
||||||
|
cacheableResponse: {
|
||||||
|
statuses: [0, 200],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -36,5 +56,3 @@ export default defineConfig({
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user