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