Compare commits

...

26 Commits

Author SHA1 Message Date
Spythere 5ffc63a815 Merge pull request #135 from Spythere/development
v1.30.4
2025-07-20 14:03:10 +02:00
Spythere 87f7ff58e8 chore: added info about offline driver for train info tooltip and scenery timetables 2025-07-19 15:34:49 +02:00
Spythere 8b6944a8e5 fix(ScheduledTrainStatus): router link only for timetable statuses with station name hrefs 2025-07-19 15:21:32 +02:00
Spythere cfeeb8fddd bump: v1.30.4 2025-07-17 14:46:02 +02:00
Spythere 89f7fd3c53 refactor: changed drivers select to datalist in active train options 2025-07-17 14:45:33 +02:00
Spythere 86259988c9 refactor: added more advanced tooltips for station table icons 2025-07-17 14:34:05 +02:00
Spythere 7b5ef18ad6 chore: added train info tooltips for scenery user badges 2025-07-17 14:09:10 +02:00
Spythere d784042691 chore: added router links to timetable train statuses in scenery view 2025-07-17 14:03:18 +02:00
Spythere d0e482aa4f chore: changed speed placement in train info tooltip 2025-07-17 13:45:45 +02:00
Spythere 3bf1db52b4 chore(scenery): advanced active train tooltip information 2025-07-11 15:33:28 +02:00
Spythere 8e713a5c6e chore: added & optimized users tooltip data typings 2025-07-11 14:46:07 +02:00
Spythere af6eb35b67 Merge pull request #134 from Spythere/development
v1.30.3
2025-07-05 02:20:55 +02:00
Spythere 1e6ab1c2d1 fix: status messages 2025-07-04 23:37:59 +02:00
Spythere fd4849bd5e bump: v1.30.3 2025-07-04 18:29:00 +02:00
Spythere bc0f4c5d3f chore: added head unit information in scenery active timetables 2025-07-04 18:28:41 +02:00
Spythere 8909a0cd40 fix: added timetable status for beginning offline 2025-07-04 18:22:41 +02:00
Spythere a2602aeefe chore: added displaying exit route speed limits in scenery view 2025-07-04 18:13:47 +02:00
Spythere 37ad9b2787 refactor: left & right track speed limits for routes in active train's timetable 2025-07-04 18:09:18 +02:00
Spythere 0b4ad679b3 chore: adjusted scenery view return button appearance 2025-07-04 17:16:08 +02:00
Spythere dd0d7897cf chore: better descriptions for active timetables statuses in scenery view 2025-07-04 17:12:38 +02:00
Spythere 1453dbda01 chore(scenery): added TWR/TN/PN badges to active timetables 2025-07-02 19:05:38 +02:00
Spythere 4af856b833 chore(scenery): changed appearance of the return button 2025-07-02 18:50:11 +02:00
Spythere 182b46a377 Merge pull request #133 from Spythere/development
hotfixes: v1.30.2
2025-06-02 01:39:13 +02:00
Spythere bb5fc395d2 chore: added LPS category to journal timetables filters 2025-06-02 01:38:01 +02:00
Spythere a91a00f88a refactor: scenery view back button routing; component setup script 2025-06-02 01:35:09 +02:00
Spythere c8d481a952 hotfix: EN category desc typo 2025-06-02 00:44:23 +02:00
23 changed files with 521 additions and 353 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "stacjownik", "name": "stacjownik",
"version": "1.30.2", "version": "1.30.4",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
+46 -7
View File
@@ -1,5 +1,13 @@
<template> <template>
<section class="info-header"> <section class="info-header">
<button
class="btn btn-return"
:title="$t('scenery.return-btn')"
@click="onReturnButtonClick"
>
<img src="/images/icon-back.svg" alt="return button" />
</button>
<a class="scenery-name" :href="station?.generalInfo?.url" target="_blank"> <a class="scenery-name" :href="station?.generalInfo?.url" target="_blank">
{{ stationName.replace(/_/g, ' ') }} {{ stationName.replace(/_/g, ' ') }}
</a> </a>
@@ -12,12 +20,21 @@
</section> </section>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { PropType, defineComponent } from 'vue'; import { onMounted, PropType, ref } from 'vue';
import { ActiveScenery, Station } from '../../typings/common'; import { ActiveScenery, Station } from '../../typings/common';
import { useRoute, useRouter } from 'vue-router';
export default defineComponent({ const route = useRoute();
props: { const router = useRouter();
const prevPath = ref('/');
onMounted(() => {
prevPath.value = (route.meta['prevPath'] as string) ?? '/';
});
defineProps({
station: { station: {
type: Object as PropType<Station> type: Object as PropType<Station>
}, },
@@ -30,21 +47,37 @@ export default defineComponent({
onlineScenery: { onlineScenery: {
type: Object as PropType<ActiveScenery> type: Object as PropType<ActiveScenery>
} }
}
}); });
function onReturnButtonClick() {
router.push(prevPath.value);
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@use '../../styles/responsive'; @use '../../styles/responsive';
@use 'sass:color';
.info-header { .btn-return {
margin-top: 1em; $bgColor: #2b2b2b;
background-color: $bgColor;
margin-bottom: 0.5em;
img {
width: 2em;
}
&:hover {
background-color: color.adjust($color: $bgColor, $lightness: 15%);
}
} }
.scenery-name { .scenery-name {
font-weight: bold; font-weight: bold;
font-size: 3em; font-size: 3em;
text-align: center;
text-transform: uppercase; text-transform: uppercase;
} }
@@ -58,4 +91,10 @@ export default defineComponent({
color: #aaa; color: #aaa;
font-size: 1.2em; font-size: 1.2em;
} }
@include responsive.smallScreen {
.scenery-name {
font-size: 2.5em;
}
}
</style> </style>
@@ -44,6 +44,7 @@
{{ route.routeName }} {{ route.routeName }}
</span> </span>
<span v-if="route.routeSpeed" class="speed">{{ route.routeSpeed }}</span> <span v-if="route.routeSpeed" class="speed">{{ route.routeSpeed }}</span>
<span v-if="route.routeSpeedExit" class="speed">| {{ route.routeSpeedExit }}</span>
<span v-if="route.routeLength" class="length"> <span v-if="route.routeLength" class="length">
{{ (route.routeLength / 1000).toFixed(1) + 'km' }} {{ (route.routeLength / 1000).toFixed(1) + 'km' }}
</span> </span>
@@ -18,7 +18,11 @@
:key="train.id" :key="train.id"
:data-status="status" :data-status="status"
> >
<router-link :to="train.driverRouteLocation"> <router-link
:to="train.driverRouteLocation"
data-tooltip-type="TrainInfoTooltip"
:data-tooltip-content="train.id"
>
<span class="user_train"> {{ train.trainNo }}</span> <span class="user_train"> {{ train.trainNo }}</span>
<span class="user_name"> <span class="user_name">
{{ train.driverName }} {{ train.driverName }}
+65 -13
View File
@@ -93,19 +93,59 @@
<span class="timetable-general"> <span class="timetable-general">
<span class="general-info"> <span class="general-info">
<div class="info-train"> <div class="info-train">
<b <!-- Cargo warnings & details badges -->
<span
class="train-badge twr"
v-if="row.train.timetableData!.twr"
data-tooltip-type="BaseTooltip" data-tooltip-type="BaseTooltip"
:data-tooltip-content="getCategoryExplanation(row.train.timetableData!.category)" :data-tooltip-content="$t('warnings.TWR')"
class="text--primary tooltip-help"
> >
TWR
</span>
<span
class="train-badge tn"
v-if="row.train.timetableData!.hasDangerousCargo"
data-tooltip-type="BaseTooltip"
:data-tooltip-content="$t('warnings.TN')"
>
TN
</span>
<span
class="train-badge pn"
v-if="row.train.timetableData!.hasExtraDeliveries"
data-tooltip-type="BaseTooltip"
:data-tooltip-content="$t('warnings.PN')"
>
PN
</span>
<!-- Train info -->
<span
data-tooltip-type="TrainInfoTooltip"
:data-tooltip-content="row.train.id"
class="tooltip-help"
>
<b class="text--primary">
{{ row.train.timetableData!.category }} {{ row.train.timetableData!.category }}
</b> </b>
<span>&nbsp;</span>
<b>{{ row.train.trainNo }}</b> <b>&nbsp;{{ row.train.trainNo }}</b>
<span>&nbsp;&bull;&nbsp;</span> &bull;
<span>{{ row.train.driverName }}</span> {{ row.train.driverName }}
<i
class="fa-solid fa-user-slash"
style="color: salmon"
v-if="!row.train.online && row.train.lastSeen <= Date.now() - 60000"
></i>
</span>
<!-- Train stop comments -->
<span <span
v-if="row.checkpointStop.comments" v-if="row.checkpointStop.comments"
class="stop-comments-icon"
data-tooltip-type="BaseTooltip" data-tooltip-type="BaseTooltip"
:data-tooltip-content="row.checkpointStop.comments" :data-tooltip-content="row.checkpointStop.comments"
> >
@@ -205,7 +245,7 @@ import { useMainStore } from '../../store/mainStore';
import { useApiStore } from '../../store/apiStore'; import { useApiStore } from '../../store/apiStore';
import ScheduledTrainStatus from './ScheduledTrainStatus.vue'; import ScheduledTrainStatus from './ScheduledTrainStatus.vue';
import { SceneryTimetableRow } from './typings'; import { SceneryTimetableRow } from './typings';
import { ActiveScenery, Station } from '../../typings/common'; import { ActiveScenery, Station, TooltipTrainInfo, Train } from '../../typings/common';
import { getTrainStopStatus, stopStatusPriority } from './utils'; import { getTrainStopStatus, stopStatusPriority } from './utils';
export default defineComponent({ export default defineComponent({
@@ -352,6 +392,7 @@ export default defineComponent({
<style lang="scss" scoped> <style lang="scss" scoped>
@use '../../styles/responsive'; @use '../../styles/responsive';
@use '../../styles/animations'; @use '../../styles/animations';
@use '../../styles/badge';
.scenery-timetable { .scenery-timetable {
height: 100%; height: 100%;
@@ -468,7 +509,19 @@ export default defineComponent({
.general-info { .general-info {
display: flex; display: flex;
flex-direction: column;
flex-wrap: wrap; flex-wrap: wrap;
}
.info-train {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
}
.info-train > .train-badge {
font-size: 0.85em;
}
.info-number { .info-number {
color: var(--clr-primary); color: var(--clr-primary);
@@ -476,13 +529,12 @@ export default defineComponent({
.info-route { .info-route {
width: 100%; width: 100%;
margin-top: 0.25em;
} }
img { .stop-comments-icon > img {
height: 0.9em; width: 1.3em;
vertical-align: middle; vertical-align: top;
margin: 0 0.25em;
}
} }
.schedule { .schedule {
@@ -1,13 +1,18 @@
<template> <template>
<div class="general-status"> <div class="general-status">
<span <router-link
v-if="computedScheduledTrain.stationNameHref"
:to="`/scenery?station=${computedScheduledTrain.stationNameHref}`"
:class="computedScheduledTrain.status" :class="computedScheduledTrain.status"
data-tooltip-type="HtmlTooltip" v-html="computedScheduledTrain.stopStatusIndicator"
:data-tooltip-content="computedScheduledTrain.stopStatusDescription"
@click.prevent="() => {}"
> >
{{ computedScheduledTrain.stopStatusIndicator }} </router-link>
</span>
<span
v-else
:class="computedScheduledTrain.status"
v-html="computedScheduledTrain.stopStatusIndicator"
></span>
</div> </div>
</template> </template>
@@ -28,66 +33,65 @@ export default defineComponent({
computedScheduledTrain() { computedScheduledTrain() {
const { status, prevElement, currentElement, nextElement } = this.sceneryTimetableRow; const { status, prevElement, currentElement, nextElement } = this.sceneryTimetableRow;
const prevDepartureIndicator = prevElement?.departureRouteExt let stopStatusIndicator = '';
? `(${prevElement.departureRouteExt}) ${prevElement.stationName}` let stationNameHref = '';
: '---';
const nextArrivalIndicator = nextElement?.arrivalRouteExt
? `(${nextElement.arrivalRouteExt}) ${nextElement.stationName}`
: `${currentElement.stationName}`;
let stopStatusDescription = '',
stopStatusIndicator = '';
switch (status) { switch (status) {
case StopStatus.ARRIVING: case StopStatus.ARRIVING:
stopStatusIndicator = `${this.$t('timetables.from')}: ${prevDepartureIndicator}`; if (prevElement) {
stopStatusDescription = this.$t('timetables.desc-arriving', { stopStatusIndicator = this.$t('timetables.desc-arriving', {
prevStationName: prevElement?.stationName ?? '', prevStationName: prevElement?.stationName ?? '',
prevDepartureLine: prevElement?.departureRouteExt ?? '' prevDepartureLine: prevElement?.departureRouteExt ?? ''
}); });
stationNameHref = prevElement?.stationName ?? '';
} else {
stopStatusIndicator = this.$t('timetables.desc-beginning');
}
break; break;
case StopStatus.ONLINE: case StopStatus.ONLINE:
case StopStatus.STOPPED: case StopStatus.STOPPED:
stopStatusIndicator = nextElement?.arrivalRouteExt stopStatusIndicator = nextElement?.arrivalRouteExt
? `${this.$t('timetables.to')}: ${nextArrivalIndicator}`
: `${this.$t('timetables.desc-end')}`;
stopStatusDescription = nextElement?.arrivalRouteExt
? this.$t(`timetables.desc-${status}`, { ? this.$t(`timetables.desc-${status}`, {
nextStationName: nextElement?.stationName, nextStationName: nextElement?.stationName,
nextArrivalLine: nextElement?.arrivalRouteExt nextArrivalLine: nextElement?.arrivalRouteExt
}) })
: ''; : this.$t(`timetables.desc-end`);
stationNameHref = nextElement?.stationName ?? '';
break; break;
case StopStatus.DEPARTED: case StopStatus.DEPARTED:
stopStatusIndicator = `${this.$t('timetables.to')}: ${nextArrivalIndicator}`;
if (!nextElement?.stationName) { if (!nextElement?.stationName) {
stopStatusDescription = this.$t('timetables.desc-departed-ends', { stopStatusIndicator = this.$t('timetables.desc-departed-ends', {
nextStationName: currentElement.stationName nextStationName: currentElement.stationName
}); });
stationNameHref = nextElement?.stationName ?? '';
} else { } else {
stopStatusDescription = this.$t('timetables.desc-departed', { stopStatusIndicator = this.$t('timetables.desc-departed', {
nextStationName: nextElement?.stationName ?? currentElement.stationName, nextStationName: nextElement?.stationName ?? currentElement.stationName,
nextArrivalLine: nextElement?.arrivalRouteExt nextArrivalLine: nextElement?.arrivalRouteExt
}); });
stationNameHref = nextElement?.stationName ?? '';
} }
break; break;
case StopStatus.DEPARTED_AWAY: case StopStatus.DEPARTED_AWAY:
stopStatusIndicator = `${this.$t('timetables.to')}: ${nextArrivalIndicator}`; stopStatusIndicator = this.$t('timetables.desc-departed-away', {
stopStatusDescription = this.$t('timetables.desc-departed-away', {
nextStationName: nextElement?.stationName, nextStationName: nextElement?.stationName,
nextArrivalLine: nextElement?.arrivalRouteExt nextArrivalLine: nextElement?.arrivalRouteExt
}); });
stationNameHref = nextElement?.stationName ?? '';
break; break;
case StopStatus.TERMINATED: case StopStatus.TERMINATED:
stopStatusIndicator = `X ${this.$t('timetables.desc-terminated')}`; stopStatusIndicator = this.$t('timetables.desc-terminated');
stopStatusDescription = this.$t('timetables.desc-terminated');
break; break;
default: default:
@@ -95,10 +99,18 @@ export default defineComponent({
} }
return { return {
...this.sceneryTimetableRow, ...this.sceneryTimetableRow,
stopStatusDescription, stationNameHref,
stopStatusIndicator stopStatusIndicator
}; };
} }
},
methods: {
navigateToScenery(sceneryName?: string) {
if (!sceneryName) return;
this.$router.push(`/scenery?station=${sceneryName}`);
}
} }
}); });
</script> </script>
@@ -106,34 +118,29 @@ export default defineComponent({
<style lang="scss" scoped> <style lang="scss" scoped>
.general-status { .general-status {
margin-top: 0.5em; margin-top: 0.5em;
cursor: help;
span.arriving { & > .arriving {
color: #ccc; color: #ccc;
} }
span.departed { & > .departed {
color: lime; color: lime;
font-weight: bold;
&-away { &-away {
font-weight: bold;
color: #5ecc5e; color: #5ecc5e;
} }
} }
span.stopped { & > .stopped {
color: #ffa600; color: #ffa600;
font-weight: bold;
} }
span.online { & > .online {
color: gold; color: gold;
} }
span.terminated { & > .terminated {
color: salmon; color: salmon;
font-weight: bold;
} }
} }
</style> </style>
+66 -36
View File
@@ -33,12 +33,12 @@
class="header-image" class="header-image"
:class="headerName" :class="headerName"
> >
<span class="header_wrapper"> <span
<img class="header_wrapper"
:src="`/images/icon-${headerName}.svg`" data-tooltip-type="BaseTooltip"
:alt="headerName" :data-tooltip-content="$t(`sceneries.headers.${headerName}`)"
:title="$t(`sceneries.headers.${headerName}`)" >
/> <img :src="`/images/icon-${headerName}.svg`" :alt="headerName" />
<img <img
class="sort-icon" class="sort-icon"
@@ -76,37 +76,49 @@
station.generalInfo.availability != 'nonPublic' && station.generalInfo.availability != 'nonPublic' &&
station.generalInfo.availability != 'unavailable' station.generalInfo.availability != 'unavailable'
" "
data-tooltip-type="BaseTooltip"
:data-tooltip-content="`${$t(`sceneries.info.${station.generalInfo.availability}`)} (${$t(
'sceneries.info.req-level',
{ lvl: station.generalInfo.reqLevel },
station.generalInfo.reqLevel
)})`"
:style="calculateExpStyle(station.generalInfo.reqLevel)" :style="calculateExpStyle(station.generalInfo.reqLevel)"
> >
{{ station.generalInfo.reqLevel >= 2 ? station.generalInfo.reqLevel : 'L' }} {{ station.generalInfo.reqLevel >= 2 ? station.generalInfo.reqLevel : 'L' }}
</span> </span>
<span v-else-if="station.generalInfo.availability == 'abandoned'"> <span
<img v-else-if="station.generalInfo.availability == 'abandoned'"
src="/images/icon-abandoned.svg" data-tooltip-type="BaseTooltip"
alt="non-public" :data-tooltip-content="$t('sceneries.info.abandoned')"
:title="$t('sceneries.info.abandoned')" >
/> <img src="/images/icon-abandoned.svg" alt="non-public" />
</span> </span>
<span v-else-if="station.generalInfo.availability == 'nonPublic'"> <span
<img v-else-if="station.generalInfo.availability == 'nonPublic'"
src="/images/icon-lock.svg" data-tooltip-type="BaseTooltip"
alt="non-public" :data-tooltip-content="$t('sceneries.info.non-public')"
:title="$t('sceneries.info.non-public')" >
/> <img src="/images/icon-lock.svg" alt="non-public" />
</span> </span>
<span v-else> <span
<img v-else
src="/images/icon-unavailable.svg" data-tooltip-type="BaseTooltip"
alt="unavailable" :data-tooltip-content="$t('sceneries.info.unavailable')"
:title="$t('sceneries.info.unavailable')" >
/> <img src="/images/icon-unavailable.svg" alt="unavailable" />
</span> </span>
</span> </span>
<span v-else> ? </span> <span
v-else
data-tooltip-type="BaseTooltip"
:data-tooltip-content="$t('sceneries.info.unknown')"
>
?
</span>
</td> </td>
<td class="station-status"> <td class="station-status">
@@ -153,7 +165,8 @@
<span <span
v-if="station.generalInfo.routes.singleElectrifiedNames.length != 0" v-if="station.generalInfo.routes.singleElectrifiedNames.length != 0"
class="track catenary" class="track catenary"
:title="`${$t('sceneries.info.single-track-routes-catenary')}${ data-tooltip-type="BaseTooltip"
:data-tooltip-content="`${$t('sceneries.info.single-track-routes-catenary')}${
station.generalInfo.routes.singleElectrifiedNames.length station.generalInfo.routes.singleElectrifiedNames.length
}`" }`"
> >
@@ -163,7 +176,8 @@
<span <span
v-if="station.generalInfo.routes.singleOtherNames.length != 0" v-if="station.generalInfo.routes.singleOtherNames.length != 0"
class="track no-catenary" class="track no-catenary"
:title="`${$t('sceneries.info.single-track-routes-other')}${ data-tooltip-type="BaseTooltip"
:data-tooltip-content="`${$t('sceneries.info.single-track-routes-other')}${
station.generalInfo.routes.singleOtherNames.length station.generalInfo.routes.singleOtherNames.length
}`" }`"
> >
@@ -177,7 +191,8 @@
<span <span
v-if="station.generalInfo.routes.doubleElectrifiedNames.length != 0" v-if="station.generalInfo.routes.doubleElectrifiedNames.length != 0"
class="track catenary" class="track catenary"
:title="`${$t('sceneries.info.double-track-routes-catenary')}${ data-tooltip-type="BaseTooltip"
:data-tooltip-content="`${$t('sceneries.info.double-track-routes-catenary')}${
station.generalInfo.routes.doubleElectrifiedNames.length station.generalInfo.routes.doubleElectrifiedNames.length
}`" }`"
> >
@@ -187,7 +202,8 @@
<span <span
v-if="station.generalInfo.routes.doubleOtherNames.length != 0" v-if="station.generalInfo.routes.doubleOtherNames.length != 0"
class="track no-catenary" class="track no-catenary"
:title="`${$t('sceneries.info.double-track-routes-other')}${ data-tooltip-type="BaseTooltip"
:data-tooltip-content="`${$t('sceneries.info.double-track-routes-other')}${
station.generalInfo.routes.doubleOtherNames.length station.generalInfo.routes.doubleOtherNames.length
}`" }`"
> >
@@ -201,7 +217,8 @@
v-if="station.generalInfo?.signalType" v-if="station.generalInfo?.signalType"
class="scenery-icon icon-info" class="scenery-icon icon-info"
:class="station.generalInfo?.controlType.replace('+', '-')" :class="station.generalInfo?.controlType.replace('+', '-')"
:title=" data-tooltip-type="BaseTooltip"
:data-tooltip-content="
$t('sceneries.info.control-type') + $t('sceneries.info.control-type') +
$t(`controls.${station.generalInfo?.controlType}`) $t(`controls.${station.generalInfo?.controlType}`)
" "
@@ -214,7 +231,8 @@
class="icon-info" class="icon-info"
:src="`/images/icon-${station.generalInfo.signalType}.svg`" :src="`/images/icon-${station.generalInfo.signalType}.svg`"
:alt="station.generalInfo.signalType" :alt="station.generalInfo.signalType"
:title=" data-tooltip-type="BaseTooltip"
:data-tooltip-content="
$t('sceneries.info.signals-type') + $t(`signals.${station.generalInfo.signalType}`) $t('sceneries.info.signals-type') + $t(`signals.${station.generalInfo.signalType}`)
" "
/> />
@@ -224,7 +242,8 @@
class="icon-info" class="icon-info"
src="/images/icon-SUP.svg" src="/images/icon-SUP.svg"
alt="SUP (RASP-UZK)" alt="SUP (RASP-UZK)"
:title="$t('sceneries.info.SUP')" data-tooltip-type="BaseTooltip"
:data-tooltip-content="$t('sceneries.info.SUP')"
/> />
<img <img
@@ -232,7 +251,8 @@
class="icon-info" class="icon-info"
src="/images/icon-ASDEK.svg" src="/images/icon-ASDEK.svg"
alt="dSAT ASDEK" alt="dSAT ASDEK"
:title="$t('sceneries.info.ASDEK')" data-tooltip-type="BaseTooltip"
:data-tooltip-content="$t('sceneries.info.ASDEK')"
/> />
<img <img
@@ -240,7 +260,8 @@
class="icon-info" class="icon-info"
src="/images/icon-unknown.svg" src="/images/icon-unknown.svg"
alt="icon-unknown" alt="icon-unknown"
:title="$t('sceneries.info.unknown')" data-tooltip-type="BaseTooltip"
:data-tooltip-content="$t('sceneries.info.unknown')"
/> />
</td> </td>
@@ -248,7 +269,7 @@
class="station-users" class="station-users"
:class="{ inactive: !station.onlineInfo }" :class="{ inactive: !station.onlineInfo }"
data-tooltip-type="UsersTooltip" data-tooltip-type="UsersTooltip"
:data-tooltip-content="JSON.stringify(station.onlineInfo?.stationTrains ?? [])" :data-tooltip-content="getUsersTooltipContent(station.onlineInfo?.stationTrains ?? [])"
> >
<span class="text--primary">{{ <span class="text--primary">{{
station.onlineInfo?.stationTrains?.length ?? '-' station.onlineInfo?.stationTrains?.length ?? '-'
@@ -318,7 +339,7 @@ import dateMixin from '../../mixins/dateMixin';
import styleMixin from '../../mixins/styleMixin'; import styleMixin from '../../mixins/styleMixin';
import { useApiStore } from '../../store/apiStore'; import { useApiStore } from '../../store/apiStore';
import { useMainStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
import { Station, Status } from '../../typings/common'; import { Station, Status, TooltipUserTrain, Train } from '../../typings/common';
import { useTooltipStore } from '../../store/tooltipStore'; import { useTooltipStore } from '../../store/tooltipStore';
import { getChangedFilters } from '../../managers/stationFilterManager'; import { getChangedFilters } from '../../managers/stationFilterManager';
import { ActiveSorter, HeadIdsType, headIconsIds, headIds } from './typings'; import { ActiveSorter, HeadIdsType, headIconsIds, headIds } from './typings';
@@ -394,6 +415,15 @@ export default defineComponent({
else this.activeSorter.dir = 1; else this.activeSorter.dir = 1;
this.activeSorter.headerName = headerName; this.activeSorter.headerName = headerName;
},
getUsersTooltipContent(stationTrains: Train[]): string {
const usersTrains: TooltipUserTrain[] = stationTrains.map((train) => ({
driverName: train.driverName,
trainNo: train.trainNo
}));
return JSON.stringify(usersTrains);
} }
} }
}); });
+3 -1
View File
@@ -13,6 +13,7 @@ import BaseTooltip from './BaseTooltip.vue';
import SpawnsTooltip from './SpawnsTooltip.vue'; import SpawnsTooltip from './SpawnsTooltip.vue';
import UsersTooltip from './UsersTooltip.vue'; import UsersTooltip from './UsersTooltip.vue';
import HtmlTooltip from './HtmlTooltip.vue'; import HtmlTooltip from './HtmlTooltip.vue';
import TrainInfoTooltip from "./TrainInfoTooltip.vue";
const BOX_PADDING_PX = 20; const BOX_PADDING_PX = 20;
@@ -23,7 +24,8 @@ export default defineComponent({
BaseTooltip, BaseTooltip,
SpawnsTooltip, SpawnsTooltip,
UsersTooltip, UsersTooltip,
HtmlTooltip HtmlTooltip,
TrainInfoTooltip
}, },
data() { data() {
@@ -0,0 +1,69 @@
<template>
<div class="tooltip-content">
<span v-if="trainInfo">
<b v-if="trainInfo.timetableData" style="text-transform: uppercase">
<span class="text--primary">{{ trainInfo.timetableData.category }}</span>
{{ getCategoryExplanation(trainInfo.timetableData.category) }}
</b>
<div class="text--primary">
<b>{{ trainInfo.stockList[0] }}</b> &bull; {{ trainInfo.length }}m &bull;
{{ (trainInfo.mass / 1000).toFixed(2) }}t
</div>
<div class="text--grayed">
{{ displayTrainPosition(trainInfo) }} - {{ trainInfo.speed }}km/h
<span v-if="!trainInfo.online" style="color: salmon">
- offline {{ lastSeenMessage(trainInfo.lastSeen) }}</span
>
</div>
<div></div>
</span>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { useTooltipStore } from '../../store/tooltipStore';
import trainCategoryMixin from '../../mixins/trainCategoryMixin';
import trainInfoMixin from '../../mixins/trainInfoMixin';
import { useMainStore } from '../../store/mainStore';
export default defineComponent({
mixins: [trainCategoryMixin, trainInfoMixin],
data: () => ({
tooltipStore: useTooltipStore(),
mainStore: useMainStore()
}),
computed: {
trainInfo() {
if (this.tooltipStore.content == '') return null;
// Passed "content" string should be the desired train's ID
return this.mainStore.trainList.find((t) => t.id === this.tooltipStore.content);
},
lastSceneryStatus() {
}
}
});
</script>
<style lang="scss" scoped>
.tooltip-content {
padding: 0.25em 0.5em;
border-radius: 0.25em;
width: 100%;
background-color: #1f1f1f;
box-shadow: 0 0 5px 2px #aaa;
}
img {
height: 1em;
}
</style>
+2 -2
View File
@@ -10,7 +10,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useTooltipStore } from '../../store/tooltipStore'; import { useTooltipStore } from '../../store/tooltipStore';
import { Train } from '../../typings/common'; import { TooltipUserTrain } from '../../typings/common';
export default defineComponent({ export default defineComponent({
data() { data() {
@@ -23,7 +23,7 @@ export default defineComponent({
trains() { trains() {
if (this.tooltipStore.content == '') return []; if (this.tooltipStore.content == '') return [];
const parsedTrains = JSON.parse(this.tooltipStore.content) as Train[]; const parsedTrains = JSON.parse(this.tooltipStore.content) as TooltipUserTrain[];
return (parsedTrains ?? []).sort((a, b) => a.trainNo - b.trainNo); return (parsedTrains ?? []).sort((a, b) => a.trainNo - b.trainNo);
} }
} }
+4 -1
View File
@@ -110,7 +110,10 @@
{{ $t('trains.scenery-offline') }} {{ $t('trains.scenery-offline') }}
</div> </div>
<div v-if="!train.online" class="train-badge offline"> <div
v-if="!train.online && train.lastSeen >= Date.now() - 60000"
class="train-badge offline"
>
<i class="fa-solid fa-user-slash"></i> <i class="fa-solid fa-user-slash"></i>
Offline {{ lastSeenMessage(train.lastSeen) }} Offline {{ lastSeenMessage(train.lastSeen) }}
</div> </div>
+11 -8
View File
@@ -30,17 +30,20 @@
</div> </div>
<div class="search-box"> <div class="search-box">
<select <datalist id="search-active-driver">
class="search-input"
name="active-trains"
id="active-trains"
v-model="searchedDriver"
>
<option value="">{{ $t('options.select-driver') }}</option>
<option v-for="driverName in activeDriverNames" :value="driverName"> <option v-for="driverName in activeDriverNames" :value="driverName">
{{ driverName }} {{ driverName }}
</option> </option>
</select> </datalist>
<input
class="search-input"
list="search-active-driver"
name="search-active-driver"
id="search-active-driver"
:placeholder="$t(`options.search-driver`)"
v-model="searchedDriver"
/>
<button class="btn btn--action search-exit" @click="onInputClear('driver')"> <button class="btn btn--action search-exit" @click="onInputClear('driver')">
<img src="/images/icon-exit.svg" alt="Trains search clear icon" /> <img src="/images/icon-exit.svg" alt="Trains search clear icon" />
+31 -14
View File
@@ -57,7 +57,14 @@
<span>{{ stop.departureLine }}</span> <span>{{ stop.departureLine }}</span>
<span v-if="stop.departureLineInfo"> <span v-if="stop.departureLineInfo">
<span> | {{ stop.departureLineInfo.routeSpeed }}</span> <span>
|
{{
stop.departureLineInfo.routeSpeedExit
? `${stop.departureLineInfo.routeSpeedExit} (${stop.departureLineInfo.routeSpeed})`
: stop.departureLineInfo.routeSpeed
}}</span
>
<img <img
:src=" :src="
@@ -85,13 +92,13 @@
</div> </div>
<div <div
v-if="stop.sceneryName != scheduleStops[i + 1]?.sceneryName" v-if="stop.nextPointRef && stop.sceneryName != stop.nextPointRef.sceneryName"
class="scenery-change-name" class="scenery-change-name"
> >
<span>{{ scheduleStops[i + 1].sceneryName }}</span> <span>{{ stop.nextPointRef.sceneryName }}</span>
<i <i
v-if="!scheduleStops[i + 1].isSceneryOnline" v-if="!stop.nextPointRef.isSceneryOnline"
class="fa-solid fa-ban fa-sm" class="fa-solid fa-ban fa-sm"
data-tooltip-type="BaseTooltip" data-tooltip-type="BaseTooltip"
:data-tooltip-content="$t('app.tooltip-scenery-offline')" :data-tooltip-content="$t('app.tooltip-scenery-offline')"
@@ -101,30 +108,33 @@
<div <div
class="scenery-route" class="scenery-route"
v-if="stop.sceneryName != scheduleStops[i + 1]?.sceneryName" v-if="stop.nextPointRef && stop.sceneryName != stop.nextPointRef.sceneryName"
> >
<span> {{ scheduleStops[i + 1].arrivalLine }}</span> <span> {{ stop.nextPointRef.arrivalLine }}</span>
<span v-if="scheduleStops[i + 1].arrivalLineInfo"> <span v-if="stop.nextPointRef.arrivalLineInfo">
<span> | {{ scheduleStops[i + 1].arrivalLineInfo!.routeSpeed }} </span> <span> | {{ stop.nextPointRef.arrivalLineInfo!.routeSpeed }}</span>
<span v-if="stop.nextPointRef.arrivalLineInfo!.routeSpeedExit"
>({{ stop.nextPointRef.arrivalLineInfo!.routeSpeedExit }})</span
>
<img <img
:src=" :src="
scheduleStops[i + 1].arrivalLineInfo?.isElectric stop.nextPointRef.arrivalLineInfo?.isElectric
? '/images/icon-catenary.svg' ? '/images/icon-catenary.svg'
: '/images/icon-we4a.png' : '/images/icon-we4a.png'
" "
data-tooltip-type="BaseTooltip" data-tooltip-type="BaseTooltip"
:data-tooltip-content=" :data-tooltip-content="
$t( $t(
`trains.${!scheduleStops[i + 1].arrivalLineInfo?.isElectric ? 'no-' : ''}catenary-tooltip` `trains.${!stop.nextPointRef.arrivalLineInfo?.isElectric ? 'no-' : ''}catenary-tooltip`
) )
" "
width="14" width="14"
/> />
<img <img
v-if="scheduleStops[i + 1].arrivalLineInfo!.isRouteSBL" v-if="stop.nextPointRef.arrivalLineInfo!.isRouteSBL"
src="/images/icon-sbl-transparent.svg" src="/images/icon-sbl-transparent.svg"
width="14" width="14"
data-tooltip-type="BaseTooltip" data-tooltip-type="BaseTooltip"
@@ -228,7 +238,7 @@ export default defineComponent({
departureLineInfo = pathData.departureLineData; departureLineInfo = pathData.departureLineData;
} }
for (const stop of followingStops) { followingStops.forEach((stop, i) => {
let isExternal = false; let isExternal = false;
if (stop.arrivalLine === currentPath.arrivalRouteExt) { if (stop.arrivalLine === currentPath.arrivalRouteExt) {
@@ -287,7 +297,9 @@ export default defineComponent({
status: stop.confirmed ? 'confirmed' : stop.stopped ? 'stopped' : 'unconfirmed', status: stop.confirmed ? 'confirmed' : stop.stopped ? 'stopped' : 'unconfirmed',
sceneryName: currentPath.stationName, sceneryName: currentPath.stationName,
isSceneryOnline: pathData?.isOnline ?? false isSceneryOnline: pathData?.isOnline ?? false,
nextPointRef: null
}; };
if (internalRouteInfo) { if (internalRouteInfo) {
@@ -309,6 +321,11 @@ export default defineComponent({
stopRows.push(rowData); stopRows.push(rowData);
// Assign this row data object to the last one as reference
if (i != 0) {
stopRows[i - 1].nextPointRef = rowData;
}
if (stop.departureLine === currentPath.departureRouteExt) { if (stop.departureLine === currentPath.departureRouteExt) {
// Reverse search for last scenery checkpoint // Reverse search for last scenery checkpoint
if (pathData?.departureLineData) { if (pathData?.departureLineData) {
@@ -328,7 +345,7 @@ export default defineComponent({
currentPath = timetablePath[++currentPathIndex]; currentPath = timetablePath[++currentPathIndex];
pathData = this.getPathSceneryData(currentPath); pathData = this.getPathSceneryData(currentPath);
} }
} });
return stopRows; return stopRows;
}, },
+2
View File
@@ -196,4 +196,6 @@ export interface TrainSchedulePoint {
isSBL: boolean; isSBL: boolean;
sceneryName: string | null; sceneryName: string | null;
isSceneryOnline: boolean; isSceneryOnline: boolean;
nextPointRef: TrainSchedulePoint | null;
} }
+1 -1
View File
@@ -22,7 +22,7 @@
"TRE", "TRS", "TRE", "TRS",
"TSE", "TSS", "TSE", "TSS",
"THE", "THS", "THE", "THS",
"LPE", "LPE", "LPS",
"LTE", "LTS", "LTE", "LTS",
"LSS", "LSS",
"LZE", "LZS", "LZE", "LZS",
+23 -20
View File
@@ -84,7 +84,7 @@
"categories": { "categories": {
"EI": "domestic express", "EI": "domestic express",
"EC": "international express", "EC": "international express",
"EN": "domestic night express", "EN": "international night express",
"MP": "intervoivodeship bullet", "MP": "intervoivodeship bullet",
"MO": "intervoivodeship regio", "MO": "intervoivodeship regio",
"MM": "international bullet", "MM": "international bullet",
@@ -337,18 +337,20 @@
}, },
"info": { "info": {
"control-type": "Control type: ", "control-type": "Control type: ",
"signals-type": "Signals type: ", "signals-type": "Signalling type: ",
"SBL": "This scenery has automatic block signalling (ABS/SBL) system on following routes: ", "SBL": "A scenery with automatic block signalling (ABS/SBL) on routes: ",
"SUP": "Requires the SUP program (level crossing remote control)", "SUP": "Requires the SUP program (level crossing remote control)",
"ASDEK": "Requires the ASDEK program (defect detection of moving rolling stock)", "ASDEK": "ASDEK program available (defect detection of moving rolling stock)",
"TWB-all": "This scenery has two-way route blockade on all routes", "TWB-all": "This scenery has two-way route blockade on all routes",
"TWB-routes": "This scenery has two-way route blockade on following routes: ", "TWB-routes": "This scenery has two-way route blockade on following routes: ",
"default": "This scenery is available by default", "default": "Scenery available in game package",
"non-public": "This scenery is not public", "nonDefault": "Sceneria available to download from forum site",
"unavailable": "This scenery is unavailable", "req-level": "all dispatcher levels | requries {lvl} dispatcher lvl | requires {lvl} dispatcher lvl",
"abandoned": "This scenery is no longer supported by its creators", "non-public": "Non-public scenery",
"unknown": "This scenery isn't recognizable right now", "unavailable": "Unavailable scenery",
"real": "Scenery with real lines: ", "abandoned": "Abandoned scenery",
"unknown": "Unknown scenery",
"real": "Scenery with real Polish routes: ",
"double-track-routes-catenary": "Electrified double-track routes count: ", "double-track-routes-catenary": "Electrified double-track routes count: ",
"single-track-routes-catenary": "Electrified single-track routes count: ", "single-track-routes-catenary": "Electrified single-track routes count: ",
"double-track-routes-other": "Not electrified double-track routes count: ", "double-track-routes-other": "Not electrified double-track routes count: ",
@@ -543,7 +545,7 @@
"no-users": "NO ACTIVE PLAYERS", "no-users": "NO ACTIVE PLAYERS",
"no-spawns": "NO OPEN SPAWNS", "no-spawns": "NO OPEN SPAWNS",
"no-scenery": "Oops! This scenery doesn't exist!", "no-scenery": "Oops! This scenery doesn't exist!",
"return-btn": "Return", "return-btn": "BACK TO THE LAST SITE",
"history-btn": "View the dispatcher history", "history-btn": "View the dispatcher history",
"info-btn": "Return to the scenery view", "info-btn": "Return to the scenery view",
"authors-title": "Scenery author | Scenery authors", "authors-title": "Scenery author | Scenery authors",
@@ -589,15 +591,16 @@
"terminated": "Timetable terminated", "terminated": "Timetable terminated",
"begins": "BEGINS HERE", "begins": "BEGINS HERE",
"terminates": "TERMINATES\nHERE", "terminates": "TERMINATES\nHERE",
"from": "FROM", "from": "Arrives from",
"to": "TO", "to": "Departs to",
"desc-arriving": "The train is not here yet.\nIt's going to come from: <b>{prevStationName} (route {prevDepartureLine})</b>", "desc-beginning": "Outside scenery / begins here",
"desc-online": "The train is at the station.\nIt's going to leave to: <b>{nextStationName} (route {nextArrivalLine})</b>", "desc-arriving": "Arrives from: <b><u>{prevStationName} ({prevDepartureLine})</u></b>",
"desc-stopped": "The train is at the station and is stopped.\nIt's going to leave towards: <b>{nextStationName} (route {nextArrivalLine})</b>", "desc-online": "On scenery / direction: <b><u>{nextStationName} ({nextArrivalLine})</u></b>",
"desc-next-arrival": "Leaves towards: <b>{nextStationName} (route {nextArrivalLine})</b>", "desc-stopped": "On scenery - stopped / direction: <b><u>{nextStationName} ({nextArrivalLine})</u></b>",
"desc-departed": "The train is at the station and it's been departed.\nLeaves towards: <b>{nextStationName} (route {nextArrivalLine})</b>", "desc-next-arrival": "On scenery / direction: <b><u>{nextStationName} ({nextArrivalLine})</u></b>",
"desc-departed-ends": "The train is at the station and it's been departed.\nLeaves towards station: <b>{nextStationName}</b>", "desc-departed": "On scenery / departed to: <b><u>{nextStationName} ({nextArrivalLine})</u></b>",
"desc-departed-away": "The train has been departed to:\n<b>{nextStationName} (route {nextArrivalLine})</b>", "desc-departed-ends": "On scenery / departed to: <b><u>{nextStationName}</u></b>",
"desc-departed-away": "Departed to: <b><u>{nextStationName} ({nextArrivalLine})</u></b>",
"desc-end": "The train terminates here", "desc-end": "The train terminates here",
"desc-terminated": "The train has been terminated" "desc-terminated": "The train has been terminated"
}, },
+16 -13
View File
@@ -81,7 +81,7 @@
"categories": { "categories": {
"EI": "ekspres krajowy", "EI": "ekspres krajowy",
"EC": "ekspres międzynarodowy", "EC": "ekspres międzynarodowy",
"EN": "ekspres krajowy nocny", "EN": "ekspres międzynarodowy nocny",
"MP": "międzywojewódzki pospieszny", "MP": "międzywojewódzki pospieszny",
"MO": "międzywojewódzki osobowy", "MO": "międzywojewódzki osobowy",
"MM": "międzynarodowy pospieszny", "MM": "międzynarodowy pospieszny",
@@ -338,8 +338,10 @@
"signals-type": "Sygnalizacja: ", "signals-type": "Sygnalizacja: ",
"SBL": "Sceneria posiada SBL na szlakach: ", "SBL": "Sceneria posiada SBL na szlakach: ",
"SUP": "Wymaga programu SUP do kontroli systemu RASP-UZK", "SUP": "Wymaga programu SUP do kontroli systemu RASP-UZK",
"ASDEK": "Wymaga programu ASDEK do detekcji stanów awaryjnych taboru w ruchu", "ASDEK": "Dostępny program ASDEK do detekcji stanów awaryjnych taboru w ruchu",
"default": "Sceneria dostępna domyślnie w paczce z grą", "default": "Sceneria dostępna domyślnie w paczce z grą",
"nonDefault": "Sceneria dostępna do pobrania ze strony forum",
"req-level": "ogólnodostępna | od {lvl} poz. DR | od {lvl} poz. DR",
"non-public": "Sceneria niepubliczna", "non-public": "Sceneria niepubliczna",
"unavailable": "Sceneria niedostępna", "unavailable": "Sceneria niedostępna",
"abandoned": "Sceneria wycofana z rozgrywki", "abandoned": "Sceneria wycofana z rozgrywki",
@@ -529,7 +531,7 @@
"no-users": "BRAK AKTYWNYCH GRACZY", "no-users": "BRAK AKTYWNYCH GRACZY",
"no-spawns": "BRAK OTWARTYCH SPAWNÓW", "no-spawns": "BRAK OTWARTYCH SPAWNÓW",
"no-scenery": "Ups! Ta sceneria nie istnieje!", "no-scenery": "Ups! Ta sceneria nie istnieje!",
"return-btn": "Powrót", "return-btn": "POWRÓT DO POPRZEDNIEJ STRONY",
"history-btn": "Przejdź do widoku historii dyżurnych ruchu", "history-btn": "Przejdź do widoku historii dyżurnych ruchu",
"info-btn": "Wróć do widoku scenerii", "info-btn": "Wróć do widoku scenerii",
"authors-title": "Autor scenerii | Autorzy scenerii", "authors-title": "Autor scenerii | Autorzy scenerii",
@@ -575,17 +577,18 @@
"terminated": "Rozkład jazdy zakończony", "terminated": "Rozkład jazdy zakończony",
"begins": "ROZPOCZYNA\nBIEG", "begins": "ROZPOCZYNA\nBIEG",
"terminates": "KOŃCZY BIEG", "terminates": "KOŃCZY BIEG",
"from": "Z", "from": "Przyjedzie z",
"to": "DO", "to": "Odjeżdża do",
"desc-arriving": "Pociągu nie ma jeszcze na tej scenerii.\nPrzyjedzie z: <b>{prevStationName} (szlak {prevDepartureLine})</b>", "desc-beginning": "Poza scenerią / rozpoczyna bieg",
"desc-online": "Pociąg jest na tej scenerii.\nOdjedzie w kierunku: <b>{nextStationName} (szlak {nextArrivalLine})</b>", "desc-arriving": "Przyjedzie z: <b><u>{prevStationName} ({prevDepartureLine})</u></b>",
"desc-stopped": "Pociąg jest na tej scenerii i odbywa postój.\nOdjedzie w kierunku: <b>{nextStationName} (szlak {nextArrivalLine})</b>", "desc-online": "Na scenerii / kierunek: <b><u>{nextStationName} ({nextArrivalLine})</u></b>",
"desc-next-arrival": "Odjeżdża do:\n<b>{nextStationName} (szlak {nextArrivalLine})</b>", "desc-stopped": "Na scenerii - postój / kierunek: <b><u>{nextStationName} ({nextArrivalLine})</u></b>",
"desc-departed": "Pociąg jest na tej scenerii i został odprawiony.\nOdjeżdża w kierunku: <b>{nextStationName} (szlak {nextArrivalLine})</b>", "desc-next-arrival": "Na scenerii / kierunek: <b><u>{nextStationName} ({nextArrivalLine})</u></b>",
"desc-departed-ends": "Pociąg jest na tej scenerii i został odprawiony.\nOdjechał w kierunku stacji: <b>{nextStationName}</b>", "desc-departed": "Na scenerii / odprawiony do: <b><u>{nextStationName} ({nextArrivalLine})</u></b>",
"desc-departed-away": "Pociąg został odprawiony i odjechał do:\n<b>{nextStationName} (szlak {nextArrivalLine})</b>", "desc-departed-ends": "Na scenerii / odprawiony do: <b><u>{nextStationName}</u></b>",
"desc-departed-away": "Odprawiony do: <b><u>{nextStationName} ({nextArrivalLine})</u></b>",
"desc-end": "Pociąg kończy bieg", "desc-end": "Pociąg kończy bieg",
"desc-terminated": "Pociąg skończył bieg" "desc-terminated": "Pociąg zakończył bieg"
}, },
"history": { "history": {
"title": "DZIENNIK ROZKŁADÓW JAZDY" "title": "DZIENNIK ROZKŁADÓW JAZDY"
+5 -2
View File
@@ -36,7 +36,10 @@ const routes: Array<RouteRecordRaw> = [
props: (route) => ({ props: (route) => ({
region: route.query.region, region: route.query.region,
station: route.query.station station: route.query.station
}) }),
beforeEnter: (to, from) => {
to.meta['prevPath'] = from.fullPath;
}
}, },
{ {
path: '/journal', path: '/journal',
@@ -72,7 +75,7 @@ const router = createRouter({
from.query['view'] === undefined && from.query['view'] === undefined &&
!savedPosition !savedPosition
) )
return { el: `.scenery-left`, behavior: 'instant', top: 3 }; return { el: `.app_main`, behavior: 'instant', top: -13 };
if (savedPosition) return savedPosition; if (savedPosition) return savedPosition;
}, },
+4 -1
View File
@@ -8,7 +8,8 @@ export const tooltipKeys = [
'VehiclePreviewTooltip', 'VehiclePreviewTooltip',
'SpawnsTooltip', 'SpawnsTooltip',
'UsersTooltip', 'UsersTooltip',
'HtmlTooltip' 'HtmlTooltip',
'TrainInfoTooltip'
] as const; ] as const;
export type TooltipType = (typeof tooltipKeys)[number]; export type TooltipType = (typeof tooltipKeys)[number];
@@ -33,6 +34,7 @@ export const useTooltipStore = defineStore('tooltipStore', {
this.content = ''; this.content = '';
}, },
// Tooltip handler reading attributes of DOM elements
handle(e: MouseEvent) { handle(e: MouseEvent) {
const targetEl = e const targetEl = e
.composedPath() .composedPath()
@@ -44,6 +46,7 @@ export const useTooltipStore = defineStore('tooltipStore', {
return; return;
} }
// Tooltip content is a string but may be parsed to objects / html in corresponding tooltip type components
const tooltipType = targetEl.getAttribute('data-tooltip-type'); const tooltipType = targetEl.getAttribute('data-tooltip-type');
const tooltipContent = targetEl.getAttribute('data-tooltip-content'); const tooltipContent = targetEl.getAttribute('data-tooltip-content');
-42
View File
@@ -297,48 +297,6 @@ a.a-button {
} }
} }
.return-btn {
display: none;
justify-content: center;
align-items: center;
position: fixed;
right: 2.5rem;
bottom: 4rem;
z-index: 100;
width: 3.5rem;
font-size: 3rem;
background-color: #555;
outline: 3px solid #222;
color: white;
border-radius: 50%;
cursor: pointer;
&:hover {
background-color: #3c3c3c;
}
img {
width: 1.3em;
}
@include responsive.smallScreen {
bottom: 1em;
right: 0;
left: 50%;
width: 1em;
height: 1em;
transform: translateX(-50%);
}
}
// Basic tooltip // Basic tooltip
[data-tooltip] { [data-tooltip] {
cursor: help; cursor: help;
+23
View File
@@ -142,6 +142,7 @@ export interface StationRoutesInfo {
isRouteSBL: boolean; isRouteSBL: boolean;
routeLength: number; routeLength: number;
routeSpeed: number; routeSpeed: number;
routeSpeedExit?: number;
routeTracks: number; routeTracks: number;
hidden?: boolean; hidden?: boolean;
realLineNo?: number; realLineNo?: number;
@@ -252,3 +253,25 @@ export interface VehicleCargo {
id: string; id: string;
weight: number; weight: number;
} }
export interface TooltipUserTrain {
driverName: string;
trainNo: number;
}
export interface TooltipTrainInfo {
mass: number;
length: number;
speed: number;
signal: string;
distance: number;
connectedTrack: string;
trainNo: number;
driverName: string;
driverLevel: number;
currentStationName: string;
currentStationHash: string;
headVehicleName: string;
stockCount: number;
trainTimetableCategory?: string;
}
+46 -100
View File
@@ -2,12 +2,6 @@
<div class="scenery-view"> <div class="scenery-view">
<div class="scenery-wrapper" ref="card-wrapper"> <div class="scenery-wrapper" ref="card-wrapper">
<div class="scenery-left"> <div class="scenery-left">
<div class="scenery-actions">
<button class="back-btn" :title="$t('scenery.return-btn')" @click="onReturnButtonClick">
<img src="/images/icon-back.svg" alt="return button" />
</button>
</div>
<SceneryHeader <SceneryHeader
:stationName="station" :stationName="station"
:station="stationInfo" :station="stationInfo"
@@ -23,8 +17,8 @@
v-for="(viewMode, i) in viewModes" v-for="(viewMode, i) in viewModes"
:key="i" :key="i"
class="btn btn--option" class="btn btn--option"
:class="{ checked: currentMode == viewMode.component }" :class="{ checked: currentMode == viewMode.component.name }"
@click="setViewMode(viewMode.component)" @click="setViewMode(viewMode.component.name!)"
> >
{{ $t(viewMode.id) }} {{ $t(viewMode.id) }}
</button> </button>
@@ -32,17 +26,17 @@
<div <div
v-if=" v-if="
apiStore.dataStatuses.sceneries == Status.Loading || apiStore.dataStatuses.sceneries == Status.Data.Loading ||
apiStore.dataStatuses.connection == Status.Loading apiStore.dataStatuses.connection == Status.Data.Loading
" "
></div> ></div>
<keep-alive v-else> <keep-alive v-else>
<component <component
:is="currentMode" :is="currentViewComponent"
:onlineScenery="onlineSceneryInfo" :onlineScenery="onlineSceneryInfo"
:station="stationInfo" :station="stationInfo"
:key="currentMode" :key="currentViewComponent.name"
></component> ></component>
</keep-alive> </keep-alive>
</div> </div>
@@ -50,40 +44,25 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { computed, defineComponent } from 'vue'; import { computed, onMounted } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import routerMixin from '../mixins/routerMixin';
import { useMainStore } from '../store/mainStore'; import { useMainStore } from '../store/mainStore';
import SceneryInfo from '../components/SceneryView/SceneryInfo.vue'; import SceneryInfo from '../components/SceneryView/SceneryInfo.vue';
import SceneryHeader from '../components/SceneryView/SceneryHeader.vue'; import SceneryHeader from '../components/SceneryView/SceneryHeader.vue';
import SceneryTimetable from '../components/SceneryView/SceneryTimetable.vue'; import SceneryTimetable from '../components/SceneryView/SceneryTimetable.vue';
import SceneryTimetablesHistory from '../components/SceneryView/SceneryTimetablesHistory.vue'; import SceneryTimetablesHistory from '../components/SceneryView/SceneryTimetablesHistory.vue';
import SceneryDispatchersHistory from '../components/SceneryView/SceneryDispatchersHistory.vue'; import SceneryDispatchersHistory from '../components/SceneryView/SceneryDispatchersHistory.vue';
import ActionButton from '../components/Global/ActionButton.vue';
import { Status } from '../typings/common';
import { useApiStore } from '../store/apiStore'; import { useApiStore } from '../store/apiStore';
import { Status } from '../typings/common';
enum SceneryViewMode { const route = useRoute();
'TIMETABLES_ACTIVE', const router = useRouter();
'TIMETABLES_HISTORY',
'SCENERY_HISTORY'
}
export default defineComponent({ const props = defineProps({
name: 'SceneryView',
components: {
SceneryInfo,
SceneryTimetable,
ActionButton,
SceneryHeader,
SceneryTimetablesHistory,
SceneryDispatchersHistory
},
props: {
region: { region: {
type: String, type: String,
required: false required: false
@@ -93,98 +72,65 @@ export default defineComponent({
type: String, type: String,
required: true required: true
} }
}, });
mixins: [routerMixin], const store = useMainStore();
const apiStore = useApiStore();
data: () => ({ const viewModes = [
store: useMainStore(),
apiStore: useApiStore(),
viewModes: [
{ {
id: 'scenery.option-active-timetables', id: 'scenery.option-active-timetables',
component: 'SceneryTimetable' component: SceneryTimetable
}, },
{ {
id: 'scenery.option-timetables-history', id: 'scenery.option-timetables-history',
component: 'SceneryTimetablesHistory' component: SceneryTimetablesHistory
}, },
{ {
id: 'scenery.option-dispatchers-history', id: 'scenery.option-dispatchers-history',
component: 'SceneryDispatchersHistory' component: SceneryDispatchersHistory
} }
], ];
sceneryViewMode: SceneryViewMode,
selectedCheckpoint: '',
currentViewCompontent: 'SceneryTimetable',
onlineFrom: -1,
Status: Status.Data
}),
setup() { const currentMode = computed(() => {
const route = useRoute(); return route.query.view?.toString() ?? 'SceneryTimetable';
});
const isComponentVisible = computed(() => route.path === '/scenery'); const currentViewComponent = computed(() => {
return (
return { viewModes.find((mode) => mode.component.name == currentMode.value)?.component ??
isComponentVisible SceneryTimetable
};
},
computed: {
currentMode() {
return this.$route.query.view?.toString() ?? 'SceneryTimetable';
},
stationInfo() {
return this.store.stationList.find(
(station) => station.name === this.station?.toString().replace(/_/g, ' ')
); );
}, });
onlineSceneryInfo() { const stationInfo = computed(() => {
return this.store.activeSceneryList.find( return store.stationList.find(
(station) => station.name === props.station?.toString().replace(/_/g, ' ')
);
});
const onlineSceneryInfo = computed(() => {
return store.activeSceneryList.find(
(scenery) => (scenery) =>
scenery.name === this.station?.toString().replace(/_/g, ' ') && scenery.name === props.station?.toString().replace(/_/g, ' ') &&
scenery.region == this.store.region.id scenery.region == store.region.id
); );
} });
},
methods: { function setViewMode(componentName: string) {
setViewMode(componentName: string) { router.push({
this.$router.push({ path: route.path,
path: this.$route.path,
query: { query: {
...this.$route.query, ...route.query,
view: componentName view: componentName
} }
}); });
},
loadSelectedCheckpoint() {
if (!this.stationInfo?.generalInfo?.checkpoints) return;
if (this.stationInfo.generalInfo.checkpoints.length == 0) return;
this.selectedCheckpoint = this.stationInfo.generalInfo.checkpoints[0];
},
onReturnButtonClick() {
this.$router.back();
} }
}
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@use '../styles/responsive'; @use '../styles/responsive';
button.back-btn {
img {
width: 2em;
}
}
.scenery { .scenery {
&-view { &-view {
display: flex; display: flex;
+1 -1
View File
@@ -3202,7 +3202,7 @@ sharp@*, sharp@^0.33.5:
showdown@^2.1.0: showdown@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz" resolved "https://registry.yarnpkg.com/showdown/-/showdown-2.1.0.tgz#1251f5ed8f773f0c0c7bfc8e6fd23581f9e545c5"
integrity sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ== integrity sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==
dependencies: dependencies:
commander "^9.0.0" commander "^9.0.0"