58 lines
2.6 KiB
Vue
58 lines
2.6 KiB
Vue
<script setup lang="ts">
|
|
import { computed } from 'vue';
|
|
import type { JourneySnapshot } from '@/journey/types';
|
|
import { SIGN_ACCENTS } from '@/journey/sign-routes';
|
|
|
|
const props = defineProps<{
|
|
journey: JourneySnapshot;
|
|
sectionLabel: string;
|
|
exit?: string | undefined;
|
|
nextLabel?: string | undefined;
|
|
nextExit?: string | undefined;
|
|
/** Set client-side only (the 03:12 window); empty on the server. */
|
|
subline?: string | undefined;
|
|
}>();
|
|
|
|
/** Decorative next-exit line (CSS generated content keeps SSR text stable). */
|
|
const next = computed(() =>
|
|
props.nextLabel === undefined ? 'END OF ROUTE' : `NEXT ${props.nextExit ?? ''} → ${props.nextLabel}`.toUpperCase(),
|
|
);
|
|
|
|
/* The window's flat exit plate: exit tab, chapter, a next-exit chevron, and
|
|
a route rule that fills along its lower edge. */
|
|
const route = computed(() => Math.min(1, Math.max(0, props.journey.routeProgress)));
|
|
const routePercent = computed(() => `${Math.round(route.value * 100)}%`);
|
|
/* MILE MARKERS: "LOG 02/04 · ..." rolls its index on 1-digit odometer
|
|
columns, each keyed by its value so a change re-enters (route-log.css).
|
|
Client-only: the server sub-line is empty, so its HTML is unchanged. */
|
|
const log = computed(() => /^LOG (\d+)\/(\d+)(.*)$/.exec(props.subline ?? ''));
|
|
const rewind = computed(() => (props.subline ?? '').endsWith('REWIND'));
|
|
/* The plate is keyed by chapter, so a new chapter is a new element: under
|
|
reduced motion it fades in over 160 ms (gantry.css) instead of swapping. */
|
|
</script>
|
|
|
|
<template>
|
|
<aside
|
|
:key="journey.activeSectionId"
|
|
class="journey-hud"
|
|
aria-hidden="true"
|
|
:data-exit="exit"
|
|
:data-arrived="journey.arrived"
|
|
:style="{
|
|
'--route': route,
|
|
'--sign-accent': SIGN_ACCENTS[journey.activeSectionId],
|
|
'--label-chars': sectionLabel.length,
|
|
}"
|
|
>
|
|
<span class="journey-hud__route" :data-next="next"><span>ROUTE</span> <strong>{{ routePercent }}</strong></span>
|
|
<span class="journey-hud__section">{{ sectionLabel }}</span>
|
|
<!-- END OF SHIFT: html[data-dawn='day'] swaps the label for data-day (CSS). -->
|
|
<strong v-if="journey.arrived" class="journey-hud__arrival" data-day="SHIFT OVER">ARRIVED</strong>
|
|
<template v-if="log">
|
|
<span class="journey-hud__subline" :data-rewind="rewind">LOG <span class="route-odo"><span v-for="(digit, i) in log[1]" :key="`${i}:${digit}`" class="route-odo__digit">{{ digit }}</span></span>/{{ log[2] }}{{ log[3] }}</span>
|
|
<span :key="log[1]" class="journey-hud__log-pulse"></span>
|
|
</template>
|
|
<span v-else class="journey-hud__subline">{{ subline }}</span>
|
|
</aside>
|
|
</template>
|