Files
Portfolio/src/components/JourneyHud.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>