Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 421ae1db6b | |||
| 90824dc0e5 | |||
| 583c2887e9 | |||
| f915094775 | |||
| d5e735b59e | |||
| 43a724bf13 | |||
| 337425d21c | |||
| 9ff9341851 | |||
| f9276f6c71 | |||
| f4f9a4729f | |||
| ed0906b63e | |||
| 2ae19123a3 | |||
| 0c2be7b927 | |||
| ffce2b572b | |||
| 1b5a26e380 | |||
| c9b681eaee | |||
| 160879adec | |||
| d6ddbe7af7 | |||
| 1c7b72ea1d | |||
| 83f5b07c7e | |||
| 77cb64e25a | |||
| 6d0cc8e7cd | |||
| 7fb4b0ae26 | |||
| ed191d597b | |||
| 5d24accb15 | |||
| 54c6850121 | |||
| fad8c40b4b | |||
| a8485b3531 | |||
| e7dcd125ec | |||
| d886f44c59 | |||
| 5ff6c0504e | |||
| 2158145ae8 | |||
| 6101c9bfb2 | |||
| bd9af9a630 | |||
| 2188dbdf9b | |||
| c4576d7802 | |||
| 86cb4cff4d | |||
| 0534848677 | |||
| c5d116e2db | |||
| 3dce5ec7c0 | |||
| 956ff8afd7 | |||
| 5b629833df | |||
| b53201a7ff | |||
| f0ddd0e27f | |||
| 94bfba2c49 | |||
| 1e4541ef0d | |||
| c25a55a7d9 | |||
| bea3c59405 | |||
| 8cd43adff3 | |||
| 5ab1963117 | |||
| 50d784a0de | |||
| f61135ce6b | |||
| 5e43ece1aa | |||
| 37e4149a34 | |||
| e515203557 | |||
| 0d79c71eba | |||
| 2bbf9a8ac3 | |||
| 45b2bd01a2 | |||
| 665ffb9dce | |||
| 1c2a93fbd5 | |||
| 57ab6cc02d |
@@ -0,0 +1,13 @@
|
|||||||
|
/* eslint-env node */
|
||||||
|
require('@rushstack/eslint-patch/modern-module-resolution');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
extends: ['plugin:vue/vue3-essential', 'eslint:recommended', '@vue/eslint-config-typescript', '@vue/eslint-config-prettier/skip-formatting'],
|
||||||
|
rules: {
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
env: {
|
|
||||||
node: true,
|
|
||||||
},
|
|
||||||
extends: [
|
|
||||||
"plugin:vue/vue3-essential",
|
|
||||||
"eslint:recommended",
|
|
||||||
"@vue/typescript/recommended",
|
|
||||||
"@vue/prettier",
|
|
||||||
"@vue/prettier/@typescript-eslint",
|
|
||||||
],
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: 2020,
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
|
||||||
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -25,4 +25,7 @@ pnpm-debug.log*
|
|||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
# Dev files
|
# Dev files
|
||||||
stockInfoDev.json
|
stockInfoDev.json
|
||||||
|
|
||||||
|
# Lock files
|
||||||
|
yarn.lock
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"printWidth": 100
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
presets: ["@vue/cli-plugin-babel/preset"],
|
|
||||||
};
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
@@ -8,22 +8,20 @@
|
|||||||
<title>Pojazdownik</title>
|
<title>Pojazdownik</title>
|
||||||
<meta name="description" content="Edytor pociągów online do symulatora Train Driver 2" />
|
<meta name="description" content="Edytor pociągów online do symulatora Train Driver 2" />
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
<link rel="icon" href="/favicon.ico" sizes="any" />
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
<link rel="apple-touch-icon" href="/apple-touch-icon-180x180.png" />
|
||||||
<link rel="manifest" href="/site.webmanifest" />
|
|
||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
||||||
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
|
|
||||||
<meta name="msapplication-TileColor" content="#da532c" />
|
<meta name="msapplication-TileColor" content="#da532c" />
|
||||||
<meta name="theme-color" content="#e4c428" />
|
<meta name="theme-color" content="#111" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>
|
||||||
<strong>
|
<strong> We're sorry but Pojazdownik doesn't work properly without JavaScript enabled. Please enable it to continue. </strong>
|
||||||
We're sorry but Pojazdownik doesn't work properly without JavaScript enabled. Please enable it to continue.
|
|
||||||
</strong>
|
|
||||||
</noscript>
|
</noscript>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,36 @@
|
|||||||
{
|
{
|
||||||
"name": "pojazdownik",
|
"name": "pojazdownik",
|
||||||
"version": "1.6.0",
|
"version": "1.8.3.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vue-tsc --noEmit && vite build",
|
"build": "vue-tsc --noEmit && vite build",
|
||||||
"preview": "yarn build && vite preview --port 4174"
|
"preview": "yarn build && vite preview --port 4174",
|
||||||
|
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false",
|
||||||
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||||
|
"format": "prettier --write src/"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"pinia": "^2.0.17",
|
"pinia": "^2.0.17",
|
||||||
|
"prettier": "^3.0.3",
|
||||||
"vue": "^3.2.37",
|
"vue": "^3.2.37",
|
||||||
"vue-i18n": "9"
|
"vue-i18n": "9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^4.1.0",
|
"@rushstack/eslint-patch": "^1.3.3",
|
||||||
|
"@vite-pwa/assets-generator": "^0.2.3",
|
||||||
|
"@vitejs/plugin-vue": "^5.0.3",
|
||||||
|
"@vue/eslint-config-prettier": "^9.0.0",
|
||||||
|
"@vue/eslint-config-typescript": "^12.0.0",
|
||||||
|
"@vue/tsconfig": "^0.5.1",
|
||||||
|
"eslint": "^8.49.0",
|
||||||
|
"eslint-plugin-vue": "^9.17.0",
|
||||||
"sass": "^1.59.3",
|
"sass": "^1.59.3",
|
||||||
"typescript": "^5.0.2",
|
"typescript": "^5.0.2",
|
||||||
"vite": "^4.2.1",
|
"vite": "^5.0.12",
|
||||||
"vite-plugin-pwa": "^0.14.6",
|
"vite-plugin-pwa": "^0.17.5",
|
||||||
"vue-tsc": "^1.2.0"
|
"vue-tsc": "^1.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,24 @@
|
|||||||
|
<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<mask id="path-1-inside-1_102_63" fill="white">
|
||||||
|
<path d="M0 250C0 111.929 111.929 6.10352e-05 250 6.10352e-05C388.071 6.10352e-05 500 111.929 500 250C500 388.071 388.071 500 250 500C111.929 500 0 388.071 0 250Z"/>
|
||||||
|
</mask>
|
||||||
|
<path d="M0 250C0 111.929 111.929 6.10352e-05 250 6.10352e-05C388.071 6.10352e-05 500 111.929 500 250C500 388.071 388.071 500 250 500C111.929 500 0 388.071 0 250Z" fill="#242424"/>
|
||||||
|
<path d="M0 222.821C0 84.7503 111.929 -27.1785 250 -27.1785C388.071 -27.1785 500 84.7503 500 222.821V250C500 126.939 388.071 27.1787 250 27.1787C111.929 27.1787 0 126.939 0 250V222.821ZM500 277.179C500 415.25 388.071 527.179 250 527.179C111.929 527.179 0 415.25 0 277.179V250C0 373.061 111.929 472.821 250 472.821C388.071 472.821 500 373.061 500 250V277.179ZM0 500V6.10352e-05V500ZM500 6.10352e-05V500V6.10352e-05Z" fill="#FFD600" mask="url(#path-1-inside-1_102_63)"/>
|
||||||
|
<path d="M210.369 301.604C210.369 301.604 210.369 341.807 210.369 364.846C210.369 387.885 202.798 417.491 171.591 417.491C140.385 417.491 132.813 417.491 132.813 417.491L132.812 78.125L250.754 78.125C274.312 78.125 294.504 80.9665 311.331 86.6494C328.311 92.1788 342.232 99.8585 353.093 109.689C364.107 119.519 372.214 131.115 377.415 144.478C382.616 157.84 385.217 172.278 385.217 187.791C385.217 204.533 382.54 219.892 377.186 233.869C371.832 247.846 363.648 259.827 352.634 269.81C341.62 279.794 327.623 287.627 310.643 293.31C293.816 298.839 273.853 301.604 250.754 301.604L210.369 301.604ZM210.369 242.854L250.754 242.854C270.946 242.854 285.479 238.016 294.351 228.34C303.224 218.663 307.66 205.147 307.66 187.791C307.66 180.111 306.512 173.123 304.218 166.825C301.923 160.528 298.405 155.152 293.663 150.698C289.074 146.09 283.184 142.558 275.995 140.1C268.958 137.643 260.544 136.414 250.754 136.414L210.369 136.414L210.369 242.854Z" fill="url(#paint0_linear_102_63)"/>
|
||||||
|
<path d="M239.215 301.604C239.215 301.604 239.215 341.807 239.215 364.846C239.215 387.885 231.643 417.491 200.437 417.491C169.231 417.491 161.659 417.491 161.659 417.491L161.658 78.125L279.6 78.125C303.158 78.125 323.35 80.9665 340.177 86.6494C357.157 92.1788 371.077 99.8585 381.938 109.689C392.952 119.519 401.06 131.115 406.261 144.478C411.462 157.84 414.062 172.278 414.062 187.791C414.062 204.533 411.385 219.892 406.031 233.869C400.677 247.846 392.493 259.827 381.479 269.81C370.465 279.794 356.468 287.627 339.488 293.31C322.662 298.839 302.699 301.604 279.6 301.604L239.215 301.604ZM239.215 242.854L279.6 242.854C299.792 242.854 314.325 238.016 323.197 228.34C332.069 218.663 336.505 205.147 336.505 187.791C336.505 180.111 335.358 173.123 333.064 166.825C330.769 160.528 327.251 155.152 322.509 150.698C317.919 146.09 312.03 142.558 304.84 140.1C297.804 137.643 289.39 136.414 279.6 136.414L239.215 136.414L239.215 242.854Z" fill="url(#paint1_linear_102_63)"/>
|
||||||
|
<path d="M210.685 301.604C210.685 301.604 210.685 341.807 210.685 364.846C210.685 387.885 203.082 417.491 171.749 417.491C140.416 417.491 132.813 417.491 132.813 417.491L132.812 78.125L251.233 78.125C274.887 78.125 295.161 80.9665 312.057 86.6494C329.105 92.1788 343.083 99.8585 353.988 109.689C365.046 119.519 373.187 131.115 378.409 144.478C383.631 157.84 386.242 172.278 386.242 187.791C386.242 204.533 383.555 219.892 378.179 233.869C372.803 247.846 364.586 259.827 353.527 269.81C342.468 279.794 328.414 287.627 311.365 293.31C294.47 298.839 274.426 301.604 251.233 301.604L210.685 301.604ZM210.685 242.854L251.233 242.854C271.508 242.854 286.099 238.016 295.008 228.34C303.916 218.663 308.37 205.147 308.37 187.791C308.37 180.111 307.218 173.123 304.914 166.825C302.611 160.528 299.078 155.152 294.316 150.698C289.709 146.09 283.795 142.558 276.576 140.1C269.511 137.643 261.063 136.414 251.233 136.414L210.685 136.414L210.685 242.854Z" fill="url(#paint2_radial_102_63)"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_102_63" x1="259.015" y1="78.125" x2="259.015" y2="417.491" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0.135417" stop-color="#FFD600"/>
|
||||||
|
<stop offset="1"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_102_63" x1="287.86" y1="78.125" x2="287.86" y2="417.491" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0.135417" stop-color="#FFD600"/>
|
||||||
|
<stop offset="1"/>
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient id="paint2_radial_102_63" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(259.527 247.808) rotate(0.36307) scale(345.948 325.206)">
|
||||||
|
<stop offset="0.484375" stop-color="white"/>
|
||||||
|
<stop offset="1"/>
|
||||||
|
</radialGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
@@ -5,15 +5,26 @@
|
|||||||
"description": "Generator składów online dla symulatora Train Driver 2",
|
"description": "Generator składów online dla symulatora Train Driver 2",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/android-chrome-192x192.png",
|
"src": "pwa-64x64.png",
|
||||||
|
"sizes": "64x64",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "pwa-192x192.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/android-chrome-512x512.png",
|
"src": "pwa-512x512.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any maskable"
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "maskable-icon-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"theme_color": "#2c3149",
|
"theme_color": "#2c3149",
|
||||||
|
|||||||
|
After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
@@ -5,19 +5,29 @@
|
|||||||
"description": "Generator składów online dla symulatora Train Driver 2",
|
"description": "Generator składów online dla symulatora Train Driver 2",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/android-chrome-192x192.png",
|
"src": "pwa-64x64.png",
|
||||||
|
"sizes": "64x64",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "pwa-192x192.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/android-chrome-512x512.png",
|
"src": "pwa-512x512.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any maskable"
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "maskable-icon-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"theme_color": "#2c3149",
|
"theme_color": "#2c3149",
|
||||||
"background_color": "#2c3149",
|
"background_color": "#2c3149",
|
||||||
"display": "standalone"
|
"display": "standalone"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,21 +12,17 @@ import AppContainerView from './views/AppContainerView.vue';
|
|||||||
import AppModals from './components/app/AppModals.vue';
|
import AppModals from './components/app/AppModals.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
components: { ImageFullscreenPreview, AppContainerView, AppModals },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
store: useStore(),
|
store: useStore(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
/* dev info testing */
|
|
||||||
// if (import.meta.env['VITE_STOCK_DEV'] == '1') {
|
|
||||||
// const data = await import('../stockInfoDev.json');
|
|
||||||
// this.store.stockData = data.default as any;
|
|
||||||
// }
|
|
||||||
this.store.fetchStockInfoData();
|
|
||||||
this.store.handleRouting();
|
this.store.handleRouting();
|
||||||
|
this.store.setupAPIData();
|
||||||
},
|
},
|
||||||
components: { ImageFullscreenPreview, AppContainerView, AppModals },
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -39,31 +35,14 @@ export default defineComponent({
|
|||||||
|
|
||||||
color: $textColor;
|
color: $textColor;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
padding: 1em 0.5em;
|
padding: 0;
|
||||||
}
|
|
||||||
|
|
||||||
/* HEADER SECTION */
|
@media screen and (max-width: $breakpointMd) {
|
||||||
|
|
||||||
h2 {
|
|
||||||
margin: 0;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
|
|
||||||
color: $accentColor;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-bottom {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 1.5em;
|
|
||||||
|
|
||||||
color: #d1d1d1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: $breakpointMd) {
|
|
||||||
#app {
|
|
||||||
font-size: calc(0.7rem + 0.75vw);
|
font-size: calc(0.7rem + 0.75vw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (orientation: landscape) and (max-width: $breakpointMd) {
|
||||||
|
font-size: calc(0.75rem + 0.4vw);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<RealStockCard v-if="store.isRealStockListCardOpen" />
|
<RealStockCard v-if="store.isRealStockListCardOpen" />
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { useStore } from '../../store';
|
import { useStore } from '../../store';
|
||||||
import RealStockCard from '../cards/RealStockCard.vue';
|
import RealStockCard from '../cards/RealStockCard.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { RealStockCard },
|
components: { RealStockCard },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
store: useStore(),
|
store: useStore(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -1,49 +1,44 @@
|
|||||||
<template>
|
<template>
|
||||||
<footer>
|
<footer>
|
||||||
<i18n-t keypath="footer.disclaimer" tag="div" class="text--grayed">
|
<i18n-t keypath="footer.disclaimer" tag="div" class="text--grayed">
|
||||||
<template #tos>
|
<template #tos>
|
||||||
<a style="color: #ccc" :href="$t('footer.tos-href')" target="_blank">
|
<a style="color: #ccc" :href="$t('footer.tos-href')" target="_blank">
|
||||||
{{ $t('footer.tos') }}
|
{{ $t('footer.tos') }}
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
|
|
||||||
<div class="text--grayed" v-if="store.stockData">
|
<div class="text--grayed" v-if="store.vehiclesAPIData">
|
||||||
{{ $t('footer.version-check', { version: store.stockData.version }) }}
|
{{ $t('footer.version-check', { version: store.vehiclesAPIData.simulatorVersion }) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
©
|
©
|
||||||
<a href="https://td2.info.pl/profile/?u=20777" target="_blank">Spythere</a>
|
<a href="https://td2.info.pl/profile/?u=20777" target="_blank">Spythere</a>
|
||||||
{{ new Date().getUTCFullYear() }} | v{{ VERSION }}{{ !isOnProductionHost ? 'dev' : '' }}
|
{{ new Date().getUTCFullYear() }} | v{{ VERSION }}{{ !isOnProductionHost ? 'dev' : '' }}
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import packageInfo from '../../../package.json';
|
import packageInfo from '../../../package.json';
|
||||||
import { useStore } from '../../store';
|
import { useStore } from '../../store';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isOnProductionHost: location.hostname == 'pojazdownik-td2.web.app',
|
isOnProductionHost: location.hostname == 'pojazdownik-td2.web.app',
|
||||||
VERSION: packageInfo.version,
|
VERSION: packageInfo.version,
|
||||||
store: useStore(),
|
store: useStore(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
footer {
|
footer {
|
||||||
display: flex;
|
text-align: center;
|
||||||
flex-direction: column;
|
padding: 1em 1em 0 1em;
|
||||||
gap: 0.25em;
|
}
|
||||||
|
</style>
|
||||||
text-align: center;
|
|
||||||
padding: 1em 1em 0 1em;
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,45 +1,49 @@
|
|||||||
<template>
|
<template>
|
||||||
<main>
|
<main>
|
||||||
<LogoSection />
|
<LogoSection />
|
||||||
<InputsSection />
|
<InputsSection />
|
||||||
<TrainImageSection />
|
<TrainImageSection />
|
||||||
<StockSection />
|
<StockSection />
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import LogoSection from '../sections/LogoSection.vue';
|
import LogoSection from '../sections/LogoSection.vue';
|
||||||
import InputsSection from '../sections/InputsSection.vue';
|
import InputsSection from '../sections/InputsSection.vue';
|
||||||
import TrainImageSection from '../sections/TrainImageSection.vue';
|
import TrainImageSection from '../sections/TrainImageSection.vue';
|
||||||
import StockSection from '../sections/StockSection.vue';
|
import StockSection from '../sections/StockSection.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { LogoSection, InputsSection, TrainImageSection, StockSection },
|
components: { LogoSection, InputsSection, TrainImageSection, StockSection },
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../styles/global.scss';
|
@import '../../styles/global.scss';
|
||||||
|
|
||||||
main {
|
main {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 1em;
|
gap: 1em;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 1500px;
|
max-width: 1350px;
|
||||||
min-height: 75vh;
|
|
||||||
|
grid-template-columns: 1fr 2fr;
|
||||||
grid-template-columns: 1fr 2fr;
|
grid-template-rows: auto 360px minmax(300px, 1fr);
|
||||||
grid-template-rows: auto 360px minmax(400px, 1fr);
|
|
||||||
}
|
background-color: darken($color: $bgColor, $amount: 5);
|
||||||
|
border-radius: 1em;
|
||||||
@media screen and (max-width: $breakpointMd) {
|
|
||||||
main {
|
padding: 1em;
|
||||||
display: flex;
|
}
|
||||||
flex-direction: column;
|
|
||||||
grid-template-columns: 1fr;
|
@media screen and (max-width: $breakpointMd) {
|
||||||
grid-template-rows: 1fr;
|
main {
|
||||||
}
|
display: flex;
|
||||||
}
|
flex-direction: column;
|
||||||
</style>
|
grid-template-columns: 1fr;
|
||||||
|
grid-template-rows: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -6,9 +6,12 @@
|
|||||||
<div class="card_nav">
|
<div class="card_nav">
|
||||||
<div class="top-pane">
|
<div class="top-pane">
|
||||||
<h1>
|
<h1>
|
||||||
{{ $t('realstock.title') }} <a href="https://td2.info.pl/profile/?u=17708" target="_blank">Railtrains997</a>
|
{{ $t('realstock.title') }}
|
||||||
|
<a href="https://td2.info.pl/profile/?u=17708" target="_blank">Railtrains997</a>
|
||||||
</h1>
|
</h1>
|
||||||
<button class="btn exit-btn" @click="store.isRealStockListCardOpen = false">⨯</button>
|
<button class="btn exit-btn" @click="store.isRealStockListCardOpen = false">
|
||||||
|
⨯
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="filters" ref="focus" tabindex="0">
|
<div class="filters" ref="focus" tabindex="0">
|
||||||
@@ -19,7 +22,11 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<datalist id="readyStockDataList">
|
<datalist id="readyStockDataList">
|
||||||
<option v-for="stock in store.readyStockList" :value="stock.stockId">
|
<option
|
||||||
|
v-for="stock in store.realCompositionList"
|
||||||
|
:value="stock.stockId"
|
||||||
|
:key="stock.name"
|
||||||
|
>
|
||||||
{{ stock.stockId }}
|
{{ stock.stockId }}
|
||||||
</option>
|
</option>
|
||||||
</datalist>
|
</datalist>
|
||||||
@@ -31,29 +38,41 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<datalist id="readyStockStringList">
|
<datalist id="readyStockStringList">
|
||||||
<option v-for="stock in computedAvailableStockTypes" :value="stock">
|
<option
|
||||||
{{ stock }}
|
v-for="stockType in computedAvailableStockTypes"
|
||||||
|
:value="stockType"
|
||||||
|
:key="stockType"
|
||||||
|
>
|
||||||
|
{{ stockType }}
|
||||||
</option>
|
</option>
|
||||||
</datalist>
|
</datalist>
|
||||||
|
|
||||||
<button class="btn" @click="resetStockFilters">{{ $t('realstock.action-reset') }}</button>
|
<button class="btn" @click="resetStockFilters">
|
||||||
|
{{ $t('realstock.action-reset') }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="card_list" ref="list" @scroll="onListScroll">
|
<ul class="card_list" ref="list" @scroll="onListScroll">
|
||||||
<li
|
<li v-for="rStock in computedReadyStockList" :key="rStock.stockId">
|
||||||
v-for="rStock in computedReadyStockList"
|
<!-- :data-last-selected="store.ch === rStock.stockId" -->
|
||||||
:key="rStock.stockId"
|
<div
|
||||||
:data-last-selected="store.chosenRealStockName === rStock.stockId"
|
class="stock-title"
|
||||||
>
|
tabindex="0"
|
||||||
<div class="stock-title" tabindex="0" @click="chooseStock(rStock)" @keydown.enter="chooseStock(rStock)">
|
@click="chooseStock(rStock)"
|
||||||
|
@keydown.enter="chooseStock(rStock)"
|
||||||
|
>
|
||||||
<img class="stock-icon" :src="getIconURL(rStock.type)" :alt="rStock.type" />
|
<img class="stock-icon" :src="getIconURL(rStock.type)" :alt="rStock.type" />
|
||||||
<b class="text--accent" style="margin-left: 5px"> {{ rStock.name }}</b>
|
<b class="text--accent" style="margin-left: 5px"> {{ rStock.name }}</b>
|
||||||
<div>{{ rStock.number }}</div>
|
<div>{{ rStock.number }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stock-thumbnails" ref="thumbnailsRef">
|
<div class="stock-thumbnails" ref="thumbnailsRef">
|
||||||
<div class="thumbnail-item" v-for="stockType in rStock.stockString.split(';')">
|
<div
|
||||||
|
class="thumbnail-item"
|
||||||
|
v-for="stockType in rStock.stockString.split(';')"
|
||||||
|
:key="stockType"
|
||||||
|
>
|
||||||
<div class="thumbnail-container">
|
<div class="thumbnail-container">
|
||||||
<div>{{ stockType }}</div>
|
<div>{{ stockType }}</div>
|
||||||
<img
|
<img
|
||||||
@@ -61,7 +80,7 @@
|
|||||||
:title="stockType"
|
:title="stockType"
|
||||||
style="opacity: 0"
|
style="opacity: 0"
|
||||||
@error="(e) => onStockItemError(e, stockType)"
|
@error="(e) => onStockItemError(e, stockType)"
|
||||||
@load="e => (e.target as HTMLElement).style.opacity = '1'"
|
@load="(e) => ((e.target as HTMLElement).style.opacity = '1')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -81,11 +100,7 @@ import { useStore } from '../../store';
|
|||||||
import imageMixin from '../../mixins/imageMixin';
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
import stockMixin from '../../mixins/stockMixin';
|
import stockMixin from '../../mixins/stockMixin';
|
||||||
|
|
||||||
import { IReadyStockItem } from '../../types';
|
import { IRealComposition } from '../../types';
|
||||||
|
|
||||||
interface ResponseJSONData {
|
|
||||||
[key: string]: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getVehicleType(stockType: string) {
|
function getVehicleType(stockType: string) {
|
||||||
if (/^E/.test(stockType)) return 'loco-e';
|
if (/^E/.test(stockType)) return 'loco-e';
|
||||||
@@ -100,7 +115,10 @@ export default defineComponent({
|
|||||||
data: () => ({
|
data: () => ({
|
||||||
store: useStore(),
|
store: useStore(),
|
||||||
responseStatus: 'loading',
|
responseStatus: 'loading',
|
||||||
isMobile: 'ontouchstart' in document.documentElement && navigator.userAgent.match(/Mobi/) ? true : false,
|
isMobile:
|
||||||
|
'ontouchstart' in document.documentElement && navigator.userAgent.match(/Mobi/)
|
||||||
|
? true
|
||||||
|
: false,
|
||||||
observer: null as IntersectionObserver | null,
|
observer: null as IntersectionObserver | null,
|
||||||
searchedReadyStockName: '',
|
searchedReadyStockName: '',
|
||||||
searchedReadyStockString: '',
|
searchedReadyStockString: '',
|
||||||
@@ -109,9 +127,8 @@ export default defineComponent({
|
|||||||
scrollTop: 0,
|
scrollTop: 0,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
async mounted() {
|
mounted() {
|
||||||
this.mountObserver();
|
this.mountObserver();
|
||||||
this.fetchStockListData();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
activated() {
|
activated() {
|
||||||
@@ -124,20 +141,22 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
computedReadyStockList() {
|
computedReadyStockList(): IRealComposition[] {
|
||||||
if (this.searchedReadyStockName == null) return this.store.readyStockList;
|
return this.store.realCompositionList
|
||||||
|
|
||||||
return this.store.readyStockList
|
|
||||||
.filter(
|
.filter(
|
||||||
(rs) =>
|
(rc) =>
|
||||||
rs.stockId.toLocaleLowerCase().includes(this.searchedReadyStockName.toLocaleLowerCase()) &&
|
rc.stockId
|
||||||
rs.stockString.toLocaleLowerCase().includes(this.searchedReadyStockString.toLocaleLowerCase())
|
.toLocaleLowerCase()
|
||||||
|
.includes(this.searchedReadyStockName.toLocaleLowerCase()) &&
|
||||||
|
rc.stockString
|
||||||
|
.toLocaleLowerCase()
|
||||||
|
.includes(this.searchedReadyStockString.toLocaleLowerCase())
|
||||||
)
|
)
|
||||||
.filter((_, i) => i <= this.visibleIndexesTo);
|
.filter((_, i) => i <= this.visibleIndexesTo);
|
||||||
},
|
},
|
||||||
|
|
||||||
computedAvailableStockTypes() {
|
computedAvailableStockTypes() {
|
||||||
return this.store.readyStockList
|
return this.store.realCompositionList
|
||||||
.reduce((acc, rs) => {
|
.reduce((acc, rs) => {
|
||||||
rs.stockString.split(';').forEach((s) => {
|
rs.stockString.split(';').forEach((s) => {
|
||||||
if (!acc.includes(s)) acc.push(s);
|
if (!acc.includes(s)) acc.push(s);
|
||||||
@@ -161,35 +180,6 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
async fetchStockListData() {
|
|
||||||
const readyStockJSONData: ResponseJSONData = await (
|
|
||||||
await fetch(`https://spythere.github.io/api/td2/data/readyStock.json?t=${Math.floor(Date.now() / 60000)}`)
|
|
||||||
).json();
|
|
||||||
|
|
||||||
if (!readyStockJSONData) {
|
|
||||||
this.responseStatus = 'error';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let stockKey in readyStockJSONData) {
|
|
||||||
const [type, number, ...name] = stockKey.split(' ');
|
|
||||||
|
|
||||||
const obj = {
|
|
||||||
number: number.replace(/_/g, '/'),
|
|
||||||
name: name.join(' '),
|
|
||||||
stockString: readyStockJSONData[stockKey],
|
|
||||||
type,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.store.readyStockList.push({
|
|
||||||
...obj,
|
|
||||||
stockId: `${obj.type} ${obj.number} ${obj.name}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.responseStatus = 'loaded';
|
|
||||||
},
|
|
||||||
|
|
||||||
mountObserver() {
|
mountObserver() {
|
||||||
this.observer = new IntersectionObserver((entries) => {
|
this.observer = new IntersectionObserver((entries) => {
|
||||||
if (entries[0].intersectionRatio > 0) this.visibleIndexesTo += 20;
|
if (entries[0].intersectionRatio > 0) this.visibleIndexesTo += 20;
|
||||||
@@ -207,7 +197,7 @@ export default defineComponent({
|
|||||||
this.searchedReadyStockString = '';
|
this.searchedReadyStockString = '';
|
||||||
},
|
},
|
||||||
|
|
||||||
chooseStock(stockItem: IReadyStockItem) {
|
chooseStock(stockItem: IRealComposition) {
|
||||||
this.loadStockFromString(stockItem.stockString);
|
this.loadStockFromString(stockItem.stockString);
|
||||||
this.lastSelectedStockId = stockItem.stockId;
|
this.lastSelectedStockId = stockItem.stockId;
|
||||||
this.store.isRealStockListCardOpen = false;
|
this.store.isRealStockListCardOpen = false;
|
||||||
@@ -378,4 +368,3 @@ ul {
|
|||||||
padding: 1em;
|
padding: 1em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" v-model="model" />
|
||||||
|
<div><slot /></div>
|
||||||
|
</label>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const model = defineModel();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
label {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: color 200ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
padding: 0.25em 0.5em;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
background-color: #222;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '\2716';
|
||||||
|
margin-right: 0.5em;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
position: absolute;
|
||||||
|
clip: rect(1px, 1px, 1px, 1px);
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
height: 1px;
|
||||||
|
width: 1px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:focus-visible + div {
|
||||||
|
outline: 1px solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:checked + div {
|
||||||
|
color: palegreen;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
color: palegreen;
|
||||||
|
content: '\2714';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
<div class="vehicle-types locos">
|
<div class="vehicle-types locos">
|
||||||
<button
|
<button
|
||||||
v-for="locoType in locomotiveTypeList"
|
v-for="locoType in locomotiveTypeList"
|
||||||
|
:key="locoType.id"
|
||||||
class="btn btn--choice"
|
class="btn btn--choice"
|
||||||
:data-selected="locoType.id == store.chosenLocoPower"
|
:data-selected="locoType.id == store.chosenLocoPower"
|
||||||
@click="selectLocoType(locoType.id)"
|
@click="selectLocoType(locoType.id)"
|
||||||
@@ -23,9 +24,11 @@
|
|||||||
@keydown.enter.prevent="addOrSwitchVehicle"
|
@keydown.enter.prevent="addOrSwitchVehicle"
|
||||||
@keydown.backspace="removeVehicle"
|
@keydown.backspace="removeVehicle"
|
||||||
>
|
>
|
||||||
<option :value="null" disabled>{{ $t('inputs.input-vehicle') }}</option>
|
<option :value="null" disabled>
|
||||||
|
{{ $t('inputs.input-vehicle') }}
|
||||||
|
</option>
|
||||||
<option v-for="loco in locoOptions" :value="loco" :key="loco.type">
|
<option v-for="loco in locoOptions" :value="loco" :key="loco.type">
|
||||||
{{ loco.type }}<b v-if="loco.supportersOnly">*</b>
|
{{ loco.type }}<b v-if="loco.isSponsorsOnly">*</b>
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@@ -34,6 +37,7 @@
|
|||||||
<div class="vehicle-types carwagons">
|
<div class="vehicle-types carwagons">
|
||||||
<button
|
<button
|
||||||
v-for="carType in carTypeList"
|
v-for="carType in carTypeList"
|
||||||
|
:key="carType.id"
|
||||||
class="btn btn--choice"
|
class="btn btn--choice"
|
||||||
:data-selected="carType.id == store.chosenCarUseType"
|
:data-selected="carType.id == store.chosenCarUseType"
|
||||||
@click="selectCarWagonType(carType.id)"
|
@click="selectCarWagonType(carType.id)"
|
||||||
@@ -50,10 +54,12 @@
|
|||||||
@keydown.enter.prevent="addOrSwitchVehicle"
|
@keydown.enter.prevent="addOrSwitchVehicle"
|
||||||
@keydown.backspace="removeVehicle"
|
@keydown.backspace="removeVehicle"
|
||||||
>
|
>
|
||||||
<option :value="null" disabled>{{ $t('inputs.input-carwagon') }}</option>
|
<option :value="null" disabled>
|
||||||
|
{{ $t('inputs.input-carwagon') }}
|
||||||
|
</option>
|
||||||
|
|
||||||
<option v-for="car in carOptions" :value="car" :key="car.type">
|
<option v-for="car in carOptions" :value="car" :key="car.type">
|
||||||
{{ car.type }}<b v-if="car.supportersOnly">*</b>
|
{{ car.type }}<b v-if="car.isSponsorsOnly">*</b>
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@@ -80,7 +86,7 @@
|
|||||||
</option>
|
</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?.cargoTypes" :value="cargo" :key="cargo.id">
|
||||||
{{ cargo.id }}
|
{{ cargo.id }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -122,6 +128,7 @@ export default defineComponent({
|
|||||||
mixins: [imageMixin, stockPreviewMixin, stockMixin],
|
mixins: [imageMixin, stockPreviewMixin, stockMixin],
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
|
store: useStore(),
|
||||||
locomotiveTypeList: [
|
locomotiveTypeList: [
|
||||||
{
|
{
|
||||||
id: 'loco-e',
|
id: 'loco-e',
|
||||||
@@ -153,12 +160,20 @@ export default defineComponent({
|
|||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
setup() {
|
computed: {
|
||||||
const store = useStore();
|
locoOptions() {
|
||||||
|
return this.store.locoDataList
|
||||||
|
.slice()
|
||||||
|
.sort((a, b) => (a.type > b.type ? 1 : -1))
|
||||||
|
.filter((loco) => loco.power == this.store.chosenLocoPower);
|
||||||
|
},
|
||||||
|
|
||||||
return {
|
carOptions() {
|
||||||
store,
|
return this.store.carDataList
|
||||||
};
|
.slice()
|
||||||
|
.sort((a, b) => (a.type > b.type ? 1 : -1))
|
||||||
|
.filter((car) => car.useType == this.store.chosenCarUseType);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@@ -169,7 +184,8 @@ export default defineComponent({
|
|||||||
addOrSwitchVehicle() {
|
addOrSwitchVehicle() {
|
||||||
if (!this.store.chosenVehicle) return;
|
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();
|
else this.switchVehicles();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -192,6 +208,32 @@ export default defineComponent({
|
|||||||
const stockObject = this.getStockObject(vehicle, this.store.chosenCargo);
|
const stockObject = this.getStockObject(vehicle, this.store.chosenCargo);
|
||||||
this.store.stockList[this.store.chosenStockListIndex] = stockObject;
|
this.store.stockList[this.store.chosenStockListIndex] = stockObject;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
selectLocoType(locoTypeId: string) {
|
||||||
|
this.store.chosenLocoPower = locoTypeId;
|
||||||
|
this.store.chosenVehicle = this.locoOptions[0];
|
||||||
|
this.store.chosenLoco = this.locoOptions[0];
|
||||||
|
},
|
||||||
|
|
||||||
|
selectCarWagonType(carWagonTypeId: string) {
|
||||||
|
this.store.chosenCarUseType = carWagonTypeId;
|
||||||
|
this.store.chosenVehicle = this.carOptions[0];
|
||||||
|
this.store.chosenCar = this.carOptions[0];
|
||||||
|
this.store.chosenCargo = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
previewVehicleByType(type: 'loco' | 'car' | 'cargo') {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (!this.store.chosenLoco && !this.store.chosenCar) return;
|
||||||
|
|
||||||
|
this.store.chosenVehicle = type == 'loco' ? this.store.chosenLoco : this.store.chosenCar;
|
||||||
|
|
||||||
|
this.store.chosenCargo =
|
||||||
|
this.store.chosenCar?.cargoTypes.find(
|
||||||
|
(cargo) => cargo.id == this.store.chosenCargo?.id
|
||||||
|
) || null;
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -207,6 +249,11 @@ export default defineComponent({
|
|||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input_container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 380px;
|
||||||
|
}
|
||||||
|
|
||||||
.input_header {
|
.input_header {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
@@ -226,6 +273,10 @@ button.btn--choice {
|
|||||||
.input_list {
|
.input_list {
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
|
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
@@ -267,4 +318,3 @@ button.btn--choice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button
|
<button
|
||||||
class="btn btn--text"
|
|
||||||
v-for="action in localeActions"
|
v-for="action in localeActions"
|
||||||
|
:key="action.name"
|
||||||
|
class="btn btn--text"
|
||||||
:data-selected="$i18n.locale == action.locale"
|
:data-selected="$i18n.locale == action.locale"
|
||||||
@click="chooseLocale(action.locale)"
|
@click="chooseLocale(action.locale)"
|
||||||
>
|
>
|
||||||
@@ -76,4 +77,3 @@ img {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
<section class="stock-section">
|
<section class="stock-section">
|
||||||
<div class="section_modes">
|
<div class="section_modes">
|
||||||
<button
|
<button
|
||||||
|
v-for="(id, i) in sectionModes"
|
||||||
|
:key="id"
|
||||||
class="btn"
|
class="btn"
|
||||||
ref="sectionButtonRefs"
|
ref="sectionButtonRefs"
|
||||||
v-for="(id, i) in sectionModes"
|
|
||||||
@click="chooseSection(id)"
|
@click="chooseSection(id)"
|
||||||
:data-selected="store.stockSectionMode == id"
|
:data-selected="store.stockSectionMode == id"
|
||||||
>
|
>
|
||||||
@@ -15,14 +16,14 @@
|
|||||||
|
|
||||||
<transition name="tab-change" mode="out-in">
|
<transition name="tab-change" mode="out-in">
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<component :is="chosenSectionComponent" :key="chosenSectionComponent"></component>
|
<component :is="chosenSectionComponent"></component>
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</transition>
|
</transition>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, KeepAlive, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import { useStore } from '../../store';
|
import { useStore } from '../../store';
|
||||||
import StockListTab from '../tabs/StockListTab.vue';
|
import StockListTab from '../tabs/StockListTab.vue';
|
||||||
import StockGeneratorTab from '../tabs/StockGeneratorTab.vue';
|
import StockGeneratorTab from '../tabs/StockGeneratorTab.vue';
|
||||||
@@ -34,7 +35,12 @@ const sectionButtonRefs = ref([]);
|
|||||||
const store = useStore();
|
const store = useStore();
|
||||||
type SectionMode = typeof store.stockSectionMode;
|
type SectionMode = typeof store.stockSectionMode;
|
||||||
|
|
||||||
const sectionModes: SectionMode[] = ['stock-list', 'wiki-list', 'number-generator', 'stock-generator'];
|
const sectionModes: SectionMode[] = [
|
||||||
|
'stock-list',
|
||||||
|
'wiki-list',
|
||||||
|
'number-generator',
|
||||||
|
'stock-generator',
|
||||||
|
];
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('keydown', (e) => {
|
window.addEventListener('keydown', (e) => {
|
||||||
@@ -43,7 +49,7 @@ onMounted(() => {
|
|||||||
if (/[1234]/.test(e.key)) {
|
if (/[1234]/.test(e.key)) {
|
||||||
const keyNum = Number(e.key);
|
const keyNum = Number(e.key);
|
||||||
store.stockSectionMode = sectionModes[keyNum - 1];
|
store.stockSectionMode = sectionModes[keyNum - 1];
|
||||||
(sectionButtonRefs.value[keyNum - 1] as HTMLButtonElement).focus();
|
(sectionButtonRefs.value[keyNum - 1] as HTMLButtonElement)?.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -134,4 +140,3 @@ function chooseSection(sectionId: SectionMode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,58 +1,59 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="train-image-section">
|
<section class="train-image-section">
|
||||||
<div class="train-image__wrapper">
|
<div class="train-image__content" :class="{ sponsor: store.chosenVehicle?.isSponsorsOnly }">
|
||||||
<div class="train-image__content" :class="{ supporter: store.chosenVehicle?.supportersOnly }">
|
<img
|
||||||
<transition name="img-message-anim">
|
tabindex="0"
|
||||||
<div class="empty-message" v-if="store.imageLoading && store.chosenVehicle?.imageSrc">
|
:src="
|
||||||
{{ $t('preview.loading') }}
|
store.chosenVehicle
|
||||||
</div>
|
? getThumbnailURL(store.chosenVehicle.type, 'small')
|
||||||
</transition>
|
: '/images/placeholder.jpg'
|
||||||
|
"
|
||||||
<div class="no-img" v-if="!store.chosenVehicle">{{ $t('preview.title') }}</div>
|
@click="onImageClick"
|
||||||
|
@keydown.enter="onImageClick"
|
||||||
<img
|
@error="onImageError"
|
||||||
v-if="store.chosenVehicle"
|
type="image/jpeg"
|
||||||
:src="`https://spythere.github.io/api/td2/images/${store.chosenVehicle.type}--300px.jpg`"
|
/>
|
||||||
:alt="store.chosenVehicle.type"
|
|
||||||
@load="onImageLoad"
|
|
||||||
@click="onImageClick"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- <div class="empty-message" v-if="store.chosenVehicle && !store.chosenVehicle.imageSrc">Ten pojazd nie ma jeszcze podglądu!</div> -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="train-image__info" v-if="store.chosenVehicle">
|
|
||||||
<b class="text--accent">{{ store.chosenVehicle.type }}</b> •
|
|
||||||
<b style="color: #ccc">
|
|
||||||
{{
|
|
||||||
$t(`preview.${isLocomotive(store.chosenVehicle) ? store.chosenVehicle.power : store.chosenVehicle.useType}`)
|
|
||||||
}}
|
|
||||||
</b>
|
|
||||||
|
|
||||||
<div style="color: #ccc">
|
|
||||||
<div>
|
|
||||||
{{ store.chosenVehicle.length }}m | {{ store.chosenVehicle.mass }}t |
|
|
||||||
{{ store.chosenVehicle.maxSpeed }} km/h
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="isLocomotive(store.chosenVehicle)">
|
|
||||||
{{ $t('preview.cabin') }} {{ store.chosenVehicle.cabinType }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else>
|
|
||||||
{{
|
|
||||||
store.chosenVehicle.useType == 'car-cargo' // ? store.stockData?.usage[store.chosenVehicle.constructionType]
|
|
||||||
? $t(`usage.${store.chosenVehicle.constructionType}`)
|
|
||||||
: `${$t('preview.construction')} ${store.chosenVehicle.constructionType}`
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<b style="color: salmon" v-if="store.chosenVehicle.supportersOnly">{{ $t('preview.sponsor-only') }}</b>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="train-image__info" v-else>{{ $t('preview.desc') }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="train-image__info" v-if="store.chosenVehicle">
|
||||||
|
<b class="text--accent">{{ store.chosenVehicle.type }}</b> •
|
||||||
|
<b style="color: #ccc">
|
||||||
|
{{
|
||||||
|
$t(
|
||||||
|
`preview.${isLocomotive(store.chosenVehicle) ? store.chosenVehicle.power : store.chosenVehicle.useType}`
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</b>
|
||||||
|
|
||||||
|
<div style="color: #ccc">
|
||||||
|
<div>
|
||||||
|
{{ store.chosenVehicle.length }}m | {{ (store.chosenVehicle.weight / 1000).toFixed(1) }}t
|
||||||
|
| {{ store.chosenVehicle.maxSpeed }} km/h
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="isLocomotive(store.chosenVehicle)">
|
||||||
|
{{ $t('preview.cabin') }} {{ store.chosenVehicle.cabinType }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
{{
|
||||||
|
store.chosenVehicle.useType == 'car-cargo'
|
||||||
|
? $t(`usage.${store.chosenVehicle.constructionType}`)
|
||||||
|
: `${$t('preview.construction')} ${store.chosenVehicle.constructionType}`
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<b style="color: salmon" v-if="store.chosenVehicle.isSponsorsOnly">{{
|
||||||
|
$t('preview.sponsor-only', [
|
||||||
|
new Date(store.chosenVehicle.sponsorsOnlyTimestamp).toLocaleDateString(
|
||||||
|
$i18n.locale == 'pl' ? 'pl-PL' : 'en-GB'
|
||||||
|
),
|
||||||
|
])
|
||||||
|
}}</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="train-image__info" v-else>{{ $t('preview.desc') }}</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -60,9 +61,18 @@
|
|||||||
import { computed, defineComponent } from 'vue';
|
import { computed, defineComponent } from 'vue';
|
||||||
import { useStore } from '../../store';
|
import { useStore } from '../../store';
|
||||||
import { isLocomotive } from '../../utils/vehicleUtils';
|
import { isLocomotive } from '../../utils/vehicleUtils';
|
||||||
import { ILocomotive, Vehicle } from '../../types';
|
import { ILocomotive, IVehicle } from '../../types';
|
||||||
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
mixins: [imageMixin],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
noImageAvailable: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
|
||||||
@@ -73,7 +83,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
chosenVehicle(vehicle: Vehicle, prevVehicle: Vehicle) {
|
chosenVehicle(vehicle: IVehicle, prevVehicle: IVehicle) {
|
||||||
if (vehicle && vehicle.type != prevVehicle?.type) {
|
if (vehicle && vehicle.type != prevVehicle?.type) {
|
||||||
this.store.imageLoading = true;
|
this.store.imageLoading = true;
|
||||||
}
|
}
|
||||||
@@ -85,16 +95,26 @@ export default defineComponent({
|
|||||||
this.store.imageLoading = false;
|
this.store.imageLoading = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
isLocomotive(vehicle: Vehicle): vehicle is ILocomotive {
|
onImageError(e: Event) {
|
||||||
|
const el = e.target as HTMLImageElement;
|
||||||
|
if (el.src == '/images/placeholder.jpg') return;
|
||||||
|
|
||||||
|
el.src = '/images/placeholder.jpg';
|
||||||
|
},
|
||||||
|
|
||||||
|
isLocomotive(vehicle: IVehicle): vehicle is ILocomotive {
|
||||||
return isLocomotive(vehicle);
|
return isLocomotive(vehicle);
|
||||||
},
|
},
|
||||||
|
|
||||||
onImageClick() {
|
onImageClick(e: Event) {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
|
||||||
const chosenVehicle = this.store.chosenVehicle;
|
const chosenVehicle = this.store.chosenVehicle;
|
||||||
|
|
||||||
if (!chosenVehicle) return;
|
if (!chosenVehicle) return;
|
||||||
|
|
||||||
this.store.vehiclePreviewSrc = `https://spythere.github.io/api/td2/images/${chosenVehicle.type}--800px.jpg`;
|
this.store.lastFocusedElement = target;
|
||||||
|
this.store.vehiclePreviewSrc = this.getThumbnailURL(chosenVehicle.type, 'large');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -104,66 +124,45 @@ export default defineComponent({
|
|||||||
@import '../../styles/global.scss';
|
@import '../../styles/global.scss';
|
||||||
|
|
||||||
.train-image-section {
|
.train-image-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
grid-row: 3;
|
grid-row: 3;
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
|
|
||||||
margin-top: 2em;
|
margin-top: 1em;
|
||||||
height: 22em;
|
height: 22em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.train-image {
|
.train-image {
|
||||||
&__wrapper {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
border: 1px solid white;
|
&.sponsor img {
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
max-width: 22em;
|
|
||||||
height: 13em;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
&.supporter {
|
|
||||||
border: 1px solid salmon;
|
border: 1px solid salmon;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
|
max-width: 380px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
border: 1px solid white;
|
||||||
|
|
||||||
cursor: zoom-in;
|
cursor: zoom-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-message,
|
|
||||||
.no-img {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
|
|
||||||
padding: 0.3em 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-message {
|
|
||||||
background: rgba(#000, 0.75);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.train-image__info {
|
.train-image__info {
|
||||||
margin: 1em 0;
|
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
padding: 0 1em;
|
padding: 0.5em;
|
||||||
|
margin: 0.5em auto;
|
||||||
|
line-height: 1.35;
|
||||||
|
|
||||||
b {
|
width: 100%;
|
||||||
font-size: 1.1em;
|
max-width: 380px;
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
background-color: $secondaryColor;
|
||||||
margin: 0.25em 0;
|
font-weight: bold;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transition animations
|
// Transition animations
|
||||||
@@ -185,4 +184,3 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -2,51 +2,110 @@
|
|||||||
<div class="number-generator tab">
|
<div class="number-generator tab">
|
||||||
<div class="tab_header">
|
<div class="tab_header">
|
||||||
<h2>{{ $t('numgen.title') }}</h2>
|
<h2>{{ $t('numgen.title') }}</h2>
|
||||||
|
<h3>{{ $t('numgen.subtitle') }}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab_content">
|
<div class="tab_content">
|
||||||
<div class="options">
|
<div class="category-select">
|
||||||
<select v-model="chosenCategory" @change="randomizeTrainNumber()">
|
<label for="category"> {{ $t('numgen.train-category') }}</label>
|
||||||
<option :value="null" disabled>{{ $t('numgen.train-category') }}</option>
|
<select id="category" v-model="chosenCategory" @change="randomizeTrainNumber()">
|
||||||
<option v-for="(_, category) in genData.categories" :value="category">
|
<option :value="null" disabled>
|
||||||
|
{{ $t('numgen.train-category') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="(_, category) in genData.categoriesRules"
|
||||||
|
:key="category"
|
||||||
|
:value="category"
|
||||||
|
>
|
||||||
{{ $t(`numgen.categories.${category}`) }}
|
{{ $t(`numgen.categories.${category}`) }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<select v-model="beginRegionName" @change="randomizeTrainNumber()">
|
<div class="regions-select">
|
||||||
<option :value="null" disabled>{{ $t('numgen.start-region') }}</option>
|
<div>
|
||||||
<option v-for="(_, name) in genData.regionNumbers" :value="name">{{ name }}</option>
|
<label for="begin-region"> {{ $t('numgen.start-region') }}</label>
|
||||||
</select>
|
<select id="begin-region" v-model="beginRegionName" @change="randomizeTrainNumber()">
|
||||||
|
<option :value="null" disabled>
|
||||||
|
{{ $t('numgen.start-region') }}
|
||||||
|
</option>
|
||||||
|
<option v-for="(_, name) in genData.regionNumbers" :key="name" :value="name">
|
||||||
|
{{ name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<select v-model="endRegionName" @change="randomizeTrainNumber()">
|
<div>
|
||||||
<option :value="null" disabled>{{ $t('numgen.end-region') }}</option>
|
<label for="end-region"> {{ $t('numgen.end-region') }}</label>
|
||||||
<option v-for="(_, name) in genData.regionNumbers" :value="name">{{ name }}</option>
|
<select id="end-region" v-model="endRegionName" @change="randomizeTrainNumber()">
|
||||||
</select>
|
<option :value="null" disabled>
|
||||||
|
{{ $t('numgen.end-region') }}
|
||||||
|
</option>
|
||||||
|
<option v-for="(_, name) in genData.regionNumbers" :key="name" :value="name">
|
||||||
|
{{ name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="generated-number" @click="copyNumber">
|
<div class="generated-number" @click="copyNumber">
|
||||||
<span v-if="trainNumber">
|
<span v-if="trainNumber">
|
||||||
{{ $t('numgen.number-info') }} <b class="text--accent">{{ trainNumber }}</b>
|
{{ $t('numgen.number-info') }}
|
||||||
|
<b class="text--accent">{{ trainNumber }}</b>
|
||||||
</span>
|
</span>
|
||||||
<span v-else>{{ $t('numgen.warning') }}</span>
|
<span v-else>{{ $t('numgen.warning') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <div v-if="chosenCategory">
|
<div class="category-rules" v-if="chosenCategory && categoryRules && trainNumber">
|
||||||
Current numbering rules: {{ $t(`numgen.rules.${chosenCategory}`) }};
|
<!-- First & second digit (the same regions) -->
|
||||||
|
<div v-if="beginRegionName && endRegionName && beginRegionName == endRegionName">
|
||||||
|
<b>{{ $t('numgen.rules.two-first-digits') }}</b>
|
||||||
|
{{ $t('numgen.rules.from-pool') }}
|
||||||
|
<b class="text--accent">{{ genData.sameRegions[beginRegionName].join(', ') }}</b>
|
||||||
|
{{ $t('numgen.rules.for-region') }} {{ beginRegionName }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<span v-if="beginRegionName && endRegionName">
|
<!-- First & second digit (different regions) -->
|
||||||
<span v-if="beginRegionName == endRegionName">
|
<div v-else>
|
||||||
pierwsze dwie cyfry:
|
<div>
|
||||||
{{ genData.sameRegions[beginRegionName].join(', ') }}
|
<b>
|
||||||
(numer w obrębie obszaru {{ beginRegionName }})
|
{{ $t('numgen.rules.first-digit') }}
|
||||||
</span>
|
<span class="text--accent">{{ trainNumber[0] }}</span>
|
||||||
|
</b>
|
||||||
|
{{ $t('numgen.rules.for-region-begin') }} {{ beginRegionName }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<span v-else>
|
<div>
|
||||||
pierwsza cyfra: {{ genData.regionNumbers[beginRegionName] }}; druga cyfra:
|
<b>
|
||||||
{{ genData.regionNumbers[endRegionName] }}
|
{{ $t('numgen.rules.second-digit') }}
|
||||||
</span>
|
<span class="text--accent">{{ trainNumber[1] }} </span>
|
||||||
</span>
|
</b>
|
||||||
</div> -->
|
{{ $t('numgen.rules.for-region-end') }} {{ endRegionName }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Third digit (non-passenger only) -->
|
||||||
|
<div v-if="categoryRules[0] != null">
|
||||||
|
<b>
|
||||||
|
{{ $t('numgen.rules.third-digit') }}
|
||||||
|
<span class="text--accent">{{ categoryRules[0] }}</span>
|
||||||
|
</b>
|
||||||
|
{{ $t('numgen.rules.for-category') }} {{ chosenCategory }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Last digits -->
|
||||||
|
<div>
|
||||||
|
<b>
|
||||||
|
{{
|
||||||
|
$t(`numgen.rules.${categoryRules[1]?.length == 3 ? 'three' : 'two'}-last-digits`)
|
||||||
|
}}</b
|
||||||
|
>
|
||||||
|
{{ $t('numgen.rules.from-range') }}
|
||||||
|
<b class="text--accent">{{ categoryRules[1] }}-{{ categoryRules[2] }}</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
<div class="tab_links">
|
<div class="tab_links">
|
||||||
<a :href="$t('numgen.td2-wiki-link')" target="_blank">
|
<a :href="$t('numgen.td2-wiki-link')" target="_blank">
|
||||||
@@ -57,8 +116,17 @@
|
|||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="tab_actions">
|
<div class="tab_actions">
|
||||||
<button class="btn" @click="randomizeTrainNumber(true)">{{ $t('numgen.action-random-region') }}</button>
|
<button class="btn" @click="randomizeTrainNumber(true)">
|
||||||
<button class="btn" @click="randomizeTrainNumber(false)">{{ $t('numgen.action-random-number') }}</button>
|
{{ $t('numgen.action-random-region') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="btn" @click="randomizeCategory">
|
||||||
|
{{ $t('numgen.action-random-category') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="btn" @click="randomizeTrainNumber(false)">
|
||||||
|
{{ $t('numgen.action-random-number') }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -69,10 +137,11 @@ import { Ref, ref } from 'vue';
|
|||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import genData from '../../constants/numberGeneratorData.json';
|
import genData from '../../constants/numberGeneratorData.json';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
type RegionName = keyof typeof genData.regionNumbers;
|
type RegionName = keyof typeof genData.regionNumbers;
|
||||||
type Category = keyof typeof genData.categories;
|
type Category = keyof typeof genData.categoriesRules;
|
||||||
|
|
||||||
const beginRegionName = ref(null) as Ref<RegionName | null>;
|
const beginRegionName = ref(null) as Ref<RegionName | null>;
|
||||||
const endRegionName = ref(null) as Ref<RegionName | null>;
|
const endRegionName = ref(null) as Ref<RegionName | null>;
|
||||||
@@ -87,6 +156,19 @@ const copyNumber = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const categoryRules = computed(() => {
|
||||||
|
if (!chosenCategory.value) return null;
|
||||||
|
|
||||||
|
return genData.categoriesRules[chosenCategory.value];
|
||||||
|
});
|
||||||
|
|
||||||
|
const randomizeCategory = () => {
|
||||||
|
const categoryKeys = Object.keys(genData.categoriesRules) as Category[];
|
||||||
|
chosenCategory.value = categoryKeys[~~(Math.random() * categoryKeys.length)];
|
||||||
|
|
||||||
|
randomizeTrainNumber(false);
|
||||||
|
};
|
||||||
|
|
||||||
const randomizeTrainNumber = (randomizeRegions = false) => {
|
const randomizeTrainNumber = (randomizeRegions = false) => {
|
||||||
// if (categoryRules.value == null) return;
|
// if (categoryRules.value == null) return;
|
||||||
|
|
||||||
@@ -100,9 +182,11 @@ const randomizeTrainNumber = (randomizeRegions = false) => {
|
|||||||
|
|
||||||
let number = '';
|
let number = '';
|
||||||
|
|
||||||
|
// Two first numbers (begin & end regions)
|
||||||
if (beginRegionName.value == endRegionName.value) {
|
if (beginRegionName.value == endRegionName.value) {
|
||||||
const sameRegionsNumbers = genData.sameRegions[beginRegionName.value!];
|
const sameRegionsNumbers = genData.sameRegions[beginRegionName.value!];
|
||||||
const randRegionNumber = sameRegionsNumbers[Math.floor(Math.random() * sameRegionsNumbers.length)];
|
const randRegionNumber =
|
||||||
|
sameRegionsNumbers[Math.floor(Math.random() * sameRegionsNumbers.length)];
|
||||||
number += randRegionNumber.toString();
|
number += randRegionNumber.toString();
|
||||||
} else {
|
} else {
|
||||||
const beginRegionNumber = genData.regionNumbers[beginRegionName.value!];
|
const beginRegionNumber = genData.regionNumbers[beginRegionName.value!];
|
||||||
@@ -117,25 +201,23 @@ const randomizeTrainNumber = (randomizeRegions = false) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Choose default category if it's not chosen
|
||||||
if (chosenCategory.value == null) chosenCategory.value = 'EI';
|
if (chosenCategory.value == null) chosenCategory.value = 'EI';
|
||||||
|
|
||||||
const rulesArray = genData.categories[chosenCategory.value].split(';').map((r) => ({
|
// Get category rules
|
||||||
index: r.split(':')[0],
|
const [thirdNumber, minRange, maxRange] = categoryRules.value!;
|
||||||
rule: r.split(':')[1],
|
|
||||||
nums: Number(r.split(':')[2] || '1'),
|
|
||||||
}));
|
|
||||||
|
|
||||||
rulesArray.forEach((r) => {
|
// Third number
|
||||||
const range = r.rule.split('-');
|
number += thirdNumber ?? '';
|
||||||
|
|
||||||
if (range.length == 1) number += r.rule;
|
// Remaining numbers
|
||||||
else {
|
const rangeNums = minRange!.length;
|
||||||
const [minRange, maxRange] = range;
|
const randRange = Math.floor(
|
||||||
const randRange = Math.floor(Math.random() * (Number(maxRange) - Number(minRange)) + Number(minRange)).toString();
|
Math.random() * (Number(maxRange) - Number(minRange)) + Number(minRange)
|
||||||
|
).toString();
|
||||||
|
const leadingZeros = new Array(Math.abs(randRange.length - rangeNums)).fill('0').join('');
|
||||||
|
|
||||||
number += new Array(Math.abs(randRange.length - r.nums)).fill('0').join('') + randRange;
|
number += `${leadingZeros}${randRange}`;
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
trainNumber.value = number;
|
trainNumber.value = number;
|
||||||
};
|
};
|
||||||
@@ -145,14 +227,39 @@ const randomizeTrainNumber = (randomizeRegions = false) => {
|
|||||||
@import '../../styles/tab.scss';
|
@import '../../styles/tab.scss';
|
||||||
@import '../../styles/global.scss';
|
@import '../../styles/global.scss';
|
||||||
|
|
||||||
.options {
|
label {
|
||||||
display: grid;
|
display: block;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
font-weight: bold;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-select {
|
||||||
|
select {
|
||||||
|
width: auto;
|
||||||
|
min-width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.regions-select {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
|
|
||||||
|
div {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.generated-number {
|
.generated-number {
|
||||||
@@ -167,14 +274,19 @@ const randomizeTrainNumber = (randomizeRegions = false) => {
|
|||||||
background-color: $secondaryColor;
|
background-color: $secondaryColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.category-rules {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.tab_actions {
|
.tab_actions {
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: repeat(3, 1fr);
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab_links {
|
.tab_links {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
margin: 0.25em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $breakpointMd) {
|
@media screen and (max-width: $breakpointMd) {
|
||||||
@@ -184,9 +296,20 @@ const randomizeTrainNumber = (randomizeRegions = false) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $breakpointSm) {
|
@media screen and (max-width: $breakpointSm) {
|
||||||
.options select {
|
.regions-select {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.regions-select select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.category-select select {
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-rules {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<div class="tab_attributes">
|
<div class="tab_attributes">
|
||||||
<label>
|
<label>
|
||||||
{{ $t('stockgen.input-mass') }}
|
{{ $t('stockgen.input-mass') }}
|
||||||
<input type="number" v-model="maxMass" step="100" max="4000" min="0" />
|
<input type="number" v-model="maxTons" step="100" max="4000" min="0" />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
@@ -28,6 +28,12 @@
|
|||||||
<input type="number" v-model="maxCarCount" step="1" max="60" min="1" />
|
<input type="number" v-model="maxCarCount" step="1" max="60" min="1" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- <hr style="margin: 1em 0" /> -->
|
||||||
|
|
||||||
|
<!-- <div class="generator_options">
|
||||||
|
<Checkbox v-model="isCarGroupingEnabled">Grupuj wylosowane wagony (ustawia podobne wagony obok siebie w składzie)</Checkbox>
|
||||||
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -37,12 +43,13 @@
|
|||||||
|
|
||||||
<div class="generator_cargo">
|
<div class="generator_cargo">
|
||||||
<button
|
<button
|
||||||
|
v-for="cargo in computedCargoData"
|
||||||
|
:key="cargo.name"
|
||||||
class="btn"
|
class="btn"
|
||||||
:data-chosen="chosenCargoTypes.includes(cargoName.toString())"
|
:data-chosen="chosenCargoTypes.includes(cargo.name)"
|
||||||
v-for="(cargoArray, cargoName) in store.stockData?.generator.cargo"
|
@click="toggleCargoChosen(cargo.name, cargo.cargoList)"
|
||||||
@click="toggleCargoChosen(cargoName.toString(), cargoArray)"
|
|
||||||
>
|
>
|
||||||
{{ $t(`cargo.${cargoName}`) }}
|
{{ $t(`cargo.${cargo.name}`) }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -78,15 +85,27 @@
|
|||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="tab_actions">
|
<div class="tab_actions">
|
||||||
<button class="btn" :data-disabled="computedChosenCarTypes.size == 0" @click="generateStock()">
|
<button
|
||||||
|
class="btn"
|
||||||
|
:data-disabled="computedChosenCarTypes.size == 0"
|
||||||
|
@click="generateStock()"
|
||||||
|
>
|
||||||
{{ $t('stockgen.action-generate') }}
|
{{ $t('stockgen.action-generate') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button class="btn" :data-disabled="computedChosenCarTypes.size == 0" @click="generateStock(true)">
|
<button
|
||||||
|
class="btn"
|
||||||
|
:data-disabled="computedChosenCarTypes.size == 0"
|
||||||
|
@click="generateStock(true)"
|
||||||
|
>
|
||||||
{{ $t('stockgen.action-generate-empty') }}
|
{{ $t('stockgen.action-generate-empty') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button class="btn" :data-disabled="computedChosenCarTypes.size == 0" @click="resetChosenCargo">
|
<button
|
||||||
|
class="btn"
|
||||||
|
:data-disabled="computedChosenCarTypes.size == 0"
|
||||||
|
@click="resetChosenCargo"
|
||||||
|
>
|
||||||
{{ $t('stockgen.action-reset') }}
|
{{ $t('stockgen.action-reset') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -104,7 +123,6 @@ import warningsMixin from '../../mixins/warningsMixin';
|
|||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'stock-generator',
|
name: 'stock-generator',
|
||||||
|
|
||||||
mixins: [stockMixin, warningsMixin],
|
mixins: [stockMixin, warningsMixin],
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
@@ -116,17 +134,32 @@ export default defineComponent({
|
|||||||
|
|
||||||
previewTimeout: -1,
|
previewTimeout: -1,
|
||||||
|
|
||||||
maxMass: 3000,
|
maxTons: 3000,
|
||||||
maxLength: 650,
|
maxLength: 650,
|
||||||
maxCarCount: 50,
|
maxCarCount: 50,
|
||||||
|
|
||||||
|
isCarGroupingEnabled: false,
|
||||||
|
|
||||||
store: useStore(),
|
store: useStore(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
computedChosenCarTypes() {
|
computedChosenCarTypes() {
|
||||||
return new Set<string>(this.chosenCarTypes.sort((c1, c2) => (c1 > c2 ? 1 : -1)));
|
return new Set<string>(this.chosenCarTypes.slice().sort((c1, c2) => (c1 > c2 ? 1 : -1)));
|
||||||
|
},
|
||||||
|
|
||||||
|
computedCargoData() {
|
||||||
|
if (!this.store.vehiclesAPIData?.generator.cargo) return [];
|
||||||
|
|
||||||
|
const cargoGeneratorData = this.store.vehiclesAPIData.generator.cargo;
|
||||||
|
|
||||||
|
return Object.keys(cargoGeneratorData)
|
||||||
|
.sort((v1, v2) => this.$t(`cargo.${v1}`).localeCompare(this.$t(`cargo.${v2}`)))
|
||||||
|
.map((v) => ({
|
||||||
|
name: v,
|
||||||
|
cargoList: cargoGeneratorData[v],
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -149,65 +182,91 @@ export default defineComponent({
|
|||||||
this.excludedCarTypes.length = 0;
|
this.excludedCarTypes.length = 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// WIP
|
||||||
|
groupStock(stockList: IStock[]) {
|
||||||
|
if (!this.isCarGroupingEnabled) return false;
|
||||||
|
|
||||||
|
stockList.sort((s1, s2) => {
|
||||||
|
return (s1.constructionType + s1.cargo?.id).localeCompare(
|
||||||
|
s2.constructionType + s2.cargo?.id
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
generateStock(empty = false) {
|
generateStock(empty = false) {
|
||||||
const generatedChosenStockList = this.chosenCargoTypes.reduce((acc, type) => {
|
const generatedChosenStockList = this.chosenCargoTypes.reduce(
|
||||||
this.store.stockData?.generator.cargo[type]
|
(acc, type) => {
|
||||||
.filter((c) => !this.excludedCarTypes.includes(c.split(':')[0]))
|
this.store.vehiclesAPIData?.generator.cargo[type]
|
||||||
.forEach((c) => {
|
.filter((c) => !this.excludedCarTypes.includes(c.split(':')[0]))
|
||||||
const [type, cargoType] = c.split(':');
|
.forEach((c) => {
|
||||||
|
const [type, cargoType] = c.split(':');
|
||||||
|
|
||||||
const carWagonObjs = this.store.carDataList.filter((cw) => cw.type.startsWith(type));
|
const carWagonObjs = this.store.carDataList.filter((cw) => cw.type.startsWith(type));
|
||||||
const cargoObjs = [] as (ICargo | undefined)[];
|
const cargoObjs = [] as (ICargo | undefined)[];
|
||||||
|
|
||||||
if (!cargoType || empty) cargoObjs.push(undefined);
|
if (!cargoType || empty) cargoObjs.push(undefined);
|
||||||
else if (cargoType == 'all') cargoObjs.push(...carWagonObjs[0]?.cargoList);
|
else if (cargoType == 'all') cargoObjs.push(...carWagonObjs[0]!.cargoTypes);
|
||||||
else cargoObjs.push(carWagonObjs[0]?.cargoList.find((cargo) => cargo.id == cargoType));
|
else
|
||||||
|
cargoObjs.push(carWagonObjs[0]?.cargoTypes.find((cargo) => cargo.id == cargoType));
|
||||||
|
|
||||||
carWagonObjs.forEach((cw) => {
|
carWagonObjs.forEach((cw) => {
|
||||||
cargoObjs.forEach((cargoObj) => {
|
cargoObjs.forEach((cargoObj) => {
|
||||||
const chosenStock = acc.find((a) => a.constructionType.includes(cw.constructionType));
|
const chosenStock = acc.find((a) =>
|
||||||
|
a.constructionType.includes(cw.constructionType)
|
||||||
|
);
|
||||||
|
|
||||||
if (!chosenStock)
|
if (!chosenStock)
|
||||||
acc.push({
|
acc.push({
|
||||||
constructionType: cw.constructionType,
|
constructionType: cw.constructionType,
|
||||||
carPool: [{ carWagon: cw, cargo: cargoObj }],
|
carPool: [{ carWagon: cw, cargo: cargoObj }],
|
||||||
});
|
});
|
||||||
else chosenStock.carPool.push({ carWagon: cw, cargo: cargoObj });
|
else chosenStock.carPool.push({ carWagon: cw, cargo: cargoObj });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, [] as { constructionType: string; carPool: { carWagon: ICarWagon; cargo?: ICargo }[] }[]);
|
},
|
||||||
|
[] as {
|
||||||
|
constructionType: string;
|
||||||
|
carPool: { carWagon: ICarWagon; cargo?: ICargo }[];
|
||||||
|
}[]
|
||||||
|
);
|
||||||
|
|
||||||
let bestGeneration: { stockList: IStock[]; value: number } = { stockList: [], value: 0 };
|
let bestGeneration: { stockList: IStock[]; value: number } = {
|
||||||
|
stockList: [],
|
||||||
|
value: 0,
|
||||||
|
};
|
||||||
|
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
const headingLoco = this.store.stockList[0]?.isLoco ? this.store.stockList[0] : undefined;
|
this.store.stockList.splice(this.store.stockList[0]?.isLoco ? 1 : 0);
|
||||||
this.store.stockList.length = headingLoco ? 1 : 0;
|
|
||||||
|
|
||||||
const maxMass =
|
let carCount = 0;
|
||||||
this.store.acceptableMass > 0 ? Math.min(this.store.acceptableMass, this.maxMass) : this.maxMass;
|
const maxWeight =
|
||||||
|
this.store.acceptableWeight > 0
|
||||||
|
? Math.min(this.store.acceptableWeight, this.maxTons * 1000)
|
||||||
|
: this.maxTons * 1000;
|
||||||
|
|
||||||
let exceeded = false;
|
// eslint-disable-next-line no-constant-condition
|
||||||
|
while (true) {
|
||||||
while (!exceeded) {
|
const randomStockType =
|
||||||
const randomStockType = generatedChosenStockList[~~(Math.random() * generatedChosenStockList.length)];
|
generatedChosenStockList[~~(Math.random() * generatedChosenStockList.length)];
|
||||||
const { carWagon, cargo } = randomStockType.carPool[~~(Math.random() * randomStockType.carPool.length)];
|
const { carWagon, cargo } =
|
||||||
|
randomStockType.carPool[~~(Math.random() * randomStockType.carPool.length)];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.store.totalMass + (cargo?.totalMass || carWagon.mass) > maxMass ||
|
this.store.totalWeight + (carWagon.weight + (cargo?.weight ?? 0)) > maxWeight ||
|
||||||
this.store.totalLength + carWagon.length > this.maxLength ||
|
this.store.totalLength + carWagon.length > this.maxLength ||
|
||||||
this.store.stockList.length > this.maxCarCount
|
carCount >= this.maxCarCount
|
||||||
) {
|
) {
|
||||||
exceeded = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addCarWagon(carWagon, cargo);
|
this.addCarWagon(carWagon, cargo);
|
||||||
|
carCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentGenerationValue = this.store.totalLength + this.store.totalMass + this.store.stockList.length;
|
const currentGenerationValue = this.store.totalLength + this.store.totalWeight + carCount;
|
||||||
|
|
||||||
if (bestGeneration.value < currentGenerationValue) {
|
if (bestGeneration.value < currentGenerationValue) {
|
||||||
bestGeneration.stockList = this.store.stockList;
|
bestGeneration.stockList = this.store.stockList;
|
||||||
@@ -215,6 +274,10 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bestStockList = bestGeneration.stockList;
|
||||||
|
|
||||||
|
this.groupStock(bestStockList);
|
||||||
|
|
||||||
this.store.stockList = bestGeneration.stockList;
|
this.store.stockList = bestGeneration.stockList;
|
||||||
this.store.stockSectionMode = 'stock-list';
|
this.store.stockSectionMode = 'stock-list';
|
||||||
},
|
},
|
||||||
@@ -261,6 +324,11 @@ export default defineComponent({
|
|||||||
@import '../../styles/global.scss';
|
@import '../../styles/global.scss';
|
||||||
@import '../../styles/tab.scss';
|
@import '../../styles/tab.scss';
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.generator_cargo,
|
.generator_cargo,
|
||||||
.generator_vehicles {
|
.generator_vehicles {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -277,13 +345,6 @@ export default defineComponent({
|
|||||||
|
|
||||||
background-color: $secondaryColor;
|
background-color: $secondaryColor;
|
||||||
|
|
||||||
&[data-chosen='true'] {
|
|
||||||
background-color: $accentColor;
|
|
||||||
color: black;
|
|
||||||
|
|
||||||
box-shadow: 0 0 5px 1px $accentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-excluded='true'] {
|
&[data-excluded='true'] {
|
||||||
background-color: gray;
|
background-color: gray;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
@@ -308,6 +369,12 @@ export default defineComponent({
|
|||||||
gap: 1em;
|
gap: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.generator_options {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.generator_warning {
|
.generator_warning {
|
||||||
background-color: $accentColor;
|
background-color: $accentColor;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
@@ -316,4 +383,3 @@ export default defineComponent({
|
|||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -5,47 +5,56 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stock_actions">
|
<div class="stock_actions">
|
||||||
<label class="file-label">
|
<button class="btn btn--image" @click="clickFileInput">
|
||||||
<div class="btn btn--image">
|
|
||||||
<img src="/images/icon-upload.svg" alt="" />
|
|
||||||
{{ $t('stocklist.action-upload') }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input type="file" @change="uploadStock" ref="conFile" accept=".con,.txt" />
|
<input type="file" @change="uploadStock" ref="conFile" accept=".con,.txt" />
|
||||||
</label>
|
<img src="/images/icon-upload.svg" alt="upload icon" />
|
||||||
|
{{ $t('stocklist.action-upload') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
<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" />
|
<img src="/images/icon-download.svg" alt="download icon" />
|
||||||
{{ $t('stocklist.action-download') }}
|
{{ $t('stocklist.action-download') }}
|
||||||
</button>
|
</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" />
|
<img src="/images/icon-copy.svg" alt="copy icon" />
|
||||||
{{ $t('stocklist.action-copy') }}
|
{{ $t('stocklist.action-copy') }}
|
||||||
</button>
|
</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" />
|
<img src="/images/icon-reset.svg" alt="reset icon" />
|
||||||
{{ $t('stocklist.action-reset') }}
|
{{ $t('stocklist.action-reset') }}
|
||||||
</button>
|
</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" />
|
<img src="/images/icon-shuffle.svg" alt="shuffle icon" />
|
||||||
{{ $t('stocklist.action-shuffle') }}
|
{{ $t('stocklist.action-shuffle') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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') }} <span class="text--accent">{{ store.chosenStockListIndex + 1 }}</span>
|
|
||||||
</b>
|
|
||||||
|
|
||||||
<b v-else>
|
|
||||||
{{ $t('stocklist.no-vehicle-chosen') }}
|
|
||||||
</b>
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn"
|
class="btn btn--image"
|
||||||
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
|
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
|
||||||
@click="moveUpStock(store.chosenStockListIndex)"
|
@click="moveUpStock(store.chosenStockListIndex)"
|
||||||
>
|
>
|
||||||
@@ -54,7 +63,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn"
|
class="btn btn--image"
|
||||||
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
|
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
|
||||||
@click="moveDownStock(store.chosenStockListIndex)"
|
@click="moveDownStock(store.chosenStockListIndex)"
|
||||||
>
|
>
|
||||||
@@ -63,7 +72,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn"
|
class="btn btn--image"
|
||||||
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
|
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
|
||||||
@click="removeStock(store.chosenStockListIndex)"
|
@click="removeStock(store.chosenStockListIndex)"
|
||||||
>
|
>
|
||||||
@@ -73,37 +82,43 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stock_specs">
|
<div class="stock_specs">
|
||||||
<b class="real-stock-info" v-if="store.chosenRealStock">
|
<b class="real-stock-info" v-if="chosenRealComposition">
|
||||||
<span class="text--accent">
|
<span class="text--accent">
|
||||||
<img :src="getIconURL(store.chosenRealStock.type)" :alt="store.chosenRealStock.type" />
|
<img :src="getIconURL(chosenRealComposition.type)" :alt="chosenRealComposition.type" />
|
||||||
{{ store.chosenRealStock.number }} {{ store.chosenRealStock.name }}
|
{{ chosenRealComposition.number }} {{ chosenRealComposition.name }}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
|
||||||
</b>
|
</b>
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
{{ $t('stocklist.mass') }} <span class="text--accent">{{ store.totalMass }}t</span> ({{
|
{{ $t('stocklist.mass') }}
|
||||||
$t('stocklist.mass-accepted')
|
<span class="text--accent">{{ (store.totalWeight / 1000).toFixed(1) }}t</span>
|
||||||
}}: <span class="text--accent">{{ store.acceptableMass ? store.acceptableMass + 't' : '-' }}</span
|
({{ $t('stocklist.mass-accepted') }}:
|
||||||
|
<span class="text--accent">{{
|
||||||
|
store.acceptableWeight ? `${~~(store.acceptableWeight / 1000)}t` : '-'
|
||||||
|
}}</span
|
||||||
>) - {{ $t('stocklist.length') }}:
|
>) - {{ $t('stocklist.length') }}:
|
||||||
<span class="text--accent">{{ store.totalLength }}m</span>
|
<span class="text--accent">{{ store.totalLength }}m</span>
|
||||||
- {{ $t('stocklist.vmax') }}: <span class="text--accent">{{ store.maxStockSpeed }} km/h</span>
|
- {{ $t('stocklist.vmax') }}
|
||||||
|
<span tabindex="0" :data-tooltip="$t('stocklist.disclaimer')">(?)</span>:
|
||||||
|
<span class="text--accent">{{ store.maxStockSpeed }} km/h</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stock_cold-start">
|
<div class="stock_spawn-settings">
|
||||||
<label>
|
<Checkbox v-if="store.stockSupportsColdStart" v-model="store.isColdStart">
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
v-model="store.isColdStart"
|
|
||||||
:disabled="!locoSupportsColdStart(store.stockList[0]?.constructionType || '')"
|
|
||||||
/>
|
|
||||||
{{ $t('stocklist.coldstart-info') }}
|
{{ $t('stocklist.coldstart-info') }}
|
||||||
</label>
|
</Checkbox>
|
||||||
|
|
||||||
|
<Checkbox v-if="store.stockSupportsDoubleManning" v-model="store.isDoubleManned">
|
||||||
|
{{ $t('stocklist.doublemanning-info') }}
|
||||||
|
</Checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stock_warnings" v-if="stockHasWarnings">
|
<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">
|
<div class="warning" v-if="trainTooLong && store.isTrainPassenger">
|
||||||
(!) {{ $t('stocklist.warning-passenger-too-long') }}
|
(!) {{ $t('stocklist.warning-passenger-too-long') }}
|
||||||
@@ -119,7 +134,7 @@
|
|||||||
<template #href>
|
<template #href>
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href="https://docs.google.com/spreadsheets/d/1bFXUsHsAu4youmNz-46Q1HslZaaoklvfoBDS553TnNk/edit"
|
href="https://docs.google.com/spreadsheets/d/1KVa5vn2d8XGkXQFwbavVudwKqUQxbLOucHWs2VYqAUE"
|
||||||
>
|
>
|
||||||
{{ $t('stocklist.acceptable-mass-docs') }}
|
{{ $t('stocklist.acceptable-mass-docs') }}
|
||||||
</a>
|
</a>
|
||||||
@@ -140,7 +155,7 @@
|
|||||||
<div class="stock-info">{{ $t('stocklist.list-empty') }}</div>
|
<div class="stock-info">{{ $t('stocklist.list-empty') }}</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<TransitionGroup name="stock-list-anim">
|
<TransitionGroup name="stock-list-anim" v-else>
|
||||||
<li
|
<li
|
||||||
v-for="(stock, i) in store.stockList"
|
v-for="(stock, i) in store.stockList"
|
||||||
:key="stock.id"
|
:key="stock.id"
|
||||||
@@ -165,14 +180,18 @@
|
|||||||
{{ i + 1 }}.
|
{{ i + 1 }}.
|
||||||
</span>
|
</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) }}
|
{{ stock.isLoco ? stock.type : getCarSpecFromType(stock.type) }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="stock-info__cargo" v-if="stock.cargo"> {{ stock.cargo.id }} </span>
|
<span class="stock-info__cargo" v-if="stock.cargo">
|
||||||
<span class="stock-info__length"> {{ stock.length }}m </span>
|
{{ stock.cargo.id }}
|
||||||
<span class="stock-info__mass">{{ stock.cargo ? stock.cargo.totalMass : stock.mass }}t </span>
|
</span>
|
||||||
<span class="stock-info__speed"> {{ stock.maxSpeed }}km/h </span>
|
<span class="stock-info__length">{{ stock.length }}m</span>
|
||||||
|
<span class="stock-info__mass"
|
||||||
|
>{{ ((stock.weight + (stock.cargo?.weight ?? 0)) / 1000).toFixed(1) }}t</span
|
||||||
|
>
|
||||||
|
<span class="stock-info__speed">{{ stock.maxSpeed }}km/h</span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
@@ -182,20 +201,19 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import TrainImage from '../sections/TrainImageSection.vue';
|
|
||||||
|
|
||||||
import { useStore } from '../../store';
|
import { useStore } from '../../store';
|
||||||
|
|
||||||
import { locoSupportsColdStart } from '../../utils/locoUtils';
|
|
||||||
import warningsMixin from '../../mixins/warningsMixin';
|
import warningsMixin from '../../mixins/warningsMixin';
|
||||||
import imageMixin from '../../mixins/imageMixin';
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
import stockPreviewMixin from '../../mixins/stockPreviewMixin';
|
import stockPreviewMixin from '../../mixins/stockPreviewMixin';
|
||||||
import StockThumbnails from '../utils/StockThumbnails.vue';
|
import StockThumbnails from '../utils/StockThumbnails.vue';
|
||||||
import stockMixin from '../../mixins/stockMixin';
|
import stockMixin from '../../mixins/stockMixin';
|
||||||
|
import Checkbox from '../common/Checkbox.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'stock-list',
|
name: 'stock-list',
|
||||||
components: { TrainImage, StockThumbnails },
|
components: { StockThumbnails, Checkbox },
|
||||||
|
|
||||||
mixins: [warningsMixin, imageMixin, stockMixin, stockPreviewMixin],
|
mixins: [warningsMixin, imageMixin, stockMixin, stockPreviewMixin],
|
||||||
|
|
||||||
@@ -216,13 +234,21 @@ export default defineComponent({
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
stockString() {
|
stockString() {
|
||||||
|
if (this.store.stockList.length == 0) return '';
|
||||||
|
|
||||||
|
const includeColdStart = this.store.isColdStart && this.store.stockSupportsColdStart;
|
||||||
|
const includeDoubleManned =
|
||||||
|
this.store.isDoubleManned && this.store.stockSupportsDoubleManning;
|
||||||
|
|
||||||
return this.store.stockList
|
return this.store.stockList
|
||||||
.map((stock, i) => {
|
.map((stock, i) => {
|
||||||
let stockTypeStr = stock.isLoco || !stock.cargo ? stock.type : `${stock.type}:${stock.cargo.id}`;
|
let stockTypeStr =
|
||||||
let coldStart =
|
stock.isLoco || !stock.cargo ? stock.type : `${stock.type}:${stock.cargo.id}`;
|
||||||
i == 0 && this.store.isColdStart && locoSupportsColdStart(stock.constructionType || '') ? ',c' : '';
|
|
||||||
|
|
||||||
return stockTypeStr + coldStart;
|
if (i == 0 && (includeColdStart || includeDoubleManned))
|
||||||
|
return `${stockTypeStr},${includeColdStart ? 'c' : ''}${includeDoubleManned ? 'd' : ''}`;
|
||||||
|
|
||||||
|
return stockTypeStr;
|
||||||
})
|
})
|
||||||
.join(';');
|
.join(';');
|
||||||
},
|
},
|
||||||
@@ -232,17 +258,25 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
chosenStockVehicle() {
|
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() {
|
stockHasWarnings() {
|
||||||
return this.tooManyLocomotives || this.trainTooHeavy || this.trainTooLong || this.locoNotSuitable;
|
return (
|
||||||
|
this.tooManyLocomotives || this.trainTooHeavy || this.trainTooLong || this.locoNotSuitable
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
chosenRealComposition() {
|
||||||
|
const currentStockString = this.store.stockList.map((s) => s.type).join(';');
|
||||||
|
|
||||||
|
return this.store.realCompositionList.find((rc) => rc.stockString == currentStockString);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
locoSupportsColdStart,
|
|
||||||
|
|
||||||
copyToClipboard() {
|
copyToClipboard() {
|
||||||
navigator.clipboard.writeText(this.stockString);
|
navigator.clipboard.writeText(this.stockString);
|
||||||
|
|
||||||
@@ -251,11 +285,17 @@ export default defineComponent({
|
|||||||
}, 20);
|
}, 20);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
clickFileInput() {
|
||||||
|
(this.$refs['conFile'] as HTMLInputElement).click();
|
||||||
|
},
|
||||||
|
|
||||||
onListItemClick(stockID: number) {
|
onListItemClick(stockID: number) {
|
||||||
const stock = this.store.stockList[stockID];
|
const stock = this.store.stockList[stockID];
|
||||||
|
|
||||||
this.store.chosenStockListIndex =
|
this.store.chosenStockListIndex =
|
||||||
this.store.chosenStockListIndex == stockID && this.store.chosenVehicle?.type == stock.type ? -1 : stockID;
|
this.store.chosenStockListIndex == stockID && this.store.chosenVehicle?.type == stock.type
|
||||||
|
? -1
|
||||||
|
: stockID;
|
||||||
|
|
||||||
if (this.store.chosenStockListIndex == -1) {
|
if (this.store.chosenStockListIndex == -1) {
|
||||||
this.store.chosenVehicle = null;
|
this.store.chosenVehicle = null;
|
||||||
@@ -341,7 +381,8 @@ export default defineComponent({
|
|||||||
|
|
||||||
availableIndexes.splice(i, -1);
|
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];
|
const tempSwap = this.store.stockList[randAvailableIndex];
|
||||||
|
|
||||||
this.store.stockList[randAvailableIndex] = this.store.stockList[i];
|
this.store.stockList[randAvailableIndex] = this.store.stockList[i];
|
||||||
@@ -352,9 +393,9 @@ export default defineComponent({
|
|||||||
downloadStock() {
|
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} ${
|
const defaultName = `${this.chosenRealComposition ? this.chosenRealComposition.stockId + ' ' : ''}${this.store.stockList[0].type} ${(this.store.totalWeight / 1000).toFixed(1)}t; ${
|
||||||
this.store.totalMass
|
this.store.totalLength
|
||||||
}t; ${this.store.totalLength}m; vmax ${this.store.maxStockSpeed}`;
|
}m; vmax ${this.store.maxStockSpeed}`;
|
||||||
|
|
||||||
const fileName = prompt(this.$t('stocklist.prompt-file'), defaultName);
|
const fileName = prompt(this.$t('stocklist.prompt-file'), defaultName);
|
||||||
|
|
||||||
@@ -426,8 +467,9 @@ export default defineComponent({
|
|||||||
@import '../../styles/tab.scss';
|
@import '../../styles/tab.scss';
|
||||||
|
|
||||||
.stock-list-tab {
|
.stock-list-tab {
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-gap: 0.5em;
|
flex-direction: column;
|
||||||
|
gap: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.warning {
|
.warning {
|
||||||
@@ -447,9 +489,9 @@ export default defineComponent({
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
|
||||||
@@ -464,21 +506,6 @@ export default defineComponent({
|
|||||||
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
input#stock-count {
|
|
||||||
width: 3em;
|
|
||||||
|
|
||||||
margin: 0;
|
|
||||||
padding: 0.25em;
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
img {
|
|
||||||
margin-right: 0.25em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.stock_actions {
|
.stock_actions {
|
||||||
@@ -487,16 +514,22 @@ export default defineComponent({
|
|||||||
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
|
||||||
label.file-label {
|
button {
|
||||||
text-align: center;
|
width: 100%;
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
input {
|
input {
|
||||||
display: none;
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stock_spawn-settings {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.real-stock-info {
|
.real-stock-info {
|
||||||
img {
|
img {
|
||||||
height: 1.3ch;
|
height: 1.3ch;
|
||||||
@@ -506,7 +539,7 @@ export default defineComponent({
|
|||||||
ul {
|
ul {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
height: 500px;
|
max-height: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul > li {
|
ul > li {
|
||||||
@@ -549,7 +582,7 @@ li > .stock-info {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.supporter {
|
.sponsor {
|
||||||
color: salmon;
|
color: salmon;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -610,4 +643,3 @@ li > .stock-info {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,356 +1,356 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="wiki-list tab">
|
<section class="wiki-list tab">
|
||||||
<div class="tab_header">
|
<div class="tab_header">
|
||||||
<h2>{{ $t('wiki.title') }}</h2>
|
<h2>{{ $t('wiki.title') }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab_content">
|
<div class="tab_content">
|
||||||
<div class="actions-panel">
|
<div class="actions-panel">
|
||||||
<div class="actions-panel_vehicles">
|
<div class="actions-panel_vehicles">
|
||||||
<button class="btn btn--choice" @click="changeWikiMode('locomotives')">
|
<button
|
||||||
{{ $t('wiki.action-vehicles') }}
|
class="btn"
|
||||||
</button>
|
:data-chosen="currentFilterMode == 'tractions'"
|
||||||
<button class="btn btn--choice" @click="changeWikiMode('carWagons')">
|
@click="toggleFilter('tractions')"
|
||||||
{{ $t('wiki.action-carriages') }}
|
>
|
||||||
</button>
|
{{ $t('wiki.action-vehicles') }}
|
||||||
</div>
|
</button>
|
||||||
|
<button
|
||||||
<div class="actions-panel_search">
|
class="btn"
|
||||||
<input type="text" :placeholder="$t('wiki.search')" v-model="searchedVehicleTypeName" />
|
:data-chosen="currentFilterMode == 'carriages'"
|
||||||
</div>
|
@click="toggleFilter('carriages')"
|
||||||
</div>
|
>
|
||||||
|
{{ $t('wiki.action-carriages') }}
|
||||||
<div class="table-wrapper" @scroll="scrollEvent" ref="table-wrapper">
|
</button>
|
||||||
<table>
|
</div>
|
||||||
<thead>
|
|
||||||
<tr>
|
<div class="actions-panel_search">
|
||||||
<th v-for="header in wikiMode == 'locomotives' ? locoHeaders : carHeaders" @click="toggleSorter(header)">
|
<input type="text" :placeholder="$t('wiki.search')" v-model="searchedVehicleTypeName" />
|
||||||
{{ $t(`wiki.header.${header.id}`) }}
|
</div>
|
||||||
|
</div>
|
||||||
<span v-if="currentModeSorter.id == header.id">
|
|
||||||
{{ currentModeSorter.direction == 1 ? `⇑` : `⇓` }}
|
<div class="table-wrapper" ref="table-wrapper">
|
||||||
</span>
|
<table>
|
||||||
</th>
|
<thead>
|
||||||
</tr>
|
<tr>
|
||||||
</thead>
|
<th v-for="header in visibleHeaders" @click="toggleSorter(header)" :key="header.id">
|
||||||
|
{{ $t(`wiki.header.${header.id}`) }}
|
||||||
<tbody v-if="wikiMode == 'locomotives'">
|
|
||||||
<tr
|
<span v-if="currentSorter.id == header.id">
|
||||||
v-for="loco in computedLocoList"
|
{{ currentSorter.direction == 1 ? `⇑` : `⇓` }}
|
||||||
@click="previewLocomotive(loco)"
|
</span>
|
||||||
@keydown.enter="previewLocomotive(loco)"
|
</th>
|
||||||
@dblclick="addLocomotive(loco)"
|
</tr>
|
||||||
tabindex="0"
|
</thead>
|
||||||
>
|
|
||||||
<td>
|
<tbody>
|
||||||
<img
|
<tr
|
||||||
:src="`https://spythere.github.io/api/td2/images/${loco.type}--300px.jpg`"
|
v-for="{ vehicle, show } in computedTableData"
|
||||||
loading="lazy"
|
tabindex="0"
|
||||||
:alt="`Lokomotywa ${loco.type}`"
|
v-show="show"
|
||||||
/>
|
:key="vehicle.type"
|
||||||
</td>
|
@click="previewVehicle(vehicle)"
|
||||||
|
@keydown.enter="previewVehicle(vehicle)"
|
||||||
<td>{{ loco.type }}</td>
|
@dblclick="addVehicle(vehicle)"
|
||||||
<td>{{ $t(`wiki.${loco.power}`) }}</td>
|
>
|
||||||
<td>{{ loco.constructionType }}</td>
|
<td style="width: 120px">
|
||||||
<td>{{ locoSupportsColdStart(loco.constructionType) ? `✓` : '✗' }}</td>
|
<img
|
||||||
<td>{{ loco.length }}m</td>
|
width="120"
|
||||||
<td>{{ loco.mass }}t</td>
|
:src="getThumbnailURL(vehicle.type, 'small')"
|
||||||
<td>{{ loco.maxSpeed }}km/h</td>
|
:alt="`${vehicle.type}`"
|
||||||
</tr>
|
loading="lazy"
|
||||||
</tbody>
|
@error="(e) => ((e.target as HTMLElement).style.display = 'none')"
|
||||||
|
/>
|
||||||
<tbody v-else>
|
</td>
|
||||||
<tr
|
|
||||||
v-for="car in computedCarList"
|
<td :data-sponsoronly="vehicle.isSponsorsOnly">
|
||||||
@keydow.enter="previewCarWagon(car)"
|
{{ vehicle.type }}
|
||||||
@click="previewCarWagon(car)"
|
</td>
|
||||||
@dblclick="addCarWagon(car)"
|
|
||||||
tabindex="0"
|
<td v-if="isLocomotive(vehicle)">
|
||||||
>
|
{{ $t(`wiki.${vehicle.power}`) }}
|
||||||
<td>
|
</td>
|
||||||
<img
|
<td v-else>{{ $t(`wiki.${vehicle.useType}`) }}</td>
|
||||||
:src="`https://spythere.github.io/api/td2/images/${car.type}--300px.jpg`"
|
|
||||||
loading="lazy"
|
<td>{{ vehicle.constructionType }}</td>
|
||||||
:alt="`Lokomotywa ${car.type}`"
|
<td>{{ vehicle.length }}m</td>
|
||||||
/>
|
<td>{{ (vehicle.weight / 1000).toFixed(1) }}t</td>
|
||||||
</td>
|
<td>{{ vehicle.maxSpeed }}km/h</td>
|
||||||
|
|
||||||
<td>{{ car.type }}</td>
|
<td v-if="currentFilterMode == 'carriages'">
|
||||||
<td>{{ car.constructionType }}</td>
|
{{ !isLocomotive(vehicle) ? vehicle.cargoTypes.length : '---' }}
|
||||||
<td>{{ car.length }}m</td>
|
</td>
|
||||||
<td>{{ car.mass }}t</td>
|
<td v-if="currentFilterMode == 'tractions'">
|
||||||
<td>{{ car.maxSpeed }}km/h</td>
|
{{ isLocomotive(vehicle) ? (vehicle.coldStart ? `✓` : '✗') : '---' }}
|
||||||
<td>{{ car.cargoList.length == 0 ? '-' : car.cargoList.length }}</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
|
||||||
</div>
|
<span ref="table-bottom"></span>
|
||||||
</div>
|
</table>
|
||||||
</section>
|
</div>
|
||||||
</template>
|
</div>
|
||||||
|
</section>
|
||||||
<script lang="ts">
|
</template>
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import { useStore } from '../../store';
|
<script lang="ts">
|
||||||
import stockPreviewMixin from '../../mixins/stockPreviewMixin';
|
import { defineComponent } from 'vue';
|
||||||
import { Vehicle } from '../../types';
|
import { useStore } from '../../store';
|
||||||
import { isLocomotive } from '../../utils/vehicleUtils';
|
import stockPreviewMixin from '../../mixins/stockPreviewMixin';
|
||||||
import stockMixin from '../../mixins/stockMixin';
|
import { IVehicle } from '../../types';
|
||||||
import { locoSupportsColdStart } from '../../utils/locoUtils';
|
import { isLocomotive } from '../../utils/vehicleUtils';
|
||||||
|
import stockMixin from '../../mixins/stockMixin';
|
||||||
type WikiMode = 'locomotives' | 'carWagons';
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
type SorterID =
|
|
||||||
| 'type'
|
type SorterID =
|
||||||
| 'constructionType'
|
| 'type'
|
||||||
| 'image'
|
| 'constructionType'
|
||||||
| 'length'
|
| 'image'
|
||||||
| 'mass'
|
| 'length'
|
||||||
| 'maxSpeed'
|
| 'weight'
|
||||||
| 'cargoCount'
|
| 'maxSpeed'
|
||||||
| 'power'
|
| 'cargoCount'
|
||||||
| 'coldStart';
|
| 'group'
|
||||||
|
| 'coldStart';
|
||||||
interface WikiHeader {
|
|
||||||
id: SorterID;
|
interface IWikiHeader {
|
||||||
sortable: boolean;
|
id: SorterID;
|
||||||
}
|
sortable: boolean;
|
||||||
|
for: 'all' | 'carriages' | 'tractions';
|
||||||
const locoHeaders: WikiHeader[] = [
|
}
|
||||||
{ id: 'image', sortable: false },
|
|
||||||
{ id: 'type', sortable: true },
|
interface IWikiRow {
|
||||||
{ id: 'power', sortable: true },
|
vehicle: IVehicle;
|
||||||
{ id: 'constructionType', sortable: true },
|
show: boolean;
|
||||||
{ id: 'coldStart', sortable: true },
|
}
|
||||||
{ id: 'length', sortable: true },
|
|
||||||
{ id: 'mass', sortable: true },
|
const headers: IWikiHeader[] = [
|
||||||
{ id: 'maxSpeed', sortable: true },
|
{ id: 'image', sortable: false, for: 'all' },
|
||||||
];
|
{ id: 'type', sortable: true, for: 'all' },
|
||||||
|
{ id: 'group', sortable: true, for: 'all' },
|
||||||
const carHeaders: WikiHeader[] = [
|
{ id: 'constructionType', sortable: true, for: 'all' },
|
||||||
{ id: 'image', sortable: false },
|
{ id: 'length', sortable: true, for: 'all' },
|
||||||
{ id: 'type', sortable: true },
|
{ id: 'weight', sortable: true, for: 'all' },
|
||||||
{ id: 'constructionType', sortable: true },
|
{ id: 'maxSpeed', sortable: true, for: 'all' },
|
||||||
{ id: 'length', sortable: true },
|
{ id: 'coldStart', sortable: true, for: 'tractions' },
|
||||||
{ id: 'mass', sortable: true },
|
{ id: 'cargoCount', sortable: true, for: 'carriages' },
|
||||||
{ id: 'maxSpeed', sortable: true },
|
];
|
||||||
{ id: 'cargoCount', sortable: true },
|
|
||||||
];
|
export default defineComponent({
|
||||||
|
mixins: [stockPreviewMixin, stockMixin, imageMixin],
|
||||||
export default defineComponent({
|
|
||||||
mixins: [stockPreviewMixin, stockMixin],
|
data() {
|
||||||
|
return {
|
||||||
data() {
|
store: useStore(),
|
||||||
return {
|
headers,
|
||||||
store: useStore(),
|
|
||||||
locoHeaders,
|
scrollTop: 0,
|
||||||
carHeaders,
|
|
||||||
|
searchedVehicleTypeName: '',
|
||||||
locosScrollTop: 0,
|
|
||||||
carsScrollTop: 0,
|
currentSorter: {
|
||||||
|
id: 'type' as SorterID,
|
||||||
wikiMode: 'locomotives' as WikiMode,
|
direction: 1,
|
||||||
searchedVehicleTypeName: '',
|
},
|
||||||
|
|
||||||
currentLocoSorter: {
|
currentFilterMode: 'all' as 'all' | 'tractions' | 'carriages',
|
||||||
id: 'type' as SorterID,
|
};
|
||||||
direction: 1,
|
},
|
||||||
},
|
|
||||||
|
activated() {
|
||||||
currentCarSorter: {
|
const tableWrapperRef = this.$refs['table-wrapper'] as HTMLElement;
|
||||||
id: 'type' as SorterID,
|
|
||||||
direction: 1,
|
tableWrapperRef.scrollTo({
|
||||||
},
|
top: this.scrollTop,
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
activated() {
|
methods: {
|
||||||
const tableWrapperRef = this.$refs['table-wrapper'] as HTMLElement;
|
isLocomotive,
|
||||||
tableWrapperRef.scrollTo({ top: this.wikiMode == 'locomotives' ? this.locosScrollTop : this.carsScrollTop });
|
|
||||||
},
|
toggleFilter(name: typeof this.currentFilterMode) {
|
||||||
|
this.currentFilterMode = this.currentFilterMode == name ? 'all' : name;
|
||||||
methods: {
|
},
|
||||||
locoSupportsColdStart,
|
|
||||||
|
toggleSorter(header: IWikiHeader) {
|
||||||
scrollEvent(e: Event) {
|
if (!header.sortable) return;
|
||||||
const tableScrollTop = (e.target as HTMLElement).scrollTop;
|
|
||||||
|
if (header.id == this.currentSorter.id) this.currentSorter.direction *= -1;
|
||||||
if (this.wikiMode == 'locomotives') this.locosScrollTop = tableScrollTop;
|
this.currentSorter.id = header.id;
|
||||||
else this.carsScrollTop = tableScrollTop;
|
},
|
||||||
},
|
|
||||||
|
sortTableRows(row1: IWikiRow, row2: IWikiRow) {
|
||||||
changeWikiMode(wikiMode: WikiMode) {
|
if (!row1.show) return 0;
|
||||||
this.searchedVehicleTypeName = '';
|
|
||||||
this.wikiMode = wikiMode;
|
const { id, direction } = this.currentSorter;
|
||||||
},
|
|
||||||
|
switch (id) {
|
||||||
toggleSorter(header: WikiHeader) {
|
case 'type':
|
||||||
if (!header.sortable) return;
|
case 'constructionType':
|
||||||
|
case 'group':
|
||||||
if (header.id == this.currentModeSorter.id) this.currentModeSorter.direction *= -1;
|
return direction == 1
|
||||||
this.currentModeSorter.id = header.id;
|
? row1.vehicle[id].localeCompare(row2.vehicle[id])
|
||||||
},
|
: row2.vehicle[id].localeCompare(row1.vehicle[id]);
|
||||||
|
|
||||||
sortVehicles(vA: Vehicle, vB: Vehicle) {
|
case 'weight':
|
||||||
const { id, direction } = this.currentModeSorter;
|
case 'length':
|
||||||
const vehiclesAreLocos = isLocomotive(vA) && isLocomotive(vB);
|
case 'maxSpeed':
|
||||||
const vehiclesAreCars = !isLocomotive(vA) && !isLocomotive(vB);
|
return Math.sign(row1.vehicle[id] - row2.vehicle[id]) * direction;
|
||||||
|
|
||||||
switch (id) {
|
case 'cargoCount':
|
||||||
case 'type':
|
return (
|
||||||
case 'constructionType':
|
(!isLocomotive(row1.vehicle) ? Math.sign(row1.vehicle.cargoTypes.length || -1) : -1) -
|
||||||
return direction == 1 ? vA[id].localeCompare(vB[id]) : vB[id].localeCompare(vA[id]);
|
(!isLocomotive(row2.vehicle) ? (row2.vehicle.cargoTypes.length || -1) * direction : -1)
|
||||||
|
);
|
||||||
case 'mass':
|
|
||||||
case 'length':
|
case 'coldStart':
|
||||||
case 'maxSpeed':
|
return (
|
||||||
return Math.sign(vA[id] - vB[id]) * direction;
|
((isLocomotive(row1.vehicle) && row1.vehicle.coldStart ? 1 : -1) -
|
||||||
|
(isLocomotive(row2.vehicle) && row2.vehicle.coldStart ? 1 : -1)) *
|
||||||
case 'cargoCount':
|
direction
|
||||||
if (vehiclesAreCars) return Math.sign((vA.cargoList.length || -1) - (vB.cargoList.length || -1)) * direction;
|
);
|
||||||
|
|
||||||
case 'coldStart':
|
default:
|
||||||
if (vehiclesAreLocos)
|
break;
|
||||||
return (
|
}
|
||||||
(locoSupportsColdStart(vA.constructionType) > locoSupportsColdStart(vB.constructionType) ? 1 : -1) *
|
|
||||||
direction
|
return direction == 1
|
||||||
);
|
? row1.vehicle.type.localeCompare(row2.vehicle.type)
|
||||||
|
: row2.vehicle.type.localeCompare(row1.vehicle.type);
|
||||||
default:
|
},
|
||||||
break;
|
},
|
||||||
}
|
|
||||||
|
computed: {
|
||||||
return direction == 1 ? vA.type.localeCompare(vB.type) : vB.type.localeCompare(vA.type);
|
computedTableData(): IWikiRow[] {
|
||||||
},
|
return this.store.vehicleDataList
|
||||||
},
|
.map((vehicle) => ({
|
||||||
|
vehicle,
|
||||||
computed: {
|
show:
|
||||||
currentModeSorter() {
|
new RegExp(`${this.searchedVehicleTypeName.trim()}`, 'i').test(vehicle.type) &&
|
||||||
return this.wikiMode == 'carWagons' ? this.currentCarSorter : this.currentLocoSorter;
|
(this.currentFilterMode == 'all' ||
|
||||||
},
|
(this.currentFilterMode == 'tractions' && isLocomotive(vehicle)) ||
|
||||||
|
(this.currentFilterMode == 'carriages' && !isLocomotive(vehicle))),
|
||||||
computedLocoList() {
|
}))
|
||||||
const trimmedSearchValue = this.searchedVehicleTypeName.trim();
|
.sort((a, b) => this.sortTableRows(a, b));
|
||||||
|
},
|
||||||
return this.store.locoDataList
|
|
||||||
.filter((loco) => new RegExp(`${trimmedSearchValue}`, 'i').test(loco.type))
|
visibleHeaders() {
|
||||||
.sort(this.sortVehicles);
|
const filtersActive = this.currentFilterMode;
|
||||||
},
|
|
||||||
|
return this.headers.filter((header) => header.for == 'all' || header.for == filtersActive);
|
||||||
computedCarList() {
|
},
|
||||||
const trimmedSearchValue = this.searchedVehicleTypeName.trim();
|
|
||||||
|
areTractionVehiclesShown() {
|
||||||
return this.store.carDataList
|
return this.currentFilterMode == 'all' || this.currentFilterMode == 'tractions';
|
||||||
.filter((car) => new RegExp(`${trimmedSearchValue}`, 'i').test(car.type))
|
},
|
||||||
.sort(this.sortVehicles);
|
|
||||||
},
|
areCarriagesShown() {
|
||||||
},
|
return this.currentFilterMode == 'all' || this.currentFilterMode == 'carriages';
|
||||||
});
|
},
|
||||||
</script>
|
},
|
||||||
|
});
|
||||||
<style lang="scss" scoped>
|
</script>
|
||||||
@import '../../styles/tab.scss';
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
.actions-panel {
|
@import '../../styles/tab.scss';
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
.actions-panel {
|
||||||
align-items: center;
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
flex-wrap: wrap;
|
align-items: center;
|
||||||
gap: 0.5em;
|
|
||||||
|
flex-wrap: wrap;
|
||||||
margin: 0.5em 0;
|
gap: 0.5em;
|
||||||
}
|
|
||||||
|
margin: 0.5em 0;
|
||||||
.actions-panel_vehicles {
|
}
|
||||||
display: flex;
|
|
||||||
gap: 0.5em;
|
.actions-panel_vehicles {
|
||||||
}
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
.actions-panel_search {
|
}
|
||||||
input {
|
|
||||||
width: auto;
|
.actions-panel_search {
|
||||||
}
|
input {
|
||||||
}
|
width: auto;
|
||||||
|
}
|
||||||
.table-wrapper {
|
}
|
||||||
overflow: auto;
|
|
||||||
height: 750px;
|
.table-wrapper {
|
||||||
max-height: 95vh;
|
overflow: auto;
|
||||||
}
|
height: 750px;
|
||||||
|
max-height: 95vh;
|
||||||
.wiki-list table {
|
}
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
.wiki-list table {
|
||||||
|
border-collapse: collapse;
|
||||||
thead {
|
width: 100%;
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
thead {
|
||||||
}
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
th {
|
}
|
||||||
background-color: #111;
|
|
||||||
padding: 0.5em;
|
th {
|
||||||
user-select: none;
|
background-color: #111;
|
||||||
-moz-user-select: none;
|
padding: 0.5em;
|
||||||
-webkit-user-select: none;
|
user-select: none;
|
||||||
}
|
-moz-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
tr {
|
}
|
||||||
cursor: pointer;
|
|
||||||
background-color: #333;
|
tr {
|
||||||
|
cursor: pointer;
|
||||||
&:nth-child(odd) {
|
background-color: #333;
|
||||||
background-color: #444;
|
|
||||||
}
|
&:first-child {
|
||||||
|
min-width: 120px;
|
||||||
&:hover {
|
}
|
||||||
background-color: #666;
|
|
||||||
}
|
&:nth-child(odd) {
|
||||||
}
|
background-color: #444;
|
||||||
|
}
|
||||||
td {
|
|
||||||
text-align: center;
|
&:hover {
|
||||||
padding: 0.25em;
|
background-color: #666;
|
||||||
height: 85px;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
td:first-child {
|
td {
|
||||||
width: 120px;
|
text-align: center;
|
||||||
}
|
height: 70px;
|
||||||
|
padding: 0.25em;
|
||||||
td img {
|
|
||||||
display: block;
|
&[data-sponsoronly='true'] {
|
||||||
width: 120px;
|
color: salmon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@media screen and (max-width: $breakpointMd) {
|
|
||||||
.wiki-list table {
|
@media screen and (max-width: $breakpointMd) {
|
||||||
td {
|
.wiki-list table {
|
||||||
width: 100px;
|
th {
|
||||||
height: auto;
|
min-width: 100px;
|
||||||
|
}
|
||||||
img {
|
|
||||||
width: 6em;
|
img {
|
||||||
}
|
max-width: 100px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $breakpointSm) {
|
@media screen and (max-width: $breakpointSm) {
|
||||||
.actions-panel {
|
.actions-panel {
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions-panel_vehicles {
|
.actions-panel_vehicles {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions-panel_search {
|
.actions-panel_search {
|
||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,52 +1,52 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="image-preview"
|
class="image-preview"
|
||||||
@click="store.vehiclePreviewSrc = ''"
|
@click="store.vehiclePreviewSrc = ''"
|
||||||
@keydown.esc="store.vehiclePreviewSrc = ''"
|
@keydown.esc="store.vehiclePreviewSrc = ''"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<img :src="store.vehiclePreviewSrc" alt="preview" />
|
<img :src="store.vehiclePreviewSrc" alt="preview" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { useStore } from '../../store';
|
import { useStore } from '../../store';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
store: useStore(),
|
store: useStore(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$el.focus();
|
this.$el.focus();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.image-preview {
|
.image-preview {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
background: rgba(black, 0.85);
|
background: rgba(black, 0.85);
|
||||||
cursor: zoom-out;
|
cursor: zoom-out;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="stock_thumbnails" ref="thumbnailsRef">
|
<div class="stock-thumbnails" ref="thumbnailsRef">
|
||||||
<div
|
<div
|
||||||
|
class="thumbnail-item"
|
||||||
v-for="(stock, stockIndex) in store.stockList"
|
v-for="(stock, stockIndex) in store.stockList"
|
||||||
|
:key="stockIndex"
|
||||||
:data-selected="store.chosenStockListIndex == stockIndex"
|
:data-selected="store.chosenStockListIndex == stockIndex"
|
||||||
|
:data-sponsor="stock.isSponsorsOnly"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
@dragstart="onDragStart(stockIndex)"
|
@dragstart="onDragStart(stockIndex)"
|
||||||
@drop="onDrop($event, stockIndex)"
|
@drop="onDrop($event, stockIndex)"
|
||||||
@dragover="allowDrop"
|
@dragover="allowDrop"
|
||||||
|
@click="onListItemClick(stockIndex)"
|
||||||
>
|
>
|
||||||
<span @click="onListItemClick(stockIndex)" :key="stock.id">
|
<b>
|
||||||
<b :class="{ supporter: stock.supportersOnly }">
|
{{ stock.type }}
|
||||||
{{ stock.type }}
|
</b>
|
||||||
</b>
|
|
||||||
|
|
||||||
<span>
|
<img
|
||||||
<img
|
draggable="false"
|
||||||
draggable="false"
|
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${stock.type}.png`"
|
||||||
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${stock.type}.png`"
|
:alt="stock.type"
|
||||||
:alt="stock.type"
|
:title="stock.type"
|
||||||
:title="stock.type"
|
@error="stockImageError($event, stock)"
|
||||||
@error="stockImageError($event, stock)"
|
/>
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -54,7 +54,11 @@ watch(
|
|||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
(thumbnailsRef.value as HTMLElement)
|
(thumbnailsRef.value as HTMLElement)
|
||||||
.querySelector(`div:nth-child(${index + 1})`)
|
.querySelector(`div:nth-child(${index + 1})`)
|
||||||
?.scrollIntoView({ block: 'nearest', inline: 'start', behavior: 'smooth' });
|
?.scrollIntoView({
|
||||||
|
block: 'nearest',
|
||||||
|
inline: 'start',
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -84,42 +88,45 @@ const allowDrop = (e: DragEvent) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.stock_thumbnails {
|
.stock-thumbnails {
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
background-color: #353a57;
|
background-color: #353a57;
|
||||||
|
}
|
||||||
|
|
||||||
> div {
|
.thumbnail-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: center;
|
||||||
cursor: pointer;
|
justify-content: space-between;
|
||||||
min-height: 100px;
|
|
||||||
|
|
||||||
&[data-selected='true'] {
|
flex-direction: column;
|
||||||
background-color: rebeccapurple;
|
gap: 0.5em;
|
||||||
}
|
|
||||||
|
|
||||||
> span {
|
padding-top: 0.5em;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.5em;
|
|
||||||
padding: 0.5em 0;
|
|
||||||
|
|
||||||
text-align: center;
|
cursor: pointer;
|
||||||
|
min-height: 100px;
|
||||||
|
font-size: 0.85em;
|
||||||
|
|
||||||
font-size: 0.85em;
|
user-select: none;
|
||||||
user-select: none;
|
-moz-user-select: none;
|
||||||
-moz-user-select: none;
|
-webkit-user-select: none;
|
||||||
}
|
|
||||||
|
&[data-selected='true'] {
|
||||||
|
background-color: rebeccapurple;
|
||||||
|
}
|
||||||
|
|
||||||
|
b {
|
||||||
|
color: #ccc;
|
||||||
|
margin: 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-sponsor='true'] > b {
|
||||||
|
color: salmon;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
max-height: 60px;
|
max-height: 60px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.supporter {
|
|
||||||
color: salmon;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"EU06": [650000, 2000000],
|
||||||
|
"EU07": [650000, 2000000],
|
||||||
|
"4E": [650000, 2000000],
|
||||||
|
"EU07E": [650000, 2000000],
|
||||||
|
"EP07": [650000, 650000],
|
||||||
|
"EP08": [650000, 650000],
|
||||||
|
"EP09": [800000, 800000],
|
||||||
|
"ET41": [700000, 4000000],
|
||||||
|
"SM42": [2400000, 2400000]
|
||||||
|
}
|
||||||
@@ -1,38 +1,71 @@
|
|||||||
{
|
{
|
||||||
"regionNumbers": {
|
"regionNumbers": {
|
||||||
"Warszawa": 1,
|
"Warszawa (1)": 1,
|
||||||
"Lublin": 2,
|
"Lublin (2)": 2,
|
||||||
"Kraków": 3,
|
"Kraków (3)": 3,
|
||||||
"Sosnowiec": 4,
|
"Sosnowiec (4)": 4,
|
||||||
"Gdańsk": 5,
|
"Gdańsk (5)": 5,
|
||||||
"Wrocław": 6,
|
"Wrocław (6)": 6,
|
||||||
"Poznań": 7,
|
"Poznań (7)": 7,
|
||||||
"Szczecin": 8,
|
"Szczecin (8)": 8,
|
||||||
"Rezerwa": 9
|
"Rezerwa (9)": 9
|
||||||
},
|
},
|
||||||
"sameRegions": {
|
"sameRegions": {
|
||||||
"Losowy": [
|
"Losowy": [
|
||||||
10, 11, 19, 91, 93, 97, 99, 20, 22, 29, 30, 33, 39, 40, 44, 49, 94, 50, 55, 59, 90, 95, 96, 66, 60, 69, 77, 70,
|
10, 11, 19, 91, 93, 97, 99, 20, 22, 29, 30, 33, 39, 40, 44, 49, 94, 50, 55, 59, 90, 95, 96,
|
||||||
79, 88, 80, 89, 92, 98
|
66, 60, 69, 77, 70, 79, 88, 80, 89, 92, 98
|
||||||
],
|
],
|
||||||
"Warszawa": [10, 11, 19, 91, 93, 97, 99],
|
"Warszawa (1)": [10, 11, 19, 91, 93, 97, 99],
|
||||||
"Lublin": [20, 22, 29],
|
"Lublin (2)": [20, 22, 29],
|
||||||
"Kraków": [30, 33, 39],
|
"Kraków (3)": [30, 33, 39],
|
||||||
"Sosnowiec": [40, 44, 49, 94],
|
"Sosnowiec (4)": [40, 44, 49, 94],
|
||||||
"Gdańsk": [50, 55, 59, 90, 95, 96],
|
"Gdańsk (5)": [50, 55, 59, 90, 95, 96],
|
||||||
"Wrocław": [66, 60, 69],
|
"Wrocław (6)": [66, 60, 69],
|
||||||
"Poznań": [77, 70, 79],
|
"Poznań (7)": [77, 70, 79],
|
||||||
"Szczecin": [88, 80],
|
"Szczecin (8)": [88, 80],
|
||||||
"Rezerwa": [89, 92, 98]
|
"Rezerwa (9)": [89, 92, 98]
|
||||||
},
|
},
|
||||||
"categories": {
|
"categoriesRules": {
|
||||||
"EI": "2:00-99:2",
|
"EI": [null, "00", "99"],
|
||||||
"MP/RP": "2:050-169:3",
|
"MP": [null, "050", "169"],
|
||||||
"RO": "2:200-999:3",
|
"RO": [null, "200", "999"],
|
||||||
"PW": "2:6;3:0-899:3",
|
"RP": [null, "050", "169"],
|
||||||
"TM": "2:4;3:0-899:3",
|
"PW": ["6", "000", "899"],
|
||||||
"TK": "2:3;3:0-899:3",
|
"TK": ["3", "000", "899"],
|
||||||
"LT": "2:5;3:0-899:3"
|
"TM": ["4", "000", "899"],
|
||||||
|
"LT": ["5", "000", "899"]
|
||||||
|
},
|
||||||
|
"categoriesNextVersion": {
|
||||||
|
"EI": [null, "00", "99"],
|
||||||
|
"EC": [null, "001", "049"],
|
||||||
|
"EN": [null, "001", "049"],
|
||||||
|
|
||||||
|
"RO": [null, "200", "999"],
|
||||||
|
"RP": [null, "050", "169"],
|
||||||
|
"RM": [null, "200", "999"],
|
||||||
|
"RA": [null, "200", "999"],
|
||||||
|
|
||||||
|
"MO": [null, "200", "999"],
|
||||||
|
"MP": [null, "050", "169"],
|
||||||
|
"MM": [null, "001", "049"],
|
||||||
|
"MH": [null, "170", "199"],
|
||||||
|
|
||||||
|
"PW": ["6", "000", "899"],
|
||||||
|
"PX": ["6", "000", "899"],
|
||||||
|
|
||||||
|
"TC": ["0", "000", "899"],
|
||||||
|
"TG": ["1", "000", "899"],
|
||||||
|
"TR": ["1", "000", "899"],
|
||||||
|
"TD": ["2", "000", "899"],
|
||||||
|
"TK": ["3", "000", "899"],
|
||||||
|
"TN": ["3", "000", "899"],
|
||||||
|
"TM": ["4", "000", "899"],
|
||||||
|
"TS": ["5", "000", "899"],
|
||||||
|
|
||||||
|
"LT": ["5", "000", "899"],
|
||||||
|
"LP": ["6", "000", "899"],
|
||||||
|
"LS": ["9", "000", "899"],
|
||||||
|
|
||||||
|
"ZN": ["9", "000", "899"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,62 +1,82 @@
|
|||||||
{
|
{
|
||||||
"EU07": {
|
"EU07": {
|
||||||
"passenger": {
|
"passenger": {
|
||||||
"650": 125
|
"650000": 125
|
||||||
},
|
},
|
||||||
"cargo": {
|
"cargo": {
|
||||||
"2000": 70
|
"2000000": 70
|
||||||
}
|
},
|
||||||
|
"none": 110
|
||||||
|
},
|
||||||
|
"4E": {
|
||||||
|
"passenger": {
|
||||||
|
"650000": 125
|
||||||
|
},
|
||||||
|
"cargo": {
|
||||||
|
"2000000": 70
|
||||||
|
},
|
||||||
|
"none": 110
|
||||||
},
|
},
|
||||||
"EU07E": {
|
"EU07E": {
|
||||||
"passenger": {
|
"passenger": {
|
||||||
"650": 125
|
"650000": 125
|
||||||
},
|
},
|
||||||
"cargo": {
|
"cargo": {
|
||||||
"2000": 70
|
"2000000": 70
|
||||||
}
|
},
|
||||||
|
"none": 110
|
||||||
},
|
},
|
||||||
"EP07": {
|
"EP07": {
|
||||||
"passenger": {
|
"passenger": {
|
||||||
"650": 125
|
"650000": 125
|
||||||
},
|
},
|
||||||
"cargo": null
|
"cargo": null,
|
||||||
|
"none": 110
|
||||||
},
|
},
|
||||||
"EP08": {
|
"EP08": {
|
||||||
"passenger": {
|
"passenger": {
|
||||||
"650": 140
|
"650000": 140
|
||||||
},
|
},
|
||||||
"cargo": null
|
"cargo": null,
|
||||||
|
"none": 110
|
||||||
|
},
|
||||||
|
"EP09": {
|
||||||
|
"passenger": {
|
||||||
|
"650000": 160
|
||||||
|
},
|
||||||
|
"cargo": null,
|
||||||
|
"none": 160
|
||||||
},
|
},
|
||||||
"ET41": {
|
"ET41": {
|
||||||
"passenger": {
|
"passenger": {
|
||||||
"700": 125
|
"700000": 125
|
||||||
},
|
},
|
||||||
"cargo": {
|
"cargo": {
|
||||||
"4000": 70
|
"4000000": 70
|
||||||
}
|
},
|
||||||
|
"none": 110
|
||||||
},
|
},
|
||||||
"SM42": {
|
"SM42": {
|
||||||
"passenger": {
|
"passenger": {
|
||||||
"95": 90,
|
"95000": 90,
|
||||||
"200": 80,
|
"200000": 80,
|
||||||
"300": 70,
|
"300000": 70,
|
||||||
"450": 60,
|
"450000": 60,
|
||||||
"750": 50,
|
"750000": 50,
|
||||||
"1130": 40,
|
"1130000": 40,
|
||||||
"1720": 30,
|
"1720000": 30,
|
||||||
"2400": 20
|
"2400000": 20
|
||||||
},
|
},
|
||||||
"cargo": {
|
"cargo": {
|
||||||
"95": 90,
|
"95000": 90,
|
||||||
"200": 80,
|
"200000": 80,
|
||||||
"300": 70,
|
"300000": 70,
|
||||||
"450": 60,
|
"450000": 60,
|
||||||
"750": 50,
|
"750000": 50,
|
||||||
"1130": 40,
|
"1130000": 40,
|
||||||
"1720": 30,
|
"1720000": 30,
|
||||||
"2400": 20
|
"2400000": 20
|
||||||
}
|
},
|
||||||
|
"none": 90
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
export const enum EVehicleUseType {
|
export const enum EVehicleUseType {
|
||||||
LOCO_ELECTRICAL = 'loco-e',
|
LOCO_ELECTRICAL = 'loco-e',
|
||||||
LOCO_DIESEL = "loco-s",
|
LOCO_DIESEL = 'loco-s',
|
||||||
EMU = "loco-ezt",
|
EMU = 'loco-ezt',
|
||||||
DMU = "loco-szt",
|
DMU = 'loco-szt',
|
||||||
|
|
||||||
CAR_PASSENGER = "car-passenger",
|
CAR_PASSENGER = 'car-passenger',
|
||||||
CAR_CARGO = "car-cargo"
|
CAR_CARGO = 'car-cargo',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const http = axios.create({
|
||||||
|
baseURL:
|
||||||
|
import.meta.env.VITE_API_DEV === '1' && import.meta.env.DEV
|
||||||
|
? 'http://localhost:5500'
|
||||||
|
: 'https://static.spythere.eu',
|
||||||
|
});
|
||||||
|
|
||||||
|
export default http;
|
||||||
@@ -1,33 +1,24 @@
|
|||||||
import localePL from './locales/pl.json';
|
import localePL from './locales/pl.json';
|
||||||
import localeEN from './locales/en.json';
|
import localeEN from './locales/en.json';
|
||||||
import { createI18n } from 'vue-i18n';
|
import { createI18n } from 'vue-i18n';
|
||||||
import axios from 'axios';
|
|
||||||
|
type LocaleMessageSchema = typeof localePL;
|
||||||
type LocaleMessageSchema = typeof localePL;
|
type LocaleKey = 'en' | 'pl';
|
||||||
type LocaleKey = 'en' | 'pl';
|
|
||||||
|
const locales: { [key in LocaleKey]: LocaleMessageSchema } = {
|
||||||
const locales: { [key in LocaleKey]: LocaleMessageSchema } = {
|
en: localeEN,
|
||||||
en: localeEN,
|
pl: localePL,
|
||||||
pl: localePL,
|
};
|
||||||
};
|
|
||||||
|
const locale =
|
||||||
const locale = window.localStorage.getItem('locale') || (/^pl\b/.test(navigator.language) ? 'pl' : 'en');
|
window.localStorage.getItem('locale') || (/^pl\b/.test(navigator.language) ? 'pl' : 'en');
|
||||||
|
|
||||||
const i18n = createI18n<[LocaleMessageSchema], 'en' | 'pl'>({
|
const i18n = createI18n<[LocaleMessageSchema], 'en' | 'pl'>({
|
||||||
locale,
|
locale,
|
||||||
fallbackLocale: 'pl',
|
fallbackLocale: 'pl',
|
||||||
legacy: false,
|
legacy: false,
|
||||||
globalInjection: true,
|
globalInjection: true,
|
||||||
messages: locales,
|
messages: locales,
|
||||||
});
|
});
|
||||||
|
|
||||||
async function fetchBackendTranslations() {
|
export default i18n;
|
||||||
const localeData = (await axios.get(`https://spythere.github.io/api/td2/data/locales.json`)).data;
|
|
||||||
|
|
||||||
i18n.global.mergeLocaleMessage('pl', localeData.pl);
|
|
||||||
i18n.global.mergeLocaleMessage('en', localeData.en);
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchBackendTranslations();
|
|
||||||
|
|
||||||
export default i18n;
|
|
||||||
|
|||||||
@@ -1,150 +1,189 @@
|
|||||||
{
|
{
|
||||||
"app": {
|
"app": {
|
||||||
"title": "ROLLING STOCK EDITOR"
|
"title": "ROLLING STOCK EDITOR"
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"disclaimer": "This site has only an informational intent. The author does not carry any responsibility for creating trains against {tos}!",
|
"disclaimer": "This site has only an informational intent. The author does not carry any responsibility for creating trains against {tos}!",
|
||||||
"tos": "Train Driver 2 simulator rules",
|
"tos": "Train Driver 2 simulator rules",
|
||||||
"tos-href": "https://docs.google.com/document/d/1UAAPUtN0d_RoS4RgOzEzllJZJhA0VcizzCzKW4QylbY/edit#heading=h.1ldcvhomwjp9",
|
"tos-href": "https://docs.google.com/document/d/1UAAPUtN0d_RoS4RgOzEzllJZJhA0VcizzCzKW4QylbY/edit#heading=h.1ldcvhomwjp9",
|
||||||
"version-check": "Site is complete for version {version} of Train Driver 2 simulator"
|
"version-check": "Site is complete for version {version} of Train Driver 2 simulator"
|
||||||
},
|
},
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"title": "CHOOSE A VEHICLE",
|
"title": "CHOOSE A VEHICLE",
|
||||||
"input-vehicle": "Choose a traction unit",
|
"input-vehicle": "Choose a traction unit",
|
||||||
"input-carwagon": "Choose a carriage",
|
"input-carwagon": "Choose a carriage",
|
||||||
"cargo-title": "Cargo (only selected freight cars)",
|
"cargo-title": "Cargo (only selected freight cars)",
|
||||||
"no-cargo-available": "no cargo available",
|
"no-cargo-available": "no cargo available",
|
||||||
"cargo-empty": "empty",
|
"cargo-empty": "empty",
|
||||||
"loco-e": "ELECTR.",
|
"loco-e": "ELECTR.",
|
||||||
"loco-s": "DIESEL",
|
"loco-s": "DIESEL",
|
||||||
"loco-ezt": "EMU",
|
"loco-ezt": "EMU",
|
||||||
"loco-szt": "DMU",
|
"loco-szt": "DMU",
|
||||||
"car-passenger": "PASSENGER",
|
"car-passenger": "PASSENGER",
|
||||||
"car-cargo": "FREIGHT",
|
"car-cargo": "FREIGHT",
|
||||||
"action-add": "ADD NEW",
|
"action-add": "ADD NEW",
|
||||||
"action-swap": "SWAP WITH",
|
"action-swap": "SWAP WITH",
|
||||||
"real-stock": "POLISH TRAIN COMPOSITIONS"
|
"real-stock": "POLISH TRAIN COMPOSITIONS"
|
||||||
},
|
},
|
||||||
"preview": {
|
"preview": {
|
||||||
"title": "RAILWAY VEHICLE PREVIEW",
|
"title": "RAILWAY VEHICLE PREVIEW",
|
||||||
"loading": "IMAGE LOADING...",
|
"loading": "IMAGE LOADING...",
|
||||||
"desc": "Choose a railway vehicle above to see its preview",
|
"desc": "Choose a railway vehicle above to see its preview",
|
||||||
"sponsor-only": "* SPONSORS ONLY",
|
"sponsor-only": "* SPONSORS ONLY UNTIL {0}",
|
||||||
"loco-e": "ELECTRIC LOCO",
|
"loco-e": "ELECTRIC LOCO",
|
||||||
"loco-s": "DIESEL LOCO",
|
"loco-s": "DIESEL LOCO",
|
||||||
"loco-ezt": "ELECTRIC M.U.",
|
"loco-ezt": "ELECTRIC M.U.",
|
||||||
"loco-szt": "DIESEL M.U.",
|
"loco-szt": "DIESEL M.U.",
|
||||||
"car-passenger": "PASSENGER CARRIAGE",
|
"car-passenger": "PASSENGER CARRIAGE",
|
||||||
"car-cargo": "FREIGHT CAR",
|
"car-cargo": "FREIGHT CARRIAGE",
|
||||||
"cabin": "Cabin type:",
|
"cabin": "Cabin type:",
|
||||||
"construction": "Construction type:"
|
"construction": "Construction type:"
|
||||||
},
|
},
|
||||||
"topbar": {
|
"topbar": {
|
||||||
"stock-list": "STOCK",
|
"stock-list": "STOCK",
|
||||||
"wiki-list": "VEHICLES",
|
"wiki-list": "VEHICLES",
|
||||||
"number-generator": "NUMBER GEN.",
|
"number-generator": "NUMBER GEN.",
|
||||||
"stock-generator": "STOCK GEN."
|
"stock-generator": "STOCK GEN."
|
||||||
},
|
},
|
||||||
"stocklist": {
|
"stocklist": {
|
||||||
"title": "STOCK EDITOR",
|
"title": "STOCK EDITOR",
|
||||||
"alert-copied": "The rolling stock has been copied to your clipboard!",
|
"disclaimer": "Theorethical value based on vehicles maximum speed in the current composition. It may be inaccurate in relation to the correct operational speed in certain configurations.",
|
||||||
"alert-empty": "Lista pojazdów jest pusta!",
|
"alert-copied": "The rolling stock has been copied to your clipboard!",
|
||||||
"prompt-file": "Name a file and download it to the Presets folder (Documents/TTSK/TrainDriver2):",
|
"alert-empty": "Lista pojazdów jest pusta!",
|
||||||
"vehicle-no": "VEHICLE NO.",
|
"prompt-file": "Name a file and download it to the Presets folder (Documents/TTSK/TrainDriver2):",
|
||||||
"no-vehicle-chosen": "NO VEHICLE CHOSEN",
|
"vehicle-no": "VEHICLE NO.",
|
||||||
"action-move-up": "MOVE UP",
|
"no-vehicle-chosen": "NO VEHICLE CHOSEN",
|
||||||
"action-move-down": "MOVE DOWN",
|
"action-move-up": "MOVE UP",
|
||||||
"action-remove": "REMOVE",
|
"action-move-down": "MOVE DOWN",
|
||||||
"action-upload": "LOAD",
|
"action-remove": "REMOVE",
|
||||||
"action-download": "DOWNLOAD",
|
"action-upload": "LOAD",
|
||||||
"action-copy": "COPY",
|
"action-download": "DOWNLOAD",
|
||||||
"action-reset": "RESET",
|
"action-copy": "COPY",
|
||||||
"action-shuffle": "SHUFFLE",
|
"action-reset": "RESET",
|
||||||
"mass": "Mass",
|
"action-shuffle": "SHUFFLE",
|
||||||
"mass-accepted": "accepted",
|
"mass": "Mass",
|
||||||
"length": "Length",
|
"mass-accepted": "accepted",
|
||||||
"vmax": "vMax",
|
"length": "Length",
|
||||||
"coldstart-info": "Cold start heading locomotive (only locos 303E & 203E type)",
|
"vmax": "vMax",
|
||||||
"list-empty": "Stock list is empty!",
|
"coldstart-info": "Locomotive cold start",
|
||||||
"warning-not-suitable": "EP07 & EP08 type locomotives are designed for passenger traffic only!",
|
"doublemanning-info": "Double manning",
|
||||||
"warning-passenger-too-long": "Maximum length of a passenger train may not be greater than 350m!",
|
"list-empty": "Stock list is empty!",
|
||||||
"warning-freight-too-long": "Maximum length of a freight train may not be greater than 650m!",
|
"warning-not-suitable": "EP07 & EP08 type locomotives are designed for passenger traffic only!",
|
||||||
"warning-too-many-locos": "This train has too many traction units!",
|
"warning-passenger-too-long": "Maximum length of a passenger train may not be greater than 350m!",
|
||||||
"warning-too-heavy": "This train is too heavy! Check {href}",
|
"warning-freight-too-long": "Maximum length of a freight train may not be greater than 650m!",
|
||||||
"acceptable-mass-docs": "acceptable rolling stock masses (PL)"
|
"warning-too-many-locos": "This train has too many traction units!",
|
||||||
},
|
"warning-too-heavy": "This train is too heavy! Check {href}",
|
||||||
"stockgen": {
|
"acceptable-mass-docs": "acceptable rolling stock masses (PL)"
|
||||||
"title": "FREIGHT TRAIN GENERATOR",
|
},
|
||||||
"properties-title": "ROLLING STOCK PROPERTIES",
|
"stockgen": {
|
||||||
"properties-desc": "⇐ Add a locomotive in the first place of the stock list to include it in a drawing!",
|
"title": "FREIGHT TRAIN GENERATOR",
|
||||||
"input-mass": "Max. mass (t)",
|
"properties-title": "ROLLING STOCK PROPERTIES",
|
||||||
"input-length": "Max. length (m)",
|
"properties-desc": "⇐ Add a locomotive in the first place of the stock list to include it in a drawing!",
|
||||||
"input-carcount": "Max. car count",
|
"input-mass": "Max. mass (t)",
|
||||||
"cargo-title": "CARGO",
|
"input-length": "Max. length (m)",
|
||||||
"cargo-desc": "Choose cargo you want to fill available cars with:",
|
"input-carcount": "Max. car count",
|
||||||
"chosen-title": "CARS WITH CHOSEN CARGO",
|
"cargo-title": "CARGO",
|
||||||
"chosen-empty-warning": "Choose at least one cargo type to see available cars!",
|
"cargo-desc": "Choose cargo you want to fill available cars with:",
|
||||||
"chosen-warning": "Cars containing chosen cargo are shown below. Hover over a type to see a preview of the car. Click it to include/exclude it from a drawing (only highlighted types will be included).",
|
"chosen-title": "CARS WITH CHOSEN CARGO",
|
||||||
"action-generate": "GENERATE",
|
"chosen-empty-warning": "Choose at least one cargo type to see available cars!",
|
||||||
"action-generate-empty": "GENERATE EMPTY",
|
"chosen-warning": "Cars containing chosen cargo are shown below. Hover over a type to see a preview of the car. Click it to include/exclude it from a drawing (only highlighted types will be included).",
|
||||||
"action-reset": "RESET CARGO"
|
"action-generate": "GENERATE",
|
||||||
},
|
"action-generate-empty": "GENERATE EMPTY",
|
||||||
"numgen": {
|
"action-reset": "RESET CARGO"
|
||||||
"title": "TRAIN NUMBER GENERATOR",
|
},
|
||||||
"alert": "The number has been copied to your clipboard!",
|
"numgen": {
|
||||||
"start-region": "Beginning construction region",
|
"title": "TRAIN NUMBER GENERATOR",
|
||||||
"end-region": "Terminating construction region",
|
"subtitle": "Generates real train number based on Polish railway instruction Ir-11",
|
||||||
"train-category": "Train category",
|
"alert": "The number has been copied to your clipboard!",
|
||||||
"number-info": "Generated train number:",
|
"start-region": "Beginning construction region",
|
||||||
"warning": "Choose category and (optionally) construction regions",
|
"end-region": "Terminating construction region",
|
||||||
"td2-wiki": "> Polish rules of train numbering (TD2 wiki)",
|
"train-category": "Train category",
|
||||||
"td2-wiki-link": "https://wiki.td2.info.pl/index.php?title=Zasady_numeracji_poci%C4%85g%C3%B3w/en",
|
"number-info": "Generated train number:",
|
||||||
"action-random-region": "DRAW REGIONS",
|
"warning": "Choose category and (optionally) construction regions",
|
||||||
"action-random-number": "DRAW A NUMBER",
|
"td2-wiki": "> Polish rules of train numbering (forum thread)",
|
||||||
"categories": {
|
"td2-wiki-link": "https://td2.info.pl/english-boards/new-train-categories-in-the-simulator/",
|
||||||
"EI": "domestic express (EI)",
|
"action-random-region": "DRAW REGIONS",
|
||||||
"MP/RP": "(inter)voivodeship bullet (MP/RP)",
|
"action-random-number": "DRAW LAST DIGITS",
|
||||||
"RO": "regional passenger (RO)",
|
"action-random-category": "DRAW A CATEGORY",
|
||||||
"PW": "empty passenger (PW)",
|
|
||||||
"TM": "mass transport freight (TM)",
|
"rules": {
|
||||||
"TK": "non-mass transport freight (TK)",
|
"first-digit": "First digit:",
|
||||||
"LT": "locomotive alone (LT)"
|
"second-digit": "Second digit:",
|
||||||
},
|
"third-digit": "Third digit:",
|
||||||
"rules": {
|
"two-first-digits": "Two first digits:",
|
||||||
"EI": "4 digits - ends within the range of 00-99",
|
"two-last-digits": "Two last digits:",
|
||||||
"MP/RP": "5 digits - ends within the range of 050-169",
|
"three-last-digits": "Three last digits:",
|
||||||
"RO": "5 digits - ends within the range of 200-999",
|
"from-pool": "from pool of",
|
||||||
"PW": "6 digits - '6' on the 3rd place; ends within the range of 000-899",
|
"for-category": "for category",
|
||||||
"TM": "6 digits - '4' on the 3rd place; ends within the range of 000-899",
|
"for-region": "for region",
|
||||||
"TK": "6 digits - '3' on the 3rd place; ends within the range of 000-899",
|
"for-region-begin": "for the beginning construction region",
|
||||||
"LT": "6 digits - '5' on the 3rd place; ends within the range of 000-899"
|
"for-region-end": "for the terminating construction region",
|
||||||
}
|
"from-range": "from range of"
|
||||||
},
|
},
|
||||||
"wiki": {
|
|
||||||
"title": "LIST OF AVAILABLE VEHICLES",
|
"categories": {
|
||||||
"action-vehicles": "TRACTION UNITS",
|
"EI": "EI - domestic express",
|
||||||
"action-carriages": "CARRIAGES",
|
"EC": "EC - international express",
|
||||||
"search": "Search for a vehicle...",
|
"EN": "EN - domestic night express",
|
||||||
"header": {
|
|
||||||
"image": "Image",
|
"MP": "MP - intervoivodeship bullet",
|
||||||
"type": "Name",
|
"RP": "RP - voivodeship bullet",
|
||||||
"power": "Type",
|
"MO": "MO - intervoivodeship regio",
|
||||||
"constructionType": "Construction",
|
"RO": "RO - voivodeship regio",
|
||||||
"coldStart": "Cold start",
|
|
||||||
"length": "Length",
|
"MM": "MM - international bullet",
|
||||||
"mass": "Mass",
|
"MH": "MH - intervoivodeship bullet (night / hotel)",
|
||||||
"maxSpeed": "Speed",
|
"RM": "RM - international voivodeship regio",
|
||||||
"cargoCount": "Cargo count"
|
"RA": "RA - voivodeship regio (urban)",
|
||||||
},
|
|
||||||
"loco-ezt": "EMU",
|
"PW": "PW - empty passenger",
|
||||||
"loco-szt": "DMU",
|
"PX": "PX - empty passenger test drive",
|
||||||
"loco-s": "Diesel locomotive",
|
|
||||||
"loco-e": "Electric locomotive"
|
"TC": "TC - international freight (intermodal)",
|
||||||
},
|
"TG": "TG - international freight (cargo)",
|
||||||
"realstock": {
|
"TR": "TR - international freight (no cargo)",
|
||||||
"title": "POLISH TRAIN COMPOSITIONS by",
|
"TD": "TD - domestic freight (intermodal)",
|
||||||
"search-name": "Search by name",
|
"TM": "TM - domestic freight (cargo)",
|
||||||
"search-stock": "Search by vehicles",
|
"TN": "TN - domestic freight (no cargo)",
|
||||||
"action-reset": "RESET"
|
"TK": "TK - freight (stations & sidings service)",
|
||||||
}
|
"TS": "TS - empty freight test drive",
|
||||||
}
|
|
||||||
|
"LT": "LT - locomotive only",
|
||||||
|
"LT-new": "LT - freight locomotive only",
|
||||||
|
|
||||||
|
"LP": "LP - passenger locomotive only",
|
||||||
|
"LS": "LS - shunting locomotive",
|
||||||
|
|
||||||
|
"ZN": "ZN - inspection / diagnostic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wiki": {
|
||||||
|
"title": "LIST OF AVAILABLE VEHICLES",
|
||||||
|
"action-vehicles": "TRACTION UNITS",
|
||||||
|
"action-carriages": "CARRIAGES",
|
||||||
|
"search": "Search for a vehicle...",
|
||||||
|
"header": {
|
||||||
|
"image": "Image",
|
||||||
|
"type": "Name",
|
||||||
|
"group": "Type group",
|
||||||
|
"constructionType": "Construction",
|
||||||
|
"coldStart": "Cold start",
|
||||||
|
"length": "Length",
|
||||||
|
"weight": "Mass",
|
||||||
|
"maxSpeed": "Speed",
|
||||||
|
"cargoCount": "Cargo count"
|
||||||
|
},
|
||||||
|
"loco-ezt": "EMU",
|
||||||
|
"loco-szt": "DMU",
|
||||||
|
"loco-s": "Diesel locomotive",
|
||||||
|
"loco-e": "Electric locomotive",
|
||||||
|
"car-passenger": "Passenger carriage",
|
||||||
|
"car-cargo": "Frieght carriage"
|
||||||
|
},
|
||||||
|
"realstock": {
|
||||||
|
"title": "POLISH TRAIN COMPOSITIONS by",
|
||||||
|
"search-name": "Search by name",
|
||||||
|
"search-stock": "Search by vehicles",
|
||||||
|
"action-reset": "RESET"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,150 +1,189 @@
|
|||||||
{
|
{
|
||||||
"app": {
|
"app": {
|
||||||
"title": "EDYTOR SKŁADÓW ONLINE"
|
"title": "EDYTOR SKŁADÓW ONLINE"
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"disclaimer": "Ta strona ma charakter informacyjny. Autor nie ponosi odpowiedzialności za tworzenie pociągów niezgodnych z {tos}!",
|
"disclaimer": "Ta strona ma charakter informacyjny. Autor nie ponosi odpowiedzialności za tworzenie pociągów niezgodnych z {tos}!",
|
||||||
"tos": "regulaminem symulatora Train Driver 2",
|
"tos": "regulaminem symulatora Train Driver 2",
|
||||||
"tos-href": "https://docs.google.com/document/d/1UAAPUtN0d_RoS4RgOzEzllJZJhA0VcizzCzKW4QylbY/edit",
|
"tos-href": "https://docs.google.com/document/d/1UAAPUtN0d_RoS4RgOzEzllJZJhA0VcizzCzKW4QylbY/edit",
|
||||||
"version-check": "Strona jest kompletna dla wersji {version} symulatora TD2"
|
"version-check": "Strona jest kompletna dla wersji {version} symulatora TD2"
|
||||||
},
|
},
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"title": "WYBIERZ POJAZD SZYNOWY",
|
"title": "WYBIERZ POJAZD SZYNOWY",
|
||||||
"input-vehicle": "Wybierz pojazd trakcyjny",
|
"input-vehicle": "Wybierz pojazd trakcyjny",
|
||||||
"input-carwagon": "Wybierz wagon",
|
"input-carwagon": "Wybierz wagon",
|
||||||
"cargo-title": "Ładunek (tylko wybrane towarowe)",
|
"cargo-title": "Ładunek (tylko wybrane towarowe)",
|
||||||
"no-cargo-available": "brak dostępnych ładunków",
|
"no-cargo-available": "brak dostępnych ładunków",
|
||||||
"cargo-empty": "próżny",
|
"cargo-empty": "próżny",
|
||||||
"loco-e": "ELEKTR.",
|
"loco-e": "ELEKTR.",
|
||||||
"loco-s": "SPAL.",
|
"loco-s": "SPAL.",
|
||||||
"loco-ezt": "EZT",
|
"loco-ezt": "EZT",
|
||||||
"loco-szt": "SZT",
|
"loco-szt": "SZT",
|
||||||
"car-passenger": "PASAŻERSKIE",
|
"car-passenger": "PASAŻERSKIE",
|
||||||
"car-cargo": "TOWAROWE",
|
"car-cargo": "TOWAROWE",
|
||||||
"action-add": "DODAJ NOWY",
|
"action-add": "DODAJ NOWY",
|
||||||
"action-swap": "ZAMIEŃ ZA",
|
"action-swap": "ZAMIEŃ ZA",
|
||||||
"real-stock": "REALNE ZESTAWIENIA"
|
"real-stock": "REALNE ZESTAWIENIA"
|
||||||
},
|
},
|
||||||
"preview": {
|
"preview": {
|
||||||
"title": "PODGLĄD WYBRANEGO POJAZDU",
|
"title": "PODGLĄD WYBRANEGO POJAZDU",
|
||||||
"loading": "ŁADOWANIE OBRAZU...",
|
"loading": "ŁADOWANIE OBRAZU...",
|
||||||
"desc": "Wybierz pojazd lub wagon, aby zobaczyć jego podgląd powyżej",
|
"desc": "Wybierz pojazd lub wagon, aby zobaczyć jego podgląd powyżej",
|
||||||
"sponsor-only": "* TYLKO DLA SPONSORÓW",
|
"sponsor-only": "* TYLKO DLA SPONSORÓW DO {0}",
|
||||||
"loco-e": "ELEKTROWÓZ",
|
"loco-e": "ELEKTROWÓZ",
|
||||||
"loco-s": "SPALINOWÓZ",
|
"loco-s": "SPALINOWÓZ",
|
||||||
"loco-ezt": "EZT",
|
"loco-ezt": "EZT",
|
||||||
"loco-szt": "SZT",
|
"loco-szt": "SZT",
|
||||||
"car-passenger": "WAGON PASAŻERSKI",
|
"car-passenger": "WAGON PASAŻERSKI",
|
||||||
"car-cargo": "WAGON TOWAROWY",
|
"car-cargo": "WAGON TOWAROWY",
|
||||||
"cabin": "Typ kabiny:",
|
"cabin": "Typ kabiny:",
|
||||||
"construction": "Typ konstrukcji:"
|
"construction": "Typ konstrukcji:"
|
||||||
},
|
},
|
||||||
"topbar": {
|
"topbar": {
|
||||||
"stock-list": "SKŁAD",
|
"stock-list": "SKŁAD",
|
||||||
"wiki-list": "POJAZDY",
|
"wiki-list": "POJAZDY",
|
||||||
"number-generator": "GNR NUMERU",
|
"number-generator": "GNR NUMERU",
|
||||||
"stock-generator": "GNR SKŁADU"
|
"stock-generator": "GNR SKŁADU"
|
||||||
},
|
},
|
||||||
"stocklist": {
|
"stocklist": {
|
||||||
"title": "EDYTOR SKŁADU",
|
"title": "EDYTOR SKŁADU",
|
||||||
"alert-copied": "Skład został skopiowany do twojego schowka!",
|
"disclaimer": "Wartość poglądowa wzorowana na prędkościach maksymalnych poszczególnych pojazdów w zestawieniu. Może nie zgadzać się z prawdziwymi prędkościami eksploatacyjnymi w konkretnych konfiguracjach.",
|
||||||
"alert-empty": "Lista pojazdów jest pusta!",
|
"alert-copied": "Skład został skopiowany do twojego schowka!",
|
||||||
"prompt-file": "Nazwij plik, a następnie pobierz do folderu Presets (Dokumenty/TTSK/TrainDriver2):",
|
"alert-empty": "Lista pojazdów jest pusta!",
|
||||||
"vehicle-no": "POJAZD NR",
|
"prompt-file": "Nazwij plik, a następnie pobierz do folderu Presets (Dokumenty/TTSK/TrainDriver2):",
|
||||||
"no-vehicle-chosen": "NIE WYBRANO POJAZDU",
|
"vehicle-no": "POJAZD NR",
|
||||||
"action-move-up": "PRZENIEŚ WYŻEJ",
|
"no-vehicle-chosen": "NIE WYBRANO POJAZDU",
|
||||||
"action-move-down": "PRZENIEŚ NIŻEJ",
|
"action-move-up": "PRZENIEŚ WYŻEJ",
|
||||||
"action-remove": "USUŃ",
|
"action-move-down": "PRZENIEŚ NIŻEJ",
|
||||||
"action-upload": "WCZYTAJ",
|
"action-remove": "USUŃ",
|
||||||
"action-download": "POBIERZ",
|
"action-upload": "WCZYTAJ",
|
||||||
"action-copy": "SKOPIUJ",
|
"action-download": "POBIERZ",
|
||||||
"action-reset": "ZRESETUJ",
|
"action-copy": "SKOPIUJ",
|
||||||
"action-shuffle": "PRZETASUJ",
|
"action-reset": "ZRESETUJ",
|
||||||
"mass": "Masa",
|
"action-shuffle": "PRZETASUJ",
|
||||||
"mass-accepted": "dopuszczalna",
|
"mass": "Masa",
|
||||||
"length": "Długość",
|
"mass-accepted": "dopuszczalna",
|
||||||
"vmax": "vMax",
|
"length": "Długość",
|
||||||
"coldstart-info": "Zimny start lokomotywy czołowej (tylko elektrowozy typów 303E i 203E)",
|
"vmax": "vMax",
|
||||||
"list-empty": "Lista pojazdów jest pusta!",
|
"coldstart-info": "Zimny start",
|
||||||
"warning-not-suitable": "Lokomotywy EP07 i EP08 są przeznaczone jedynie do ruchu pasażerskiego!",
|
"doublemanning-info": "Podwójna obsada",
|
||||||
"warning-passenger-too-long": "Maksymalna długość składów pasażerskich nie może przekraczać 350m!",
|
"list-empty": "Lista pojazdów jest pusta!",
|
||||||
"warning-freight-too-long": "Maksymalna długość składów innych niż pasażerskie nie może przekraczać 650m!",
|
"warning-not-suitable": "Lokomotywy EP07 i EP08 są przeznaczone jedynie do ruchu pasażerskiego!",
|
||||||
"warning-too-many-locos": "Ten skład posiada za dużo pojazdów trakcyjnych!",
|
"warning-passenger-too-long": "Maksymalna długość składów pasażerskich nie może przekraczać 350m!",
|
||||||
"warning-too-heavy": "Ten skład jest za ciężki! Sprawdź {href}",
|
"warning-freight-too-long": "Maksymalna długość składów innych niż pasażerskie nie może przekraczać 650m!",
|
||||||
"acceptable-mass-docs": "dopuszczalne masy składów"
|
"warning-too-many-locos": "Ten skład posiada za dużo pojazdów trakcyjnych!",
|
||||||
},
|
"warning-too-heavy": "Ten skład jest za ciężki! Sprawdź {href}",
|
||||||
"stockgen": {
|
"acceptable-mass-docs": "dopuszczalne masy składów"
|
||||||
"title": "GENERATOR SKŁADU TOWAROWEGO",
|
},
|
||||||
"properties-title": "WŁAŚCIWOŚCI SKŁADU",
|
"stockgen": {
|
||||||
"properties-desc": "⇐ Dodaj lokomotywę na pierwsze miejsce listy, aby uwzględnić ją przy losowaniu składu!",
|
"title": "GENERATOR SKŁADU TOWAROWEGO",
|
||||||
"input-mass": "Maksymalna masa (t)",
|
"properties-title": "WŁAŚCIWOŚCI SKŁADU",
|
||||||
"input-length": "Maks. długość (m)",
|
"properties-desc": "⇐ Dodaj lokomotywę na pierwsze miejsce listy, aby uwzględnić ją przy losowaniu składu!",
|
||||||
"input-carcount": "Maks. liczba wagonów",
|
"input-mass": "Maksymalna masa (t)",
|
||||||
"cargo-title": "ŁADUNEK",
|
"input-length": "Maks. długość (m)",
|
||||||
"cargo-desc": "Wybierz ładunki, którymi chcesz wypełnić dostępne wagony:",
|
"input-carcount": "Maks. liczba wagonów",
|
||||||
"chosen-title": "WAGONY Z WYBRANYMI ŁADUNKAMI",
|
"cargo-title": "ŁADUNEK",
|
||||||
"chosen-empty-warning": "Wybierz co najmniej jeden ładunek, aby zobaczyć wagony, które go posiadają!",
|
"cargo-desc": "Wybierz ładunki, którymi chcesz wypełnić dostępne wagony:",
|
||||||
"chosen-warning": "Wagony posiadające wybrane ładunki. Najedź na nazwę, aby zobaczyć podgląd wagonu. Kliknij, aby wyłączyć z losowania (tylko podświetlone nazwy będą uwzględnione).",
|
"chosen-title": "WAGONY Z WYBRANYMI ŁADUNKAMI",
|
||||||
"action-generate": "WYGENERUJ",
|
"chosen-empty-warning": "Wybierz co najmniej jeden ładunek, aby zobaczyć wagony, które go posiadają!",
|
||||||
"action-generate-empty": "WYGENERUJ PRÓŻNE WAGONY",
|
"chosen-warning": "Wagony posiadające wybrane ładunki. Najedź na nazwę, aby zobaczyć podgląd wagonu. Kliknij, aby wyłączyć z losowania (tylko podświetlone nazwy będą uwzględnione).",
|
||||||
"action-reset": "ZRESETUJ ŁADUNKI"
|
"action-generate": "WYGENERUJ",
|
||||||
},
|
"action-generate-empty": "WYGENERUJ PRÓŻNE WAGONY",
|
||||||
"numgen": {
|
"action-reset": "ZRESETUJ ŁADUNKI"
|
||||||
"title": "GENERATOR NUMERU POCIĄGU",
|
},
|
||||||
"alert": "Numer został skopiowany do twojego schowka!",
|
"numgen": {
|
||||||
"start-region": "Początkowy obszar konstrukcyjny",
|
"title": "GENERATOR NUMERU POCIĄGU",
|
||||||
"end-region": "Końcowy obszar konstrukcyjny",
|
"subtitle": "Generuje realny numer pociągu na podstawie instrukcji Ir-11",
|
||||||
"train-category": "Kategoria pociągu",
|
"alert": "Numer został skopiowany do twojego schowka!",
|
||||||
"number-info": "Wygenerowany numer pociągu:",
|
"start-region": "Początkowy obszar konstrukcyjny",
|
||||||
"warning": "Wybierz kategorię oraz (opcjonalnie) obszary konstrukcyjne",
|
"end-region": "Końcowy obszar konstrukcyjny",
|
||||||
"td2-wiki": "> Szczegółowe zasady numeracji (wikipedia TD2)",
|
"train-category": "Kategoria pociągu",
|
||||||
"td2-wiki-link": "https://wiki.td2.info.pl/index.php?title=Zasady_numeracji_poci%C4%85g%C3%B3w",
|
"number-info": "Wygenerowany numer pociągu:",
|
||||||
"action-random-region": "LOSUJ OBSZARY",
|
"warning": "Wybierz kategorię oraz (opcjonalnie) obszary konstrukcyjne",
|
||||||
"action-random-number": "LOSUJ NUMER",
|
"td2-wiki": "> Szczegółowe zasady numeracji (wątek forum)",
|
||||||
"categories": {
|
"td2-wiki-link": "https://td2.info.pl/ogloszenia/nowe-kategorie-pociagow-w-symulatorze/",
|
||||||
"EI": "ekspres krajowy (EI)",
|
"action-random-region": "LOSUJ OBSZARY",
|
||||||
"MP/RP": "(między)wojewódzki pośpieszny (MP/RP)",
|
"action-random-number": "LOSUJ KOŃCÓWKĘ",
|
||||||
"RO": "wojewódzki osobowy (RO)",
|
"action-random-category": "LOSUJ KATEGORIĘ",
|
||||||
"PW": "próżny \"służbowy\" (PW)",
|
|
||||||
"TM": "towarowy do przewozów masowych (TM)",
|
"rules": {
|
||||||
"TK": "towarowy do obsługi stacji (TK)",
|
"first-digit": "Pierwsza cyfra:",
|
||||||
"LT": "lokomotywa luzem (LT)"
|
"second-digit": "Druga cyfra:",
|
||||||
},
|
"third-digit": "Trzecia cyfra:",
|
||||||
"rules": {
|
"two-first-digits": "Dwie pierwsze cyfry:",
|
||||||
"EI": "4 cyfry - końcówka z przedziału 00-99",
|
"two-last-digits": "Dwie ostatnie cyfry:",
|
||||||
"MP/RP": "5 cyfr - końcówka z przedziału 050-169",
|
"three-last-digits": "Trzy ostatnie cyfry:",
|
||||||
"RO": "5 cyfr - końcówka z przedziału 200-999",
|
"from-pool": "z puli",
|
||||||
"PW": "6 cyfr - '6' na 3. miejscu; końcówka z przedziału 000-899",
|
"for-category": "dla kategorii",
|
||||||
"TM": "6 cyfr - '4' na 3. miejscu; końcówka z przedziału 000-899",
|
"for-region": "dla obszaru",
|
||||||
"TK": "6 cyfr - '3' na 3. miejscu; końcówka z przedziału 000-899",
|
"for-region-begin": "dla początkowego obszaru konstrukcyjnego",
|
||||||
"LT": "6 cyfr - '5' na 3. miejscu; końcówka z przedziału 000-899"
|
"for-region-end": "dla końcowego obszaru konstrukcyjnego",
|
||||||
}
|
"from-range": "z przedziału"
|
||||||
},
|
},
|
||||||
"wiki": {
|
|
||||||
"title": "LISTA DOSTĘPNYCH POJAZDÓW",
|
"categories": {
|
||||||
"action-vehicles": "POJ. TRAKCYJNE",
|
"EI": "EI - ekspres krajowy",
|
||||||
"action-carriages": "WAGONY",
|
"EC": "EC - ekspres międzynarodowy",
|
||||||
"search": "Wyszukaj pojazd...",
|
"EN": "EN - ekspres krajowy nocny",
|
||||||
"header": {
|
|
||||||
"image": "Zdjęcie",
|
"MP": "MP - międzywojewódzki pośpieszny",
|
||||||
"type": "Nazwa",
|
"RP": "RP - wojewódzki pośpieszny",
|
||||||
"power": "Rodzaj",
|
"MO": "MO - międzywojewódzki osobowy",
|
||||||
"constructionType": "Konstrukcja",
|
"RO": "RO - wojewódzki osobowy",
|
||||||
"coldStart": "Zimny start",
|
|
||||||
"length": "Długość",
|
"MM": "MM - międzynarodowy pośpieszny",
|
||||||
"mass": "Masa",
|
"MH": "MH - międzywoj. pośpieszny nocny / hotelowy",
|
||||||
"maxSpeed": "Prędkość",
|
"RM": "RM - wojewódzki osobowy międzynarodowy",
|
||||||
"cargoCount": "Ładunki"
|
"RA": "RA - wojewódzki osobowy algomeracyjny",
|
||||||
},
|
|
||||||
"loco-ezt": "EZT",
|
"PW": "PW - pasażerski próżny (\"służbowy\")",
|
||||||
"loco-szt": "SZT",
|
"PX": "PX - pasażerski próżny próbny",
|
||||||
"loco-s": "Spalinowóz",
|
|
||||||
"loco-e": "Elektrowóz"
|
"TC": "TC - towarowy międzynarodowy intermodalny",
|
||||||
},
|
"TG": "TG - towarowy międzynarodowy masowy",
|
||||||
"realstock": {
|
"TR": "TR - towarowy międzynarodowy niemasowy",
|
||||||
"title": "ZESTAWIENIA REALNE by",
|
"TD": "TD - towarowy krajowy intermodalny",
|
||||||
"search-name": "Szukaj po nazwie",
|
"TM": "TM - towarowy krajowy masowy",
|
||||||
"search-stock": "Szukaj po pojazdach",
|
"TN": "TN - towarowy krajowy niemasowy",
|
||||||
"action-reset": "RESETUJ"
|
"TK": "TK - towarowy do obsługi stacji i bocznic",
|
||||||
}
|
"TS": "TS - towarowy próżny próbny",
|
||||||
}
|
|
||||||
|
"LT": "LT - lokomotywa luzem",
|
||||||
|
"LT-new": "LT - lokomotywa towarowa luzem",
|
||||||
|
|
||||||
|
"LP": "LP - lokomotywa pasażerska luzem",
|
||||||
|
"LS": "LS - lokomotywa manewrowa",
|
||||||
|
|
||||||
|
"ZN": "ZN - inspekcyjny / diagnostyczny"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wiki": {
|
||||||
|
"title": "LISTA DOSTĘPNYCH POJAZDÓW",
|
||||||
|
"action-vehicles": "POJ. TRAKCYJNE",
|
||||||
|
"action-carriages": "WAGONY",
|
||||||
|
"search": "Wyszukaj pojazd...",
|
||||||
|
"header": {
|
||||||
|
"image": "Zdjęcie",
|
||||||
|
"type": "Nazwa",
|
||||||
|
"group": "Rodzaj",
|
||||||
|
"constructionType": "Konstrukcja",
|
||||||
|
"coldStart": "Zimny start",
|
||||||
|
"length": "Długość",
|
||||||
|
"weight": "Masa",
|
||||||
|
"maxSpeed": "Prędkość",
|
||||||
|
"cargoCount": "Ładunki"
|
||||||
|
},
|
||||||
|
"loco-ezt": "EZT",
|
||||||
|
"loco-szt": "SZT",
|
||||||
|
"loco-s": "Spalinowóz",
|
||||||
|
"loco-e": "Elektrowóz",
|
||||||
|
"car-passenger": "Wagon pasażerski",
|
||||||
|
"car-cargo": "Wagon towarowy"
|
||||||
|
},
|
||||||
|
"realstock": {
|
||||||
|
"title": "ZESTAWIENIA REALNE by",
|
||||||
|
"search-name": "Szukaj po nazwie",
|
||||||
|
"search-stock": "Szukaj po pojazdach",
|
||||||
|
"action-reset": "RESETUJ"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,14 +1,8 @@
|
|||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
import { createPinia } from 'pinia';
|
import { createPinia } from 'pinia';
|
||||||
import { registerSW } from 'virtual:pwa-register';
|
|
||||||
|
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import i18n from './i18n-setup';
|
import i18n from './i18n-setup';
|
||||||
const pinia = createPinia();
|
const pinia = createPinia();
|
||||||
|
|
||||||
registerSW({
|
|
||||||
immediate: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
createApp(App).use(pinia).use(i18n).mount('#app');
|
createApp(App).use(pinia).use(i18n).mount('#app');
|
||||||
|
|
||||||
|
|||||||
@@ -5,5 +5,13 @@ export default defineComponent({
|
|||||||
getIconURL(name: string, ext = 'svg'): string {
|
getIconURL(name: string, ext = 'svg'): string {
|
||||||
return `/images/icon-${name}.${ext}`;
|
return `/images/icon-${name}.${ext}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getThumbnailURL(vehicleType: string, size: 'small' | 'large') {
|
||||||
|
return `${
|
||||||
|
import.meta.env.VITE_API_DEV === '1'
|
||||||
|
? 'http://localhost:5500'
|
||||||
|
: 'https://static.spythere.eu'
|
||||||
|
}/images/${vehicleType}--${size == 'small' ? 300 : 800}px.jpg`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { useStore } from '../store';
|
import { useStore } from '../store';
|
||||||
import { ICargo, ICarWagon, ILocomotive, IStock, Vehicle } from '../types';
|
import { ICarWagon, ILocomotive, IStock, ICargo, IVehicle } from '../types';
|
||||||
import { isLocomotive } from '../utils/vehicleUtils';
|
import { isLocomotive } from '../utils/vehicleUtils';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -15,26 +15,27 @@ export default defineComponent({
|
|||||||
return `${Math.random().toString(36).slice(5)}`;
|
return `${Math.random().toString(36).slice(5)}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
getStockObject(vehicle: Vehicle, cargo?: ICargo | null, count = 1): IStock {
|
getStockObject(vehicle: IVehicle, cargo?: ICargo | null, count = 1): IStock {
|
||||||
const isLoco = isLocomotive(vehicle);
|
const isLoco = isLocomotive(vehicle);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: this.getStockId(),
|
id: this.getStockId(),
|
||||||
type: vehicle.type,
|
type: vehicle.type,
|
||||||
length: vehicle.length,
|
length: vehicle.length,
|
||||||
mass: vehicle.mass,
|
weight: vehicle.weight,
|
||||||
maxSpeed: vehicle.maxSpeed,
|
maxSpeed: vehicle.maxSpeed,
|
||||||
isLoco,
|
isLoco,
|
||||||
cargo: !isLoco && vehicle.loadable && cargo ? cargo : undefined,
|
cargo: !isLoco && vehicle.loadable && cargo ? cargo : undefined,
|
||||||
count,
|
count,
|
||||||
imgSrc: vehicle.imageSrc,
|
imgSrc: vehicle.imageSrc,
|
||||||
useType: isLoco ? vehicle.power : vehicle.useType,
|
useType: isLoco ? vehicle.power : vehicle.useType,
|
||||||
supportersOnly: vehicle.supportersOnly,
|
isSponsorsOnly: vehicle.isSponsorsOnly,
|
||||||
constructionType: vehicle.constructionType,
|
constructionType: vehicle.constructionType,
|
||||||
|
sponsorsOnlyTimestamp: vehicle.sponsorsOnlyTimestamp,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
addVehicle(vehicle: Vehicle | null, cargo?: ICargo | null) {
|
addVehicle(vehicle: IVehicle | null, cargo?: ICargo | null) {
|
||||||
if (!vehicle) return;
|
if (!vehicle) return;
|
||||||
|
|
||||||
const stock = this.getStockObject(vehicle, cargo);
|
const stock = this.getStockObject(vehicle, cargo);
|
||||||
@@ -46,7 +47,8 @@ export default defineComponent({
|
|||||||
addLocomotive(loco: ILocomotive) {
|
addLocomotive(loco: ILocomotive) {
|
||||||
const stockObj = this.getStockObject(loco);
|
const stockObj = this.getStockObject(loco);
|
||||||
|
|
||||||
if (this.store.stockList.length > 0 && !this.store.stockList[0].isLoco) this.store.stockList.unshift(stockObj);
|
if (this.store.stockList.length > 0 && !this.store.stockList[0].isLoco)
|
||||||
|
this.store.stockList.unshift(stockObj);
|
||||||
else this.store.stockList.push(stockObj);
|
else this.store.stockList.push(stockObj);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -69,26 +71,31 @@ export default defineComponent({
|
|||||||
this.store.swapVehicles = false;
|
this.store.swapVehicles = false;
|
||||||
|
|
||||||
stockArray.forEach((type, i) => {
|
stockArray.forEach((type, i) => {
|
||||||
let vehicle: Vehicle | null = null;
|
let vehicle: IVehicle | null = null;
|
||||||
let vehicleCargo: ICargo | null = null;
|
let vehicleCargo: ICargo | null = null;
|
||||||
|
|
||||||
if (/^(EU|EP|ET|SM|EN|2EN|SN)/.test(type)) {
|
const isLoco = /^(EU|EP|ET|SM|EN|2EN|SN)/.test(type);
|
||||||
const [locoType, coldStart] = type.split(',');
|
|
||||||
|
if (isLoco) {
|
||||||
|
const [locoType, spawnProps] = type.split(',');
|
||||||
vehicle = this.store.locoDataList.find((loco) => loco.type == locoType) || null;
|
vehicle = this.store.locoDataList.find((loco) => loco.type == locoType) || null;
|
||||||
|
|
||||||
if (i == 0 && coldStart == 'c') this.store.isColdStart = true;
|
// Spawn settings
|
||||||
|
if (i == 0 && spawnProps) {
|
||||||
|
this.store.isColdStart = spawnProps.includes('c');
|
||||||
|
this.store.isDoubleManned = spawnProps.includes('d');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const [carType, cargo] = type.split(':');
|
const [carType, cargo] = type.split(':');
|
||||||
vehicle = this.store.carDataList.find((car) => car.type == carType) || null;
|
vehicle = this.store.carDataList.find((car) => car.type == carType) || null;
|
||||||
|
|
||||||
if (cargo) vehicleCargo = vehicle?.cargoList.find((c) => c.id == cargo) || null;
|
if (cargo) vehicleCargo = vehicle?.cargoTypes.find((c) => c.id == cargo) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vehicle) console.log('Brak pojazdu:', type);
|
if (!vehicle) console.log('Brak pojazdu / rodzaj pojazdu źle wczytany:', type);
|
||||||
|
|
||||||
this.addVehicle(vehicle, vehicleCargo);
|
this.addVehicle(vehicle, vehicleCargo);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { useStore } from '../store';
|
import { useStore } from '../store';
|
||||||
import { ICarWagon, ILocomotive, IStock, Vehicle } from '../types';
|
import { ICarWagon, ILocomotive, IStock, IVehicle } from '../types';
|
||||||
|
import { isLocomotive } from '../utils/vehicleUtils';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
@@ -9,45 +10,9 @@ export default defineComponent({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {},
|
||||||
locoOptions() {
|
|
||||||
return this.store.locoDataList
|
|
||||||
.sort((a, b) => (a.type > b.type ? 1 : -1))
|
|
||||||
.filter((loco) => loco.power == this.store.chosenLocoPower);
|
|
||||||
},
|
|
||||||
|
|
||||||
carOptions() {
|
|
||||||
return this.store.carDataList
|
|
||||||
.sort((a, b) => (a.type > b.type ? 1 : -1))
|
|
||||||
.filter((car) => car.useType == this.store.chosenCarUseType);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
selectLocoType(locoTypeId: string) {
|
|
||||||
this.store.chosenLocoPower = locoTypeId;
|
|
||||||
this.store.chosenVehicle = this.locoOptions[0];
|
|
||||||
this.store.chosenLoco = this.locoOptions[0];
|
|
||||||
},
|
|
||||||
|
|
||||||
selectCarWagonType(carWagonTypeId: string) {
|
|
||||||
this.store.chosenCarUseType = carWagonTypeId;
|
|
||||||
this.store.chosenVehicle = this.carOptions[0];
|
|
||||||
this.store.chosenCar = this.carOptions[0];
|
|
||||||
this.store.chosenCargo = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
previewVehicleByType(type: 'loco' | 'car' | 'cargo') {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (!this.store.chosenLoco && !this.store.chosenCar) return;
|
|
||||||
|
|
||||||
this.store.chosenVehicle = type == 'loco' ? this.store.chosenLoco : this.store.chosenCar;
|
|
||||||
|
|
||||||
this.store.chosenCargo =
|
|
||||||
this.store.chosenCar?.cargoList.find((cargo) => cargo.id == this.store.chosenCargo?.id) || null;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
previewStock(stock: IStock) {
|
previewStock(stock: IStock) {
|
||||||
if (this.store.chosenVehicle?.imageSrc != stock.imgSrc) this.store.imageLoading = true;
|
if (this.store.chosenVehicle?.imageSrc != stock.imgSrc) this.store.imageLoading = true;
|
||||||
|
|
||||||
@@ -81,6 +46,11 @@ export default defineComponent({
|
|||||||
this.store.chosenCargo = null;
|
this.store.chosenCargo = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
previewVehicle(vehicle: IVehicle) {
|
||||||
|
if (isLocomotive(vehicle)) this.previewLocomotive(vehicle);
|
||||||
|
else this.previewCarWagon(vehicle);
|
||||||
|
},
|
||||||
|
|
||||||
resetPreview() {
|
resetPreview() {
|
||||||
this.store.chosenVehicle = null;
|
this.store.chosenVehicle = null;
|
||||||
this.store.chosenCar = null;
|
this.store.chosenCar = null;
|
||||||
@@ -89,4 +59,3 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
trainTooHeavy() {
|
trainTooHeavy() {
|
||||||
return this.store.acceptableMass && this.store.totalMass > this.store.acceptableMass;
|
return this.store.acceptableWeight && this.store.totalWeight > this.store.acceptableWeight;
|
||||||
},
|
},
|
||||||
|
|
||||||
locoNotSuitable() {
|
locoNotSuitable() {
|
||||||
@@ -31,10 +31,12 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
tooManyLocomotives() {
|
tooManyLocomotives() {
|
||||||
return this.store.stockList.reduce((acc, stock) => {
|
return (
|
||||||
if (stock.isLoco) acc += stock.count;
|
this.store.stockList.reduce((acc, stock) => {
|
||||||
return acc;
|
if (stock.isLoco) acc += stock.count;
|
||||||
}, 0) > 2;
|
return acc;
|
||||||
|
}, 0) > 2
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,68 +1,145 @@
|
|||||||
import { IStore } from './types';
|
import {
|
||||||
|
IVehiclesAPI,
|
||||||
|
ICarWagon,
|
||||||
|
ILocomotive,
|
||||||
|
ICargo,
|
||||||
|
IVehicle,
|
||||||
|
IStock,
|
||||||
|
IRealComposition,
|
||||||
|
} from './types';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import {
|
import {
|
||||||
acceptableMass,
|
acceptableWeight,
|
||||||
carDataList,
|
carDataList,
|
||||||
chosenRealStock,
|
|
||||||
isTrainPassenger,
|
isTrainPassenger,
|
||||||
locoDataList,
|
locoDataList,
|
||||||
maxStockSpeed,
|
maxStockSpeed,
|
||||||
totalLength,
|
totalLength,
|
||||||
totalMass,
|
totalWeight,
|
||||||
} from './utils/vehicleUtils';
|
} from './utils/vehicleUtils';
|
||||||
|
import http from './http';
|
||||||
|
import i18n from './i18n-setup';
|
||||||
|
|
||||||
export const useStore = defineStore({
|
export const useStore = defineStore({
|
||||||
id: 'store',
|
id: 'store',
|
||||||
state: () =>
|
state: () => ({
|
||||||
({
|
chosenCar: null as ICarWagon | null,
|
||||||
chosenCar: null,
|
chosenLoco: null as ILocomotive | null,
|
||||||
chosenLoco: null,
|
chosenCargo: null as ICargo | null,
|
||||||
chosenCargo: null,
|
chosenVehicle: null as IVehicle | null,
|
||||||
chosenVehicle: null,
|
|
||||||
|
|
||||||
isColdStart: false,
|
isColdStart: false,
|
||||||
|
isDoubleManned: false,
|
||||||
|
|
||||||
showSupporter: false,
|
imageLoading: false,
|
||||||
imageLoading: false,
|
|
||||||
|
|
||||||
chosenLocoPower: 'loco-e',
|
chosenLocoPower: 'loco-e',
|
||||||
chosenCarUseType: 'car-passenger',
|
chosenCarUseType: 'car-passenger',
|
||||||
|
|
||||||
stockList: [],
|
stockList: [] as IStock[],
|
||||||
cargoOptions: [],
|
cargoOptions: [] as any[][],
|
||||||
|
|
||||||
readyStockList: [],
|
swapVehicles: false,
|
||||||
|
|
||||||
swapVehicles: false,
|
chosenStockListIndex: -1,
|
||||||
|
|
||||||
chosenStockListIndex: -1,
|
vehiclePreviewSrc: '',
|
||||||
chosenRealStockName: undefined,
|
|
||||||
|
|
||||||
vehiclePreviewSrc: '',
|
stockSectionMode: 'stock-list',
|
||||||
|
|
||||||
stockSectionMode: 'stock-list',
|
isRandomizerCardOpen: false,
|
||||||
|
isRealStockListCardOpen: false,
|
||||||
|
|
||||||
isRandomizerCardOpen: false,
|
vehiclesAPIData: undefined as IVehiclesAPI | undefined,
|
||||||
isRealStockListCardOpen: false,
|
|
||||||
|
|
||||||
stockData: undefined,
|
lastFocusedElement: null as HTMLElement | null,
|
||||||
} as IStore),
|
}),
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
locoDataList: (state) => locoDataList(state),
|
locoDataList: (state) => locoDataList(state.vehiclesAPIData),
|
||||||
carDataList: (state) => carDataList(state),
|
carDataList: (state) => carDataList(state.vehiclesAPIData),
|
||||||
totalMass: (state) => totalMass(state),
|
vehicleDataList: (state) => [
|
||||||
totalLength: (state) => totalLength(state),
|
...locoDataList(state.vehiclesAPIData),
|
||||||
maxStockSpeed: (state) => maxStockSpeed(state),
|
...carDataList(state.vehiclesAPIData),
|
||||||
isTrainPassenger: (state) => isTrainPassenger(state),
|
],
|
||||||
chosenRealStock: (state) => chosenRealStock(state),
|
totalWeight: (state) => totalWeight(state.stockList),
|
||||||
acceptableMass: (state) => acceptableMass(state),
|
totalLength: (state) => totalLength(state.stockList),
|
||||||
|
maxStockSpeed: (state) => maxStockSpeed(state.stockList),
|
||||||
|
isTrainPassenger: (state) => isTrainPassenger(state.stockList),
|
||||||
|
acceptableWeight: (state) => acceptableWeight(state.stockList),
|
||||||
|
|
||||||
|
realCompositionList: (state) => {
|
||||||
|
if (!state.vehiclesAPIData) return [];
|
||||||
|
|
||||||
|
return Object.keys(state.vehiclesAPIData.realCompositions).reduce<IRealComposition[]>(
|
||||||
|
(acc, key) => {
|
||||||
|
const [type, number, ...name] = key.split(' ');
|
||||||
|
|
||||||
|
const obj = {
|
||||||
|
number: number.replace(/_/g, '/'),
|
||||||
|
name: name.join(' '),
|
||||||
|
stockString: state.vehiclesAPIData!.realCompositions[key],
|
||||||
|
type,
|
||||||
|
};
|
||||||
|
|
||||||
|
acc.push({
|
||||||
|
stockId: `${obj.type} ${obj.number} ${obj.name}`,
|
||||||
|
...obj,
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
stockSupportsColdStart: (state) => {
|
||||||
|
if (state.stockList.length == 0) return false;
|
||||||
|
if (!state.stockList[0].isLoco) return false;
|
||||||
|
|
||||||
|
const headingLoco = state.stockList[0];
|
||||||
|
|
||||||
|
return (
|
||||||
|
state.vehiclesAPIData?.vehicleProps.find(
|
||||||
|
(stock) => stock.type == headingLoco.constructionType
|
||||||
|
)?.coldStart ?? false
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
stockSupportsDoubleManning: (state) => {
|
||||||
|
if (state.stockList.length == 0) return false;
|
||||||
|
if (!state.stockList[0].isLoco) return false;
|
||||||
|
|
||||||
|
const headingLoco = state.stockList[0];
|
||||||
|
|
||||||
|
return (
|
||||||
|
state.vehiclesAPIData?.vehicleProps.find(
|
||||||
|
(stock) => stock.type == headingLoco.constructionType
|
||||||
|
)?.doubleManned ?? false
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
async fetchStockInfoData() {
|
async fetchVehiclesAPI() {
|
||||||
const stockData = await (await fetch(`https://spythere.github.io/api/td2/data/stockInfo.json`)).json();
|
try {
|
||||||
this.stockData = stockData;
|
const vehiclesData = (await http.get<IVehiclesAPI>('/vehicles.json')).data;
|
||||||
|
this.vehiclesAPIData = vehiclesData;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async setupAPIData() {
|
||||||
|
await this.fetchVehiclesAPI();
|
||||||
|
this.mergeBackendTranslations();
|
||||||
|
},
|
||||||
|
|
||||||
|
async mergeBackendTranslations() {
|
||||||
|
if (!this.vehiclesAPIData) return;
|
||||||
|
|
||||||
|
i18n.global.mergeLocaleMessage('pl', this.vehiclesAPIData.vehicleLocales.pl);
|
||||||
|
i18n.global.mergeLocaleMessage('en', this.vehiclesAPIData.vehicleLocales.en);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleRouting() {
|
handleRouting() {
|
||||||
@@ -82,5 +159,3 @@ export const useStore = defineStore({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,268 +1,329 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@400;700;900&display=swap');
|
$breakpointMd: 960px;
|
||||||
|
$breakpointSm: 550px;
|
||||||
$breakpointMd: 960px;
|
|
||||||
$breakpointSm: 550px;
|
$bgColor: #2b3552;
|
||||||
|
$textColor: #fff;
|
||||||
$bgColor: #2b3552;
|
$secondaryColor: #1b1b1b;
|
||||||
$textColor: #fff;
|
$accentColor: #e4c428;
|
||||||
$secondaryColor: #222;
|
|
||||||
$accentColor: #e4c428;
|
@font-face {
|
||||||
|
font-family: 'Lato';
|
||||||
::-webkit-scrollbar {
|
src:
|
||||||
width: 7px;
|
url('/fonts/Lato-Light.woff2') format('woff2'),
|
||||||
height: 7px;
|
url('/fonts/Lato-Light.woff') format('woff');
|
||||||
|
font-weight: 300;
|
||||||
&-track {
|
font-style: normal;
|
||||||
background: #222;
|
font-display: swap;
|
||||||
border-radius: 0.5rem;
|
}
|
||||||
}
|
|
||||||
|
@font-face {
|
||||||
&-thumb {
|
font-family: 'Lato';
|
||||||
border-radius: 1rem;
|
src:
|
||||||
background: #777;
|
url('/fonts/Lato-Bold.woff2') format('woff2'),
|
||||||
}
|
url('/fonts/Lato-Bold.woff') format('woff');
|
||||||
|
font-weight: bold;
|
||||||
&-corner {
|
font-style: normal;
|
||||||
background: #222;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@font-face {
|
||||||
body,
|
font-family: 'Lato';
|
||||||
html {
|
src:
|
||||||
margin: 0;
|
url('/fonts/Lato-Regular.woff2') format('woff2'),
|
||||||
padding: 0;
|
url('/fonts/Lato-Regular.woff') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
font-family: 'Lato', sans-serif;
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
background-color: $bgColor;
|
}
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
::-webkit-scrollbar {
|
||||||
|
width: 7px;
|
||||||
*,
|
height: 7px;
|
||||||
*::before,
|
|
||||||
*::after {
|
&-track {
|
||||||
box-sizing: border-box;
|
background: #222;
|
||||||
}
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
a {
|
|
||||||
color: white;
|
&-thumb {
|
||||||
text-decoration: none;
|
border-radius: 1rem;
|
||||||
|
background: #777;
|
||||||
transition: color 250ms;
|
}
|
||||||
|
|
||||||
&:visited {
|
&-corner {
|
||||||
color: white;
|
background: #222;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
&:hover,
|
|
||||||
&:focus {
|
body,
|
||||||
color: $accentColor;
|
html {
|
||||||
}
|
margin: 0;
|
||||||
}
|
padding: 0;
|
||||||
|
|
||||||
select,
|
font-family: Lato, sans-serif;
|
||||||
option,
|
|
||||||
input,
|
background-color: $bgColor;
|
||||||
button {
|
overflow-x: hidden;
|
||||||
font-family: 'Lato', sans-serif;
|
}
|
||||||
font-size: 1em;
|
|
||||||
}
|
*,
|
||||||
|
*::before,
|
||||||
button {
|
*::after {
|
||||||
border: none;
|
box-sizing: border-box;
|
||||||
outline: none;
|
}
|
||||||
background: none;
|
|
||||||
|
a {
|
||||||
padding: 0;
|
color: white;
|
||||||
margin: 0;
|
text-decoration: none;
|
||||||
|
|
||||||
cursor: pointer;
|
transition: color 250ms;
|
||||||
|
|
||||||
font-size: 1em;
|
&:visited {
|
||||||
color: white;
|
color: white;
|
||||||
|
}
|
||||||
&:hover {
|
|
||||||
color: $accentColor;
|
&:hover,
|
||||||
}
|
&:focus {
|
||||||
}
|
color: $accentColor;
|
||||||
|
}
|
||||||
.btn {
|
}
|
||||||
padding: 0.4em 0.75em;
|
|
||||||
|
select,
|
||||||
outline: none;
|
option,
|
||||||
background-color: #222;
|
input,
|
||||||
border-radius: 8px;
|
button {
|
||||||
font-weight: bold;
|
font-family: Lato, sans-serif;
|
||||||
|
font-size: 1em;
|
||||||
transition: all 250ms;
|
}
|
||||||
|
|
||||||
&:hover {
|
button {
|
||||||
color: $accentColor;
|
border: none;
|
||||||
}
|
outline: none;
|
||||||
|
background: none;
|
||||||
&.btn--outline {
|
|
||||||
background: none;
|
padding: 0;
|
||||||
font-weight: bold;
|
margin: 0;
|
||||||
outline: 1px solid $accentColor;
|
|
||||||
}
|
cursor: pointer;
|
||||||
|
|
||||||
&:focus-visible {
|
font-size: 1em;
|
||||||
color: $accentColor;
|
color: white;
|
||||||
outline: 1px solid white;
|
|
||||||
}
|
&:hover {
|
||||||
|
color: $accentColor;
|
||||||
&[data-disabled='true'] {
|
}
|
||||||
user-select: none;
|
}
|
||||||
pointer-events: none;
|
|
||||||
-moz-user-select: none;
|
[data-tooltip]:hover::after,
|
||||||
-webkit-user-select: none;
|
[data-tooltip]:focus::after {
|
||||||
|
position: absolute;
|
||||||
opacity: 0.75;
|
transform: translateX(10px);
|
||||||
background-color: #2b2b2b;
|
|
||||||
}
|
content: attr(data-tooltip);
|
||||||
|
color: white;
|
||||||
&--image {
|
background: black;
|
||||||
display: flex;
|
padding: 0.5em;
|
||||||
justify-content: center;
|
max-width: 300px;
|
||||||
align-items: center;
|
z-index: 100;
|
||||||
gap: 0.5em;
|
}
|
||||||
|
|
||||||
img {
|
[data-tooltip] {
|
||||||
width: 1.3em;
|
cursor: pointer;
|
||||||
vertical-align: middle;
|
}
|
||||||
}
|
|
||||||
}
|
.btn {
|
||||||
|
padding: 0.4em 0.75em;
|
||||||
&--text {
|
|
||||||
font-weight: bold;
|
outline: none;
|
||||||
transition: all 250ms;
|
background-color: $secondaryColor;
|
||||||
background: none;
|
border-radius: 8px;
|
||||||
padding: 0;
|
font-weight: bold;
|
||||||
|
|
||||||
&:focus-visible {
|
transition:
|
||||||
outline: 1px solid white;
|
color 150ms,
|
||||||
}
|
background-color 150ms;
|
||||||
}
|
|
||||||
}
|
&:hover {
|
||||||
|
color: $accentColor;
|
||||||
select,
|
}
|
||||||
input[type='text'],
|
|
||||||
input[type='number'] {
|
&.btn--outline {
|
||||||
background: none;
|
background: none;
|
||||||
border: 2px solid #aaa;
|
font-weight: bold;
|
||||||
outline: none;
|
outline: 1px solid $accentColor;
|
||||||
|
}
|
||||||
padding: 0.25em 0.35em;
|
|
||||||
|
&:focus-visible {
|
||||||
color: white;
|
color: $accentColor;
|
||||||
font-size: 1em;
|
outline: 1px solid white;
|
||||||
|
}
|
||||||
width: 18em;
|
|
||||||
|
&[data-chosen='true'] {
|
||||||
&:focus-visible {
|
background-color: $accentColor;
|
||||||
border-color: $accentColor;
|
color: black;
|
||||||
}
|
|
||||||
|
box-shadow: 0 0 5px 1px $accentColor;
|
||||||
&::placeholder {
|
}
|
||||||
color: #aaa;
|
|
||||||
}
|
&[data-disabled='true'] {
|
||||||
}
|
user-select: none;
|
||||||
|
pointer-events: none;
|
||||||
option {
|
-moz-user-select: none;
|
||||||
color: white;
|
-webkit-user-select: none;
|
||||||
border: none;
|
|
||||||
background-color: $bgColor;
|
opacity: 0.75;
|
||||||
}
|
background-color: #2b2b2b;
|
||||||
|
}
|
||||||
ul {
|
|
||||||
list-style: none;
|
&--image {
|
||||||
margin: 0;
|
display: flex;
|
||||||
padding: 0;
|
justify-content: center;
|
||||||
}
|
align-items: center;
|
||||||
|
gap: 0.5em;
|
||||||
.text {
|
|
||||||
&--accent {
|
img {
|
||||||
color: $accentColor;
|
width: 1.3em;
|
||||||
}
|
vertical-align: middle;
|
||||||
|
}
|
||||||
&--grayed {
|
}
|
||||||
color: #aaa;
|
|
||||||
}
|
&--text {
|
||||||
}
|
font-weight: bold;
|
||||||
|
transition: all 250ms;
|
||||||
.g-card {
|
background: none;
|
||||||
position: fixed;
|
padding: 0;
|
||||||
top: 1em;
|
|
||||||
left: 0;
|
&:focus-visible {
|
||||||
|
outline: 1px solid white;
|
||||||
width: 100vw;
|
}
|
||||||
height: 100vh;
|
}
|
||||||
|
}
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
select,
|
||||||
|
input[type='text'],
|
||||||
z-index: 200;
|
input[type='number'] {
|
||||||
|
background: $bgColor;
|
||||||
&_bg {
|
border: 2px solid #aaa;
|
||||||
position: fixed;
|
outline: none;
|
||||||
top: 0;
|
|
||||||
left: 0;
|
padding: 0.25em 0.35em;
|
||||||
|
|
||||||
width: 100vw;
|
color: white;
|
||||||
height: 100vh;
|
font-size: 1em;
|
||||||
|
|
||||||
background-color: #000000aa;
|
width: 18em;
|
||||||
z-index: 10;
|
|
||||||
}
|
&:focus-visible {
|
||||||
}
|
border-color: $accentColor;
|
||||||
|
}
|
||||||
.g-choice {
|
|
||||||
input {
|
&::placeholder {
|
||||||
display: none;
|
color: #aaa;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
span {
|
|
||||||
padding: 0.25em 1em;
|
option {
|
||||||
border-radius: 0.25em;
|
color: white;
|
||||||
border: 2px solid white;
|
border: none;
|
||||||
margin: 0.25em;
|
background-color: $bgColor;
|
||||||
cursor: pointer;
|
}
|
||||||
|
|
||||||
transition: all 100ms ease;
|
ul {
|
||||||
}
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
span:focus {
|
padding: 0;
|
||||||
color: $accentColor;
|
}
|
||||||
outline: none;
|
|
||||||
}
|
.text {
|
||||||
|
&--accent {
|
||||||
label > input:checked + span {
|
color: $accentColor;
|
||||||
color: $accentColor;
|
}
|
||||||
border-color: $accentColor;
|
|
||||||
}
|
&--grayed {
|
||||||
}
|
color: #aaa;
|
||||||
|
}
|
||||||
// Vue Transition anims
|
}
|
||||||
.slide-top {
|
|
||||||
&-enter-from,
|
hr {
|
||||||
&-leave-to {
|
height: 3px;
|
||||||
transform: translateY(-100%);
|
background-color: white;
|
||||||
}
|
outline: none;
|
||||||
|
margin: 0;
|
||||||
&-enter-active,
|
}
|
||||||
&-leave-active {
|
|
||||||
transition: transform 100ms ease-in-out;
|
.g-card {
|
||||||
}
|
position: fixed;
|
||||||
}
|
top: 1em;
|
||||||
|
left: 0;
|
||||||
.card-appear {
|
|
||||||
&-enter-from,
|
width: 100vw;
|
||||||
&-leave-to {
|
height: 100vh;
|
||||||
opacity: 0;
|
|
||||||
}
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
&-enter-active,
|
|
||||||
&-leave-active {
|
z-index: 200;
|
||||||
transition: all 100ms ease-in-out;
|
|
||||||
}
|
&_bg {
|
||||||
}
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
background-color: #000000aa;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.g-choice {
|
||||||
|
input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
padding: 0.25em 1em;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
border: 2px solid white;
|
||||||
|
margin: 0.25em;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
transition: all 100ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
span:focus {
|
||||||
|
color: $accentColor;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
label > input:checked + span {
|
||||||
|
color: $accentColor;
|
||||||
|
border-color: $accentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vue Transition anims
|
||||||
|
.slide-top {
|
||||||
|
&-enter-from,
|
||||||
|
&-leave-to {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-enter-active,
|
||||||
|
&-leave-active {
|
||||||
|
transition: transform 100ms ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-appear {
|
||||||
|
&-enter-from,
|
||||||
|
&-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-enter-active,
|
||||||
|
&-leave-active {
|
||||||
|
transition: all 100ms ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,12 +8,18 @@
|
|||||||
padding: 0.5em 1em;
|
padding: 0.5em 1em;
|
||||||
|
|
||||||
background-color: $secondaryColor;
|
background-color: $secondaryColor;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 1.35em;
|
font-size: 1.35em;
|
||||||
text-align: center;
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0.5em 0 0 0;
|
||||||
|
font-size: 1.15em;
|
||||||
|
color: #aaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
@@ -58,13 +64,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
|
||||||
height: 3px;
|
|
||||||
background-color: white;
|
|
||||||
outline: none;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 470px) {
|
@media only screen and (max-width: 470px) {
|
||||||
.tab_attributes {
|
.tab_attributes {
|
||||||
label {
|
label {
|
||||||
@@ -77,4 +76,3 @@ hr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,101 +1,88 @@
|
|||||||
export type Vehicle = ILocomotive | ICarWagon;
|
export type IVehicle = ILocomotive | ICarWagon;
|
||||||
export type StockSectionMode = 'STOCK_LIST' | 'STOCK_GENERATOR';
|
export type StockSectionMode = 'STOCK_LIST' | 'STOCK_GENERATOR';
|
||||||
|
|
||||||
export interface IStore {
|
export type TLocoGroup = 'loco-e' | 'loco-s' | 'loco-ezt' | 'loco-szt';
|
||||||
chosenCar: ICarWagon | null;
|
export type TCarWagonGroup = 'car-passenger' | 'car-cargo';
|
||||||
chosenLoco: ILocomotive | null;
|
|
||||||
chosenCargo: ICargo | null;
|
|
||||||
chosenVehicle: Vehicle | null;
|
|
||||||
|
|
||||||
isColdStart: boolean;
|
|
||||||
|
|
||||||
showSupporter: boolean;
|
|
||||||
imageLoading: boolean;
|
|
||||||
|
|
||||||
chosenLocoPower: string;
|
|
||||||
chosenCarUseType: string;
|
|
||||||
|
|
||||||
stockList: IStock[];
|
|
||||||
readyStockList: IReadyStockItem[];
|
|
||||||
cargoOptions: any[][];
|
|
||||||
|
|
||||||
chosenStockListIndex: number;
|
|
||||||
chosenRealStockName?: string;
|
|
||||||
|
|
||||||
swapVehicles: boolean;
|
|
||||||
vehiclePreviewSrc: string;
|
|
||||||
|
|
||||||
isRandomizerCardOpen: boolean;
|
|
||||||
isRealStockListCardOpen: boolean;
|
|
||||||
|
|
||||||
stockSectionMode: 'stock-list' | 'stock-generator' | 'number-generator' | 'wiki-list';
|
|
||||||
stockData?: IStockData;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TStockInfoKey = 'loco-e' | 'loco-s' | 'loco-ezt' | 'loco-szt' | 'car-passenger' | 'car-cargo';
|
|
||||||
|
|
||||||
export interface IStockProps {
|
export interface IStockProps {
|
||||||
type: string;
|
type: string;
|
||||||
length: number;
|
length: number;
|
||||||
mass: number;
|
// mass: number;
|
||||||
cargo: string;
|
weight: number;
|
||||||
|
// cargo?: string | null;
|
||||||
|
cargoTypes: ICargo[] | null;
|
||||||
|
coldStart?: boolean;
|
||||||
|
doubleManned?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IStockData {
|
export interface ICargo {
|
||||||
version: string;
|
id: string;
|
||||||
|
weight: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVehiclesAPI {
|
||||||
|
simulatorVersion: string;
|
||||||
|
|
||||||
generator: {
|
generator: {
|
||||||
passenger: any;
|
|
||||||
cargo: {
|
cargo: {
|
||||||
[key: string]: string[];
|
[key: string]: string[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
info: {
|
vehicleInfo: {
|
||||||
'car-cargo': [string, string, boolean, boolean, string][];
|
'car-cargo': [string, string, boolean, number | null, string][];
|
||||||
'car-passenger': [string, string, boolean, boolean, string][];
|
'car-passenger': [string, string, boolean, number | null, string][];
|
||||||
'loco-e': [string, string, string, string, boolean][];
|
'loco-e': [string, string, string, string, number | null][];
|
||||||
'loco-s': [string, string, string, string, boolean][];
|
'loco-s': [string, string, string, string, number | null][];
|
||||||
'loco-szt': [string, string, string, string, boolean][];
|
'loco-szt': [string, string, string, string, number | null][];
|
||||||
'loco-ezt': [string, string, string, string, boolean][];
|
'loco-ezt': [string, string, string, string, number | null][];
|
||||||
};
|
};
|
||||||
|
|
||||||
props: IStockProps[];
|
vehicleProps: IStockProps[];
|
||||||
|
|
||||||
usage: { [key: string]: string };
|
vehicleLocales: {
|
||||||
|
pl: {
|
||||||
|
cargo: Record<string, string>;
|
||||||
|
usage: Record<string, string>;
|
||||||
|
};
|
||||||
|
en: {
|
||||||
|
cargo: Record<string, string>;
|
||||||
|
usage: Record<string, string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
realCompositions: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILocomotive {
|
export interface ILocomotive {
|
||||||
type: string;
|
type: string;
|
||||||
power: string;
|
power: TLocoGroup;
|
||||||
|
group: TLocoGroup;
|
||||||
constructionType: string;
|
constructionType: string;
|
||||||
cabinType: string;
|
cabinType: string;
|
||||||
maxSpeed: number;
|
maxSpeed: number;
|
||||||
supportersOnly: boolean;
|
isSponsorsOnly: boolean;
|
||||||
|
sponsorsOnlyTimestamp: number;
|
||||||
imageSrc: string;
|
imageSrc: string;
|
||||||
|
weight: number;
|
||||||
mass: number;
|
|
||||||
length: number;
|
length: number;
|
||||||
|
coldStart: boolean;
|
||||||
|
doubleManned: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICarWagon {
|
export interface ICarWagon {
|
||||||
//"203V_PKPC_Fll_01","203V",true,false,"100",img
|
|
||||||
type: string;
|
type: string;
|
||||||
useType: 'car-passenger' | 'car-cargo';
|
useType: TCarWagonGroup;
|
||||||
|
group: TCarWagonGroup;
|
||||||
constructionType: string;
|
constructionType: string;
|
||||||
loadable: boolean;
|
loadable: boolean;
|
||||||
supportersOnly: boolean;
|
isSponsorsOnly: boolean;
|
||||||
|
sponsorsOnlyTimestamp: number;
|
||||||
maxSpeed: number;
|
maxSpeed: number;
|
||||||
imageSrc: string;
|
imageSrc: string;
|
||||||
|
weight: number;
|
||||||
mass: number;
|
|
||||||
length: number;
|
length: number;
|
||||||
cargoList: { id: string; totalMass: number }[];
|
cargoTypes: ICargo[];
|
||||||
}
|
|
||||||
|
|
||||||
export interface ICargo {
|
|
||||||
id: string;
|
|
||||||
totalMass: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IStock {
|
export interface IStock {
|
||||||
@@ -104,20 +91,21 @@ export interface IStock {
|
|||||||
useType: string;
|
useType: string;
|
||||||
constructionType: string;
|
constructionType: string;
|
||||||
length: number;
|
length: number;
|
||||||
mass: number;
|
// mass: number;
|
||||||
|
weight: number;
|
||||||
maxSpeed: number;
|
maxSpeed: number;
|
||||||
cargo?: { id: string; totalMass: number };
|
cargo?: ICargo;
|
||||||
isLoco: boolean;
|
isLoco: boolean;
|
||||||
supportersOnly: boolean;
|
isSponsorsOnly: boolean;
|
||||||
|
sponsorsOnlyTimestamp: number;
|
||||||
count: number;
|
count: number;
|
||||||
imgSrc?: string;
|
imgSrc?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IReadyStockItem {
|
export interface IRealComposition {
|
||||||
stockId: string;
|
stockId: string;
|
||||||
stockString: string;
|
stockString: string;
|
||||||
type: string;
|
type: string;
|
||||||
number: string;
|
number: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
const supportedConstructions = ['303e', '203e'];
|
|
||||||
|
|
||||||
export function locoSupportsColdStart(constructionType: string) {
|
|
||||||
return new RegExp(`(${supportedConstructions.join('|')})`).test(constructionType);
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import speedLimitTable from '../constants/speedLimits.json';
|
|
||||||
export type LocoType = keyof typeof speedLimitTable;
|
|
||||||
|
|
||||||
export const calculateSpeedLimit = (locoType: LocoType, stockMass: number, isTrainPassenger: boolean) => {
|
|
||||||
const speedTable = speedLimitTable[locoType][isTrainPassenger ? 'passenger' : 'cargo'];
|
|
||||||
|
|
||||||
if (!speedTable) return undefined;
|
|
||||||
|
|
||||||
let speedLimit = 0;
|
|
||||||
for (let mass in speedTable) if (stockMass > Number(mass)) speedLimit = (speedTable as any)[mass];
|
|
||||||
|
|
||||||
return speedLimit;
|
|
||||||
};
|
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import speedLimits from '../constants/speedLimits.json';
|
||||||
|
import massLimits from '../constants/massLimits.json';
|
||||||
|
|
||||||
|
export type SpeedLimitLocoType = keyof typeof speedLimits;
|
||||||
|
export type MassLimitLocoType = keyof typeof massLimits;
|
||||||
|
|
||||||
|
export function calculateSpeedLimit(
|
||||||
|
locoType: SpeedLimitLocoType,
|
||||||
|
stockTotalWeight: number,
|
||||||
|
stockCount: number,
|
||||||
|
isTrainPassenger: boolean
|
||||||
|
) {
|
||||||
|
if (speedLimits[locoType] === undefined) return 0;
|
||||||
|
|
||||||
|
if (stockCount == 1) return speedLimits[locoType]['none'];
|
||||||
|
|
||||||
|
const stockType = isTrainPassenger ? 'passenger' : 'cargo';
|
||||||
|
const speedTable = speedLimits[locoType][stockType];
|
||||||
|
|
||||||
|
if (!speedTable) return undefined;
|
||||||
|
|
||||||
|
let speedLimit = 0;
|
||||||
|
for (const mass in speedTable)
|
||||||
|
if (stockTotalWeight > Number(mass)) speedLimit = (speedTable as any)[mass];
|
||||||
|
|
||||||
|
return speedLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateMassLimit(locoType: MassLimitLocoType, isTrainPassenger: boolean) {
|
||||||
|
if (massLimits[locoType] === undefined) return 0;
|
||||||
|
|
||||||
|
return massLimits[locoType][isTrainPassenger ? 0 : 1] || 0;
|
||||||
|
}
|
||||||
@@ -1,38 +1,49 @@
|
|||||||
import { EVehicleUseType } from '../enums/EVehicleUseType';
|
import { EVehicleUseType } from '../enums/EVehicleUseType';
|
||||||
import { ICarWagon, ILocomotive, IStore, TStockInfoKey } from '../types';
|
import { ICarWagon, ILocomotive, IStock, IVehiclesAPI, TCarWagonGroup, TLocoGroup } from '../types';
|
||||||
import { LocoType, calculateSpeedLimit } from './speedLimitUtils';
|
import {
|
||||||
|
MassLimitLocoType,
|
||||||
|
SpeedLimitLocoType,
|
||||||
|
calculateMassLimit,
|
||||||
|
calculateSpeedLimit,
|
||||||
|
} from './vehicleLimitsUtils';
|
||||||
|
|
||||||
export function isLocomotive(vehicle: ILocomotive | ICarWagon): vehicle is ILocomotive {
|
export function isLocomotive(vehicle: ILocomotive | ICarWagon): vehicle is ILocomotive {
|
||||||
return (vehicle as ILocomotive).power !== undefined;
|
return (vehicle as ILocomotive).power !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function locoDataList(state: IStore) {
|
export function locoDataList(vehiclesData: IVehiclesAPI | undefined) {
|
||||||
if (!state.stockData) return [];
|
if (!vehiclesData) return [];
|
||||||
|
|
||||||
const stockData = state.stockData;
|
return Object.keys(vehiclesData.vehicleInfo).reduce((acc, vehiclePower) => {
|
||||||
|
|
||||||
return Object.keys(stockData.info).reduce((acc, vehiclePower) => {
|
|
||||||
if (!vehiclePower.startsWith('loco')) return acc;
|
if (!vehiclePower.startsWith('loco')) return acc;
|
||||||
|
|
||||||
const locoVehiclesData = stockData.info[vehiclePower as 'loco-e' | 'loco-s' | 'loco-ezt' | 'loco-szt'];
|
const locoVehiclesData = vehiclesData.vehicleInfo[vehiclePower as TLocoGroup];
|
||||||
|
|
||||||
locoVehiclesData.forEach((loco) => {
|
locoVehiclesData.forEach((loco) => {
|
||||||
if (state.showSupporter && !loco[4]) return;
|
// if (!loco[4]) return;
|
||||||
|
|
||||||
const [type, constructionType, cabinType, maxSpeed, supportersOnly] = loco;
|
const [type, constructionType, cabinType, maxSpeed, sponsorsTimestamp] = loco;
|
||||||
const locoProps = stockData.props.find((prop) => constructionType == prop.type);
|
const locoProps = vehiclesData.vehicleProps.find((prop) => constructionType == prop.type);
|
||||||
|
|
||||||
acc.push({
|
acc.push({
|
||||||
power: vehiclePower,
|
power: vehiclePower as TLocoGroup,
|
||||||
|
group: vehiclePower as TLocoGroup,
|
||||||
type,
|
type,
|
||||||
constructionType,
|
constructionType,
|
||||||
cabinType,
|
cabinType,
|
||||||
maxSpeed: Number(maxSpeed),
|
maxSpeed: Number(maxSpeed),
|
||||||
supportersOnly,
|
isSponsorsOnly: Number(sponsorsTimestamp) > Date.now(),
|
||||||
|
sponsorsOnlyTimestamp: Number(sponsorsTimestamp),
|
||||||
imageSrc: '',
|
imageSrc: '',
|
||||||
|
|
||||||
length: locoProps?.length && type.startsWith('2EN') ? locoProps.length * 2 : locoProps?.length || 0,
|
length:
|
||||||
mass: locoProps?.mass && type.startsWith('2EN') ? 253 : locoProps?.mass || 0,
|
locoProps?.length && type.startsWith('2EN')
|
||||||
|
? locoProps.length * 2
|
||||||
|
: locoProps?.length ?? 0,
|
||||||
|
weight: locoProps?.weight && type.startsWith('2EN') ? 253000 : locoProps?.weight ?? 0,
|
||||||
|
|
||||||
|
coldStart: locoProps?.coldStart ?? false,
|
||||||
|
doubleManned: locoProps?.doubleManned ?? false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -40,38 +51,36 @@ export function locoDataList(state: IStore) {
|
|||||||
}, [] as ILocomotive[]);
|
}, [] as ILocomotive[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function carDataList(state: IStore) {
|
export function carDataList(vehiclesData: IVehiclesAPI | undefined) {
|
||||||
if (!state.stockData) return [];
|
if (!vehiclesData) return [];
|
||||||
|
|
||||||
const stockData = state.stockData;
|
return Object.keys(vehiclesData.vehicleInfo).reduce((acc, vehicleUseType) => {
|
||||||
|
|
||||||
return Object.keys(stockData.info).reduce((acc, vehicleUseType) => {
|
|
||||||
if (!vehicleUseType.startsWith('car')) return acc;
|
if (!vehicleUseType.startsWith('car')) return acc;
|
||||||
|
|
||||||
const carVehiclesData = stockData.info[vehicleUseType as 'car-passenger' | 'car-cargo'];
|
const carVehiclesData = vehiclesData.vehicleInfo[vehicleUseType as TCarWagonGroup];
|
||||||
|
|
||||||
carVehiclesData.forEach((car) => {
|
carVehiclesData.forEach((car) => {
|
||||||
if (state.showSupporter && !car[3]) return;
|
const [type, constructionType, loadable, sponsorsOnlyTimestamp, maxSpeed] = car;
|
||||||
|
|
||||||
const carPropsData = stockData.props.find((v) => car[0].toString().startsWith(v.type));
|
if (sponsorsOnlyTimestamp && Number(sponsorsOnlyTimestamp) <= Date.now()) return;
|
||||||
|
|
||||||
|
const carPropsData = vehiclesData.vehicleProps.find((v) =>
|
||||||
|
type.toString().startsWith(v.type)
|
||||||
|
);
|
||||||
|
|
||||||
acc.push({
|
acc.push({
|
||||||
useType: vehicleUseType as 'car-passenger' | 'car-cargo',
|
useType: vehicleUseType as TCarWagonGroup,
|
||||||
type: car[0],
|
group: vehicleUseType as TCarWagonGroup,
|
||||||
constructionType: car[1],
|
type,
|
||||||
loadable: car[2],
|
constructionType,
|
||||||
supportersOnly: car[3],
|
loadable,
|
||||||
maxSpeed: Number(car[4]),
|
isSponsorsOnly: Number(sponsorsOnlyTimestamp) > Date.now(),
|
||||||
|
sponsorsOnlyTimestamp: Number(sponsorsOnlyTimestamp),
|
||||||
|
maxSpeed: Number(maxSpeed),
|
||||||
imageSrc: '',
|
imageSrc: '',
|
||||||
cargoList:
|
cargoTypes: carPropsData?.cargoTypes ?? [],
|
||||||
!carPropsData || carPropsData.cargo === null
|
|
||||||
? []
|
|
||||||
: carPropsData.cargo.split(';').map((cargo) => ({
|
|
||||||
id: cargo.split(':')[0],
|
|
||||||
totalMass: Number(cargo.split(':')[1]),
|
|
||||||
})),
|
|
||||||
|
|
||||||
mass: carPropsData?.mass || 0,
|
weight: carPropsData?.weight || 0,
|
||||||
length: carPropsData?.length || 0,
|
length: carPropsData?.length || 0,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -80,23 +89,23 @@ export function carDataList(state: IStore) {
|
|||||||
}, [] as ICarWagon[]);
|
}, [] as ICarWagon[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function totalMass(state: IStore) {
|
export function totalWeight(stockList: IStock[]) {
|
||||||
return ~~state.stockList.reduce(
|
return stockList.reduce(
|
||||||
(acc, stock) => acc + (stock.cargo ? stock.cargo.totalMass : stock.mass) * stock.count,
|
(acc, stock) => acc + (stock.weight + (stock.cargo?.weight ?? 0)) * stock.count,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function totalLength(state: IStore) {
|
export function totalLength(stockList: IStock[]) {
|
||||||
return state.stockList.reduce((acc, stock) => acc + stock.length * stock.count, 0);
|
return stockList.reduce((acc, stock) => acc + stock.length * stock.count, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function maxStockSpeed(state: IStore) {
|
export function maxStockSpeed(stockList: IStock[]) {
|
||||||
const stockSpeedLimit = state.stockList.reduce(
|
const stockSpeedLimit = stockList.reduce(
|
||||||
(acc, stock) => (stock.maxSpeed < acc || acc == 0 ? stock.maxSpeed : acc),
|
(acc, stock) => (stock.maxSpeed < acc || acc == 0 ? stock.maxSpeed : acc),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
const headingLoco = state.stockList[0]?.isLoco ? state.stockList[0] : undefined;
|
const headingLoco = stockList[0]?.isLoco ? stockList[0] : undefined;
|
||||||
|
|
||||||
if (!headingLoco) return stockSpeedLimit;
|
if (!headingLoco) return stockSpeedLimit;
|
||||||
|
|
||||||
@@ -104,58 +113,34 @@ export function maxStockSpeed(state: IStore) {
|
|||||||
|
|
||||||
if (/^(EN|2EN|SN)/.test(locoType)) return stockSpeedLimit;
|
if (/^(EN|2EN|SN)/.test(locoType)) return stockSpeedLimit;
|
||||||
|
|
||||||
const stockMass = totalMass(state);
|
const speedLimitByMass = calculateSpeedLimit(
|
||||||
|
locoType as SpeedLimitLocoType,
|
||||||
const speedLimitByMass = calculateSpeedLimit(locoType as LocoType, stockMass, isTrainPassenger(state));
|
totalWeight(stockList),
|
||||||
|
stockList.length,
|
||||||
|
isTrainPassenger(stockList)
|
||||||
|
);
|
||||||
|
|
||||||
return speedLimitByMass ? Math.min(stockSpeedLimit, speedLimitByMass) : stockSpeedLimit;
|
return speedLimitByMass ? Math.min(stockSpeedLimit, speedLimitByMass) : stockSpeedLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function acceptableMass(state: IStore) {
|
export function acceptableWeight(stockList: IStock[]) {
|
||||||
if (state.stockList.length == 0 || !state.stockList[0].isLoco) return 0;
|
if (stockList.length == 0 || !stockList[0].isLoco) return 0;
|
||||||
const activeLocomotiveType = state.stockList[0].type;
|
|
||||||
|
|
||||||
if (/^SM/.test(activeLocomotiveType)) return 2400;
|
const activeLocomotiveType = stockList[0].type.split('-')[0];
|
||||||
|
|
||||||
// Elektryczne EU07 / EP07 / EP08 / ET41
|
const locoMassLimit = calculateMassLimit(
|
||||||
|
activeLocomotiveType as MassLimitLocoType,
|
||||||
|
isTrainPassenger(stockList)
|
||||||
|
);
|
||||||
|
|
||||||
// Pasażerski elektr.
|
return locoMassLimit;
|
||||||
if (isTrainPassenger(state)) {
|
|
||||||
if (/^(EU|EP)/.test(activeLocomotiveType)) return 650;
|
|
||||||
if (/^ET/.test(activeLocomotiveType)) return 700;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Towarowy / inny elektr.
|
|
||||||
if (/^EU/.test(activeLocomotiveType)) return 2000;
|
|
||||||
if (/^ET/.test(activeLocomotiveType)) return 4000;
|
|
||||||
if (/^EP/.test(activeLocomotiveType)) return 650;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isTrainPassenger(state: IStore) {
|
export function isTrainPassenger(stockList: IStock[]) {
|
||||||
if (state.stockList.length == 0) return false;
|
if (stockList.length == 0) return false;
|
||||||
if (state.stockList.every((stock) => stock.isLoco)) return false;
|
if (stockList.every((stock) => stock.isLoco)) return false;
|
||||||
|
|
||||||
return state.stockList
|
return stockList
|
||||||
.filter((stock) => !stock.isLoco)
|
.filter((stock) => !stock.isLoco)
|
||||||
.every((stock) => stock.useType === EVehicleUseType.CAR_PASSENGER);
|
.every((stock) => stock.useType === EVehicleUseType.CAR_PASSENGER);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function chosenRealStock(state: IStore) {
|
|
||||||
const currentStockString = state.stockList
|
|
||||||
.reduce((acc, stock) => {
|
|
||||||
for (let i = 0; i < stock.count; i++) acc.push(stock.type);
|
|
||||||
return acc;
|
|
||||||
}, [] as string[])
|
|
||||||
.join(';');
|
|
||||||
|
|
||||||
const realStockObj = state.readyStockList.find((readyStock) => readyStock.stockString == currentStockString);
|
|
||||||
|
|
||||||
state.chosenRealStockName = realStockObj?.stockId ?? undefined;
|
|
||||||
|
|
||||||
return realStockObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,37 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<MainContainer />
|
<MainContainer />
|
||||||
<Footer />
|
<FooterVue />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { useStore } from '../store';
|
import { useStore } from '../store';
|
||||||
|
|
||||||
import MainContainer from '../components/app/MainContainer.vue';
|
import MainContainer from '../components/app/MainContainer.vue';
|
||||||
import Footer from '../components/app/Footer.vue';
|
import FooterVue from '../components/app/Footer.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
MainContainer,
|
MainContainer,
|
||||||
Footer,
|
FooterVue,
|
||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
store: useStore(),
|
store: useStore(),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.app-container {
|
.app-container {
|
||||||
min-height: 100vh;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
display: flex;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
|
||||||
}
|
min-height: 100vh;
|
||||||
</style>
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
declare module '*.vue' {
|
declare module '*.vue' {
|
||||||
import type { DefineComponent } from 'vue'
|
import type { DefineComponent } from 'vue';
|
||||||
const component: DefineComponent<{}, {}, any>
|
const component: DefineComponent<{}, {}, any>;
|
||||||
export default component
|
export default component;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,13 @@ export default defineConfig({
|
|||||||
VitePWA({
|
VitePWA({
|
||||||
registerType: 'autoUpdate',
|
registerType: 'autoUpdate',
|
||||||
|
|
||||||
|
devOptions: {
|
||||||
|
suppressWarnings: true,
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
|
||||||
workbox: {
|
workbox: {
|
||||||
// globPatterns: ['**/*.{js,css,html,png,svg,img}'],
|
globPatterns: ['**/*.{js,css,html,jpg,png,svg,img,woff,woff2}'],
|
||||||
|
|
||||||
runtimeCaching: [
|
runtimeCaching: [
|
||||||
{
|
{
|
||||||
@@ -24,10 +29,24 @@ export default defineConfig({
|
|||||||
cacheName: 'swdr-images-cache',
|
cacheName: 'swdr-images-cache',
|
||||||
expiration: {
|
expiration: {
|
||||||
maxEntries: 50,
|
maxEntries: 50,
|
||||||
maxAgeSeconds: 60 * 60 * 24 * 7, // <== 7 days
|
maxAgeSeconds: 60 * 60 * 24, // <== 1 day
|
||||||
},
|
},
|
||||||
cacheableResponse: {
|
cacheableResponse: {
|
||||||
statuses: [404],
|
statuses: [0, 200, 404],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
urlPattern: /^https:\/\/static.spythere.eu\/.*/i,
|
||||||
|
handler: 'CacheFirst',
|
||||||
|
options: {
|
||||||
|
cacheName: 'spythere-api-cache',
|
||||||
|
expiration: {
|
||||||
|
maxEntries: 100,
|
||||||
|
maxAgeSeconds: 60 * 60 * 24, // <== 1 day
|
||||||
|
},
|
||||||
|
cacheableResponse: {
|
||||||
|
statuses: [200, 302],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -36,5 +55,3 @@ export default defineConfig({
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||