mirror of
https://github.com/Spythere/pojazdownik.git
synced 2026-05-03 19:48:11 +00:00
section improvements; hotfixes
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user