Aktualizacja 1.1.0

Aktualizacja aplikacji do wersji 1.1.0
This commit is contained in:
Spythere
2022-08-21 15:18:46 +02:00
committed by GitHub
84 changed files with 3564 additions and 31544 deletions
+20
View File
@@ -0,0 +1,20 @@
favicon-16.png,1655763491752,9466d68dd3e7264743d111b4d9daa9b9ebf5a1874864af6697959c213ed45612
favicon-32.png,1655763491751,34aef805af25dfe0e087b5f1a01045a47defa12d011844e4d0548a52739b8f0b
favicon-48.png,1655763491751,b21b8239ae384859534ac832de34814d7b10aad05042782bcd5d75975d56bd79
favicon-64.png,1655763491752,04d94f07b4cd96c15c208f7c68d5baaefe0bca4a2a1151d5de418bf2d538c143
favicon.ico,1655763491752,e9ee44bf0b3ce07e36c98c560c9a87c22c38e1460488ca5bd94c70916b2cd887
index.html,1655763491742,6cc46c025eedf62c31579560aaf8f9fadcd54fa9d450160b922b14672d82445e
img/add-icon.a0f313fe.svg,1655763491750,be43aa8b694c90777971ad2d7f611f52c7ee172131fe2e24a82348891bb6aa18
img/higher-icon.68df973e.svg,1655763491750,258bcb0fc4c62a0ecd53681bfa4777451e0cb4b980e1094f68d89d4077fa657a
img/IC.43b58201.svg,1655763491750,c814276f77745dc05af02c813afdc4bfec5a2d4b142934f9f883ee8c9622c73f
css/app.a8467909.css,1655763491744,b2567c88ddb261fb8962cf0053f187c1ce2019795256de4fa5e2c720a1b73335
img/lower-icon.7c306380.svg,1655763491750,286e1759ea8d4c11327f425f6d6af41603b329dab36c419b228acbea217c2080
img/remove-icon.09c0dbc9.svg,1655763491750,1e01bccdf38cee755fc54c9768b4089fdb11d904a6b9aa86fcb20a2357dc8d61
img/sub-icon.bfa10f67.svg,1655763491750,afa120d2f8afe18161a6ed011eb0482ef9604fd35c1d3be8cd93b1087b1138eb
img/swap-icon.c8ef8416.svg,1655763491742,b1c1d418526746e20b13076cc97cd4aa1b8f87956d38dfa71f3fb17d1df5c236
img/randomize-icon.07a5b5e4.svg,1655763491750,4bda1cbd316c62073c0f5d97b5be7796b3e061d2e877a91025045957c33a9cd5
img/logo.7829bbf4.svg,1655763491750,bbe24d40385986ee767f877b14819b0a931b5728e31586e5ea67f10e944ad285
js/app.6494b8c9.js,1655763491744,58778452c387fc6b8723abd52a21f02faf41b55c07e5063793e40360fa1e081f
js/chunk-vendors.1b7ed749.js,1655763491750,e1a2040adc8697ef77b80c78587eac75347761d0c7abada1cecc335cb633fa9e
js/app.6494b8c9.js.map,1655763491750,9431fbec12e405b1fadf565d909aa30570d5fab015521d44e06239fc0b29d567
js/chunk-vendors.1b7ed749.js.map,1655763491751,3a350cdb593cd206de7722ff286df2674324651ae567641b6667c405b1a39e4c
+2
View File
@@ -0,0 +1,2 @@
favicon.ico,1636572668861,665ba81c9556ba00abb1a0fa575fe801b3c35f23481b17528a211b8c3021f7cd
index.html,1636208571087,df7f13c7144f26437556415ade15cdd1d8bcb0d0dcf275436c40c3bd2df8a586
@@ -0,0 +1,20 @@
# This file was auto-generated by the Firebase CLI
# https://github.com/firebase/firebase-tools
name: Deploy to Firebase Hosting on merge
'on':
push:
branches:
- main
jobs:
build_and_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm ci && npm run build
- uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: '${{ secrets.GITHUB_TOKEN }}'
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_POJAZDOWNIK_TD2 }}'
channelId: live
projectId: pojazdownik-td2
@@ -0,0 +1,17 @@
# This file was auto-generated by the Firebase CLI
# https://github.com/firebase/firebase-tools
name: Deploy to Firebase Hosting on PR
'on': pull_request
jobs:
build_and_preview:
if: '${{ github.event.pull_request.head.repo.full_name == github.repository }}'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm ci && npm run build
- uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: '${{ secrets.GITHUB_TOKEN }}'
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_POJAZDOWNIK_TD2 }}'
projectId: pojazdownik-td2
+1 -7
View File
@@ -21,10 +21,4 @@ pnpm-debug.log*
*.njsproj
*.sln
*.sw?
node_modules
firebase/
.github
firebase.json
.firebase
node_modules
Binary file not shown.
+1 -23
View File
@@ -1,24 +1,2 @@
# pojazdownik
# Pojazdownik
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
+12
View File
@@ -0,0 +1,12 @@
{
"hosting": {
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}
+40
View File
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>Pojazdownik</title>
<meta name="description" content="Edytor pociągów online do symulatora Train Driver 2" />
<meta name="keywords" content="pojazdownik td2 train driver stacjownik spythere">
<link rel="apple-touch-icon" sizes="57x57" href="/apple-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="/apple-icon-60x60.png" />
<link rel="apple-touch-icon" sizes="72x72" href="/apple-icon-72x72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="/apple-icon-76x76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="/apple-icon-114x114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="/apple-icon-120x120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="/apple-icon-144x144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/apple-icon-152x152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png" />
<link rel="icon" type="image/png" sizes="192x192" href="/android-icon-192x192.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/manifest.json" />
<meta name="msapplication-TileColor" content="#ffffff" />
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png" />
<meta name="theme-color" content="#ffffff" />
</head>
<body>
<noscript>
<strong>
We're sorry but Pojazdownik doesn't work properly without JavaScript enabled. Please enable it to continue.
</strong>
</noscript>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
+543 -29477
View File
File diff suppressed because it is too large Load Diff
+10 -22
View File
@@ -1,33 +1,21 @@
{
"name": "pojazdownik",
"version": "1.0.10",
"version": "1.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"deploy": "npm run build && firebase deploy --only hosting"
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview"
},
"dependencies": {
"core-js": "^3.6.5",
"vue": "^3.0.0",
"vue-class-component": "^8.0.0-0"
"pinia": "^2.0.17",
"vue": "^3.2.37"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-typescript": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.0.0",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/eslint-config-typescript": "^7.0.0",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-vue": "^7.0.0",
"prettier": "^2.2.1",
"@vitejs/plugin-vue": "^3.0.0",
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
"typescript": "~4.1.5"
"typescript": "^4.6.4",
"vite": "^3.0.0",
"vue-tsc": "^0.38.4"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

+2
View File
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 B

After

Width:  |  Height:  |  Size: 1.1 KiB

+4
View File
@@ -0,0 +1,4 @@
<svg width="805" height="340" viewBox="0 0 805 340" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M421.173 339.641H313.5H56.2114C30.2949 339.641 13.3674 312.455 24.8028 289.198L157.384 19.5565C163.271 7.58439 175.452 0 188.793 0H462.447H560.788L534.229 63.1224L438.041 61.3291H241.375C237.487 61.3291 233.951 63.5835 232.309 67.1089L203.745 128.473C200.659 135.102 205.498 142.693 212.81 142.693H323.318C330.653 142.693 335.492 150.327 332.362 156.96L316.205 191.202C314.554 194.702 311.031 196.935 307.161 196.935H184.626C176.848 196.935 169.776 201.444 166.494 208.495L140.62 264.079C137.534 270.708 142.373 278.299 149.685 278.299H339.094L410.764 106.519L518.437 109.747L421.173 339.641Z" fill="#AAAAAA"/>
<path d="M667 340H499.408C464.627 336.329 465.197 305.129 472.515 288.244C503.113 217.645 567.678 68.5186 581.16 36.8041C594.642 5.08971 621.677 -0.881214 633.51 0.0976257C672.713 0.464691 757.214 0.978582 781.596 0.0976257C805.978 -0.783331 806.098 21.7545 803.11 33.1335L789.843 65.0682C778.728 65.3129 747.174 65.6555 709.884 65.0682C672.593 64.4809 654.904 93.2098 650.721 107.648C640.323 131.385 615.295 188.475 598.371 226.944C581.447 265.412 609.008 277.477 624.904 278.7C636.617 279.312 670.442 280.535 712.035 280.535C708.019 323.409 680.506 339.266 667 340Z" fill="#AAAAAA"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg width="492" height="340" viewBox="0 0 492 340" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 339.641L97.1941 106.519L204.789 109.747L107.595 339.641H0Z" fill="#AAAAAA"/>
<path d="M124.451 61.3291L148.84 0H247.11L220.57 63.1224L124.451 61.3291Z" fill="#AAAAAA"/>
<path d="M358 340H186.772C152.017 336.329 152.587 305.129 159.9 288.244C190.475 217.645 254.993 68.5186 268.466 36.8041C281.938 5.08971 308.954 -0.881214 320.778 0.0976257C359.952 0.464691 444.393 0.978582 468.757 0.0976257C493.122 -0.783331 493.241 21.7545 490.255 33.1335L476.998 65.0682C465.891 65.3129 434.36 65.6555 397.097 65.0682C359.833 64.4809 342.157 93.2098 337.976 107.648C327.586 131.385 302.576 188.475 285.664 226.944C268.752 265.412 296.294 277.477 312.179 278.7C323.883 279.312 357.683 280.535 399.246 280.535C395.233 323.409 371.496 339.266 358 340Z" fill="#AAAAAA"/>
</svg>

After

Width:  |  Height:  |  Size: 874 B

+4
View File
@@ -0,0 +1,4 @@
<svg width="573" height="345" viewBox="0 0 573 345" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M459.284 100.593C481.1 100.593 499.759 104.755 515.26 113.08C531.049 121.405 543.249 133.461 551.86 149.249C560.472 165.038 564.778 184.414 564.778 207.379V238.381H413.211C413.785 256.466 419.096 270.675 429.143 281.009C439.477 291.344 453.686 296.511 471.771 296.511C486.985 296.511 500.764 295.075 513.107 292.205C525.451 289.047 538.225 284.311 551.43 277.995V327.513C539.947 333.254 527.747 337.416 514.83 340C502.199 342.871 486.842 344.306 468.757 344.306C445.218 344.306 424.406 340 406.322 331.388C388.237 322.489 374.027 309.141 363.693 291.344C353.359 273.546 348.192 251.155 348.192 224.172C348.192 196.614 352.785 173.793 361.971 155.708C371.444 137.336 384.505 123.558 401.155 114.372C417.804 105.186 437.18 100.593 459.284 100.593ZM459.715 146.235C447.371 146.235 437.037 150.254 428.712 158.292C420.675 166.329 415.938 178.816 414.503 195.753H504.496C504.496 186.28 502.773 177.812 499.329 170.348C496.171 162.885 491.291 157 484.689 152.694C478.086 148.388 469.762 146.235 459.715 146.235Z" fill="#AAAAAA"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 340H106.328V220.784C140.159 215.951 207.822 219.657 207.822 273.143V340H323.816V273.143C321.399 248.172 306.739 196.297 267.43 188.564C296.428 162.143 307.437 142.113 309.316 135.4C320.862 92.9765 319.466 7.00155 221.516 2.49068C123.565 -2.02018 33.026 0.611155 0 2.49068V340ZM106.328 74.9867V146.677H191.712C220.71 146.677 222.321 74.9867 191.712 74.9867H106.328Z" fill="#AAAAAA"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+6
View File
@@ -0,0 +1,6 @@
<svg width="865" height="340" viewBox="0 0 865 340" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M85.5517 57.7359H12.3739C5.53999 57.7359 0 52.1959 0 45.362V13.6569C0 6.82301 5.53999 1.28302 12.3739 1.28302H280.154C286.988 1.28302 292.528 6.823 292.528 13.6569V45.3619C292.528 52.1959 286.988 57.7359 280.154 57.7359H185.7C179.905 57.7359 174.887 61.7567 173.623 67.4115L114.879 330.324C113.616 335.979 108.597 340 102.803 340H59.3706C51.5637 340 45.7076 332.859 47.2362 325.203L97.6861 72.5326C99.2147 64.8768 93.3586 57.7359 85.5517 57.7359Z" fill="#AAAAAA"/>
<path d="M274.691 226.346L318.335 11.8875C319.743 4.97005 325.827 0 332.886 0H371.034C380.69 0 387.831 9.04464 385.839 18.4924C373.813 75.5102 358.843 176.491 349.894 214.838C337.956 265.984 363.82 286.442 386.103 286.442H470.132C478.333 286.442 484.981 293.09 484.981 301.29V324.04C484.981 331.714 479.143 338.022 471.472 338.258C452.439 338.841 415.114 339.102 354.072 337.587C271.627 335.542 266.799 262.574 274.691 226.346Z" fill="#AAAAAA"/>
<path d="M548.561 340H522.451C509.799 340 500.393 328.294 503.117 315.938L568.801 18.0724C570.802 8.99892 578.843 2.53754 588.135 2.53754H613.112C625.736 2.53754 635.135 14.1921 632.461 26.5289L567.91 324.395C565.937 333.502 557.88 340 548.561 340Z" fill="#AAAAAA"/>
<path d="M834.221 1.28302H792.978C789.649 1.28302 786.482 2.60485 784.148 4.9786C754.312 35.3241 693.247 95.7214 660.754 121.716C622.263 152.509 624.83 166.878 629.961 183.187C630.78 185.788 682.732 278.003 711.131 329.819C713.308 333.79 717.458 336.237 721.986 336.237H771.22C780.801 336.237 786.759 325.837 781.881 317.59C757.605 276.543 720.93 214.63 709.508 195.732C696.492 174.193 697.882 167.219 706.296 155.244C706.722 154.638 707.21 154.074 707.741 153.558L842.836 22.5396C850.815 14.801 845.337 1.28302 834.221 1.28302Z" fill="#AAAAAA"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Before

Width:  |  Height:  |  Size: 258 B

After

Width:  |  Height:  |  Size: 258 B

Before

Width:  |  Height:  |  Size: 165 B

After

Width:  |  Height:  |  Size: 165 B

+5
View File
@@ -0,0 +1,5 @@
<svg width="440" height="340" viewBox="0 0 440 340" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 339.926V119.216H62.8299V339.926H0Z" fill="#AAAAAA"/>
<rect y="27.3874" width="62.8299" height="62.8299" fill="#AAAAAA"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M115.994 340H222.321V220.784C256.153 215.951 323.816 219.657 323.816 273.143V340H439.809V273.143C437.393 248.172 422.732 196.297 383.423 188.564C412.422 162.143 423.431 142.113 425.31 135.4C436.856 92.9765 435.46 7.00155 337.509 2.49068C239.559 -2.02018 149.02 0.611155 115.994 2.49068V340ZM222.321 74.9867V146.677H307.705C336.704 146.677 338.315 74.9867 307.705 74.9867H222.321Z" fill="#AAAAAA"/>
</svg>

After

Width:  |  Height:  |  Size: 687 B

Before

Width:  |  Height:  |  Size: 171 B

After

Width:  |  Height:  |  Size: 171 B

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Before

Width:  |  Height:  |  Size: 236 B

After

Width:  |  Height:  |  Size: 236 B

Before

Width:  |  Height:  |  Size: 155 B

After

Width:  |  Height:  |  Size: 155 B

Before

Width:  |  Height:  |  Size: 515 B

After

Width:  |  Height:  |  Size: 515 B

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

-25
View File
@@ -1,25 +0,0 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="favicon.ico" />
<link rel="icon" href="favicon-16.png" sizes="16x16" type="image/png" />
<link rel="icon" href="favicon-32.png" sizes="32x32" type="image/png" />
<link rel="icon" href="favicon-48.png" sizes="48x48" type="image/png" />
<link rel="icon" href="favicon-62.png" sizes="62x62" type="image/png" />
<title>Pojazdownik</title>
</head>
<body>
<noscript>
<strong
>We're sorry but Pojazdownik doesn't work properly without JavaScript enabled. Please enable it to
continue.</strong
>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
+41
View File
@@ -0,0 +1,41 @@
{
"name": "App",
"icons": [
{
"src": "\/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "\/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "\/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "\/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "\/android-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "\/android-icon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

+94 -58
View File
@@ -1,89 +1,108 @@
<template>
<header>
<img :src="logoSVG" alt="logo pojazdownik" />
</header>
<div class="image-preview" v-if="store.vehiclePreviewSrc != ''" @click="() => (store.vehiclePreviewSrc = '')">
<img :src="store.vehiclePreviewSrc" alt="preview" />
</div>
<main>
<div class="image-preview" v-if="store.vehiclePreviewSrc != ''" @click="() => (store.vehiclePreviewSrc = '')">
<img :src="store.vehiclePreviewSrc" alt="preview" />
</div>
<div class="g-card-dimmer" v-if="store.isRandomizerCardOpen" @click="store.isRandomizerCardOpen = false"></div>
<div class="g-card-dimmer" v-if="store.isRealStockListCardOpen" @click="store.isRealStockListCardOpen = false"></div>
<inputs-section />
<list-section />
</main>
<footer>
<div class="text--grayed" style="margin-bottom: 0.25em">
Ta strona ma charakter informacyjny. Autor nie ponosi odpowiedzialności za tworzenie pociągów niezgodnych z
regulaminem symulatora Train Driver 2!
</div>
&copy;
<a href="https://td2.info.pl/profile/?u=20777" target="_blank">Spythere</a>
{{ new Date().getUTCFullYear() }} | v{{ VERSION }}
</footer>
<keep-alive>
<transition name="card-appear">
<RandomizerCard />
</transition>
</keep-alive>
<keep-alive>
<transition name="card-appear">
<RealStockCard />
</transition>
</keep-alive>
<div class="app_container">
<main>
<LogoSection />
<InputsSection />
<TrainImageSection />
<ListSection />
</main>
<footer>
<div class="text--grayed" style="margin-bottom: 0.25em">
Ta strona ma charakter informacyjny. Autor nie ponosi odpowiedzialności za tworzenie pociągów niezgodnych z
<a
style="color: #ccc"
href="https://docs.google.com/document/d/1UAAPUtN0d_RoS4RgOzEzllJZJhA0VcizzCzKW4QylbY/edit"
target="_blank"
>
regulaminem symulatora Train Driver 2</a
>!
</div>
&copy;
<a href="https://td2.info.pl/profile/?u=20777" target="_blank">Spythere</a>
{{ new Date().getUTCFullYear() }} | v{{ VERSION }}
</footer>
</div>
</template>
<script lang="ts">
import packageInfo from '.././package.json';
import { defineComponent, inject } from 'vue';
import { defineComponent } from 'vue';
import ListSection from '@/components/ListSection.vue';
import InputsSection from '@/components/InputsSection.vue';
import { IStore } from './types';
import InputsSection from './components/InputsSection.vue';
import ListSection from './components/ListSection.vue';
import { useStore } from './store';
import TrainImageSection from './components/TrainImageSection.vue';
import LogoSection from './components/LogoSection.vue';
import RandomizerCard from './components/cards/RandomizerCard.vue';
import RealStockCard from './components/cards/RealStockCard.vue';
export default defineComponent({
components: {
ListSection,
InputsSection,
TrainImageSection,
RandomizerCard,
LogoSection,
RealStockCard,
},
data: () => ({
VERSION: packageInfo.version,
logoSVG: require('@/assets/logo.svg'),
}),
setup() {
const store = inject('Store') as IStore;
// const readyStockJSON = await (await fetch('https://spythere.github.io/api/readyStock.json')).json();
const store = useStore();
return {
store,
};
},
mounted() {
window.addEventListener('keydown', (ev) => {
if (this.store.vehiclePreviewSrc == '') return;
if (ev.key.toLowerCase() == 'escape') this.store.vehiclePreviewSrc = '';
// if(ev.key.toLowerCase() == 'enter')
});
// window.focus();
},
});
</script>
<style lang="scss">
@import './styles/global';
.app_container {
min-height: 100vh;
display: flex;
flex-direction: column;
padding: 1em;
}
/* APP */
#app {
margin: 0 auto;
color: $textColor;
min-height: 100vh;
padding: 0.5em 1em;
overflow: hidden;
display: grid;
display: flex;
justify-content: center;
grid-template-columns: minmax(200px, 1200px);
grid-template-rows: 5.5em 1fr auto;
}
/* HEADER SECTION */
@@ -91,12 +110,11 @@ export default defineComponent({
header {
text-align: center;
margin-top: 1em;
img {
width: 35em;
}
}
h2 {
margin: 0;
margin-bottom: 0.5em;
@@ -137,21 +155,38 @@ h2 {
/* MAIN SECTION */
main {
margin-top: 8em;
display: grid;
gap: 1em 3em;
width: 100vw;
max-width: 1300px;
min-height: 75vh;
grid-template-columns: 1fr 2fr;
grid-template-rows: auto 360px minmax(400px, 1fr);
padding: 0.5em;
}
/* FOOTER SECTION */
footer {
margin-top: 1.5em;
margin-top: auto;
text-align: center;
}
@media screen and (max-width: 800px) {
#app {
font-size: calc(0.75vw + 0.6rem);
/* MOBILE VIEWS */
@media screen and (max-width: $breakpointMd) {
main {
display: flex;
flex-direction: column;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
main {
margin-top: 3.5em;
#app {
font-size: calc(0.75vw + 0.7rem);
}
header {
@@ -163,7 +198,7 @@ footer {
}
}
@media screen and (max-width: 650px) {
@media screen and (max-width: $breakpointSm) {
header {
font-size: 0.75em;
@@ -173,3 +208,4 @@ footer {
}
}
</style>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 640 B

-28
View File
@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.0"
width="791.32538"
height="340"
id="svg2">
<defs
id="defs4" />
<g
transform="translate(-6.31e-4,-712.36218)"
id="layer1">
<g
transform="translate(-35.785713,150.35711)"
id="g2239">
<g
transform="translate(0,-50)"
id="g2288">
<path
d="M 242.05316,862.5559 L 409.15371,612.00507 L 827.1117,611.95328 L 769.48955,698.50507 L 497.34525,699.01219 L 391.64564,861.99747 L 657.77165,862.08805 L 599.72093,952.00507 L 329.35483,952.00507 L 242.05316,862.5559 z M 35.786344,861.91508 L 204.55316,612.08342 L 346.18935,612.00507 L 123.4711,951.99662 L 35.786344,861.91508 z "
style="fill:rgb(170,170,170)"
id="path2290" />
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 972 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

+230 -363
View File
@@ -1,353 +1,281 @@
<template>
<section class="inputs">
<div class="input inputs_loco">
<div class="input_container">
<h2 class="input_header">LOKOMOTYWA / ZESP. TRAKCYJNY</h2>
<div class="input_radio">
<section class="inputs-section">
<div class="input_container">
<h2 class="input_header">WYBIERZ POJAZDY / WAGONY</h2>
<div class="input_list type">
<div class="vehicle-types locos">
<button
v-for="label in locoLabels"
:key="label.id"
@click="onLocoPowerChange(label.id)"
:class="{ checked: store.chosenLocoPower == label.id }"
data-ignore-outside="1"
v-for="locoType in locomotiveTypeList"
class="btn--choice"
:data-selected="locoType.id == store.chosenLocoPower"
@click="selectLocoType(locoType.id)"
>
{{ label.title }}
{{ locoType.value }}
</button>
</div>
<div class="input_list type">
<select
id="loco-select"
ref="loco-select"
v-model="store.chosenLoco"
@change="onLocoTypeChange"
data-select="loco"
data-ignore-outside="1"
>
<option :value="null" disabled>Wybierz pojazd z listy</option>
<option v-for="loco in locoOptions" :value="loco" :key="loco.type">
{{ loco.supportersOnly ? '*W*' : '' }}
{{ loco.type }}
</option>
</select>
<button class="btn--add" @click="addVehicle" title="Dodaj pojazd">
<img :src="icons.add" alt="add vehicle" data-ignore-outside="1" />
</button>
<!-- <button class="btn--swap" @click="prepareSwapVehicles" title="Zamień pojazdy">
<img :src="icons.swap" alt="swap vehicle" />
</button> -->
</div>
<div class="input_ready-stock">
<button class="btn" @click="setReadyStockList(true)"><b>REALNE ZESTAWIENIA</b></button>
<ready-stock-list />
</div>
<div class="input_checkbox">
<!-- <button @click="onShowSupporterChange" :class="{ checked: this.store.showSupporter }" data-ignore-outside="1">
Pokaż tylko pojazdy dla weteranów
</button> -->
</div>
<select
id="locomotives-list"
v-model="store.chosenLoco"
@focus="onVehicleSelect('loco')"
@change="onVehicleSelect('loco')"
@keydown.enter.prevent="addOrSwitchVehicle"
@keydown.backspace="removeVehicle"
>
<option :value="null" disabled>Wybierz pojazd trakcyjny</option>
<option v-for="loco in locoOptions" :value="loco" :key="loco.type">
{{ loco.type }}
</option>
</select>
</div>
</div>
<div class="spacer"></div>
<div class="input inputs_car">
<div class="input_container">
<h2 class="input_header">RODZAJ WAGONU</h2>
<div class="input_radio">
<div class="input_list type">
<div class="vehicle-types carwagons">
<button
v-for="label in carLabels"
:key="label.id"
@click="onCarUseTypeChange(label.id)"
:class="{ checked: store.chosenCarUseType == label.id }"
data-ignore-outside="1"
v-for="carType in carTypeList"
class="btn--choice"
:data-selected="carType.id == store.chosenCarUseType"
@click="selectCarWagonType(carType.id)"
>
{{ label.title }}
{{ carType.value }}
</button>
</div>
<div class="input_list type">
<select
id="car-select"
ref="car-select"
v-model="store.chosenCar"
@change="onCarTypeChange"
data-select="car"
data-ignore-outside="1"
>
<option :value="null" disabled>Wybierz wagon z listy</option>
<option v-for="car in carOptions" :value="car" :key="car.type">
{{ car.supportersOnly ? '*W*' : '' }}
{{ car.type }}
</option>
</select>
<select
id="carwagons-list"
v-model="store.chosenCar"
@focus="onVehicleSelect('car')"
@change="onVehicleSelect('car')"
@keydown.enter.prevent="addOrSwitchVehicle"
@keydown.backspace="removeVehicle"
>
<option :value="null" disabled>Wybierz wagon</option>
<button class="btn--add" @click="addVehicle" title="Dodaj pojazd">
<img :src="icons.add" alt="add vehicle" data-ignore-outside="1" />
</button>
<option v-for="car in carOptions" :value="car" :key="car.type">
{{ car.type }}
</option>
</select>
</div>
<!-- <button class="btn--swap" @click="prepareSwapVehicles" title="Zamień pojazdy">
<img :src="icons.swap" alt="swap vehicle" />
</button> -->
</div>
<div class="input_list cargo">
<label for="cargo-select">Ładunek (tylko wybrane towarowe)</label>
<select
id="cargo-select"
:disabled="
(store.chosenCar && !store.chosenCar.loadable) ||
(store.chosenCar && store.chosenCar.useType == 'car-passenger') ||
!store.chosenCar
"
data-select="cargo"
data-ignore-outside="1"
v-model="store.chosenCargo"
@focus="onVehicleSelect('car')"
@change="onVehicleSelect('car')"
@keydown.enter.prevent="addOrSwitchVehicle"
@keydown.backspace="removeVehicle"
>
<option :value="null" v-if="!store.chosenCar || !store.chosenCar.loadable">brak dostępnych ładunków</option>
<option :value="null" v-else>próżny</option>
<div class="input_list cargo">
<select
id="cargo-select"
:disabled="
(store.chosenCar && !store.chosenCar.loadable) ||
(store.chosenCar && store.chosenCar.useType == 'car-passenger') ||
!store.chosenCar
"
data-select="cargo"
data-ignore-outside="1"
v-model="store.chosenCargo"
>
<option :value="null" v-if="!store.chosenCar || !store.chosenCar.loadable">brak dostępnych ładunków</option>
<option :value="null" v-else>próżny</option>
<option v-for="cargo in store.chosenCar?.cargoList" :value="cargo" :key="cargo.id">
{{ cargo.id }}
</option>
</select>
</div>
<option v-for="cargo in store.chosenCar?.cargoList" :value="cargo" :key="cargo.id">
{{ cargo.id }}
</option>
</select>
</div>
<div class="input_actions">
<button class="btn" @click="addVehicle">DODAJ NOWY</button>
<button
class="btn"
@click="switchVehicles"
:disabled="store.chosenStockListIndex == -1"
:data-disabled="store.chosenStockListIndex == -1"
>
ZAMIEŃ ZA
<b class="text--accent">
{{ store.chosenStockListIndex == -1 ? '' : `${store.chosenStockListIndex + 1}.` }}
</b>
</button>
<button class="btn" @click="store.isRealStockListCardOpen = true"><b>REALNE ZESTAWIENIA</b></button>
</div>
</div>
</section>
</template>
<script lang="ts">
import { ICarWagon, ILocomotive, IStore } from '@/types';
import { defineComponent, inject, provide, ref } from 'vue';
import { defineComponent } from 'vue';
import ReadyStockList from '@/components/ReadyStockList.vue';
import { IStock } from '../types';
import imageMixin from '../mixins/imageMixin';
import { useStore } from '../store';
import { isLocomotive } from '../utils/vehicleUtils';
interface ILocoType {
id: string;
value: string;
desc: string;
}
export default defineComponent({
components: {
ReadyStockList,
},
setup() {
const store = inject('Store') as IStore;
const isReadyStockListOpen = ref(false);
provide('isReadyStockListOpen', isReadyStockListOpen);
return {
store,
isReadyStockListOpen,
locoDataList: inject('locoDataList') as ILocomotive[],
carDataList: inject('carDataList') as ICarWagon[],
isTrainPassenger: inject('isTrainPassenger') as boolean,
totalLength: inject('totalLength') as number,
totalMass: inject('totalMass') as number,
maxStockSpeed: inject('maxStockSpeed') as number,
maxAllowedSpeed: inject('maxAllowedSpeed') as number,
isLocomotive: inject('isLocomotive') as (vehicle: ILocomotive | ICarWagon) => vehicle is ILocomotive,
};
},
mounted() {
document.addEventListener('keydown', (ev) => {
const keyName = ev.key.toLowerCase();
if (keyName == 'enter') {
ev.preventDefault();
this.addVehicle();
}
if (keyName == 'backspace') {
if (this.store.stockList.length == 0) return;
const lastStock = this.store.stockList.slice(-1)[0];
if (lastStock.count > 1) lastStock.count--;
else this.store.stockList.splice(-1);
}
// if (keyName == 'arrowdown') {
// const chosenVehicle = this.store.chosenCar || this.store.chosenLoco;
// if(!chosenVehicle) return;
// ev.preventDefault();
// }
});
this.onLocoPowerChange('loco-e');
this.onCarUseTypeChange('car-passenger');
},
mixins: [imageMixin],
data: () => ({
icons: {
add: require('@/assets/add-icon.svg'),
swap: require('@/assets/swap-icon.svg'),
},
locoLabels: [
locomotiveTypeList: [
{
id: 'loco-e',
title: 'ELEKTROWÓZ',
value: 'ELEKTR',
desc: 'ELEKTRYCZNE',
},
{
id: 'loco-s',
title: 'SPALINOWÓZ',
value: 'SPAL',
desc: 'SPALINOWE',
},
{
id: 'loco-ezt',
title: 'EZT',
value: 'EZT',
desc: 'ELEKTR. ZESPOŁY TRAKCYJNE',
},
{
id: 'loco-szt',
title: 'SZT',
value: 'SZT',
desc: 'SPAL. ZESPOŁY TRAKCYJNE',
},
],
] as ILocoType[],
carLabels: [
carTypeList: [
{
id: 'car-passenger',
title: 'PASAŻERSKI',
value: 'PAS',
desc: 'PASAŻERSKIE',
},
{
id: 'car-cargo',
title: 'TOWAROWY',
value: 'TOW',
desc: 'TOWAROWE',
},
],
}),
setup() {
const store = useStore();
return {
store,
};
},
computed: {
locoOptions() {
return this.locoDataList
.filter((loco) => loco.power == this.store.chosenLocoPower)
return this.store.locoDataList
.sort((a, b) => (a.type > b.type ? 1 : -1))
.sort((a) => (a.supportersOnly ? 1 : -1));
.filter((loco) => loco.power == this.store.chosenLocoPower);
},
carOptions() {
return this.carDataList
.filter((car) => car.useType == this.store.chosenCarUseType)
return this.store.carDataList
.sort((a, b) => (a.type > b.type ? 1 : -1))
.sort((a) => (a.supportersOnly ? 1 : -1));
.filter((car) => car.useType == this.store.chosenCarUseType);
},
},
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;
},
prepareSwapVehicles() {
this.store.swapVehicles = true;
},
setReadyStockList(bool = false) {
this.isReadyStockListOpen = bool;
onVehicleSelect(type: 'loco' | 'car') {
this.$nextTick(() => {
if (!this.store.chosenLoco && !this.store.chosenCar) return;
this.store.chosenVehicle = type == 'loco' ? this.store.chosenLoco : this.store.chosenCar;
});
},
onShowSupporterChange() {
this.store.showSupporter = !this.store.showSupporter;
if (this.store.showSupporter) {
const chosenVehicle = this.store.chosenCar || this.store.chosenLoco;
if (!chosenVehicle) return;
if (!chosenVehicle.supportersOnly) {
this.store.chosenCar = null;
this.store.chosenLoco = null;
}
}
addOrSwitchVehicle() {
if (this.store.chosenStockListIndex == -1) this.addVehicle();
else this.switchVehicles();
},
onLocoPowerChange(inputId: string) {
this.store.chosenLoco = null;
this.store.imageLoading = false;
removeVehicle() {
if (this.store.stockList.length == 0) return;
this.store.chosenLocoPower = inputId;
// this.store.chosenStockListIndex = -1;
const lastStock = this.store.stockList.slice(-1)[0];
(this.$refs['loco-select'] as HTMLElement).focus();
if (lastStock.count > 1) lastStock.count--;
else this.store.stockList.splice(-1);
},
onCarUseTypeChange(inputId: string) {
this.store.chosenCar = null;
this.store.imageLoading = false;
switchVehicles() {
if (this.store.chosenStockListIndex == -1) return;
this.store.chosenCarUseType = inputId;
// this.store.chosenStockListIndex = -1;
if (inputId == 'car-passenger') this.store.chosenCargo = null;
},
onCarTypeChange() {
this.store.chosenCargo = null;
this.store.chosenLoco = null;
// this.store.chosenStockListIndex = -1;
this.store.imageLoading = true;
},
onLocoTypeChange() {
this.store.chosenCargo = null;
this.store.chosenCar = null;
// this.store.chosenStockListIndex = -1
this.store.imageLoading = true;
},
addVehicle() {
const vehicle = this.store.chosenCar || this.store.chosenLoco;
const vehicle = this.store.chosenVehicle;
if (!vehicle) return;
const stockObj = {
const stockObj: IStock = {
id: `${Date.now()}`,
useType: isLocomotive(vehicle) ? vehicle.power : vehicle.useType,
type: vehicle.type,
length: vehicle.length,
mass: vehicle.mass,
maxSpeed: vehicle.maxSpeed,
isLoco: this.isLocomotive(vehicle),
isLoco: isLocomotive(vehicle),
cargo:
!this.isLocomotive(vehicle) && vehicle.loadable && this.store.chosenCargo
? this.store.chosenCargo
: undefined,
!isLocomotive(vehicle) && vehicle.loadable && this.store.chosenCargo ? this.store.chosenCargo : undefined,
count: 1,
imgSrc: vehicle.imageSrc,
useType: this.isLocomotive(vehicle) ? vehicle.power : vehicle.useType,
supportersOnly: vehicle.supportersOnly,
};
if (this.store.chosenStockListIndex != -1) {
let currentStock = this.store.stockList[this.store.chosenStockListIndex];
this.store.stockList[this.store.chosenStockListIndex] = stockObj;
},
if (this.isLocomotive(vehicle) && currentStock && currentStock.type == vehicle.type) {
this.store.stockList[this.store.chosenStockListIndex].count++;
return;
}
addVehicle() {
const vehicle = this.store.chosenVehicle;
if (
!this.isLocomotive(vehicle) &&
currentStock &&
currentStock.type == vehicle.type &&
currentStock.cargo?.id == this.store.chosenCargo?.id
) {
this.store.stockList[this.store.chosenStockListIndex].count++;
if (!vehicle) return;
return;
}
this.store.stockList[this.store.chosenStockListIndex] = stockObj;
return;
}
const stockObj: IStock = {
id: `${Date.now()}`,
useType: isLocomotive(vehicle) ? vehicle.power : vehicle.useType,
type: vehicle.type,
length: vehicle.length,
mass: vehicle.mass,
maxSpeed: vehicle.maxSpeed,
isLoco: isLocomotive(vehicle),
cargo:
!isLocomotive(vehicle) && vehicle.loadable && this.store.chosenCargo ? this.store.chosenCargo : undefined,
count: 1,
imgSrc: vehicle.imageSrc,
supportersOnly: vehicle.supportersOnly,
};
const previousStock =
this.store.stockList.length > 0 ? this.store.stockList[this.store.stockList.length - 1] : null;
if (this.isLocomotive(vehicle) && previousStock && previousStock.type == vehicle.type) {
if (isLocomotive(vehicle) && previousStock && previousStock.type == vehicle.type) {
this.store.stockList[this.store.stockList.length - 1].count++;
return;
}
if (
!this.isLocomotive(vehicle) &&
!isLocomotive(vehicle) &&
previousStock &&
previousStock.type == vehicle.type &&
previousStock.cargo?.id == this.store.chosenCargo?.id
@@ -357,7 +285,7 @@ export default defineComponent({
return;
}
if (this.isLocomotive(vehicle) && this.store.stockList.length > 0 && !this.store.stockList[0].isLoco)
if (isLocomotive(vehicle) && this.store.stockList.length > 0 && !this.store.stockList[0].isLoco)
this.store.stockList.unshift(stockObj);
else this.store.stockList.push(stockObj);
},
@@ -368,130 +296,69 @@ export default defineComponent({
<style lang="scss" scoped>
@import '../styles/global';
.inputs {
.inputs-section {
display: flex;
justify-content: space-between;
&_car {
&.disabled {
opacity: 0.75;
pointer-events: none;
}
grid-row: 2;
grid-column: 1;
}
.input_header {
margin-bottom: 1em;
}
.btn--choice {
margin-right: 0.5em;
font-weight: bold;
background-color: #444;
&[data-selected='true'] {
background-color: $accentColor;
color: black;
}
@media screen and (max-width: 800px) {
flex-direction: column;
transition: all 120ms ease;
}
.input_list {
margin: 0.5em 0;
label {
display: block;
font-weight: bold;
color: $accentColor;
margin-bottom: 0.3em;
}
select:focus {
border-color: $accentColor;
}
}
.input {
&_header {
margin-bottom: 1em;
.input_actions {
display: flex;
flex-wrap: wrap;
button {
margin: 0.5em 0.5em 0 0;
}
}
&_radio {
button {
padding: 0.25em 0.55em;
margin-right: 0.5em;
border: 2px solid white;
color: white;
font-size: 1em;
}
.vehicle-types {
margin-bottom: 0.5em;
}
button:focus {
color: $accentColor;
}
button.checked {
border-color: $accentColor;
color: $accentColor;
font-weight: bold;
}
}
&_checkbox {
margin: 1em 0;
padding: 0 1.5em;
button {
position: relative;
color: #999;
&::before {
content: '';
width: 1.5ch;
height: 1.5ch;
display: block;
position: absolute;
bottom: 0.2ch;
left: -1.5em;
background-color: #999;
}
&.checked {
color: white;
font-weight: bold;
&::before {
background-color: $accentColor;
}
}
&:focus {
outline: 1px solid $accentColor;
}
}
}
&_list {
margin: 0.5em 0;
display: flex;
select:focus {
border-color: $accentColor;
}
}
&_list button {
margin-left: 0.5em;
font-size: 0.8em;
&:hover img {
border-color: $accentColor;
}
&:focus img {
border-color: $accentColor;
}
img {
border: 2px solid white;
padding: 0.25em;
height: 2.35em;
vertical-align: middle;
}
}
@media screen and (max-width: 800px) {
@media screen and (max-width: $breakpointMd) {
.inputs-section {
justify-content: center;
text-align: center;
}
margin: 1em 0;
&_header {
text-align: center;
}
&_container > * {
display: flex;
justify-content: center;
}
.input_actions {
justify-content: center;
}
}
</style>
+337 -324
View File
@@ -1,196 +1,192 @@
<template>
<div class="bottom">
<div class="bg-dimmer" v-if="isRandomizerCardOpen"></div>
<section class="stock-list-section">
<div class="list_actions">
<button class="btn" @click="downloadStock">POBIERZ POCIĄG</button>
<button class="btn" @click="resetStock">ZRESETUJ LISTĘ</button>
<span class="spacer"></span>
<button class="btn" @click="shuffleCars">TASUJ WAGONY</button>
<button class="btn" @click="store.isRandomizerCardOpen = true">LOSUJ SKŁAD</button>
</div>
<train-image />
<div class="stock_actions" :data-disabled="store.chosenStockListIndex == -1">
<b class="no">
POJAZD NR <span class="text--accent">{{ store.chosenStockListIndex + 1 }}</span> &nbsp;
</b>
<section class="spacer"></section>
<div class="count">
<button
class="action-btn"
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
@click="subStock(store.chosenStockListIndex)"
>
<img :src="getIconURL('sub')" alt="subtract vehicle count" />
1
</button>
<section class="stock-list">
<div class="stock-list_buttons">
<button class="btn" @click="downloadStock">POBIERZ POCIĄG</button>
<button class="btn" @click="resetStock">ZRESETUJ LISTĘ</button>
<span></span>
<button class="btn" @click="shuffleCars">TASUJ WAGONY</button>
<button class="btn" @click="openRandomizerCard">LOSUJ SKŁAD</button>
<input
v-if="chosenStockVehicle"
v-model="chosenStockVehicle.count"
type="number"
min="1"
name="stock-count"
id="stock-count"
/>
<transition name="card-anim">
<randomizer-card v-if="isRandomizerCardOpen" />
</transition>
</div>
<input v-else id="stock-count" type="number" value="0" :tabindex="store.chosenStockListIndex == -1 ? -1 : 0" />
<div class="stock-list_specs">
<div>
Masa: <span class="text--accent">{{ totalMass }}t</span> | Długość:
<span class="text--accent">{{ totalLength }}m</span>
| Vmax pociągu: <span class="text--accent">{{ maxStockSpeed }} km/h</span>
</div>
<!-- <div v-if="store.chosenRealStockName" style="margin-top: 0.25rem">
<b>{{ store.chosenRealStockName.toLocaleUpperCase() }}</b>
</div> -->
</div>
<div class="stock-list_string">
<button class="btn--text" v-if="store.stockList.length > 0" @click="copyToClipboard">
Skopiuj pociąg w formie tekstowej do schowka
<button
class="action-btn"
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
@click="addStock(store.chosenStockListIndex)"
>
<img :src="getIconURL('add')" alt="add vehicle count" />
1
</button>
</div>
<div class="warnings">
<div class="warning" v-if="warnings.locoNotSuitable.value">
Lokomotywy EP07 i EP08 przeznaczone jedynie do ruchu pasażerskiego!
</div>
<button
class="action-btn"
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
@click="moveUpStock(store.chosenStockListIndex)"
>
<img :src="getIconURL('higher')" alt="move up vehicle" />
Przenieś wyżej
</button>
<div class="warning" v-if="warnings.trainTooLong.value">
Ten skład jest za długi (pasażerskie max. 350m, towarowe max. 650m)!
</div>
<button
class="action-btn"
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
@click="moveDownStock(store.chosenStockListIndex)"
>
<img :src="getIconURL('lower')" alt="move down vehicle" />
Przenieś niżej
</button>
<div class="warning" v-if="warnings.trainTooHeavy.value">
Ten skład jest za ciężki! Sprawdź
<a target="_blank" href="https://docs.google.com/spreadsheets/d/1bFXUsHsAu4youmNz-46Q1HslZaaoklvfoBDS553TnNk/edit">
dopuszczalne masy składów
</a>
</div>
<button
class="action-btn"
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
@click="removeStock(store.chosenStockListIndex)"
>
<img :src="getIconURL('remove')" alt="remove vehicle" />
Usuń
</button>
</div>
<div class="warning" v-if="warnings.tooManyLocos.value">Ten skład posiada za dużo pojazdów trakcyjnych!</div>
<div class="stock_clipboard-text" v-if="store.stockList.length > 0">
<button class="btn" @click="copyToClipboard">Skopiuj tekst składu do schowka</button>
</div>
<div class="stock_specs">
<b class="real-stock-info" v-if="store.chosenRealStock">
<span class="text--accent">
<img :src="getIconURL(store.chosenRealStock.type)" :alt="store.chosenRealStock.type" />
{{ store.chosenRealStock.number }} {{ store.chosenRealStock.name }}
</span>
|
</b>
<span>
Masa: <span class="text--accent">{{ store.totalMass }}t</span> - Długość:
<span class="text--accent">{{ store.totalLength }}m</span>
- Vmax pociągu: <span class="text--accent">{{ store.maxStockSpeed }} km/h</span>
</span>
</div>
<div class="stock_warnings">
<div class="warning" v-if="locoNotSuitable">
Lokomotywy EP07 i EP08 przeznaczone jedynie do ruchu pasażerskiego!
</div>
<ul ref="list" data-ignore-outside="1">
<li v-if="store.stockList.length == 0" class="list-empty">
<div class="item-content">Lista pojazdów jest pusta!</div>
</li>
<div class="warning" v-if="trainTooLong && store.isTrainPassenger">
Maksymalna długość składów pasażerskich nie może przekraczać 350m!
</div>
<div class="warning" v-if="trainTooLong && !store.isTrainPassenger">
Maksymalna długość składów innych niż pasażerskie nie może przekraczać 650m!
</div>
<div class="warning" v-if="trainTooHeavy">
Ten skład jest za ciężki! Sprawdź
<a
target="_blank"
href="https://docs.google.com/spreadsheets/d/1bFXUsHsAu4youmNz-46Q1HslZaaoklvfoBDS553TnNk/edit"
>
dopuszczalne masy składów
</a>
</div>
<div class="warning" v-if="tooManyLocomotives">Ten skład posiada za dużo pojazdów trakcyjnych!</div>
</div>
<ul ref="list">
<li v-if="store.stockList.length == 0" class="list-empty">
<div class="stock-info">Lista pojazdów jest pusta!</div>
</li>
<transition-group name="stock-list-anim">
<li
v-for="(stock, i) in store.stockList"
:key="stock.type + i"
:class="{ loco: stock.isLoco, selected: store.chosenStockListIndex == i }"
:data-id="i"
:key="stock.id"
:class="{ loco: stock.isLoco }"
tabindex="0"
@focus="onListItemFocus(i)"
:ref="`item-${i}`"
@click="onListItemClick(i)"
@keydown.enter="onListItemClick(i)"
@keydown.w="moveUpStock(i)"
@keydown.s="moveDownStock(i)"
@keydown.backspace="removeStock(i)"
ref="itemRefs"
>
<div
class="item-content"
class="stock-info"
@dragstart="onDragStart(i)"
@drop="onDrop($event, i)"
@dragover="allowDrop"
draggable="true"
>
<span class="stock__type" :class="{ supporter: stock.supportersOnly }">
<span class="stock-info__no" :data-selected="i == store.chosenStockListIndex">
<span v-if="i == store.chosenStockListIndex">&bull;&nbsp;</span>
{{ i + 1 }}.
</span>
<span class="stock-info__type">
{{ stock.isLoco ? stock.type : getCarSpecFromType(stock.type) }}
</span>
<span class="stock__cargo" v-if="stock.cargo"> {{ stock.cargo.id }} </span>
<span class="stock__length"> {{ stock.length }}m </span>
<span class="stock__mass">{{ stock.cargo ? stock.cargo.totalMass : stock.mass }}t </span>
<span class="stock__speed"> {{ stock.maxSpeed }}km/h </span>
</div>
<div class="item-actions">
<div class="count">
<button class="action-btn" @click="subStock(i)">
<img :src="icons.sub" alt="subtract vehicle count" />
</button>
<span class="stock-info__cargo" v-if="stock.cargo"> {{ stock.cargo.id }} </span>
<span class="stock-info__length"> {{ stock.length }}m </span>
<span class="stock-info__mass">{{ stock.cargo ? stock.cargo.totalMass : stock.mass }}t </span>
<span class="stock-info__speed"> {{ stock.maxSpeed }}km/h </span>
<span>{{ stock.count }} </span>
<button class="action-btn" @click="addStock(i)">
<img :src="icons.add" alt="add vehicle count" />
</button>
</div>
<button class="action-btn" @click="moveUpStock(i)">
<img :src="icons.higher" alt="move up vehicle" />
</button>
<button class="action-btn" @click="moveDownStock(i)">
<img :src="icons.lower" alt="move down vehicle" />
</button>
<button class="action-btn" @click="removeStock(i)">
<img :src="icons.remove" alt="remove vehicle" />
</button>
<span class="stock-info__count"> x{{ stock.count }} </span>
</div>
</li>
</ul>
</section>
</div>
</transition-group>
</ul>
</section>
</template>
<script lang="ts">
import { computed, ComputedRef, defineComponent, inject, provide, reactive, ref } from 'vue';
import { ICarWagon, ILocomotive, IStore } from '@/types';
import RandomizerCard from './RandomizerCard.vue';
import { defineComponent } from 'vue';
import TrainImage from './TrainImageSection.vue';
import TrainImage from './TrainImage.vue';
import { useStore } from '../store';
import warningsMixin from '../mixins/warningsMixin';
import imageMixin from '../mixins/imageMixin';
export default defineComponent({
components: { RandomizerCard, TrainImage },
components: { TrainImage },
mixins: [warningsMixin, imageMixin],
setup() {
const store = inject('Store') as IStore;
const isRandomizerCardOpen = ref(false);
provide('isCardOpen', isRandomizerCardOpen);
provide('chosenLength', ref(350));
provide('chosenMass', ref(1000));
provide('chosenLocoType', ref('loco-e'));
provide('chosenCarTypes', reactive([]));
provide('includeSupporterVehicles', ref(false));
const store = useStore();
return {
store,
locoDataList: inject('locoDataList') as ILocomotive[],
carDataList: inject('carDataList') as ICarWagon[],
isTrainPassenger: inject('isTrainPassenger') as boolean,
totalLength: inject('totalLength') as number,
totalMass: inject('totalMass') as number,
maxStockSpeed: inject('maxStockSpeed') as number,
maxAllowedSpeed: inject('maxAllowedSpeed') as number,
warnings: inject('warnings') as {
locoNotSuitable: ComputedRef<boolean>;
trainTooLong: ComputedRef<boolean>;
trainTooHeavy: ComputedRef<boolean>;
tooManyLocos: ComputedRef<boolean>;
},
isRandomizerCardOpen,
hasSupporterOnlyVehicle: computed(() => store.stockList.some((stock) => stock.supportersOnly)),
};
},
mounted() {
document.addEventListener('click', (event: Event) => {
if (!event.target) return;
event.stopPropagation();
const targetNode = event.target as HTMLElement;
const attr = targetNode.attributes.getNamedItem('data-ignore-outside');
if (
!attr &&
!(this.$refs['list'] as Node).contains(targetNode) &&
targetNode.tagName.toLowerCase() != 'select' &&
targetNode.tagName.toLowerCase() != 'option'
)
this.store.chosenStockListIndex = -1;
});
},
data: () => ({
icons: {
add: require('@/assets/add-icon.svg'),
sub: require('@/assets/sub-icon.svg'),
remove: require('@/assets/remove-icon.svg'),
lower: require('@/assets/lower-icon.svg'),
higher: require('@/assets/higher-icon.svg'),
},
imageOffsetY: 0,
draggedVehicleID: -1,
@@ -209,11 +205,19 @@ export default defineComponent({
})
.join(';');
},
chosenStockVehicle() {
return this.store.chosenStockListIndex == -1 ? undefined : this.store.stockList[this.store.chosenStockListIndex];
},
},
methods: {
stockHasWarnings() {
return this.tooManyLocomotives || this.trainTooHeavy || this.trainTooLong || this.locoNotSuitable;
},
copyToClipboard() {
if (Object.values(this.warnings).some((v) => v.value == true)) {
if (this.stockHasWarnings()) {
alert('Jazda tym pociągiem jest niezgodna z regulaminem symulatora! Zmień parametry zestawienia!');
return;
}
@@ -225,30 +229,36 @@ export default defineComponent({
}, 20);
},
onListItemFocus(vehicleID: number) {
onListItemClick(vehicleID: number) {
const vehicle = this.store.stockList[vehicleID];
this.store.chosenStockListIndex = vehicleID;
this.store.chosenStockListIndex =
this.store.chosenStockListIndex == vehicleID && this.store.chosenVehicle?.type == vehicle.type ? -1 : vehicleID;
if ((this.store.chosenCar || this.store.chosenLoco)?.imageSrc != vehicle.imgSrc) this.store.imageLoading = true;
if (this.store.chosenStockListIndex == -1) {
this.store.chosenVehicle = null;
return;
}
if (this.store.chosenVehicle?.imageSrc != vehicle.imgSrc) this.store.imageLoading = true;
if (this.store.showSupporter && !vehicle.supportersOnly) {
this.store.showSupporter = false;
}
if (vehicle.isLoco) {
const chosenLoco = this.store.locoDataList.find((v) => v.type == vehicle.type) || null;
this.store.chosenVehicle = chosenLoco;
this.store.chosenLoco = chosenLoco;
// this.store.chosenCargo = null;
this.store.chosenLocoPower = vehicle.useType;
this.store.chosenLoco = this.locoDataList.find((v) => v.type == vehicle.type) || null;
this.store.chosenCar = null;
this.store.chosenCargo = null;
} else {
this.store.chosenCarUseType = vehicle.useType;
const chosenCar = this.store.carDataList.find((v) => v.type == vehicle.type) || null;
this.store.chosenVehicle = chosenCar;
this.store.chosenCar = chosenCar;
this.store.chosenLoco = null;
this.store.chosenCar = this.carDataList.find((v) => v.type == vehicle.type) || null;
this.store.chosenCargo = vehicle.cargo || null;
this.store.chosenCarUseType = vehicle.useType;
}
if (this.store.swapVehicles) {
@@ -274,16 +284,22 @@ export default defineComponent({
},
addStock(index: number) {
if (index == -1) return;
this.store.stockList[index].count++;
},
subStock(index: number) {
if (index == -1) return;
if (this.store.stockList[index].count < 2) return;
this.store.stockList[index].count--;
},
removeStock(index: number) {
if (index == -1) return;
this.store.stockList = this.store.stockList.filter((stock, i) => i != index);
},
@@ -294,15 +310,20 @@ export default defineComponent({
this.store.stockList[index] = this.store.stockList[index - 1];
this.store.stockList[index - 1] = tempStock;
this.store.chosenStockListIndex = index - 1;
},
moveDownStock(index: number) {
if (index == -1) return;
if (index > this.store.stockList.length - 2) return;
const tempStock = this.store.stockList[index];
this.store.stockList[index] = this.store.stockList[index + 1];
this.store.stockList[index + 1] = tempStock;
this.store.chosenStockListIndex = index + 1;
},
shuffleCars() {
@@ -325,17 +346,13 @@ export default defineComponent({
}
},
openRandomizerCard() {
this.isRandomizerCardOpen = true;
},
downloadStock() {
if (Object.values(this.warnings).some((v) => v.value == true)) {
alert('Jazda tym pociągiem może być niezgodna z regulaminem symulatora! Zmień parametry zestawienia!');
return;
}
if (this.store.stockList.length == 0) return alert('Lista pojazdów jest pusta!');
const fileName = prompt('Nazwij plik:', 'pociag');
if (this.stockHasWarnings())
return alert('Jazda tym pociągiem jest niezgodna z regulaminem symulatora! Zmień parametry zestawienia!');
const fileName = prompt('Nazwij plik:', `${this.store.chosenRealStockName || this.store.stockList[0].type}`);
if (!fileName) return;
@@ -358,18 +375,16 @@ export default defineComponent({
onDrop(e: DragEvent, vehicleIndex: number) {
e.preventDefault();
let targetEl: Element | null = this.$refs[`item-${vehicleIndex}`] as Element;
let targetEl = (this.$refs['itemRefs'] as Element[])[vehicleIndex];
if (!targetEl) return;
const dataID = targetEl.attributes.getNamedItem('data-id')?.textContent;
const tempVehicle = this.store.stockList[vehicleIndex];
if (!dataID) return;
const tempVehicle = this.store.stockList[Number(dataID)];
this.store.stockList[Number(dataID)] = this.store.stockList[this.draggedVehicleID];
this.store.stockList[vehicleIndex] = this.store.stockList[this.draggedVehicleID];
this.store.stockList[this.draggedVehicleID] = tempVehicle;
this.store.chosenStockListIndex = vehicleIndex;
},
allowDrop(e: DragEvent) {
@@ -382,41 +397,12 @@ export default defineComponent({
<style lang="scss" scoped>
@import '../styles/global';
.bg-dimmer {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(black, 0.85);
z-index: 100;
}
.bottom {
display: flex;
justify-content: space-between;
margin-top: 2.5em;
@media screen and (max-width: 1150px) {
flex-direction: column;
align-items: center;
.image {
display: flex;
padding: 0 0 2em 0;
}
}
}
.warnings {
margin-top: 0.5em;
.stock_warnings {
margin-top: 1em;
}
.warning {
padding: 0.25em;
margin-top: 0.5em;
background: $accentColor;
color: black;
@@ -428,140 +414,154 @@ export default defineComponent({
}
}
.spacer {
flex: 2 1 10%;
.stock-list-section {
grid-row: 1 / 4;
grid-column: 2;
}
.stock-list {
flex-grow: 3;
.list_actions {
display: flex;
width: 100%;
&_string {
margin-top: 1em;
font-weight: bold;
.spacer {
flex-grow: 2;
}
&_buttons {
display: flex;
button {
margin-right: 0.5em;
span {
flex-grow: 2;
}
button {
font-size: 0.9em;
padding: 0.4em 0.55em;
margin: 0 0.5em 1em 0;
&:nth-child(5) {
margin-right: 0;
}
&:focus {
color: $accentColor;
border-color: $accentColor;
}
&:nth-child(5) {
margin-right: 0;
}
}
}
ul {
margin-top: 1em;
.stock_actions {
display: flex;
justify-content: center;
align-items: center;
max-height: 500px;
overflow: auto;
flex-wrap: wrap;
margin: 1em 0;
border: 1px solid white;
padding: 0 0.3em;
&[data-disabled='true'] {
opacity: 0.8;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
pointer-events: none;
}
ul li {
input#stock-count {
width: 3em;
margin: 0;
padding: 0.25em;
outline: none;
cursor: pointer;
border: none;
}
&.list-empty {
border: 1px solid whitesmoke;
padding: 0 0.5em;
button {
margin: 0.25em;
padding: 0.25em;
&:focus-visible {
outline: 1px solid white;
}
&.selected .item-content {
color: $accentColor;
}
img {
vertical-align: text-bottom;
margin-right: 0.25em;
&:focus .item-content {
color: $accentColor;
}
&:hover .item-content {
color: $accentColor;
}
display: flex;
align-items: center;
justify-content: space-between;
.item-content {
display: flex;
flex-wrap: wrap;
color: white;
font-weight: 700;
margin: 0.5em 0;
transition: color 100ms;
span {
padding: 0.5em;
margin-right: 0.25em;
margin-top: 0.25em;
}
@media screen and (max-width: 800px) {
span {
padding: 0.25em;
}
}
}
.item-actions {
display: flex;
align-items: center;
.count {
display: flex;
align-items: center;
margin-right: 0.5em;
span {
margin: 0 0.5em;
/* font-size: 1.25em; */
color: $accentColor;
}
}
img {
vertical-align: middle;
width: 1.3em;
height: 1.3em;
}
button {
margin: 0 0.25em;
&:focus {
outline: 1px solid white;
}
}
width: 1.1em;
height: 1.1em;
}
}
}
.stock {
&__type {
background-color: #222;
.stock_clipboard-text {
font-weight: bold;
&.supporter {
background-color: #ff887b;
& > .btn {
margin: 0 0.5em 0.5em 0;
}
}
.real-stock-info {
img {
height: 1.3ch;
}
}
ul {
position: relative;
overflow: auto;
height: 70vh;
margin-top: 1em;
}
ul > li {
display: flex;
align-items: center;
justify-content: space-between;
min-width: 500px;
margin: 0.25em 0;
outline: none;
cursor: pointer;
&:focus-visible {
outline: 1px solid white;
}
&.list-empty {
background-color: $secondaryColor;
padding: 0.5em;
}
}
li > .stock-info {
display: flex;
color: white;
font-weight: 700;
transition: color 100ms;
& > span {
padding: 0.5em;
margin-right: 0.25em;
display: flex;
justify-content: center;
align-items: center;
}
}
.stock-info {
&__no,
&__type {
background-color: $secondaryColor;
}
&__count {
background-color: #e04e3e;
}
&__no {
min-width: 3.5em;
text-align: right;
&[data-selected='true'] {
color: $accentColor;
}
}
@@ -576,18 +576,31 @@ export default defineComponent({
}
}
.card-anim {
&-enter {
opacity: 0;
}
.stock-list-anim {
&-move, /* apply transition to moving elements */
&-enter-active,
&-leave-active {
transition: opacity 300ms;
transition: all 250ms ease;
}
&-enter-from {
opacity: 0;
transform: translateY(-25px);
}
&-leave-to {
opacity: 0;
}
&-leave-active {
position: absolute;
}
}
@media screen and (max-width: $breakpointMd) {
ul {
min-height: auto;
}
}
</style>
+33
View File
@@ -0,0 +1,33 @@
<template>
<section class="logo-section">
<img src="/images/logo.svg" alt="logo pojazdownik" />
</section>
</template>
<script lang="ts">
export default {
setup() {
return {};
},
};
</script>
<style lang="scss" scoped>
.logo-section {
grid-row: 1;
grid-column: 1;
margin-bottom: 1.5em;
display: flex;
justify-content: center;
align-items: center;
}
img {
width: 25em;
@media screen and (max-width: 400px) {
width: 20em;
}
}
</style>
-458
View File
@@ -1,458 +0,0 @@
<template>
<div class="card">
<!-- <button class="btn" @click="closeCard">X</button> -->
<div class="wrapper">
<!-- <h1>LOSUJ SKŁAD <img :src="icons.randomize" alt="losuj skład" /></h1>
<h3>
Skład zostanie dołączony do dodanej na liście lokomotywy czołowej bądź wygenerowany z losową w przypadku jej
braku
</h3> -->
<div class="car-preview">
<div class="image-wrapper">
<div v-if="isPreviewLoading" class="loading">ŁADOWANIE...</div>
<img v-if="focusedCar" :src="focusedCar?.imageSrc" :alt="focusedCar.type" @load="onPreviewLoaded" />
</div>
<b class="text--accent" v-if="focusedCar">
{{ focusedCar.type.split('_')[0] }} {{ focusedCar.type.split('_')[2] }}
</b>
<b v-else>Podgląd typu wagonu</b>
<div v-if="focusedCar">{{ carUsage[focusedCar.type.split('_')[0]] }}</div>
<div v-else>Najedź na rodzaj wagonu aby wyświetlić informacje</div>
</div>
<div class="car-choice">
<p>Dobierz rodzaje wagonów</p>
<div>
<button
class="btn choice-btn"
v-for="carType in carTypeList.filter((_, i) => i < 15)"
:key="carType"
@click="toggleCarType(carType)"
@mouseenter="displayPreview(carType)"
:class="{ chosen: chosenCarTypes.includes(carType) }"
>
{{ carType }}
</button>
</div>
<div style="margin-top: 0.5em">
<button
class="btn choice-btn"
v-for="carType in carTypeList.filter((_, i) => i >= 15)"
:key="carType"
@click="toggleCarType(carType)"
@mouseenter="displayPreview(carType)"
:class="{ chosen: chosenCarTypes.includes(carType) }"
>
{{ carType }}
</button>
</div>
</div>
<div class="length-choice">
<p>Wybierz preferowaną długość składu (m) i (opcjonalnie) max. masę (t)</p>
<input
type="number"
v-model="chosenLength"
name="length"
max="650"
min="20"
step="10"
title="Długość składu (m)"
/>
<input
type="number"
v-model="chosenMass"
name="length"
max="4000"
min="100"
step="100"
title="Masa składu (t)"
/>
</div>
<div style="margin: 1em 0">
<button class="btn choice-btn" :class="{ chosen: loadableByDefault }" @click="changeLoadableByDefault">
DOMYŚLNIE ŁADOWNE
</button>
</div>
<button class="btn" style="font-size: 1.15em; margin-top: 2em" @click="randomize">LOSUJ SKŁAD!</button>
<button class="btn" style="font-size: 1.15em; margin-top: 2em" @click="closeCard">ZAMKNIJ</button>
</div>
</div>
</template>
<script lang="ts">
import { ICargo, ICarWagon, ILocomotive, IStore } from '@/types';
import { ComputedRef, defineComponent, inject } from 'vue';
import carUsage from '@/data/carUsage.json';
export default defineComponent({
setup() {
const isCardOpen = inject('isCardOpen') as boolean;
const store = inject('Store') as IStore;
const carDataList = inject('carDataList') as ComputedRef<ICarWagon[]>;
return {
isCardOpen,
store,
locoDataList: inject('locoDataList') as ILocomotive[],
chosenLength: inject('chosenLength') as number,
chosenMass: inject('chosenMass') as number,
carDataList,
carTypeList: carDataList.value.reduce((list, car) => {
const type = car.type.split('_')[0];
if (list.includes(type)) return list;
list.push(type);
return list;
}, [] as string[]),
chosenLocoType: inject('chosenLocoType') as string,
chosenCarTypes: inject('chosenCarTypes') as string[],
includeSupporterVehicles: inject('includeSupporterVehicles') as boolean,
};
},
data: () => ({
icons: {
randomize: require('@/assets/randomize-icon.svg'),
},
focusedCar: null as ICarWagon | null,
isPreviewLoading: false,
loadableByDefault: false,
cargoTypes: [
'203V',
'208Kf',
'209c',
'29R',
'304C',
'304Ca',
'401Ka',
'401Zb',
'408S',
'412W',
'412Z',
'424Z',
'426S',
'429W',
'441V',
'504a',
'612a',
'627Z',
],
carUsage: carUsage as { [key: string]: string },
}),
methods: {
closeCard() {
this.isCardOpen = false;
},
displayPreview(carType: string) {
const list = this.carDataList.filter((car) => car.type.includes(carType));
const randIndex = Math.floor(Math.random() * list.length);
if (this.focusedCar?.type == list[randIndex].type) return;
this.focusedCar = list[randIndex];
this.isPreviewLoading = true;
},
onPreviewLoaded() {
this.isPreviewLoading = false;
},
changeLoadableByDefault() {
this.loadableByDefault = !this.loadableByDefault;
},
randomize() {
if (this.chosenCarTypes.length == 0) {
alert('Wybierz przynajmniej jeden rodzaj wagonów!');
return;
}
if (this.chosenLength <= 20) {
alert('Długość składu musi być większa niż 20m!');
return;
}
if (this.chosenMass <= 100) {
alert('Masa składu musi być większa niż 100t!');
return;
}
if (this.chosenLength > 650) {
alert('Długość składu nie może przekraczać 650m dla pociągów towarowych!');
return;
}
let totalStockLength = 0;
let totalStockMass = 0;
if (this.store.stockList.length == 0 || !this.store.stockList[0].isLoco) {
this.store.stockList.length = 0;
let locoSet = this.locoDataList
.filter((loco) => loco.power == 'loco-e' || loco.power == 'loco-s')
.filter((loco) => (!this.includeSupporterVehicles && loco.supportersOnly ? false : true));
if (this.chosenCarTypes.some((car) => this.cargoTypes.includes(car)))
locoSet = locoSet.filter((loco) => !loco.type.startsWith('EP'));
const randLoco = locoSet[Math.floor(Math.random() * locoSet.length)];
this.addLoco(randLoco);
} else this.store.stockList.length = 1;
totalStockLength += this.store.stockList[0].length;
totalStockMass += this.store.stockList[0].mass;
let availableCarsSet = this.carDataList.filter((cargoCar) => {
if (!this.includeSupporterVehicles && cargoCar.supportersOnly) return false;
if (this.chosenCarTypes.find((carType) => cargoCar.type.includes(carType))) return true;
return false;
});
while (totalStockLength < this.chosenLength && totalStockMass < this.chosenMass) {
const randCarIndex = Math.floor(Math.random() * availableCarsSet.length);
const randCar = availableCarsSet[randCarIndex];
// const count = Math.random() < 0.25 ? Math.floor(Math.random() * 2) + 1 : 1;
const count = 1;
if (randCar.length * count + totalStockLength >= this.chosenLength) break;
let randCargo = undefined;
let randNum = this.loadableByDefault ? 1 : Math.random();
if (randCar.cargoList.length != 0 && randNum >= 0.6)
randCargo = randCar.cargoList[Math.floor(Math.random() * randCar.cargoList.length)];
if ((randCargo?.totalMass || randCar.mass) * count + totalStockMass >= this.chosenMass) break;
for (let i = 0; i < count; i++) this.addCar(randCar, randCargo);
totalStockLength += randCar.length * count;
totalStockMass += randCargo?.totalMass || randCar.mass;
}
this.isCardOpen = false;
},
toggleCarType(carType: string) {
if (this.chosenCarTypes.includes(carType)) this.chosenCarTypes.splice(this.chosenCarTypes.indexOf(carType), 1);
else this.chosenCarTypes.push(carType);
},
addLoco(loco: ILocomotive) {
const previousStock =
this.store.stockList.length > 0 ? this.store.stockList[this.store.stockList.length - 1] : null;
if (previousStock && previousStock.type == loco.type) {
this.store.stockList[this.store.stockList.length - 1].count++;
return;
}
const stockObj = {
type: loco.type,
length: loco.length,
mass: loco.mass,
maxSpeed: loco.maxSpeed,
isLoco: true,
cargo: undefined,
count: 1,
imgSrc: loco.imageSrc,
useType: loco.power,
supportersOnly: loco.supportersOnly,
};
if (this.store.stockList.length > 0 && !this.store.stockList[0].isLoco) this.store.stockList.unshift(stockObj);
else this.store.stockList.push(stockObj);
},
addCar(car: ICarWagon, cargo?: ICargo) {
const previousStock =
this.store.stockList.length > 0 ? this.store.stockList[this.store.stockList.length - 1] : null;
if (previousStock && previousStock.type == car.type && previousStock.cargo?.id == cargo?.id) {
this.store.stockList[this.store.stockList.length - 1].count++;
return;
}
const stockObj = {
type: car.type,
length: car.length,
mass: car.mass,
maxSpeed: car.maxSpeed,
isLoco: false,
cargo: car.loadable && cargo ? cargo : undefined,
count: 1,
imgSrc: car.imageSrc,
useType: car.useType,
supportersOnly: car.supportersOnly,
};
this.store.stockList.push(stockObj);
},
},
});
</script>
<style lang="scss" scoped>
/* @import url('../styles/global.scss'); */
.card {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
overflow: auto;
max-height: 95vh;
z-index: 100;
display: flex;
justify-content: center;
text-align: center;
border: 2px solid white;
width: 700px;
padding: 2em 1em;
background: rgba(black, 0.95);
@media screen and (max-width: 700px) {
width: 95%;
}
border-radius: 1em;
}
p {
font-size: 1.2em;
}
h1 {
display: flex;
justify-content: center;
flex-wrap: wrap;
margin: 0;
img {
margin-left: 0.5em;
width: 2em;
}
}
h3 {
margin: 0 0 2em 0;
color: #999;
}
button,
input {
margin: 0.25em;
}
input {
font-size: 1.2em;
}
.car-choice div {
display: grid;
grid-template-columns: repeat(5, 1fr);
justify-content: center;
@media screen and (max-width: 800px) {
/* display: flex; */
/* flex-wrap: wrap; */
grid-template-columns: repeat(4, 1fr);
}
}
button.choice-btn {
color: gray;
border-color: gray;
*:focus {
color: white;
border-color: white;
}
user-select: none;
}
button.chosen {
border-color: gold;
color: gold;
}
.car-preview {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
b {
font-size: 1.3em;
}
.image-wrapper {
position: relative;
width: 300px;
height: 180px;
border: 1px solid white;
margin-bottom: 1em;
.loading {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
padding: 0.5em 0;
z-index: 102;
background-color: rgba(black, 0.75);
}
img {
width: 100%;
height: 100%;
}
}
}
@media screen and (max-width: 600px) {
.car-preview .image-wrapper {
width: 20em;
height: 13em;
}
}
</style>
-163
View File
@@ -1,163 +0,0 @@
<template>
<div class="image">
<div class="image__wrapper">
<div
class="image__content"
:class="{
supporter: (store.chosenLoco || store.chosenCar)?.supportersOnly,
}"
>
<div class="no-img" v-if="!store.chosenCar && !store.chosenLoco">PODGLĄD WYBRANEGO POJAZDU</div>
<div class="empty-message" v-if="store.imageLoading">ŁADOWANIE OBRAZU...</div>
<img
v-if="store.chosenLoco || store.chosenCar"
:src="store.chosenLoco?.imageSrc || store.chosenCar?.imageSrc"
:alt="store.chosenLoco?.type || store.chosenCar?.type"
@load="onImageLoad"
@click="onImageClick"
/>
</div>
</div>
<div class="image__info" v-if="store.chosenLoco || store.chosenCar">
<b class="text--accent">{{ (store.chosenLoco || store.chosenCar)?.type }} </b>
<div style="color: #ccc">
<b>{{ vehicleTypes[store.chosenLoco?.power || store.chosenCar?.useType || 'loco-e'] }}</b>
<div>
{{ (store.chosenCar || store.chosenLoco)?.length }}m | {{ (store.chosenCar || store.chosenLoco)?.mass }}t |
{{ (store.chosenCar || store.chosenLoco)?.maxSpeed }} km/h
</div>
<div v-if="store.chosenLoco">Typ kabiny: {{ store.chosenLoco.cabinType }}</div>
<div v-if="store.chosenCar">
{{
store.chosenCar.useType == 'car-cargo'
? carUsage[store.chosenCar.constructionType]
: 'Typ konstrukcji: ' + store.chosenCar.constructionType
}}
</div>
</div>
</div>
<div class="image__info" v-else>Wybierz pojazd lub wagon, aby zobaczyć jego podgląd powyżej</div>
</div>
</template>
<script lang="ts">
import carUsage from '@/data/carUsage.json';
import { IStore } from '@/types';
import { defineComponent, inject } from 'vue';
export default defineComponent({
setup() {
const store = inject('Store') as IStore;
return {
store,
};
},
data() {
return {
vehicleTypes: {
'loco-e': 'ELEKTROWÓZ',
'loco-s': 'SPALINOWÓZ',
'loco-ezt': 'EZT',
'loco-szt': 'SZT',
'car-passenger': 'WAGON PASAŻERSKI',
'car-cargo': 'WAGON TOWAROWY',
} as { [key: string]: string },
carUsage: carUsage as { [key: string]: string },
};
},
methods: {
onImageLoad() {
this.store.imageLoading = false;
},
onImageClick() {
const chosenVehicle = this.store.chosenCar || this.store.chosenLoco;
if (!chosenVehicle) return;
this.store.vehiclePreviewSrc = chosenVehicle.imageSrc.replace('300', '800');
},
},
});
</script>
<style lang="scss" scoped>
.image {
flex-grow: 2;
display: flex;
flex-direction: column;
align-items: center;
}
.image {
&__wrapper {
max-width: 380px;
width: 22em;
height: 13em;
}
&__content {
border: 1px solid white;
position: relative;
height: 100%;
&.supporter {
border: 1px solid salmon;
}
img {
width: 100%;
height: 100%;
cursor: pointer;
}
.empty-message,
.no-img {
position: absolute;
left: 0;
top: 0;
display: flex;
justify-content: center;
align-items: flex-end;
width: 100%;
height: 100%;
padding: 0.3em 0;
}
.empty-message {
background: rgba(#000, 0.75);
}
}
}
.image__info {
text-align: center;
margin: 1em 0;
font-size: 1.1em;
b {
font-size: 1.1em;
}
div {
margin: 0.25em 0;
}
}
</style>
+197
View File
@@ -0,0 +1,197 @@
<template>
<section class="train-image-section">
<div class="train-image__wrapper">
<div class="train-image__content">
<transition name="img-message-anim">
<div class="empty-message" v-if="store.imageLoading">ŁADOWANIE OBRAZU...</div>
</transition>
<div class="no-img" v-if="!store.chosenVehicle">PODGLĄD WYBRANEGO POJAZDU</div>
<img
v-if="store.chosenVehicle"
:src="store.chosenVehicle.imageSrc"
:alt="store.chosenVehicle.type"
@load="onImageLoad"
@click="onImageClick"
/>
</div>
<div class="train-image__info" v-if="store.chosenVehicle">
<b class="text--accent">{{ store.chosenVehicle.type }}</b> &bull;
<b style="color: #ccc">{{
vehicleTypes[
isLocomotive(store.chosenVehicle) ? store.chosenVehicle.power : store.chosenVehicle.useType || 'loco-e'
]
}}</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)">Typ kabiny: {{ store.chosenVehicle.cabinType }}</div>
<div v-else>
{{
store.chosenVehicle.useType == 'car-cargo'
? carUsage[store.chosenVehicle.constructionType]
: 'Typ konstrukcji: ' + store.chosenVehicle.constructionType
}}
</div>
</div>
</div>
<div class="train-image__info" v-else>Wybierz pojazd lub wagon, aby zobaczyć jego podgląd powyżej</div>
</div>
</section>
</template>
<script lang="ts">
import carUsage from '../data/carUsage.json';
import { computed, defineComponent } from 'vue';
import { useStore } from '../store';
import { isLocomotive } from '../utils/vehicleUtils';
import { ILocomotive, IVehicleData, Vehicle } from '../types';
export default defineComponent({
setup() {
const store = useStore();
return {
store,
chosenVehicle: computed(() => store.chosenVehicle),
};
},
data() {
return {
vehicleTypes: {
'loco-e': 'ELEKTROWÓZ',
'loco-s': 'SPALINOWÓZ',
'loco-ezt': 'EZT',
'loco-szt': 'SZT',
'car-passenger': 'WAGON PASAŻERSKI',
'car-cargo': 'WAGON TOWAROWY',
} as { [key: string]: string },
carUsage: carUsage as { [key: string]: string },
};
},
watch: {
chosenVehicle(vehicle: Vehicle, prevVehicle: Vehicle) {
if (vehicle && vehicle.type != prevVehicle?.type) {
this.store.imageLoading = true;
}
},
},
methods: {
onImageLoad() {
this.store.imageLoading = false;
},
isLocomotive(vehicle: Vehicle): vehicle is ILocomotive {
return isLocomotive(vehicle);
},
onImageClick() {
const chosenVehicle = this.store.chosenVehicle;
if (!chosenVehicle) return;
this.store.vehiclePreviewSrc = chosenVehicle.imageSrc.replace('300', '800');
},
},
});
</script>
<style lang="scss" scoped>
@import '../styles/global.scss';
.train-image-section {
grid-row: 3;
grid-column: 1;
margin-top: 2em;
height: 290px;
}
.train-image {
&__wrapper {
text-align: center;
}
&__content {
border: 1px solid white;
position: relative;
overflow: hidden;
width: 22em;
height: 13em;
margin: 0 auto;
&.supporter {
border: 1px solid salmon;
}
img {
width: 100%;
height: 100%;
cursor: pointer;
}
.empty-message,
.no-img {
position: absolute;
left: 0;
bottom: 0;
padding: 0.3em 0;
width: 100%;
}
.empty-message {
background: rgba(#000, 0.75);
}
}
}
.train-image__info {
margin: 1em 0;
font-size: 1.1em;
padding: 0 1em;
b {
font-size: 1.1em;
}
div {
margin: 0.25em 0;
}
}
// Transition animations
.img-message-anim {
&-enter-from,
&-leave-to {
opacity: 0;
}
&-enter-active,
&-leave-active {
transition: opacity 75ms ease-in 100ms;
}
}
@media screen and (max-width: $breakpointMd) {
.train-image-section {
justify-content: center;
}
}
</style>
+647
View File
@@ -0,0 +1,647 @@
<template>
<div class="randomizer-card g-card" v-if="store.isRandomizerCardOpen">
<div class="g-card_bg" @click="store.isRandomizerCardOpen = false"></div>
<div class="card_content">
<transition name="slide-top">
<div class="warning-message" v-if="warningMessage">{{ warningMessage }}</div>
</transition>
<div class="card_wrapper" ref="cardWrapper" tabindex="0">
<h1><img :src="getIconURL('randomize')" alt="ikona losowania" /> LOSUJ SKŁAD</h1>
<div class="random-stock-selections">
<div class="first-row">
<h3>WŁAŚCIWOŚCI SKŁADU</h3>
<div class="max-values">
<span>
<label for="stock-mass">Maks. masa (t)</label>
<input type="number" id="stock-mass" v-model="maxStockMass" />
</span>
<span>
<label for="stock-mass">Maks. długość (m)</label>
<input type="number" id="stock-mass" v-model="maxStockLength" />
</span>
<span>
<label for="stock-count">Maks. liczba wagonów</label>
<input type="number" id="stock-count" v-model="maxStockCount" />
</span>
</div>
</div>
<div class="second-row">
<div class="select-box locos">
<h3>LOKOMOTYWA</h3>
<select
v-model="chosenLocomotive"
@change="onLocomotivePreviewSelect()"
@focus="onLocomotivePreviewSelect()"
>
<option :value="undefined">Wybierz lokomotywę</option>
<option v-for="loco in store.locoDataList.filter((l) => !l.type.includes('EN'))" :value="loco">
{{ loco.type }}
</option>
</select>
</div>
<div class="car-preview">
<div v-if="isPreviewLoading" class="loading">ŁADOWANIE...</div>
<span class="preview-message" v-if="!previewVehicle">
WYBIERZ POJAZD LUB WAGON, BY ZOBACZYĆ JEGO PODGLĄD
</span>
<img v-else :src="previewVehicle.imageSrc" :alt="previewVehicle.type" />
<span class="preview-message info" v-if="previewVehicle">
<button @click="prevPreviewIndex">&lt;</button>
<span>
{{ previewVehicle.type }}
{{
isLocomotive(previewVehicle)
? ''
: `(${previewIndex + 1} z ${focusedCarWagon?.availableCars.length})`
}}
</span>
<button @click="nextPreviewIndex">&gt;</button>
</span>
</div>
</div>
<div class="select-box carwagons">
<h3>
WAGONY
<button class="btn btn--text" @click="showRules = !showRules">[ zasady dodawania wagonów ]</button>
</h3>
<div class="rules" v-if="showRules">
<ul>
<li>
<b class="text--accent">Typ wagonu</b> musi zaczynać się typem konstrukcyjnym (np. <i>111a</i> lub
<i>203V</i>), wariantem np. <i>111a Grafitti 1</i> lub jego początkiem, np. <i>111a PKPIC</i> (wtedy
losowanie obejmuje wszystkie dostępne warianty typu o takim początku)
</li>
<li>
<b class="text--accent">Ładunek</b> można wybrać po uprzednim wpisaniu typu konstrukcyjnego wagonu
towarowego (zakładając, że je posiada)
</li>
<li>
<b class="text--accent">Szansa</b> (waga) określa prawdopodobieństwo wylosowania danego typu wagonu.
Im większa liczba względem reszty wag, tym bardziej prawdopodobne, że zostanie on wybrany
</li>
<li>
<b class="text--accent">Warianty</b> pokazują liczbę możliwych wagonów w puli w ramach losowania
danego typu
</li>
</ul>
</div>
<div class="list-wrapper">
<ul class="carwagon-list">
<li class="text--accent" style="font-weight: bold">
<div>Typ wagonu</div>
<div>Ładunek</div>
<div>Szansa</div>
<div>Warianty</div>
<div>Usuń</div>
</li>
<li v-for="(stockWagon, i) in chosenCarWagonList">
<div>
<input
class="carwagon-type g-input"
type="text"
list="types-datalist"
v-model="stockWagon.stockString"
@input="onCarWagonTypeInput(stockWagon)"
@focus="onCarWagonTypeFocus(stockWagon)"
placeholder="Wybierz wagon..."
/>
<datalist id="types-datalist">
<option value="">Wybierz wagon</option>
<option v-for="carOptionType in allCarOptionsList" :value="carOptionType">
{{ carOptionType }}
</option>
</datalist>
</div>
<div>
<select class="carwagon-cargo" v-model="stockWagon.chosenCargo">
<option :value="undefined">brak</option>
<option
:value="{ id: 'random', totalMass: 0 }"
v-if="stockWagon.availableCargo && stockWagon.availableCargo.length > 0"
>
losowy
</option>
<option v-for="cargo in stockWagon.availableCargo" :value="cargo">
{{ cargo.id }}
</option>
</select>
</div>
<div>
<span class="carwagon-chance">
<input type="number" v-model="stockWagon.chance" max="100" min="1" />
</span>
</div>
<div class="variant-count">{{ stockWagon.availableCars.length }}</div>
<div class="carwagon-remove">
<button @click="removeFromRandomStockList(i)">
<img :src="getIconURL('remove')" alt="remove" />
</button>
</div>
</li>
</ul>
</div>
<button class="btn btn--outline" style="margin-top: 0.5em;" @click="addToRandomStockList">+ NOWY WAGON</button>
</div>
</div>
<div class="stock-actions">
<button class="btn" style="font-size: 1.15em; margin-top: 2em" @click="generateRandomStock">
LOSUJ SKŁAD!
</button>
<button class="btn" style="font-size: 1.15em; margin-top: 2em" @click="store.isRandomizerCardOpen = false">
ZAMKNIJ
</button>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { ICargo, ICarWagon, ILocomotive, Vehicle } from '../../types';
import { useStore } from '../../store';
import stockMixin from '../../mixins/stockMixin';
import imageMixin from '../../mixins/imageMixin';
import { isLocomotive } from '../../utils/vehicleUtils';
interface RandomStockCarWagon {
stockString: string;
chance: number;
availableCars: ICarWagon[];
availableCargo?: ICargo[];
chosenCargo?: ICargo;
}
export default defineComponent({
setup() {
const store = useStore();
return {
store,
};
},
mixins: [stockMixin, imageMixin],
activated() {
(this.$refs['cardWrapper'] as any).focus();
},
data: () => ({
maxStockMass: 650,
maxStockLength: 350,
maxStockCount: 30,
chosenCarWagonList: [] as RandomStockCarWagon[],
chosenLocomotive: undefined as ILocomotive | undefined,
warningMessage: '',
showRules: false,
isPreviewLoading: false,
focusedCarWagon: undefined as RandomStockCarWagon | undefined,
randomFocusedWagonVariant: undefined as ICarWagon | undefined,
previewVehicle: undefined as Vehicle | undefined,
previewIndex: 0,
}),
watch: {
'focusedCarWagon.availableCars': {
handler(cars?: RandomStockCarWagon['availableCars'], prevCars?: RandomStockCarWagon['availableCars']) {
const prevAvailableCarsStr = prevCars?.map((car) => car.type).join(',') || '';
const availableCarsStr = cars?.map((car) => car.type).join(',') || '';
if (prevAvailableCarsStr != availableCarsStr || (this.previewVehicle && isLocomotive(this.previewVehicle))) {
this.previewIndex = 0;
this.randomFocusedWagonVariant = this.focusedCarWagon?.availableCars[this.previewIndex];
//~~(Math.random() * this.focusedCarWagon.availableCars.length)
this.previewVehicle = this.randomFocusedWagonVariant;
}
},
},
},
computed: {
allCarOptionsList() {
const list: string[] = [];
this.store.carDataList.forEach((carData) => {
const splittedTypeList = carData.type.split('_');
for (let i = 0; i < splittedTypeList.length; i++) {
const typeToCheck = carData.type
.split('_', i + 1)
.join('_')
.replace(/_/g, ' ');
if (!list.includes(typeToCheck)) list.push(typeToCheck);
}
});
return list.sort((a, b) => (a > b ? 1 : -1));
},
},
methods: {
isLocomotive,
nextPreviewIndex() {
if (!this.focusedCarWagon || (this.previewVehicle && isLocomotive(this.previewVehicle))) return;
if (this.previewIndex > this.focusedCarWagon.availableCars.length - 2) return;
this.randomFocusedWagonVariant = this.focusedCarWagon.availableCars[++this.previewIndex];
this.previewVehicle = this.randomFocusedWagonVariant;
},
prevPreviewIndex() {
if (!this.focusedCarWagon || (this.previewVehicle && isLocomotive(this.previewVehicle))) return;
if (this.previewIndex == 0) return;
this.randomFocusedWagonVariant = this.focusedCarWagon.availableCars[--this.previewIndex];
this.previewVehicle = this.randomFocusedWagonVariant;
},
onLocomotivePreviewSelect() {
this.previewVehicle = this.chosenLocomotive;
},
onCarWagonTypeInput(carWagon: RandomStockCarWagon) {
const constructionType = carWagon.stockString.split(' ')[0];
const carWagonObj = this.store.carDataList.find((car) => car.constructionType == constructionType);
const allAvailableCars = this.store.carDataList.filter(
(car) => car.type.startsWith(carWagon.stockString.replace(/ /g, '_')) && carWagon.stockString.length != 0
);
carWagon.availableCars = allAvailableCars;
carWagon.availableCargo = carWagonObj?.cargoList || undefined;
if (!carWagonObj?.cargoList) {
carWagon.chosenCargo = undefined;
}
},
onCarWagonTypeFocus(carWagon: RandomStockCarWagon) {
this.focusedCarWagon = carWagon;
this.previewVehicle = this.randomFocusedWagonVariant;
},
addToRandomStockList() {
const randTypeList = this.allCarOptionsList.filter((carOption) =>
/1|2/g.test(carOption.split(' ').length.toString())
);
// const randType = randTypeList[Math.floor(Math.random() * randTypeList.length)];
const randomStockCarWagon: RandomStockCarWagon = {
stockString: '',
chance: 10,
chosenCargo: undefined,
availableCargo: undefined,
availableCars: [],
};
this.chosenCarWagonList.push(randomStockCarWagon);
},
removeFromRandomStockList(index: number) {
this.chosenCarWagonList.splice(index, 1);
},
validateCarWagonList() {
return (
this.chosenCarWagonList.length > 0 &&
this.chosenCarWagonList.every((carWagon) => carWagon.availableCars.length > 0)
);
},
generateRandomStock() {
let totalLength = 0;
let totalMass = 0;
if (!this.chosenLocomotive) {
this.warningMessage = 'Nie wybrano lokomotywy!';
return;
}
if (!this.validateCarWagonList()) {
this.warningMessage = 'Wpisano niepoprawne wartości w liście wagonów!';
return;
}
this.store.stockList.length = 0;
this.addLocomotive(this.chosenLocomotive);
totalLength += this.chosenLocomotive.length;
totalMass += this.chosenLocomotive.mass;
while (true) {
const { carWagon, cargo } = this.getRandomStock();
const totalMassAfter = totalMass + (cargo?.totalMass || carWagon.mass);
const totalLengthAfter = totalLength + carWagon.length;
if (
this.store.stockList.length > this.maxStockCount ||
totalLengthAfter > this.maxStockLength ||
totalMassAfter > this.maxStockMass
)
break;
this.addCarWagon(carWagon, cargo);
totalLength = totalLengthAfter;
totalMass = totalMassAfter;
}
this.store.isRandomizerCardOpen = false;
this.warningMessage = '';
},
getRandomStock(): { carWagon: ICarWagon; cargo?: ICargo } {
const totalChancePot = this.chosenCarWagonList.reduce((total, car) => {
total += car.chance;
return total;
}, 0);
let rand = Math.random() * totalChancePot;
let randCarWagon: ICarWagon | undefined = undefined;
let randCargo: ICargo | undefined = undefined;
for (let wagonItem of this.chosenCarWagonList) {
if (rand < wagonItem.chance) {
randCarWagon = { ...wagonItem.availableCars[Math.floor(Math.random() * wagonItem.availableCars.length)] };
randCargo =
wagonItem.chosenCargo?.id == 'random'
? { ...wagonItem.availableCargo![~~(Math.random() * wagonItem.availableCargo!.length)] }
: wagonItem.chosenCargo;
break;
}
rand -= wagonItem.chance;
}
return { carWagon: randCarWagon!, cargo: randCargo };
},
},
});
</script>
<style lang="scss" scoped>
@import '../../styles/global.scss';
h1 {
display: flex;
justify-content: center;
align-items: center;
font-size: 2.5em;
text-align: center;
img {
width: 1.75em;
margin-right: 0.25em;
}
}
h3 {
color: $accentColor;
margin: 0 0 0.5em 0;
}
.card_content {
overflow-y: hidden;
border: 2px solid white;
width: 95vw;
max-width: 750px;
height: 90vh;
max-height: 900px;
background-color: #111;
border-radius: 1em;
z-index: 99;
.card_wrapper {
display: flex;
flex-direction: column;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
padding: 0.5em 1em;
}
}
.warning-message {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 101;
font-size: 1.2em;
text-align: center;
padding: 0.25em;
background-color: #b2222288;
}
.random-stock-selections {
text-align: left;
.select-box {
padding: 0.5em 0;
}
}
.first-row > .max-values {
display: flex;
align-items: center;
flex-wrap: wrap;
label {
display: block;
}
input {
width: auto;
margin: 0.25em 0.5em 0 0;
}
}
.second-row {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
margin-top: 1em;
.car-preview {
position: relative;
width: 300px;
height: 180px;
border: 1px solid white;
.loading {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
padding: 0.5em 0;
z-index: 102;
background-color: rgba(black, 0.75);
}
.preview-message {
font-weight: bold;
text-align: center;
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 100%;
padding: 0.5em;
&.info {
button {
font-size: 1.2em;
}
background-color: #111111dd;
display: flex;
justify-content: space-between;
text-align: center;
}
}
img {
width: 100%;
height: 100%;
}
}
}
.rules {
margin: 0.5em 0;
ul {
list-style: inside;
border: 1px solid $accentColor;
padding: 0.5em;
}
}
.list-wrapper {
overflow: auto;
}
.carwagon-list li {
margin: 0.5em 0;
min-width: 450px;
display: grid;
grid-template-columns: 3fr 2fr 1fr 1fr 3em;
gap: 0 0.5em;
align-items: center;
input,
select,
.variant-count {
width: 100%;
height: 35px;
}
.carwagon-chance input {
font-weight: bold;
}
.carwagon-remove {
display: flex;
justify-content: center;
align-items: center;
button img {
width: 1.15em;
vertical-align: middle;
}
}
.variant-count {
display: flex;
align-items: center;
font-weight: bold;
}
}
.stock-actions {
display: flex;
justify-content: center;
margin-top: auto;
padding: 1em 0;
.btn {
margin-right: 0.5em;
}
}
@media screen and (max-width: 650px) {
.car-preview {
width: 20em;
height: 13em;
margin: 0 auto;
}
h3 {
text-align: center;
}
.select-box {
margin: 0.5em auto;
}
.max-values {
justify-content: center;
span {
margin: 0.25em;
}
}
}
</style>
@@ -1,62 +1,62 @@
<template>
<div class="ready-stock-list" v-if="isOpen">
<div class="top-sticky">
<button class="btn btn--text exit" @click="exit">&lt; POWRÓT</button>
<div class="real-stock-card g-card" v-if="store.isRealStockListCardOpen">
<div class="g-card_bg" @click="store.isRealStockListCardOpen = false"></div>
<div class="header">
<h1>
REALNE ZESTAWIENIA
<div>by <a href="https://td2.info.pl/profile/?u=17708" target="_blank">Railtrains997</a></div>
</h1>
<p>
{{ isMobile ? 'Przytrzymaj zestawienie' : 'Kliknij na zestawienie prawym przyciskiem myszy' }}, aby zobaczyć
je na stronie <i>vagonweb.cz</i>
</p>
<div class="card_content">
<div class="top-sticky">
<button class="btn btn--text exit-btn" @click="store.isRealStockListCardOpen = false">&lt; POWRÓT</button>
<input type="text" tabindex="0" v-model="searchedReadyStockName" placeholder="Szukaj zestawienia..." />
<div class="header">
<h1>
REALNE ZESTAWIENIA
<div>by <a href="https://td2.info.pl/profile/?u=17708" target="_blank">Railtrains997</a></div>
</h1>
<p>
Pełne informacje o zestawieniach dostępne na stronie
<a href="http://bocznica.eu/files/archiwum/2021r_2021-11-04.html" target="_blank">bocznica.eu</a> (stan na
listopad 2021r.)
</p>
<input type="text" tabindex="0" v-model="searchedReadyStockName" placeholder="Szukaj zestawienia..." />
</div>
</div>
<ul v-if="responseStatus == 'loaded'">
<li
v-for="(stock, key) in computedReadyStockList"
:key="key"
tabindex="0"
@click="choseStock(stock.name, stock.type, stock.number, stock.stockString)"
@keydown.enter="choseStock(stock.name, stock.type, stock.number, stock.stockString)"
>
<img :src="getIconURL(stock.type)" :alt="stock.type" />
<b class="text--accent"> {{ stock.name }}</b>
<div>{{ stock.number }}</div>
</li>
</ul>
</div>
<ul v-if="responseStatus == 'loaded'">
<li
v-for="(stock, key) in computedReadyStockList"
:key="key"
tabindex="0"
@contextmenu="openPreview($event, stock.type, stock.number)"
@click="choseStock(stock.name, stock.type, stock.number, stock.stockString)"
@keydown.space="openPreview($event, stock.type, stock.number)"
@keydown.enter="choseStock(stock.name, stock.type, stock.number, stock.stockString)"
>
<img v-if="stock.type != 'iR' && stock.type != 'RE'" :src="icons[stock.type]" alt="" />
<span v-else>{{ stock.type }}</span>
<b class="text--accent"> {{ stock.name }}</b>
<div>{{ stock.number }}</div>
</li>
</ul>
</div>
</template>
<script lang="ts">
import { ICarWagon, ILocomotive, IStore } from '@/types';
import { defineComponent, inject } from 'vue';
import { defineComponent } from 'vue';
import { Vehicle, IStock, IReadyStockList } from '../../types';
interface ReadyStockList {
[key: string]: { stockString: string; type: string; number: string; name: string };
}
import { useStore } from '../../store';
import { isLocomotive } from '../../utils/vehicleUtils';
import imageMixin from '../../mixins/imageMixin';
interface ResponseJSONData {
[key: string]: string;
}
export default defineComponent({
mixins: [imageMixin],
setup() {
return {
isOpen: inject('isReadyStockListOpen'),
store: inject('Store') as IStore,
locoDataList: inject('locoDataList') as ILocomotive[],
carDataList: inject('carDataList') as ICarWagon[],
isLocomotive: inject('isLocomotive') as (vehicle: ILocomotive | ICarWagon) => vehicle is ILocomotive,
store: useStore(),
};
},
@@ -64,25 +64,18 @@ export default defineComponent({
responseStatus: 'loading',
isMobile: 'ontouchstart' in document.documentElement && navigator.userAgent.match(/Mobi/) ? true : false,
readyStockList: {} as ReadyStockList,
searchedReadyStockName: '',
icons: {
EIC: require('@/assets/EIC.png'),
IC: require('@/assets/IC.svg'),
TLK: require('@/assets/TLK.png'),
} as { [key: string]: string },
}),
computed: {
computedReadyStockList() {
if (this.searchedReadyStockName == null) return this.readyStockList;
if (this.searchedReadyStockName == null) return this.store.readyStockList;
let filtered: ReadyStockList = {};
let filtered: IReadyStockList = {};
for (let key in this.readyStockList) {
for (let key in this.store.readyStockList) {
if (key.toLocaleLowerCase().includes(this.searchedReadyStockName.toLocaleLowerCase()))
filtered[key] = this.readyStockList[key];
filtered[key] = this.store.readyStockList[key];
}
return filtered;
@@ -90,21 +83,8 @@ export default defineComponent({
},
methods: {
exit() {
this.isOpen = false;
},
openPreview(e: Event, type: string, number: string) {
e.preventDefault();
const isRegio = type == 'RE' || type == 'iR';
const zeme = isRegio ? 'PREG' : 'PKPIC';
const rok = isRegio ? '&rok=2013' : '';
const url = `https://www.vagonweb.cz/razeni/vlak.php?zeme=${zeme}&kategorie=${type}&cislo=${number}${rok}`;
window.open(url);
getImageUrl(name: string) {
return new URL(`./dir/${name}.png`, import.meta.url).href;
},
choseStock(name: string, type: string, number: string, stockString: string) {
@@ -117,32 +97,34 @@ export default defineComponent({
this.store.swapVehicles = false;
this.store.chosenRealStockName = `${type} ${number} ${name}`;
stockArray.forEach((type, i) => {
let vehicle;
if (i == 0) vehicle = this.locoDataList.find((loco) => loco.type == stockArray[0]);
else vehicle = this.carDataList.find((car) => car.type == type);
let vehicle: Vehicle | null = null;
if (i == 0) vehicle = this.store.locoDataList.find((loco) => loco.type == stockArray[0]) || null;
else vehicle = this.store.carDataList.find((car) => car.type == type) || null;
this.addVehicle(vehicle);
});
this.exit();
this.store.chosenStockListIndex = -1;
this.store.chosenVehicle = null;
this.store.isRealStockListCardOpen = false;
},
addVehicle(vehicle: ILocomotive | ICarWagon | undefined) {
addVehicle(vehicle: Vehicle | null) {
if (!vehicle) return;
const stockObj = {
const stockObj: IStock = {
id: `${Date.now() + this.store.stockList.length}`,
type: vehicle.type,
length: vehicle.length,
mass: vehicle.mass,
maxSpeed: vehicle.maxSpeed,
isLoco: this.isLocomotive(vehicle),
isLoco: isLocomotive(vehicle),
cargo: undefined,
count: 1,
imgSrc: vehicle.imageSrc,
useType: this.isLocomotive(vehicle) ? vehicle.power : vehicle.useType,
useType: isLocomotive(vehicle) ? vehicle.power : vehicle.useType,
supportersOnly: vehicle.supportersOnly,
};
@@ -176,7 +158,7 @@ export default defineComponent({
name += ' ' + splittedKey[i];
}
this.readyStockList[stockKey] = {
this.store.readyStockList[stockKey] = {
type: splittedKey[0],
number: splittedKey[1].replace(/_/g, '/'),
name,
@@ -190,9 +172,11 @@ export default defineComponent({
</script>
<style lang="scss" scoped>
.exit {
padding: 1em 0;
@import '../../styles/global.scss';
.exit-btn {
font-size: 1.2em;
margin: 0.5em 0;
}
input {
@@ -204,16 +188,8 @@ input {
}
}
.ready-stock-list {
position: fixed;
z-index: 101;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #333;
.card_content {
background-color: #1c1c1c;
border-radius: 1em;
height: 85vh;
@@ -223,11 +199,12 @@ input {
padding: 0 1em;
overflow-y: auto;
z-index: 100;
.top-sticky {
position: sticky;
top: 0;
background: #333;
background: #1c1c1c;
}
.header {
@@ -275,10 +252,10 @@ input {
cursor: pointer;
background: #444;
background: #2b2b2b;
img {
max-width: 1.5em;
height: 0.85em;
}
span {
@@ -296,3 +273,4 @@ input {
}
}
</style>
+5 -5
View File
@@ -1342,7 +1342,7 @@
],
[
"304C_PLK_Rob",
"304c",
"304C",
false,
false,
"140",
@@ -1446,7 +1446,7 @@
],
[
"Gor77_PKP_Bhp_01",
"Bhp",
"Gor77",
false,
false,
"120",
@@ -1454,7 +1454,7 @@
],
[
"Gor77_PKP_Bhp_02",
"Bhp",
"Gor77",
false,
false,
"120",
@@ -1462,7 +1462,7 @@
],
[
"Gor77_PKP_Bhp_03",
"Bhp",
"Gor77",
false,
false,
"120",
@@ -1585,7 +1585,7 @@
],
[
"29R_PLPOL_Zaekk_01",
"29R_PLPOL",
"29R",
true,
false,
"100",
+7 -30
View File
@@ -1,33 +1,10 @@
import { createApp, Directive } from "vue";
import App from "./App.vue";
import { Store, isLocomotive, locoDataList, carDataList, totalLength, totalMass, maxAllowedSpeed, maxStockSpeed, isTrainPassenger, warnings } from "./store";
const clickOutsideDirective: Directive = {
beforeMount(el, binding) {
el.clickOutsideEvent = (event: Event) => {
if (!(el == event.target || el.contains(event.target))) {
binding.value();
}
};
document.addEventListener("click", el.clickOutsideEvent);
},
}
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
const pinia = createPinia();
createApp(App)
.provide('Store', Store)
.provide('isLocomotive', isLocomotive)
.provide('locoDataList', locoDataList)
.provide('carDataList', carDataList)
.provide('totalMass', totalMass)
.provide('totalLength', totalLength)
.provide('maxStockSpeed', maxStockSpeed)
.provide('maxAllowedSpeed', maxAllowedSpeed)
.provide('isTrainPassenger', isTrainPassenger)
.provide('warnings', warnings)
.directive('click-outside', clickOutsideDirective)
.mount("#app");
.use(pinia)
.mount('#app');
+9
View File
@@ -0,0 +1,9 @@
import { defineComponent } from 'vue';
export default defineComponent({
methods: {
getIconURL(name: string, ext = 'svg'): string {
return `/images/icon-${name}.${ext}`;
},
},
});
+65
View File
@@ -0,0 +1,65 @@
import { defineComponent } from 'vue';
import { useStore } from '../store';
import { ICargo, ICarWagon, ILocomotive, IStock, Vehicle } from '../types';
import { isLocomotive } from '../utils/vehicleUtils';
export default defineComponent({
setup() {
return {
store: useStore(),
};
},
methods: {
getStockId() {
return `${Math.random().toString(36).slice(5)}`;
},
getStockObject(vehicle: Vehicle, cargo?: ICargo, count = 1): IStock {
const isLoco = isLocomotive(vehicle);
return {
id: this.getStockId(),
type: vehicle.type,
length: vehicle.length,
mass: vehicle.mass,
maxSpeed: vehicle.maxSpeed,
isLoco,
cargo: !isLoco && vehicle.loadable && cargo ? cargo : undefined,
count,
imgSrc: vehicle.imageSrc,
useType: isLoco ? vehicle.power : vehicle.useType,
supportersOnly: false,
};
},
addLocomotive(loco: ILocomotive) {
const previousStock =
this.store.stockList.length > 0 ? this.store.stockList[this.store.stockList.length - 1] : null;
if (previousStock && previousStock.type == loco.type) {
this.store.stockList[this.store.stockList.length - 1].count++;
return;
}
const stockObj = this.getStockObject(loco);
if (this.store.stockList.length > 0 && !this.store.stockList[0].isLoco) this.store.stockList.unshift(stockObj);
else this.store.stockList.push(stockObj);
},
addCarWagon(car: ICarWagon, cargo?: ICargo) {
const previousStock =
this.store.stockList.length > 0 ? this.store.stockList[this.store.stockList.length - 1] : null;
if (previousStock && previousStock.type == car.type && previousStock.cargo?.id == cargo?.id) {
this.store.stockList[this.store.stockList.length - 1].count++;
return;
}
const stockObj = this.getStockObject(car, cargo);
this.store.stockList.push(stockObj);
},
},
});
+65
View File
@@ -0,0 +1,65 @@
import { defineComponent } from 'vue';
import { useStore } from '../store';
export default defineComponent({
setup() {
const store = useStore();
return {
store,
};
},
computed: {
trainTooLong() {
return (
(this.store.totalLength > 350 && this.store.isTrainPassenger) ||
(this.store.totalLength > 650 && !this.store.isTrainPassenger)
);
},
trainTooHeavy() {
const totalMass = this.store.totalMass;
const isTrainPassenger = this.store.isTrainPassenger;
const stockList = this.store.stockList;
if (stockList.length == 0 || !stockList[0].isLoco) return false;
const activeLocomotiveType = stockList[0].type;
// Spalinowy SM
if (/^SM/.test(activeLocomotiveType) && totalMass > 2400) return true;
// Elektryczne EU07 / EP07 / EP08 / ET41
// Pasażerski elektr.
if (isTrainPassenger) {
if (/^(EU|EP)/.test(activeLocomotiveType) && totalMass > 650) return true;
if (/^ET/.test(activeLocomotiveType) && totalMass > 700) return true;
return false;
}
// Towarowy / inny elektr.
if (/^EU/.test(activeLocomotiveType) && totalMass > 2000) return true;
if (/^ET/.test(activeLocomotiveType) && totalMass > 4000) return true;
return false;
},
locoNotSuitable() {
return (
!this.store.isTrainPassenger &&
this.store.stockList.length > 1 &&
!this.store.stockList.every((stock) => stock.isLoco) &&
this.store.stockList.find((stock) => stock.isLoco && stock.type.startsWith('EP'))
);
},
tooManyLocomotives() {
return this.store.stockList.reduce((acc, stock) => {
if (stock.isLoco) acc += stock.count;
return acc;
}, 0) > 2;
},
},
});
+40 -301
View File
@@ -1,311 +1,50 @@
import { ICargo, ICarWagon, ILocomotive, IStock, IStore, IVehicleData } from "./types";
import { reactive } from "@vue/reactivity";
import vehicleDataJSON from "@/data/vehicleData.json";
import vehiclePropsJSON from "@/data/vehicleProps.json";
import { EVehicleUseType } from "./enums/EVehicleUseType";
import { computed } from "vue";
export const Store: IStore = reactive({
chosenCar: null as ICarWagon | null,
chosenLoco: null as ILocomotive | null,
chosenCargo: null as ICargo | null,
showSupporter: false,
imageLoading: false,
chosenLocoPower: "loco-e",
chosenCarUseType: "car-passenger",
stockList: [] as IStock[],
cargoOptions: [] as any[][],
swapVehicles: false,
chosenStockListIndex: -1,
chosenRealStockName: null,
// locoOptions: [] as ILocomotive[],
// carOptions: [] as ICarWagon[],
vehiclePreviewSrc: ""
})
export function isLocomotive(vehicle: ILocomotive | ICarWagon): vehicle is ILocomotive {
return (vehicle as ILocomotive).power !== undefined;
}
export const locoDataList = computed(() => Object.keys(vehicleDataJSON).reduce(
(acc, vehicleTypeKey) => {
if (!vehicleTypeKey.startsWith("loco")) return acc;
const locoVehiclesData = (vehicleDataJSON as IVehicleData)[
vehicleTypeKey
];
locoVehiclesData.forEach((loco) => {
if (Store.showSupporter && !loco[4]) return;
const locoType = loco[0] as string;
let length = 0,
mass = 0;
// Elektrowozy
if (vehicleTypeKey.startsWith("loco-e")) {
// 32m dla ET41, reszta 16
length = locoType.startsWith("ET") ? 32 : 16;
// 80t dla wszystkich EU06, EP08
mass = 80;
// 83t dla: EU07 o nr większych niż 300 & dla wszystkich EP07 oprócz nr 135,242,1002,1048
const locoNumber = Number(locoType.split("-")[1]);
if (
(locoType.startsWith("EU") && locoNumber > 300) ||
(locoType.startsWith("EP") &&
![242, 135, 1002, 1048].includes(locoNumber))
) {
mass = 83;
}
}
// Spalinowozy
if (vehicleTypeKey.startsWith("loco-s")) {
length = 14;
mass = 74;
}
// EZT
if (vehicleTypeKey.startsWith("loco-ezt")) {
// EN57
length = 65;
mass = 126;
// EN71
if (locoType.startsWith("EN71")) {
length = 86;
mass = 182;
}
// 2xEN57
if (locoType.startsWith("2EN57")) {
length = 130;
mass = 253;
}
}
// SZT
if (vehicleTypeKey.startsWith("loco-szt")) {
length = 14;
mass = 23;
}
acc.push({
power: vehicleTypeKey,
type: loco[0] as string,
constructionType: loco[1] as string,
cabinType: loco[2] as string,
maxSpeed: Number(loco[3] as string),
supportersOnly: loco[4] as boolean,
imageSrc: loco[5] as string,
length,
mass,
});
});
return acc;
},
[] as ILocomotive[]
));
export const carDataList = computed(() => Object.keys(vehicleDataJSON).reduce(
(acc, vehicleTypeKey) => {
if (!vehicleTypeKey.startsWith("car")) return acc;
const carVehiclesData = (vehicleDataJSON as IVehicleData)[
vehicleTypeKey
];
carVehiclesData.forEach((car) => {
if (Store.showSupporter && !car[3]) return;
import { IStore } from './types';
import { defineStore } from 'pinia';
import { carDataList, chosenRealStock, isTrainPassenger, locoDataList, maxStockSpeed, totalLength, totalMass } from './utils/vehicleUtils';
const carPropsData = vehiclePropsJSON.find((v) =>
car[0].toString().includes(v.type)
);
export const useStore = defineStore({
id: 'store',
state: () =>
({
chosenCar: null,
chosenLoco: null,
chosenCargo: null,
chosenVehicle: null,
acc.push({
useType: vehicleTypeKey,
type: car[0] as string,
constructionType: car[1] as string,
loadable: car[2] as boolean,
supportersOnly: car[3] as boolean,
maxSpeed: Number(car[4] as string),
imageSrc: car[5] as string,
cargoList:
carPropsData?.cargo.includes(";") ? carPropsData.cargo.split(";").map((cargo) => ({
id: cargo.split(":")[0],
totalMass: Number(cargo.split(":")[1]),
})) : [],
mass: carPropsData?.mass || 0,
length: carPropsData?.length || 0,
});
});
showSupporter: false,
imageLoading: false,
return acc;
},
[] as ICarWagon[]
));
chosenLocoPower: 'loco-e',
chosenCarUseType: 'car-passenger',
stockList: [],
cargoOptions: [],
readyStockList: {},
swapVehicles: false,
chosenStockListIndex: -1,
chosenRealStockName: undefined,
vehiclePreviewSrc: '',
isRandomizerCardOpen: false,
isRealStockListCardOpen: false,
} as IStore),
export const totalMass = computed(() => {
return Store.stockList.reduce(
(acc, stock) =>
acc +
(stock.cargo ? stock.cargo.totalMass : stock.mass) * stock.count,
0
)
getters: {
locoDataList: (state) => locoDataList(state),
carDataList: (state) => carDataList(state),
totalMass: (state) => totalMass(state),
totalLength: (state) => totalLength(state),
maxStockSpeed: (state) => maxStockSpeed(state),
isTrainPassenger: (state) => isTrainPassenger(state),
chosenRealStock: (state) => chosenRealStock(state)
},
});
export const totalLength = computed(() => {
return Store.stockList.reduce(
(acc, stock) => acc + stock.length * stock.count,
0
)
});
export const maxStockSpeed = computed(() => {
return Store.stockList.reduce(
(acc, stock) =>
stock.maxSpeed < acc || acc == 0 ? stock.maxSpeed : acc,
0
)
});
export const isTrainPassenger = computed(() => {
if (Store.stockList.length == 0) return false;
if (Store.stockList.every(stock => stock.isLoco)) return false;
return Store.stockList
.filter((stock) => !stock.isLoco)
.every((stock) => stock.useType === EVehicleUseType.CAR_PASSENGER);
})
export const maxAllowedSpeed = computed(() => {
if (Store.stockList.length < 1) return -1;
if (!Store.stockList[0].isLoco) return -1;
const headingLoco = Store.stockList[0];
if (headingLoco.type.startsWith("EU07")) {
if (isTrainPassenger.value && totalMass.value <= 650) return 125;
if (!isTrainPassenger.value && totalMass.value <= 2000) return 70;
return -1;
}
if (headingLoco.type.startsWith("EP07")) {
if (isTrainPassenger.value && totalMass.value <= 650) return 125;
if (!isTrainPassenger.value) return -1;
return -1;
}
if (headingLoco.type.startsWith("EP08")) {
if (isTrainPassenger.value && totalMass.value <= 650) return 140;
if (!isTrainPassenger.value) return -1;
return -1;
}
if (headingLoco.type.startsWith("ET41")) {
if (isTrainPassenger.value && totalMass.value <= 700) return 125;
if (!isTrainPassenger.value && totalMass.value <= 4000) return 70;
return -1;
}
if (headingLoco.type.startsWith("SM42")) {
if (totalMass.value <= 95) return 90;
if (totalMass.value <= 200) return 80;
if (totalMass.value <= 300) return 70;
if (totalMass.value <= 450) return 60;
if (totalMass.value <= 750) return 50;
if (totalMass.value <= 1130) return 40;
if (totalMass.value <= 1720) return 30;
if (totalMass.value <= 2400) return 20;
return -1;
}
return Store.stockList.reduce(
(acc, stock) =>
stock.maxSpeed < acc || acc == 0 ? stock.maxSpeed : acc,
0
);
})
export const warnings = {
trainTooLong: computed(() => {
if (isTrainPassenger.value && totalLength.value > 350) return true;
if (!isTrainPassenger.value && totalLength.value > 650) return true;
return false;
}),
locoNotSuitable: computed(() => {
if (!isTrainPassenger.value
&& Store.stockList.length > 1
&& !Store.stockList.every(stock => stock.isLoco)
&& Store.stockList.find(stock => stock.isLoco && stock.type.startsWith("EP"))) return true;
return false;
}),
trainTooHeavy: computed(() => {
if (Store.stockList.length == 0 || !Store.stockList[0].isLoco) return false;
const headingLoco = Store.stockList[0];
if (isTrainPassenger.value && (headingLoco.type.startsWith("EU") || headingLoco.type.startsWith("EP")) && totalMass.value > 650) return true;
if (isTrainPassenger.value && headingLoco.type.startsWith("ET") && totalMass.value > 700) return true;
if (!isTrainPassenger.value && headingLoco.type.startsWith("EU") && totalMass.value > 2000) return true;
if (!isTrainPassenger.value && headingLoco.type.startsWith("ET") && totalMass.value > 4000) return true;
if (headingLoco.type.startsWith("SM") && totalMass.value > 2400) return true;
return false;
}),
tooManyLocos: computed(() => {
if (Store.stockList.reduce((acc, stock) => {
if (!stock.isLoco) return acc;
acc += stock.count;
return acc;
}, 0) > 2) return true;
return false;
})
}
// export const trainTooLong = computed(() => {
// if (isTrainPassenger.value && totalLength.value > 350) return true;
// if (!isTrainPassenger.value && totalLength.value > 650) return true;
// return false;
// })
// export const locoNotSuitable = computed(() => {
// if (!isTrainPassenger.value && Store.stockList.length > 1 && Store.stockList.find(stock => stock.isLoco && stock.type.startsWith("EP"))) return true;
// return false;
// })
+184 -105
View File
@@ -1,169 +1,248 @@
@import url("https://fonts.googleapis.com/css2?family=Lato:wght@400;700;900&display=swap");
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@400;700;900&display=swap');
$bgColor: #2C3149;
$breakpointMd: 960px;
$breakpointSm: 550px;
$bgColor: #2c3149;
$textColor: #fff;
$accentColor: #FFD600;
$secondaryColor: #222;
$accentColor: #ffd600;
::-webkit-scrollbar {
width: 0.5rem;
height: 0.5rem;
width: 0.5rem;
height: 0.5rem;
&-track {
background: #222;
border-radius: 0.5rem;
}
&-track {
background: #222;
border-radius: 0.5rem;
}
&-thumb {
border-radius: 1rem;
background: #777;
}
&-thumb {
border-radius: 1rem;
background: #777;
}
}
body,
html {
margin: 0;
padding: 0;
margin: 0;
padding: 0;
min-height: 100vh;
font-family: "Lato", sans-serif;
font-family: 'Lato', sans-serif;
background-color: $bgColor;
width: 100vw;
overflow-x: hidden;
background-color: $bgColor;
width: 100vw;
overflow-x: hidden;
}
*,
*::before,
*::after {
box-sizing: border-box;
box-sizing: border-box;
}
a {
color: white;
text-decoration: none;
&:visited {
color: white;
text-decoration: none;
}
&:visited {
color: white;
}
&:hover, &:focus {
color: $accentColor;
}
}
p {
font-size: 1.2em;
font-weight: bold;
&:hover,
&:focus {
color: $accentColor;
}
}
select,
option,
input,
button {
font-family: "Lato", sans-serif;
font-size: 1em;
font-family: 'Lato', sans-serif;
font-size: 1em;
}
button {
border: none;
outline: none;
background: none;
border: none;
outline: none;
background: none;
padding: 0;
margin: 0;
padding: 0;
margin: 0;
cursor: pointer;
cursor: pointer;
font-size: 1em;
color: white;
font-size: 1em;
color: white;
&:hover {
color: $accentColor;
}
&:hover {
color: $accentColor;
}
}
button.btn {
padding: 0.25em 1em;
padding: 0.4em 0.75em;
border-radius: 0.25em;
border: 2px solid white;
outline: none;
outline: none;
background-color: #222;
transition: all 250ms;
&.btn--outline {
background: none;
font-weight: bold;
outline: 1px solid $accentColor;
}
&:focus-visible {
color: $accentColor;
outline: 1px solid white;
}
&[data-disabled='true'] {
user-select: none;
pointer-events: none;
-moz-user-select: none;
-webkit-user-select: none;
opacity: 0.7;
}
&--text {
font-weight: bold;
transition: all 250ms;
background: none;
padding: 0;
&:hover {
color: $accentColor;
border-color: $accentColor;
&:focus-visible {
outline: 1px solid white;
}
}
&:focus {
color: $accentColor;
}
&--choice {
padding: 0.25em 0.3em;
background-color: #222;
&--text {
font-weight: bold;
transition: all 250ms;
border: none;
&:focus-visible {
outline: 1px solid white;
}
}
}
select,
input {
background: none;
border: 2px solid white;
outline: none;
padding: 0.25em 0.35em;
button.btn-rect {
width: 1.5em;
height: 1.5em;
font-size: 1.65em;
color: white;
font-size: 1em;
line-height: 1em;
width: 18em;
border-radius: 0.25em;
border: 2px solid black;
outline: none;
background: white;
color: black;
transition: all 250ms;
&:hover {
color: white;
background-color: black;
}
}
select {
background: none;
border: 2px solid white;
outline: none;
padding: 0.25em 0.35em;
color: white;
font-size: 1em;
width: 18em;
&:focus-visible {
border-color: $accentColor;
}
}
option {
color: black;
border: none;
color: black;
border: none;
}
ul {
list-style: none;
margin: 0;
padding: 0;
list-style: none;
margin: 0;
padding: 0;
}
.text {
&--accent {
color: $accentColor;
}
&--accent {
color: $accentColor;
}
&--grayed {
color: gray;
}
}
&--grayed {
color: gray;
}
}
.g-card {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
z-index: 200;
&_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;
}
}
+15 -2
View File
@@ -1,8 +1,12 @@
export type Vehicle = ILocomotive | ICarWagon;
export interface IStore {
chosenCar: ICarWagon | null;
chosenLoco: ILocomotive | null;
chosenCargo: ICargo | null;
chosenVehicle: Vehicle | null;
showSupporter: boolean;
imageLoading: boolean;
@@ -10,13 +14,17 @@ export interface IStore {
chosenCarUseType: string;
stockList: IStock[];
readyStockList: IReadyStockList;
cargoOptions: any[][];
chosenStockListIndex: number;
chosenRealStockName: string | null;
chosenRealStockName?: string;
swapVehicles: boolean;
vehiclePreviewSrc: string;
isRandomizerCardOpen: boolean;
isRealStockListCardOpen: boolean;
}
export interface IVehicleData {
@@ -39,7 +47,7 @@ export interface ILocomotive {
export interface ICarWagon {
//"203V_PKPC_Fll_01","203V",true,false,"100",img
type: string;
useType: string;
useType: 'car-passenger' | 'car-cargo';
constructionType: string;
loadable: boolean;
supportersOnly: boolean;
@@ -57,6 +65,7 @@ export interface ICargo {
}
export interface IStock {
id: string;
useType: string;
type: string;
length: number;
@@ -67,4 +76,8 @@ export interface IStock {
supportersOnly: boolean;
count: number;
imgSrc: string;
}
export interface IReadyStockList {
[key: string]: { stockString: string; type: string; number: string; name: string };
}
-22
View File
@@ -1,22 +0,0 @@
import { EVehicleUseType } from "@/enums/EVehicleUseType";
import { ICarWagon, ILocomotive, IStock } from "@/types";
export const verifyTrainSpec = (stockList: IStock[], vehicleMass: number, vehicleUseType: string) => {
const hasHeadLoco = stockList.length > 0
&& (stockList[0].useType == EVehicleUseType.LOCO_ELECTRICAL
|| stockList[0].useType == EVehicleUseType.LOCO_DIESEL);
if (!hasHeadLoco) return;
const headLoco = stockList[0];
const carList = stockList.filter(stock => !stock.isLoco);
const isTrainPassenger = carList.length != 0
? carList.every(stock => stock.useType == EVehicleUseType.CAR_PASSENGER)
&& vehicleUseType == EVehicleUseType.CAR_PASSENGER
: false;
}
+275
View File
@@ -0,0 +1,275 @@
import { computed } from 'vue';
import { EVehicleUseType } from '../enums/EVehicleUseType';
import { ICarWagon, ILocomotive, IStore, IVehicleData } from '../types';
import vehicleDataJSON from '../data/vehicleData.json';
import vehiclePropsJSON from '../data/vehicleProps.json';
export function isLocomotive(vehicle: ILocomotive | ICarWagon): vehicle is ILocomotive {
return (vehicle as ILocomotive).power !== undefined;
}
export function locoDataList(state: IStore) {
return Object.keys(vehicleDataJSON).reduce((acc, vehicleTypeKey) => {
if (!vehicleTypeKey.startsWith('loco')) return acc;
const locoVehiclesData = (vehicleDataJSON as IVehicleData)[vehicleTypeKey];
locoVehiclesData.forEach((loco) => {
if (state.showSupporter && !loco[4]) return;
const locoType = loco[0] as string;
let length = 0,
mass = 0;
// Elektrowozy
if (vehicleTypeKey.startsWith('loco-e')) {
// 32m dla ET41, reszta 16
length = locoType.startsWith('ET') ? 32 : 16;
// 80t dla wszystkich EU06, EP08
mass = 80;
// 83t dla: EU07 o nr większych niż 300 & dla wszystkich EP07 oprócz nr 135,242,1002,1048
const locoNumber = Number(locoType.split('-')[1]);
if (
(locoType.startsWith('EU') && locoNumber > 300) ||
(locoType.startsWith('EP') && ![242, 135, 1002, 1048].includes(locoNumber))
) {
mass = 83;
}
}
// Spalinowozy
if (vehicleTypeKey.startsWith('loco-s')) {
length = 14;
mass = 74;
}
// EZT
if (vehicleTypeKey.startsWith('loco-ezt')) {
// EN57
length = 65;
mass = 126;
// EN71
if (locoType.startsWith('EN71')) {
length = 86;
mass = 182;
}
// 2xEN57
if (locoType.startsWith('2EN57')) {
length = 130;
mass = 253;
}
}
// SZT
if (vehicleTypeKey.startsWith('loco-szt')) {
length = 14;
mass = 23;
}
acc.push({
power: vehicleTypeKey,
type: loco[0] as string,
constructionType: loco[1] as string,
cabinType: loco[2] as string,
maxSpeed: Number(loco[3] as string),
supportersOnly: loco[4] as boolean,
imageSrc: loco[5] as string,
length,
mass,
});
});
return acc;
}, [] as ILocomotive[]);
}
export function carDataList(state: IStore) {
return Object.keys(vehicleDataJSON).reduce((acc, vehicleTypeKey) => {
if (!vehicleTypeKey.startsWith('car')) return acc;
const carVehiclesData = (vehicleDataJSON as IVehicleData)[vehicleTypeKey];
carVehiclesData.forEach((car) => {
if (state.showSupporter && !car[3]) return;
const carPropsData = vehiclePropsJSON.find((v) => car[0].toString().includes(v.type));
acc.push({
useType: vehicleTypeKey as 'car-passenger' | 'car-cargo',
type: car[0] as string,
constructionType: car[1] as string,
loadable: car[2] as boolean,
supportersOnly: car[3] as boolean,
maxSpeed: Number(car[4] as string),
imageSrc: car[5] as string,
cargoList: carPropsData?.cargo.includes(';')
? carPropsData.cargo.split(';').map((cargo) => ({
id: cargo.split(':')[0],
totalMass: Number(cargo.split(':')[1]),
}))
: [],
mass: carPropsData?.mass || 0,
length: carPropsData?.length || 0,
});
});
return acc;
}, [] as ICarWagon[]);
}
export function totalMass(state: IStore) {
return state.stockList.reduce(
(acc, stock) => acc + (stock.cargo ? stock.cargo.totalMass : stock.mass) * stock.count,
0
);
}
export function totalLength(state: IStore) {
return state.stockList.reduce((acc, stock) => acc + stock.length * stock.count, 0);
}
export function maxStockSpeed(state: IStore) {
return state.stockList.reduce((acc, stock) => (stock.maxSpeed < acc || acc == 0 ? stock.maxSpeed : acc), 0);
}
export function isTrainPassenger(state: IStore) {
if (state.stockList.length == 0) return false;
if (state.stockList.every((stock) => stock.isLoco)) return false;
return state.stockList
.filter((stock) => !stock.isLoco)
.every((stock) => stock.useType === EVehicleUseType.CAR_PASSENGER);
}
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 = Object.values(state.readyStockList).find((readyStock) => readyStock.stockString == currentStockString);
state.chosenRealStockName = realStockObj ? `${realStockObj.type} ${realStockObj.number} ${realStockObj.name}` : undefined;
return realStockObj;
}
// export function maxAllowedSpeed(state: IStore) {
// if (state.stockList.length < 1) return -1;
// if (!state.stockList[0].isLoco) return -1;
// const headingLoco = state.stockList[0];
// const isPassenger = isTrainPassenger(state);
// if (headingLoco.type.startsWith('EU07')) {
// if (isPassenger && totalMass.value <= 650) return 125;
// if (!isPassenger && totalMass.value <= 2000) return 70;
// return -1;
// }
// if (headingLoco.type.startsWith('EP07')) {
// if (isPassenger && totalMass.value <= 650) return 125;
// if (!isPassenger) return -1;
// return -1;
// }
// if (headingLoco.type.startsWith('EP08')) {
// if (isPassenger && totalMass.value <= 650) return 140;
// if (!isPassenger) return -1;
// return -1;
// }
// if (headingLoco.type.startsWith('ET41')) {
// if (isPassenger && totalMass.value <= 700) return 125;
// if (!isPassenger && totalMass.value <= 4000) return 70;
// return -1;
// }
// if (headingLoco.type.startsWith('SM42')) {
// if (totalMass.value <= 95) return 90;
// if (totalMass.value <= 200) return 80;
// if (totalMass.value <= 300) return 70;
// if (totalMass.value <= 450) return 60;
// if (totalMass.value <= 750) return 50;
// if (totalMass.value <= 1130) return 40;
// if (totalMass.value <= 1720) return 30;
// if (totalMass.value <= 2400) return 20;
// return -1;
// }
// return Store.stockList.reduce((acc, stock) => (stock.maxSpeed < acc || acc == 0 ? stock.maxSpeed : acc), 0);
// });
// export const warnings = {
// trainTooLong: computed(() => {
// if (isTrainPassenger.value && totalLength.value > 350) return true;
// if (!isTrainPassenger.value && totalLength.value > 650) return true;
// return false;
// }),
// locoNotSuitable: computed(() => {
// if (
// !isTrainPassenger.value &&
// Store.stockList.length > 1 &&
// !Store.stockList.every((stock) => stock.isLoco) &&
// Store.stockList.find((stock) => stock.isLoco && stock.type.startsWith('EP'))
// )
// return true;
// return false;
// }),
// trainTooHeavy: computed(() => {
// if (Store.stockList.length == 0 || !Store.stockList[0].isLoco) return false;
// const headingLoco = Store.stockList[0];
// if (
// isTrainPassenger.value &&
// (headingLoco.type.startsWith('EU') || headingLoco.type.startsWith('EP')) &&
// totalMass.value > 650
// )
// return true;
// if (isTrainPassenger.value && headingLoco.type.startsWith('ET') && totalMass.value > 700) return true;
// if (!isTrainPassenger.value && headingLoco.type.startsWith('EU') && totalMass.value > 2000) return true;
// if (!isTrainPassenger.value && headingLoco.type.startsWith('ET') && totalMass.value > 4000) return true;
// if (headingLoco.type.startsWith('SM') && totalMass.value > 2400) return true;
// return false;
// }),
// tooManyLocos: computed(() => {
// if (
// Store.stockList.reduce((acc, stock) => {
// if (!stock.isLoco) return acc;
// acc += stock.count;
// return acc;
// }, 0) > 2
// )
// return true;
// return false;
// }),
// };
+2 -1
View File
@@ -1,4 +1,5 @@
/* eslint-disable */
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
+11 -34
View File
@@ -1,41 +1,18 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM"],
"skipLibCheck": true
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
+9
View File
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
+7
View File
@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()]
})
-3
View File
@@ -1,3 +0,0 @@
module.exports = {
lintOnSave: false,
};
+528
View File
@@ -0,0 +1,528 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@babel/parser@^7.16.4":
version "7.18.9"
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.18.9.tgz"
integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==
"@vitejs/plugin-vue@^3.0.0":
version "3.0.1"
resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-3.0.1.tgz"
integrity sha512-Ll9JgxG7ONIz/XZv3dssfoMUDu9qAnlJ+km+pBA0teYSXzwPCIzS/e1bmwNYl5dcQGs677D21amgfYAnzMl17A==
"@volar/code-gen@0.38.9":
version "0.38.9"
resolved "https://registry.npmjs.org/@volar/code-gen/-/code-gen-0.38.9.tgz"
integrity sha512-n6LClucfA+37rQeskvh9vDoZV1VvCVNy++MAPKj2dT4FT+Fbmty/SDQqnsEBtdEe6E3OQctFvA/IcKsx3Mns0A==
dependencies:
"@volar/source-map" "0.38.9"
"@volar/source-map@0.38.9":
version "0.38.9"
resolved "https://registry.npmjs.org/@volar/source-map/-/source-map-0.38.9.tgz"
integrity sha512-ba0UFoHDYry+vwKdgkWJ6xlQT+8TFtZg1zj9tSjj4PykW1JZDuM0xplMotLun4h3YOoYfY9K1huY5gvxmrNLIw==
"@volar/vue-code-gen@0.38.9":
version "0.38.9"
resolved "https://registry.npmjs.org/@volar/vue-code-gen/-/vue-code-gen-0.38.9.tgz"
integrity sha512-tzj7AoarFBKl7e41MR006ncrEmNPHALuk8aG4WdDIaG387X5//5KhWC5Ff3ZfB2InGSeNT+CVUd74M0gS20rjA==
dependencies:
"@volar/code-gen" "0.38.9"
"@volar/source-map" "0.38.9"
"@vue/compiler-core" "^3.2.37"
"@vue/compiler-dom" "^3.2.37"
"@vue/shared" "^3.2.37"
"@volar/vue-typescript@0.38.9":
version "0.38.9"
resolved "https://registry.npmjs.org/@volar/vue-typescript/-/vue-typescript-0.38.9.tgz"
integrity sha512-iJMQGU91ADi98u8V1vXd2UBmELDAaeSP0ZJaFjwosClQdKlJQYc6MlxxKfXBZisHqfbhdtrGRyaryulnYtliZw==
dependencies:
"@volar/code-gen" "0.38.9"
"@volar/source-map" "0.38.9"
"@volar/vue-code-gen" "0.38.9"
"@vue/compiler-sfc" "^3.2.37"
"@vue/reactivity" "^3.2.37"
"@vue/compiler-core@3.2.37", "@vue/compiler-core@^3.2.37":
version "3.2.37"
resolved "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.37.tgz"
integrity sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg==
dependencies:
"@babel/parser" "^7.16.4"
"@vue/shared" "3.2.37"
estree-walker "^2.0.2"
source-map "^0.6.1"
"@vue/compiler-dom@3.2.37", "@vue/compiler-dom@^3.2.37":
version "3.2.37"
resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz"
integrity sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ==
dependencies:
"@vue/compiler-core" "3.2.37"
"@vue/shared" "3.2.37"
"@vue/compiler-sfc@3.2.37", "@vue/compiler-sfc@^3.2.37":
version "3.2.37"
resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz"
integrity sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg==
dependencies:
"@babel/parser" "^7.16.4"
"@vue/compiler-core" "3.2.37"
"@vue/compiler-dom" "3.2.37"
"@vue/compiler-ssr" "3.2.37"
"@vue/reactivity-transform" "3.2.37"
"@vue/shared" "3.2.37"
estree-walker "^2.0.2"
magic-string "^0.25.7"
postcss "^8.1.10"
source-map "^0.6.1"
"@vue/compiler-ssr@3.2.37":
version "3.2.37"
resolved "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz"
integrity sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw==
dependencies:
"@vue/compiler-dom" "3.2.37"
"@vue/shared" "3.2.37"
"@vue/devtools-api@^6.2.1":
version "6.2.1"
resolved "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.2.1.tgz"
integrity sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ==
"@vue/reactivity-transform@3.2.37":
version "3.2.37"
resolved "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz"
integrity sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg==
dependencies:
"@babel/parser" "^7.16.4"
"@vue/compiler-core" "3.2.37"
"@vue/shared" "3.2.37"
estree-walker "^2.0.2"
magic-string "^0.25.7"
"@vue/reactivity@3.2.37", "@vue/reactivity@^3.2.37":
version "3.2.37"
resolved "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.37.tgz"
integrity sha512-/7WRafBOshOc6m3F7plwzPeCu/RCVv9uMpOwa/5PiY1Zz+WLVRWiy0MYKwmg19KBdGtFWsmZ4cD+LOdVPcs52A==
dependencies:
"@vue/shared" "3.2.37"
"@vue/runtime-core@3.2.37":
version "3.2.37"
resolved "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.37.tgz"
integrity sha512-JPcd9kFyEdXLl/i0ClS7lwgcs0QpUAWj+SKX2ZC3ANKi1U4DOtiEr6cRqFXsPwY5u1L9fAjkinIdB8Rz3FoYNQ==
dependencies:
"@vue/reactivity" "3.2.37"
"@vue/shared" "3.2.37"
"@vue/runtime-dom@3.2.37":
version "3.2.37"
resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.37.tgz"
integrity sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw==
dependencies:
"@vue/runtime-core" "3.2.37"
"@vue/shared" "3.2.37"
csstype "^2.6.8"
"@vue/server-renderer@3.2.37":
version "3.2.37"
resolved "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.37.tgz"
integrity sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA==
dependencies:
"@vue/compiler-ssr" "3.2.37"
"@vue/shared" "3.2.37"
"@vue/shared@3.2.37", "@vue/shared@^3.2.37":
version "3.2.37"
resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.2.37.tgz"
integrity sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw==
anymatch@~3.1.2:
version "3.1.2"
resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz"
integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
dependencies:
normalize-path "^3.0.0"
picomatch "^2.0.4"
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
braces@~3.0.2:
version "3.0.2"
resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
"chokidar@>=3.0.0 <4.0.0":
version "3.5.3"
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
dependencies:
anymatch "~3.1.2"
braces "~3.0.2"
glob-parent "~5.1.2"
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
readdirp "~3.6.0"
optionalDependencies:
fsevents "~2.3.2"
csstype@^2.6.8:
version "2.6.20"
resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz"
integrity sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==
esbuild-android-64@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.50.tgz#a46fc80fa2007690e647680d837483a750a3097f"
integrity sha512-H7iUEm7gUJHzidsBlFPGF6FTExazcgXL/46xxLo6i6bMtPim6ZmXyTccS8yOMpy6HAC6dPZ/JCQqrkkin69n6Q==
esbuild-android-arm64@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.50.tgz#bdda7851fa7f5f770d6ff0ad593a8945d3a0fcdd"
integrity sha512-NFaoqEwa+OYfoYVpQWDMdKII7wZZkAjtJFo1WdnBeCYlYikvUhTnf2aPwPu5qEAw/ie1NYK0yn3cafwP+kP+OQ==
esbuild-darwin-64@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.50.tgz#f0535435f9760766f30db14a991ee5ca94c022a4"
integrity sha512-gDQsCvGnZiJv9cfdO48QqxkRV8oKAXgR2CGp7TdIpccwFdJMHf8hyIJhMW/05b/HJjET/26Us27Jx91BFfEVSA==
esbuild-darwin-arm64@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.50.tgz#76a41a40e8947a15ae62970e9ed2853883c4b16c"
integrity sha512-36nNs5OjKIb/Q50Sgp8+rYW/PqirRiFN0NFc9hEvgPzNJxeJedktXwzfJSln4EcRFRh5Vz4IlqFRScp+aiBBzA==
esbuild-freebsd-64@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.50.tgz#2ed6633c17ed42c20a1bd68e82c4bbc75ea4fb57"
integrity sha512-/1pHHCUem8e/R86/uR+4v5diI2CtBdiWKiqGuPa9b/0x3Nwdh5AOH7lj+8823C6uX1e0ufwkSLkS+aFZiBCWxA==
esbuild-freebsd-arm64@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.50.tgz#cb115f4cdafe9cdbe58875ba482fccc54d32aa43"
integrity sha512-iKwUVMQztnPZe5pUYHdMkRc9aSpvoV1mkuHlCoPtxZA3V+Kg/ptpzkcSY+fKd0kuom+l6Rc93k0UPVkP7xoqrw==
esbuild-linux-32@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.50.tgz#fe2b724994dcf1d4e48dc4832ff008ad7d00bcfd"
integrity sha512-sWUwvf3uz7dFOpLzYuih+WQ7dRycrBWHCdoXJ4I4XdMxEHCECd8b7a9N9u7FzT6XR2gHPk9EzvchQUtiEMRwqw==
esbuild-linux-64@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.50.tgz#7851ab5151df9501a2187bd4909c594ad232b623"
integrity sha512-u0PQxPhaeI629t4Y3EEcQ0wmWG+tC/LpP2K7yDFvwuPq0jSQ8SIN+ARNYfRjGW15O2we3XJvklbGV0wRuUCPig==
esbuild-linux-arm64@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.50.tgz#76a76afef484a0512f1fbbcc762edd705dee8892"
integrity sha512-ZyfoNgsTftD7Rp5S7La5auomKdNeB3Ck+kSKXC4pp96VnHyYGjHHXWIlcbH8i+efRn9brszo1/Thl1qn8RqmhQ==
esbuild-linux-arm@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.50.tgz#6d7a8c0712091b0c3a668dd5d8b5c924adbaeb12"
integrity sha512-VALZq13bhmFJYFE/mLEb+9A0w5vo8z+YDVOWeaf9vOTrSC31RohRIwtxXBnVJ7YKLYfEMzcgFYf+OFln3Y0cWg==
esbuild-linux-mips64le@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.50.tgz#43426909c1884c5dc6b40765673a08a7ec1d2064"
integrity sha512-ygo31Vxn/WrmjKCHkBoutOlFG5yM9J2UhzHb0oWD9O61dGg+Hzjz9hjf5cmM7FBhAzdpOdEWHIrVOg2YAi6rTw==
esbuild-linux-ppc64le@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.50.tgz#c754ea3da1dd180c6e9b6b508dc18ce983d92b11"
integrity sha512-xWCKU5UaiTUT6Wz/O7GKP9KWdfbsb7vhfgQzRfX4ahh5NZV4ozZ4+SdzYG8WxetsLy84UzLX3Pi++xpVn1OkFQ==
esbuild-linux-riscv64@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.50.tgz#f3b2dd3c4c2b91bf191d3b98a9819c8aa6f5ad7f"
integrity sha512-0+dsneSEihZTopoO9B6Z6K4j3uI7EdxBP7YSF5rTwUgCID+wHD3vM1gGT0m+pjCW+NOacU9kH/WE9N686FHAJg==
esbuild-linux-s390x@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.50.tgz#3dfbc4578b2a81995caabb79df2b628ea86a5390"
integrity sha512-tVjqcu8o0P9H4StwbIhL1sQYm5mWATlodKB6dpEZFkcyTI8kfIGWiWcrGmkNGH2i1kBUOsdlBafPxR3nzp3TDA==
esbuild-netbsd-64@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.50.tgz#17dbf51eaa48d983e794b588d195415410ef8c85"
integrity sha512-0R/glfqAQ2q6MHDf7YJw/TulibugjizBxyPvZIcorH0Mb7vSimdHy0XF5uCba5CKt+r4wjax1mvO9lZ4jiAhEg==
esbuild-openbsd-64@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.50.tgz#cf6b1a50c8cf67b0725aaa4bce9773976168c50e"
integrity sha512-7PAtmrR5mDOFubXIkuxYQ4bdNS6XCK8AIIHUiZxq1kL8cFIH5731jPcXQ4JNy/wbj1C9sZ8rzD8BIM80Tqk29w==
esbuild-sunos-64@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.50.tgz#f705ae0dd914c3b45dc43319c4f532216c3d841f"
integrity sha512-gBxNY/wyptvD7PkHIYcq7se6SQEXcSC8Y7mE0FJB+CGgssEWf6vBPfTTZ2b6BWKnmaP6P6qb7s/KRIV5T2PxsQ==
esbuild-windows-32@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.50.tgz#6364905a99c1e6c1e2fe7bfccebd958131b1cd6c"
integrity sha512-MOOe6J9cqe/iW1qbIVYSAqzJFh0p2LBLhVUIWdMVnNUNjvg2/4QNX4oT4IzgDeldU+Bym9/Tn6+DxvUHJXL5Zw==
esbuild-windows-64@0.14.50:
version "0.14.50"
resolved "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.50.tgz"
integrity sha512-r/qE5Ex3w1jjGv/JlpPoWB365ldkppUlnizhMxJgojp907ZF1PgLTuW207kgzZcSCXyquL9qJkMsY+MRtaZ5yQ==
esbuild-windows-arm64@0.14.50:
version "0.14.50"
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.50.tgz#e7ddde6a97194051a5a4ac05f4f5900e922a7ea5"
integrity sha512-EMS4lQnsIe12ZyAinOINx7eq2mjpDdhGZZWDwPZE/yUTN9cnc2Ze/xUTYIAyaJqrqQda3LnDpADKpvLvol6ENQ==
esbuild@^0.14.47:
version "0.14.50"
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.50.tgz"
integrity sha512-SbC3k35Ih2IC6trhbMYW7hYeGdjPKf9atTKwBUHqMCYFZZ9z8zhuvfnZihsnJypl74FjiAKjBRqFkBkAd0rS/w==
optionalDependencies:
esbuild-android-64 "0.14.50"
esbuild-android-arm64 "0.14.50"
esbuild-darwin-64 "0.14.50"
esbuild-darwin-arm64 "0.14.50"
esbuild-freebsd-64 "0.14.50"
esbuild-freebsd-arm64 "0.14.50"
esbuild-linux-32 "0.14.50"
esbuild-linux-64 "0.14.50"
esbuild-linux-arm "0.14.50"
esbuild-linux-arm64 "0.14.50"
esbuild-linux-mips64le "0.14.50"
esbuild-linux-ppc64le "0.14.50"
esbuild-linux-riscv64 "0.14.50"
esbuild-linux-s390x "0.14.50"
esbuild-netbsd-64 "0.14.50"
esbuild-openbsd-64 "0.14.50"
esbuild-sunos-64 "0.14.50"
esbuild-windows-32 "0.14.50"
esbuild-windows-64 "0.14.50"
esbuild-windows-arm64 "0.14.50"
estree-walker@^2.0.2:
version "2.0.2"
resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz"
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
dependencies:
to-regex-range "^5.0.1"
fsevents@~2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
has@^1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz"
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
dependencies:
function-bind "^1.1.1"
immutable@^4.0.0:
version "4.1.0"
resolved "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz"
integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
dependencies:
binary-extensions "^2.0.0"
is-core-module@^2.9.0:
version "2.9.0"
resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz"
integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==
dependencies:
has "^1.0.3"
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
is-glob@^4.0.1, is-glob@~4.0.1:
version "4.0.3"
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
dependencies:
is-extglob "^2.1.1"
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
magic-string@^0.25.7:
version "0.25.9"
resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz"
integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==
dependencies:
sourcemap-codec "^1.4.8"
nanoid@^3.3.4:
version "3.3.4"
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz"
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
picomatch@^2.0.4, picomatch@^2.2.1:
version "2.3.1"
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
pinia@^2.0.17:
version "2.0.17"
resolved "https://registry.npmjs.org/pinia/-/pinia-2.0.17.tgz"
integrity sha512-AtwLwEWQgIjofjgeFT+nxbnK5lT2QwQjaHNEDqpsi2AiCwf/NY78uWTeHUyEhiiJy8+sBmw0ujgQMoQbWiZDfA==
dependencies:
"@vue/devtools-api" "^6.2.1"
vue-demi "*"
postcss@^8.1.10, postcss@^8.4.14:
version "8.4.14"
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz"
integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==
dependencies:
nanoid "^3.3.4"
picocolors "^1.0.0"
source-map-js "^1.0.2"
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz"
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
dependencies:
picomatch "^2.2.1"
resolve@^1.22.1:
version "1.22.1"
resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz"
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
dependencies:
is-core-module "^2.9.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
rollup@^2.75.6:
version "2.77.0"
resolved "https://registry.npmjs.org/rollup/-/rollup-2.77.0.tgz"
integrity sha512-vL8xjY4yOQEw79DvyXLijhnhh+R/O9zpF/LEgkCebZFtb6ELeN9H3/2T0r8+mp+fFTBHZ5qGpOpW2ela2zRt3g==
optionalDependencies:
fsevents "~2.3.2"
sass@^1.26.5:
version "1.54.0"
resolved "https://registry.npmjs.org/sass/-/sass-1.54.0.tgz"
integrity sha512-C4zp79GCXZfK0yoHZg+GxF818/aclhp9F48XBu/+bm9vXEVAYov9iU3FBVRMq3Hx3OA4jfKL+p2K9180mEh0xQ==
dependencies:
chokidar ">=3.0.0 <4.0.0"
immutable "^4.0.0"
source-map-js ">=0.6.2 <2.0.0"
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
sourcemap-codec@^1.4.8:
version "1.4.8"
resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz"
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz"
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
dependencies:
is-number "^7.0.0"
typescript@^4.6.4:
version "4.7.4"
resolved "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz"
integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==
vite@^3.0.0:
version "3.0.3"
resolved "https://registry.npmjs.org/vite/-/vite-3.0.3.tgz"
integrity sha512-sDIpIcl3mv1NUaSzZwiXGEy1ZoWwwC2vkxUHY6yiDacR6zf//ZFuBJrozO62gedpE43pmxnLATNR5IYUdAEkMQ==
dependencies:
esbuild "^0.14.47"
postcss "^8.4.14"
resolve "^1.22.1"
rollup "^2.75.6"
optionalDependencies:
fsevents "~2.3.2"
vue-demi@*:
version "0.13.5"
resolved "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.5.tgz"
integrity sha512-tO3K2bML3AwiHmVHeKCq6HLef2st4zBXIV5aEkoJl6HZ+gJWxWv2O8wLH8qrA3SX3lDoTDHNghLX1xZg83MXvw==
vue-tsc@^0.38.4:
version "0.38.9"
resolved "https://registry.npmjs.org/vue-tsc/-/vue-tsc-0.38.9.tgz"
integrity sha512-Yoy5phgvGqyF98Fb4mYqboR4Q149jrdcGv5kSmufXJUq++RZJ2iMVG0g6zl+v3t4ORVWkQmRpsV4x2szufZ0LQ==
dependencies:
"@volar/vue-typescript" "0.38.9"
vue@^3.2.37:
version "3.2.37"
resolved "https://registry.npmjs.org/vue/-/vue-3.2.37.tgz"
integrity sha512-bOKEZxrm8Eh+fveCqS1/NkG/n6aMidsI6hahas7pa0w/l7jkbssJVsRhVDs07IdDq7h9KHswZOgItnwJAgtVtQ==
dependencies:
"@vue/compiler-dom" "3.2.37"
"@vue/compiler-sfc" "3.2.37"
"@vue/runtime-dom" "3.2.37"
"@vue/server-renderer" "3.2.37"
"@vue/shared" "3.2.37"