feat: overhead gantry top bar with a docking sign window
The top bar becomes a steel gantry. Its centre window is a mask cut-out the 3D exit sign docks into; between signs the window shows the dash read-out. The drive controls move into the right bay, and the reading viewport is clipped at the beam, so the empty band under the bar is gone.
This commit is contained in:
@@ -253,6 +253,8 @@ onUnmounted(() => removePageListeners());
|
||||
:journey="journey"
|
||||
:section-label="activeSectionLabel"
|
||||
:exit="exitFor(journey.activeSectionId)"
|
||||
:next-label="nextLabelFor(journey.activeSectionId)"
|
||||
:next-exit="nextExitFor(journey.activeSectionId)"
|
||||
/>
|
||||
<header class="site-header">
|
||||
<SiteNavigation
|
||||
|
||||
@@ -7,8 +7,15 @@ const props = defineProps<{
|
||||
journey: JourneySnapshot;
|
||||
sectionLabel: string;
|
||||
exit?: string | undefined;
|
||||
nextLabel?: string | undefined;
|
||||
nextExit?: 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 ?? ''} \u2192 ${props.nextLabel}`.toUpperCase(),
|
||||
);
|
||||
|
||||
const routePercent = computed(() => `${Math.round(Math.min(1, Math.max(0, props.journey.routeProgress)) * 100)}%`);
|
||||
|
||||
// The dash clock is decorative and CSS-generated, so SSR and hydrated text stay identical.
|
||||
@@ -24,7 +31,7 @@ onUnmounted(() => window.clearInterval(timer));
|
||||
|
||||
<template>
|
||||
<aside class="journey-hud" aria-hidden="true" :data-exit="exit" :data-clock="clock">
|
||||
<span class="journey-hud__route"><span>ROUTE</span> <strong>{{ routePercent }}</strong></span>
|
||||
<span class="journey-hud__route" :data-next="next"><span>ROUTE</span> <strong>{{ routePercent }}</strong></span>
|
||||
<span class="journey-hud__section">{{ sectionLabel }}</span>
|
||||
<strong v-if="journey.arrived" class="journey-hud__arrival">ARRIVED</strong>
|
||||
</aside>
|
||||
|
||||
@@ -93,6 +93,22 @@ onBeforeUnmount(() => {
|
||||
compactQuery = null;
|
||||
});
|
||||
|
||||
/** The gantry's sign window drives on to the next exit (or back to the
|
||||
* start from the destination), exactly like following that exit's link. */
|
||||
const nextIndex = computed(() =>
|
||||
activeIndex.value >= props.items.length - 1 ? 0 : activeIndex.value + 1,
|
||||
);
|
||||
const windowLabel = computed(() => {
|
||||
const next = props.items[nextIndex.value];
|
||||
if (next === undefined) return "Drive on";
|
||||
return nextIndex.value === 0
|
||||
? `Back to the start: ${next.label}`
|
||||
: `Drive to the next exit: ${next.label}`;
|
||||
});
|
||||
const driveOn = (): void => {
|
||||
list.value?.querySelectorAll<HTMLAnchorElement>("a")[nextIndex.value]?.click();
|
||||
};
|
||||
|
||||
const navigate = (event: MouseEvent): void => {
|
||||
props.onSectionLinkClick(event, true);
|
||||
if (window.matchMedia("(max-width: 767px)").matches)
|
||||
@@ -117,5 +133,13 @@ const navigate = (event: MouseEvent): void => {
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
<button
|
||||
v-if="enhanced"
|
||||
type="button"
|
||||
class="gantry-window"
|
||||
:aria-label="windowLabel"
|
||||
:title="windowLabel"
|
||||
@click="driveOn"
|
||||
></button>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user