mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 05:18:11 +00:00
dodano dymki kontekstowe oraz podgląd pojazdu
This commit is contained in:
+7
-4
@@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<div class="app_container" v-cloak>
|
||||
<PopUp />
|
||||
|
||||
<transition name="modal-anim">
|
||||
<keep-alive>
|
||||
<TrainModal v-if="store.chosenModalTrainId" />
|
||||
@@ -36,15 +38,14 @@ import { defineComponent, watch } from 'vue';
|
||||
import axios from 'axios';
|
||||
import { version } from '.././package.json';
|
||||
|
||||
import Clock from './components/App/Clock.vue';
|
||||
|
||||
import { useMainStore } from './store/mainStore';
|
||||
|
||||
import Clock from './components/App/Clock.vue';
|
||||
import StatusIndicator from './components/App/StatusIndicator.vue';
|
||||
import AppHeader from './components/App/AppHeader.vue';
|
||||
import TrainModal from './components/TrainsView/TrainModal.vue';
|
||||
|
||||
import StorageManager from './managers/storageManager';
|
||||
import PopUp from './components/PopUp/PopUp.vue';
|
||||
import { useApiStore } from './store/apiStore';
|
||||
import { Status } from './typings/common';
|
||||
|
||||
@@ -55,7 +56,8 @@ export default defineComponent({
|
||||
Clock,
|
||||
StatusIndicator,
|
||||
AppHeader,
|
||||
TrainModal
|
||||
TrainModal,
|
||||
PopUp
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
@@ -217,6 +219,7 @@ export default defineComponent({
|
||||
grid-template-columns: 100%;
|
||||
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.app_main {
|
||||
|
||||
@@ -7,11 +7,12 @@
|
||||
</p>
|
||||
|
||||
<img
|
||||
class="traction-only"
|
||||
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${computedStockList[0].split(':')[0]}${
|
||||
/^EN/.test(computedStockList[0]) ? 'rb' : ''
|
||||
}.png`"
|
||||
@error="onImageError($event, computedStockList[0])"
|
||||
width="400"
|
||||
width="300"
|
||||
height="60"
|
||||
/>
|
||||
</div>
|
||||
@@ -23,7 +24,13 @@
|
||||
{{ stockName.split(':')[1] }}
|
||||
</p>
|
||||
|
||||
<span>
|
||||
<span
|
||||
@mouseenter="
|
||||
popupStore.onPopUpShow($event, 'VehiclePreviewPopUp', stockName.split(':')[0])
|
||||
"
|
||||
@mousemove="popupStore.onPopUpMove"
|
||||
@mouseleave="popupStore.onPopUpHide"
|
||||
>
|
||||
<img
|
||||
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${stockName.split(':')[0]}${
|
||||
/^EN/.test(stockName) ? 'rb' : ''
|
||||
@@ -69,6 +76,7 @@
|
||||
<script lang="ts">
|
||||
import { PropType, defineComponent } from 'vue';
|
||||
import { useApiStore } from '../../store/apiStore';
|
||||
import { usePopupStore } from '../../store/popupStore';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -84,7 +92,8 @@ export default defineComponent({
|
||||
|
||||
data() {
|
||||
return {
|
||||
apiStore: useApiStore()
|
||||
apiStore: useApiStore(),
|
||||
popupStore: usePopupStore()
|
||||
};
|
||||
},
|
||||
|
||||
@@ -139,6 +148,7 @@ export default defineComponent({
|
||||
ul > li > span {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
img {
|
||||
@@ -147,6 +157,10 @@ img {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
img.traction-only {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: center;
|
||||
color: #aaa;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div class="popup-content">
|
||||
<img src="/images/icon-diamond.svg" alt="" />
|
||||
<span>{{ popupStore.currentPopupContent }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { usePopupStore } from '../../store/popupStore';
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
popupStore: usePopupStore()
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.popup-content {
|
||||
gap: 0.5em;
|
||||
|
||||
padding: 0.5em;
|
||||
border-radius: 0.25em;
|
||||
|
||||
width: 100%;
|
||||
|
||||
background-color: #333;
|
||||
box-shadow: 0 0 10px 2px #aaa;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
height: 1em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div class="popup" v-show="popupStore.currentPopupComponent" ref="preview">
|
||||
<component v-if="popupStore.currentPopupComponent" :is="popupStore.currentPopupComponent" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import DonatorPopUp from './DonatorPopUp.vue';
|
||||
import TrainCommentsPopUp from './TrainCommentsPopUp.vue';
|
||||
import VehiclePreviewPopUp from './VehiclePreviewPopUp.vue';
|
||||
import { usePopupStore } from '../../store/popupStore';
|
||||
|
||||
export default defineComponent({
|
||||
components: { DonatorPopUp, TrainCommentsPopUp, VehiclePreviewPopUp },
|
||||
|
||||
data() {
|
||||
return {
|
||||
popupStore: usePopupStore()
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
'popupStore.popupPosition': {
|
||||
deep: true,
|
||||
handler(val: typeof this.popupStore.popupPosition) {
|
||||
const previewEl = this.$refs['preview'] as HTMLElement;
|
||||
|
||||
previewEl.style.top = `${val.y}px`;
|
||||
previewEl.style.left = `${val.x}px`;
|
||||
previewEl.style.transform = 'translateY(1.5rem)';
|
||||
|
||||
this.$nextTick(() => {
|
||||
const isOutside =
|
||||
val.y + previewEl.getBoundingClientRect().height > window.innerHeight + window.scrollY;
|
||||
|
||||
previewEl.style.transform = `translate(-50%, calc(${
|
||||
isOutside ? '-100% - 1.5rem' : '1.5rem'
|
||||
}))`;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.popup {
|
||||
position: absolute;
|
||||
z-index: 250;
|
||||
max-width: 400px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div class="popup-content">
|
||||
<span>{{ popupStore.currentPopupContent }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { usePopupStore } from '../../store/popupStore';
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
popupStore: usePopupStore()
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.popup-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
|
||||
padding: 0.25em 0.5em;
|
||||
border-radius: 0.25em;
|
||||
|
||||
width: 100%;
|
||||
background-color: #333;
|
||||
box-shadow: 0 0 5px 2px #aaa;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 1em;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<div class="popup-content">
|
||||
<div v-if="imageState == 'loading'" class="loading-info">
|
||||
{{ $t('vehicle-preview.loading') }}
|
||||
</div>
|
||||
|
||||
<div v-if="imageState == 'error'">{{ $t('vehicle-preview.error') }}</div>
|
||||
|
||||
<img
|
||||
v-if="popupStore.currentPopupContent"
|
||||
@load="onImageLoad"
|
||||
@error="onImageError"
|
||||
width="300"
|
||||
height="176"
|
||||
class="rounded-md w-full h-auto"
|
||||
:src="`https://spythere.github.io/api/td2/images/${popupStore.currentPopupContent}--300px.jpg`"
|
||||
/>
|
||||
|
||||
<div class="vehicle-name" v-if="imageState != 'error'">
|
||||
{{ popupStore.currentPopupContent.replace(/_/g, ' ') }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { usePopupStore } from '../../store/popupStore';
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
popupStore: usePopupStore(),
|
||||
imageState: 'loading'
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.imageState = 'loading';
|
||||
},
|
||||
|
||||
methods: {
|
||||
onImageLoad() {
|
||||
this.imageState = 'loaded';
|
||||
},
|
||||
|
||||
onImageError(e: Event) {
|
||||
this.imageState = 'error';
|
||||
|
||||
(e.target as HTMLElement).style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.popup-content {
|
||||
// min-w-[300px] min-h-[200px] p-2 bg-slate-800 rounded-md
|
||||
|
||||
width: 300px;
|
||||
min-height: 200px;
|
||||
background-color: #333;
|
||||
box-shadow: 0 0 10px 2px #aaa;
|
||||
|
||||
padding: 0.5em;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
.loading-info {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.vehicle-name {
|
||||
text-align: center;
|
||||
margin-top: 0.5em;
|
||||
color: #ccc;
|
||||
text-wrap: wrap;
|
||||
}
|
||||
</style>
|
||||
@@ -119,8 +119,16 @@
|
||||
<span v-if="station.onlineInfo?.dispatcherName">
|
||||
<b
|
||||
v-if="apiStore.donatorsData.includes(station.onlineInfo.dispatcherName)"
|
||||
:title="$t('donations.dispatcher-message')"
|
||||
@click.stop="openDonationModal"
|
||||
@mouseenter="
|
||||
popupStore.onPopUpShow(
|
||||
$event,
|
||||
'DonatorPopUp',
|
||||
$t('donations.dispatcher-message')
|
||||
)
|
||||
"
|
||||
@mousemove="popupStore.onPopUpMove"
|
||||
@mouseleave="popupStore.onPopUpHide"
|
||||
>
|
||||
<img src="/images/icon-diamond.svg" alt="" />
|
||||
{{ station.onlineInfo.dispatcherName }}
|
||||
@@ -311,6 +319,7 @@ import { HeadIdsTypes, headIconsIds, headIds } from '../../scripts/data/stationH
|
||||
import StationStatusBadge from '../Global/StationStatusBadge.vue';
|
||||
import { Status } from '../../typings/common';
|
||||
import { useApiStore } from '../../store/apiStore';
|
||||
import { usePopupStore } from '../../store/popupStore';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -339,13 +348,16 @@ export default defineComponent({
|
||||
setup() {
|
||||
const mainStore = useMainStore();
|
||||
const apiStore = useApiStore();
|
||||
const popupStore = usePopupStore();
|
||||
|
||||
const stationFiltersStore = useStationFiltersStore();
|
||||
|
||||
return {
|
||||
Status: Status.Data,
|
||||
stationFiltersStore,
|
||||
mainStore,
|
||||
apiStore
|
||||
apiStore,
|
||||
popupStore
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -36,7 +36,11 @@
|
||||
<div class="train-driver">
|
||||
<b
|
||||
v-if="apiStore.donatorsData.includes(train.driverName)"
|
||||
:title="$t('donations.driver-message')"
|
||||
@mouseenter="
|
||||
popupStore.onPopUpShow($event, 'DonatorPopUp', $t('donations.driver-message'))
|
||||
"
|
||||
@mousemove="popupStore.onPopUpMove"
|
||||
@mouseleave="popupStore.onPopUpHide"
|
||||
>
|
||||
{{ train.driverName }}
|
||||
<img src="/images/icon-diamond.svg" alt="donator diamond icon" />
|
||||
@@ -47,14 +51,22 @@
|
||||
|
||||
<div class="general-timetable" v-if="train.timetableData">
|
||||
<strong>{{ train.timetableData.route.replace('|', ' - ') }}</strong>
|
||||
<img
|
||||
<span
|
||||
v-if="getSceneriesWithComments(train.timetableData).length > 0"
|
||||
class="image-warning"
|
||||
src="/images/icon-warning.svg"
|
||||
:title="`${$t('trains.timetable-comments')} (${getSceneriesWithComments(
|
||||
train.timetableData
|
||||
)})`"
|
||||
/>
|
||||
@mouseenter="
|
||||
popupStore.onPopUpShow(
|
||||
$event,
|
||||
'TrainCommentsPopUp',
|
||||
`${$t('trains.timetable-comments')} (${getSceneriesWithComments(
|
||||
train.timetableData
|
||||
)})`
|
||||
)
|
||||
"
|
||||
@mousemove="popupStore.onPopUpMove"
|
||||
@mouseleave="popupStore.onPopUpHide"
|
||||
>
|
||||
<img class="image-warning" src="/images/icon-warning.svg" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<hr style="margin: 0.25em 0" />
|
||||
@@ -125,6 +137,7 @@ import ProgressBar from '../Global/ProgressBar.vue';
|
||||
import { useMainStore } from '../../store/mainStore';
|
||||
import { useApiStore } from '../../store/apiStore';
|
||||
import StockList from '../Global/StockList.vue';
|
||||
import { usePopupStore } from '../../store/popupStore';
|
||||
|
||||
export default defineComponent({
|
||||
mixins: [trainInfoMixin, styleMixin],
|
||||
@@ -144,7 +157,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
store: useMainStore(),
|
||||
apiStore: useApiStore()
|
||||
apiStore: useApiStore(),
|
||||
popupStore: usePopupStore()
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -48,6 +48,10 @@
|
||||
"confirm-button": "UPDATE NOW",
|
||||
"later-button": "LATER"
|
||||
},
|
||||
"vehicle-preview": {
|
||||
"loading": "Loading preview...",
|
||||
"error": "Oops! The vehicle preview seems to be missing! :/"
|
||||
},
|
||||
"data-status": {
|
||||
"S1-offline": "<b>S1 signal</b> <br> The app is working in offline mode!",
|
||||
"S1a-connection": "<b>S1a signal</b> <br> Cannot connect with Stacjownik API service!",
|
||||
|
||||
@@ -38,6 +38,10 @@
|
||||
"footer": {
|
||||
"discord": "Serwer Discord Stacjownika"
|
||||
},
|
||||
"vehicle-preview": {
|
||||
"loading": "Ładowanie podglądu...",
|
||||
"error": "Ups! Nie znaleziono podglądu pojazdu! :/"
|
||||
},
|
||||
"data-status": {
|
||||
"S1-offline": "<b>Sygnał S1</b> <br> Aplikacja działa w trybie offline!",
|
||||
"S1a-connection": "<b>Sygnał S1a</b> <br> Błąd podczas próby połączenia się z API Stacjownika!",
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
export const usePopupStore = defineStore('popupStore', {
|
||||
state: () => ({
|
||||
popupPosition: { x: 0, y: 0 },
|
||||
currentPopupComponent: null as
|
||||
| null
|
||||
| 'DonatorPopUp'
|
||||
| 'TrainCommentsPopUp'
|
||||
| 'VehiclePreviewPopUp',
|
||||
currentPopupContent: '',
|
||||
donatorPopupVisible: false
|
||||
}),
|
||||
|
||||
actions: {
|
||||
onPopUpShow(e: MouseEvent, componentKey: typeof this.currentPopupComponent, value?: string) {
|
||||
this.popupPosition.x = e.pageX;
|
||||
this.popupPosition.y = e.pageY;
|
||||
|
||||
this.currentPopupComponent = componentKey;
|
||||
this.currentPopupContent = value ?? '';
|
||||
},
|
||||
|
||||
onPopUpMove(e: MouseEvent) {
|
||||
this.popupPosition.x = e.pageX;
|
||||
this.popupPosition.y = e.pageY;
|
||||
},
|
||||
|
||||
onPopUpHide() {
|
||||
this.currentPopupComponent = null;
|
||||
this.currentPopupContent = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -11,20 +11,14 @@ export interface RegionCounters {
|
||||
|
||||
export interface StoreState {
|
||||
region: { id: string; value: string };
|
||||
|
||||
isOffline: boolean;
|
||||
|
||||
isNewUpdate: boolean;
|
||||
|
||||
dispatcherStatsName: string;
|
||||
dispatcherStatsData?: API.DispatcherStats.Response;
|
||||
|
||||
driverStatsName: string;
|
||||
driverStatsData?: API.DriverStats.Response;
|
||||
driverStatsStatus: Status.Data;
|
||||
|
||||
chosenModalTrainId?: string;
|
||||
|
||||
blockScroll: boolean;
|
||||
modalLastClickedTarget: EventTarget | null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user