mirror of
https://github.com/Spythere/pojazdownik.git
synced 2026-05-03 05:18:10 +00:00
chore: improved stock actions layout; added stock bookmarking
This commit is contained in:
+22
-2
@@ -18,10 +18,30 @@ export default defineComponent({
|
||||
return { store: useStore() };
|
||||
},
|
||||
|
||||
async created() {
|
||||
this.store.handleRouting();
|
||||
created() {
|
||||
this.loadStockDataFromStorage();
|
||||
this.store.setupAPIData();
|
||||
},
|
||||
|
||||
methods: {
|
||||
loadStockDataFromStorage() {
|
||||
const savedData = localStorage.getItem('savedStockData');
|
||||
|
||||
if (!savedData) {
|
||||
localStorage.setItem('savedStockData', JSON.stringify({}));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.store.storageStockData = JSON.parse(savedData);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Wystąpił błąd podczas przetwarzania danych o składach z localStorage!',
|
||||
error
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -2,15 +2,21 @@
|
||||
<section class="stock-list-tab">
|
||||
<div class="tab_content">
|
||||
<div class="stock_actions">
|
||||
<button class="btn btn--image" @click="clickFileInput">
|
||||
<button
|
||||
class="btn btn--image"
|
||||
@click="clickFileInput"
|
||||
:data-button-tooltip="$t('stocklist.action-upload-file')"
|
||||
>
|
||||
<input type="file" @change="uploadStockFromFile" ref="conFile" accept=".con,.txt" />
|
||||
<ArrowUpTrayIcon />
|
||||
{{ $t('stocklist.action-upload-file') }}
|
||||
<FolderPlusIcon />
|
||||
</button>
|
||||
|
||||
<button class="btn btn--image" @click="uploadStockFromClipboard">
|
||||
<ArrowUpTrayIcon />
|
||||
{{ $t('stocklist.action-upload-clipboard') }}
|
||||
<button
|
||||
class="btn btn--image"
|
||||
@click="uploadStockFromClipboard"
|
||||
:data-button-tooltip="$t('stocklist.action-upload-clipboard')"
|
||||
>
|
||||
<ClipboardDocumentCheckIcon />
|
||||
</button>
|
||||
|
||||
<button
|
||||
@@ -18,9 +24,9 @@
|
||||
:data-disabled="stockIsEmpty"
|
||||
:disabled="stockIsEmpty"
|
||||
@click="downloadStock"
|
||||
:data-button-tooltip="$t('stocklist.action-download')"
|
||||
>
|
||||
<ArrowDownTrayIcon />
|
||||
{{ $t('stocklist.action-download') }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
@@ -28,9 +34,19 @@
|
||||
:data-disabled="stockIsEmpty"
|
||||
:disabled="stockIsEmpty"
|
||||
@click="copyToClipboard"
|
||||
:data-button-tooltip="$t('stocklist.action-copy')"
|
||||
>
|
||||
<ClipboardDocumentIcon />
|
||||
{{ $t('stocklist.action-copy') }}
|
||||
<DocumentDuplicateIcon />
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="btn btn--image"
|
||||
:data-disabled="stockIsEmpty"
|
||||
:disabled="stockIsEmpty"
|
||||
@click="saveStockDataToStorage"
|
||||
:data-button-tooltip="$t('stocklist.action-bookmark')"
|
||||
>
|
||||
<BookmarkIcon />
|
||||
</button>
|
||||
|
||||
<button
|
||||
@@ -38,9 +54,9 @@
|
||||
:data-disabled="stockIsEmpty"
|
||||
:disabled="stockIsEmpty"
|
||||
@click="resetStock"
|
||||
:data-button-tooltip="$t('stocklist.action-reset')"
|
||||
>
|
||||
<ArrowUturnLeftIcon />
|
||||
{{ $t('stocklist.action-reset') }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
@@ -48,9 +64,9 @@
|
||||
:data-disabled="stockIsEmpty"
|
||||
:disabled="stockIsEmpty"
|
||||
@click="shuffleCars"
|
||||
:data-button-tooltip="$t('stocklist.action-shuffle')"
|
||||
>
|
||||
<ArrowPathIcon />
|
||||
{{ $t('stocklist.action-shuffle') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -244,10 +260,14 @@ import {
|
||||
ArrowPathIcon,
|
||||
ArrowUpTrayIcon,
|
||||
ArrowUturnLeftIcon,
|
||||
BookmarkIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
ClipboardDocumentIcon,
|
||||
FolderPlusIcon,
|
||||
ClipboardDocumentCheckIcon,
|
||||
TrashIcon,
|
||||
DocumentDuplicateIcon,
|
||||
} from '@heroicons/vue/20/solid';
|
||||
|
||||
export default defineComponent({
|
||||
@@ -255,13 +275,17 @@ export default defineComponent({
|
||||
components: {
|
||||
StockThumbnails,
|
||||
Checkbox,
|
||||
FolderPlusIcon,
|
||||
ArrowDownTrayIcon,
|
||||
ArrowPathIcon,
|
||||
ArrowUpTrayIcon,
|
||||
ArrowUturnLeftIcon,
|
||||
BookmarkIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
ClipboardDocumentIcon,
|
||||
ClipboardDocumentCheckIcon,
|
||||
DocumentDuplicateIcon,
|
||||
TrashIcon,
|
||||
},
|
||||
|
||||
@@ -289,7 +313,6 @@ export default defineComponent({
|
||||
return this.store.realCompositionList.find((rc) => rc.stockString == currentStockString);
|
||||
},
|
||||
|
||||
|
||||
stockIsEmpty() {
|
||||
return this.store.stockList.length == 0;
|
||||
},
|
||||
@@ -495,6 +518,30 @@ export default defineComponent({
|
||||
inputEl.value = '';
|
||||
},
|
||||
|
||||
saveStockDataToStorage() {
|
||||
if (this.store.stockList.length == 0) return;
|
||||
|
||||
const defaultName = `${this.store.stockList[0].vehicleRef.type} ${(this.store.totalWeight / 1000).toFixed(1)}t; ${this.store.totalLength}m; vmax ${this.store.maxStockSpeed}`;
|
||||
const entryName = prompt(this.$t('stocklist.prompt-bookmark'), defaultName);
|
||||
|
||||
if (!entryName) return;
|
||||
|
||||
if (entryName in this.store.storageStockData) {
|
||||
const overwriteData = confirm(this.$t('stocklist.prompt-bookmark-overwrite'));
|
||||
|
||||
if (!overwriteData) return;
|
||||
}
|
||||
|
||||
this.store.storageStockData[entryName] = this.store.stockString;
|
||||
|
||||
try {
|
||||
localStorage.setItem('savedStockData', JSON.stringify(this.store.storageStockData));
|
||||
this.store.chosenStorageStockName = entryName;
|
||||
} catch (error) {
|
||||
console.error('Wystąpił błąd podczas zapisywania składu do localStorage!', error);
|
||||
}
|
||||
},
|
||||
|
||||
async uploadStockFromClipboard() {
|
||||
try {
|
||||
const content = await navigator.clipboard.readText();
|
||||
@@ -588,7 +635,7 @@ export default defineComponent({
|
||||
display: grid;
|
||||
gap: 0.5em;
|
||||
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
grid-template-columns: repeat(auto-fit, minmax(70px, 1fr));
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
|
||||
@@ -6,91 +6,106 @@
|
||||
</div>
|
||||
|
||||
<div class="tab_content">
|
||||
<div class="tab_actions">
|
||||
<button
|
||||
class="btn btn--image"
|
||||
@click="saveStockDataToStorage"
|
||||
:disabled="store.stockList.length == 0"
|
||||
:data-disabled="store.stockList.length == 0"
|
||||
>
|
||||
<FolderArrowDownIcon />
|
||||
ZAPISZ OBECNY SKŁAD
|
||||
</button>
|
||||
<button
|
||||
class="btn btn--image"
|
||||
@click="removeStockIndexFromStorage"
|
||||
:disabled="currentStockIndex == -1"
|
||||
:data-disabled="currentStockIndex == -1"
|
||||
>
|
||||
<TrashIcon />
|
||||
<span>USUŃ ZAPISANY SKŁAD</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="storage-list-wrapper">
|
||||
<transition-group name="storage-list-anim" tag="ul" class="storage-list">
|
||||
<li
|
||||
v-for="(stockString, stockName) in store.storageStockData"
|
||||
:key="stockName"
|
||||
:data-current="store.chosenStorageStockName == stockName"
|
||||
>
|
||||
<div class="storage-item-top">
|
||||
<button class="btn btn--text btn-name" @click="chooseStorageStock(stockName)">
|
||||
{{ stockName }}
|
||||
</button>
|
||||
|
||||
<ul class="storage-stock-list">
|
||||
<li v-for="(stockList, i) in storageStockData" :key="i">
|
||||
</li>
|
||||
</ul>
|
||||
<div class="storage-item-top-actions">
|
||||
<button class="btn btn--text" @click="toggleStorageEntryExpand(stockName)">
|
||||
<ChevronDownIcon
|
||||
v-if="!expandedEntries.includes(stockName)"
|
||||
style="width: 25px"
|
||||
/>
|
||||
<ChevronUpIcon v-else style="width: 25px" />
|
||||
</button>
|
||||
|
||||
<button class="btn btn--text" @click="removeStockIndexFromStorage(stockName)">
|
||||
<TrashIcon style="width: 25px" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="storage-item-expandable" v-if="expandedEntries.includes(stockName)">
|
||||
{{
|
||||
stockString
|
||||
.split(';')
|
||||
.map((s) => s.split(/:|,/)[0])
|
||||
.join(' + ')
|
||||
}}
|
||||
</div>
|
||||
</li>
|
||||
</transition-group>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onActivated, Reactive, reactive, ref } from 'vue';
|
||||
import { IStock } from '../../types/common.types';
|
||||
import { FolderArrowDownIcon, TrashIcon } from '@heroicons/vue/20/solid';
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
FolderArrowDownIcon,
|
||||
TrashIcon,
|
||||
} from '@heroicons/vue/20/solid';
|
||||
|
||||
import { useStore } from '../../store';
|
||||
import stockMixin from '../../mixins/stockMixin';
|
||||
|
||||
const store = useStore();
|
||||
export default defineComponent({
|
||||
components: {
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
FolderArrowDownIcon,
|
||||
TrashIcon,
|
||||
},
|
||||
|
||||
let storageStockData: Reactive<IStock[][]> = reactive([]);
|
||||
const currentStockIndex = ref(-1);
|
||||
mixins: [stockMixin],
|
||||
|
||||
onActivated(() => {
|
||||
// loadStockDataFromStorage();
|
||||
data: () => ({
|
||||
store: useStore(),
|
||||
|
||||
expandedEntries: [] as string[],
|
||||
}),
|
||||
|
||||
methods: {
|
||||
removeStockIndexFromStorage(stockName: string) {
|
||||
delete this.store.storageStockData[stockName];
|
||||
this.store.chosenStorageStockName = '';
|
||||
|
||||
try {
|
||||
localStorage.setItem('savedStockData', JSON.stringify(this.store.storageStockData));
|
||||
} catch (error) {
|
||||
console.error('Wystąpił błąd podczas usuwania składu z localStorage!', error);
|
||||
}
|
||||
},
|
||||
|
||||
chooseStorageStock(stockName: string) {
|
||||
try {
|
||||
this.loadStockFromString(this.store.storageStockData[stockName]);
|
||||
this.store.chosenStorageStockName = stockName;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
|
||||
toggleStorageEntryExpand(stockName: string) {
|
||||
const stockIndex = this.expandedEntries.indexOf(stockName);
|
||||
|
||||
if (stockIndex == -1) this.expandedEntries.push(stockName);
|
||||
else this.expandedEntries.splice(stockIndex, 1);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
function loadStockDataFromStorage() {
|
||||
const savedData = localStorage.getItem('savedStockData');
|
||||
|
||||
if (!savedData) {
|
||||
localStorage.setItem('savedStockData', JSON.stringify([]));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
storageStockData = JSON.parse(savedData);
|
||||
} catch (error) {
|
||||
console.error('Wystąpił błąd podczas przetwarzania danych o składach z localStorage!', error);
|
||||
}
|
||||
}
|
||||
|
||||
function saveStockDataToStorage() {
|
||||
if (store.stockList.length == 0) return;
|
||||
|
||||
storageStockData.push(store.stockList);
|
||||
|
||||
try {
|
||||
localStorage.setItem('savedStockData', JSON.stringify(storageStockData));
|
||||
currentStockIndex.value = storageStockData.length;
|
||||
} catch (error) {
|
||||
console.error('Wystąpił błąd podczas zapisywania składu do localStorage!', error);
|
||||
storageStockData.pop();
|
||||
}
|
||||
}
|
||||
|
||||
function removeStockIndexFromStorage() {
|
||||
if (currentStockIndex.value == -1) return;
|
||||
|
||||
storageStockData.splice(currentStockIndex.value, 1);
|
||||
|
||||
try {
|
||||
localStorage.setItem('savedStockData', JSON.stringify(storageStockData));
|
||||
currentStockIndex.value = currentStockIndex.value - 1;
|
||||
} catch (error) {
|
||||
console.error('Wystąpił błąd podczas usuwania składu z localStorage!', error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -99,4 +114,65 @@ function removeStockIndexFromStorage() {
|
||||
.tab_actions {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.storage-list-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
ul.storage-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
ul.storage-list > li {
|
||||
padding: 0.5em;
|
||||
background-color: global.$secondaryColor;
|
||||
|
||||
&[data-current='true'] {
|
||||
background-color: #3b3b3b;
|
||||
}
|
||||
}
|
||||
|
||||
.storage-item-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.storage-item-top button.btn-name {
|
||||
font-size: 1.2em;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.storage-item-top-actions {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.storage-item-expandable {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.storage-list-anim {
|
||||
&-move,
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition: all 120ms ease-in-out;
|
||||
}
|
||||
|
||||
&-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateY(-25px);
|
||||
}
|
||||
|
||||
&-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&-leave-active {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
"alert-copied": "The rolling stock has been copied to your clipboard!",
|
||||
"alert-empty": "Lista pojazdów jest pusta!",
|
||||
"prompt-file": "Name a file and download it to the Presets folder (Documents/TTSK/TrainDriver2):",
|
||||
"prompt-bookmark": "Enter the name of this composition:",
|
||||
"prompt-bookmark-overwrite": "A composition with this name is already bookmarked! Do you want to overwrite it?",
|
||||
"vehicle-no": "VEHICLE NO.",
|
||||
"no-vehicle-chosen": "NO VEHICLE CHOSEN",
|
||||
"action-move-up": "MOVE UP",
|
||||
@@ -63,6 +65,7 @@
|
||||
"action-copy": "COPY TO CLIPBOARD",
|
||||
"action-reset": "RESET",
|
||||
"action-shuffle": "SHUFFLE",
|
||||
"action-bookmark": "BOOKMARK",
|
||||
"mass": "Mass",
|
||||
"mass-accepted": "accepted",
|
||||
"length": "Length",
|
||||
|
||||
+5
-2
@@ -53,17 +53,20 @@
|
||||
"alert-copied": "Skład został skopiowany do twojego schowka!",
|
||||
"alert-empty": "Lista pojazdów jest pusta!",
|
||||
"prompt-file": "Nazwij plik, a następnie pobierz do folderu Presets (Dokumenty/TTSK/TrainDriver2):",
|
||||
"prompt-bookmark": "Nazwij skład do zapisania:",
|
||||
"prompt-bookmark-overwrite": "Skład o tej nazwie jest już zapisany. Czy chcesz go podmienić?",
|
||||
"vehicle-no": "POJAZD NR",
|
||||
"no-vehicle-chosen": "NIE WYBRANO POJAZDU",
|
||||
"action-move-up": "PRZENIEŚ WYŻEJ",
|
||||
"action-move-down": "PRZENIEŚ NIŻEJ",
|
||||
"action-remove": "USUŃ",
|
||||
"action-upload-file": "WCZYTAJ (PLIK)",
|
||||
"action-upload-clipboard": "WCZYTAJ (SCHOWEK)",
|
||||
"action-upload-file": "WCZYTAJ Z PLIKU",
|
||||
"action-upload-clipboard": "WCZYTAJ ZE SCHOWKA",
|
||||
"action-download": "POBIERZ DO PLIKU",
|
||||
"action-copy": "SKOPIUJ DO SCHOWKA",
|
||||
"action-reset": "ZRESETUJ",
|
||||
"action-shuffle": "PRZETASUJ",
|
||||
"action-bookmark": "ZAPISZ",
|
||||
"mass": "Masa",
|
||||
"mass-accepted": "dopuszczalna",
|
||||
"length": "Długość",
|
||||
|
||||
+3
-6
@@ -61,6 +61,9 @@ export const useStore = defineStore({
|
||||
|
||||
lastFocusedElement: null as HTMLElement | null,
|
||||
|
||||
storageStockData: {} as Record<string, string>,
|
||||
chosenStorageStockName: '',
|
||||
|
||||
compatibleSimulatorVersion: '2024.3.1',
|
||||
}),
|
||||
|
||||
@@ -137,11 +140,5 @@ export const useStore = defineStore({
|
||||
async setupAPIData() {
|
||||
await this.fetchVehiclesAPI();
|
||||
},
|
||||
|
||||
handleRouting() {
|
||||
if (window.location.search.includes('trainId=')) {
|
||||
const trainId = window.location.search;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
+39
-15
@@ -122,21 +122,43 @@ button {
|
||||
}
|
||||
}
|
||||
|
||||
[data-tooltip]:hover::after,
|
||||
[data-tooltip]:focus::after {
|
||||
position: absolute;
|
||||
transform: translateX(10px);
|
||||
|
||||
content: attr(data-tooltip);
|
||||
color: white;
|
||||
background: black;
|
||||
padding: 0.5em;
|
||||
max-width: 300px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
[data-tooltip] {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover::after,
|
||||
&:focus-visible::after {
|
||||
position: absolute;
|
||||
transform: translateX(10px);
|
||||
|
||||
content: attr(data-tooltip);
|
||||
color: white;
|
||||
background: black;
|
||||
padding: 0.5em;
|
||||
max-width: 300px;
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
|
||||
[data-button-tooltip] {
|
||||
position: relative;
|
||||
|
||||
&:hover::after,
|
||||
&:focus-visible::after {
|
||||
position: absolute;
|
||||
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
|
||||
top: 100%;
|
||||
transform: translateY(5px);
|
||||
border-radius: inherit;
|
||||
|
||||
content: attr(data-button-tooltip);
|
||||
color: white;
|
||||
background: #111;
|
||||
padding: 0.5em;
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
|
||||
.btn,
|
||||
@@ -190,7 +212,8 @@ button {
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
|
||||
img, svg {
|
||||
img,
|
||||
svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
@@ -200,7 +223,8 @@ button {
|
||||
font-weight: bold;
|
||||
transition: all 250ms;
|
||||
background: none;
|
||||
padding: 0;
|
||||
padding: 0.25em;
|
||||
border-radius: 0;
|
||||
|
||||
&:focus-visible {
|
||||
outline: 1px solid white;
|
||||
|
||||
Reference in New Issue
Block a user