39948-vm/frontend/src/lib/navigationHelpers.ts
2026-03-28 17:11:39 +04:00

137 lines
3.7 KiB
TypeScript

/**
* Navigation Helpers
*
* Shared utilities for page navigation in RuntimePresentation and constructor.tsx.
* Handles target page resolution, back navigation detection, and transition blocking.
*/
import type { RuntimePage } from '../types/runtime';
import type {
NavigableElement,
NavigationTarget,
TransitionPhase,
} from '../types/presentation';
/**
* Resolve target page from element navigation properties.
* Supports both targetPageSlug (new) and targetPageId (legacy).
*
* @param element - Element with navigation properties
* @param pages - Available pages to search
* @returns The target page or undefined if not found
*/
export const resolveNavigationTarget = (
element: NavigableElement,
pages: RuntimePage[],
): NavigationTarget | null => {
const targetPageSlug = element.targetPageSlug;
const legacyTargetPageId = element.targetPageId;
let targetPage: RuntimePage | undefined;
if (targetPageSlug) {
targetPage = pages.find((p) => p.slug === targetPageSlug);
} else if (legacyTargetPageId) {
targetPage = pages.find((p) => p.id === legacyTargetPageId);
}
if (!targetPage) {
return null;
}
const isBack = isBackNavigation(element);
return {
page: targetPage,
pageId: targetPage.id,
transitionVideoUrl: element.transitionVideoUrl,
isBack,
};
};
/**
* Determine if navigation direction is "back".
* Elements with navType='back' or type='navigation_prev' navigate backwards.
*
* @param element - Element to check
* @returns true if this is a back navigation
*/
export const isBackNavigation = (element: NavigableElement): boolean => {
return element.navType === 'back' || element.type === 'navigation_prev';
};
/**
* Get navigation direction based on element properties.
*
* @param element - Element with navigation properties
* @returns 'back' or 'forward'
*/
export const getNavigationDirection = (
element: NavigableElement,
): 'back' | 'forward' => {
return isBackNavigation(element) ? 'back' : 'forward';
};
/**
* Check if transition is actively blocking navigation.
* Navigation should be blocked during preparing, playing, or reversing phases.
*
* @param transitionPhase - Current transition phase
* @param isBuffering - Whether video is buffering
* @returns true if navigation should be blocked
*/
export const isTransitionBlocking = (
transitionPhase: TransitionPhase,
isBuffering: boolean,
): boolean => {
const activePhases: TransitionPhase[] = ['preparing', 'playing', 'reversing'];
return activePhases.includes(transitionPhase) || isBuffering;
};
/**
* Check if element has a playable transition.
* A transition is playable if it has a video URL, and for back navigation,
* either supports reverse or has a separate reverse video.
*
* @param element - Element with transition properties
* @param direction - Navigation direction
* @returns true if element has a playable transition
*/
export const hasPlayableTransition = (
element: {
transitionVideoUrl?: string;
transitionReverseMode?: string;
reverseVideoUrl?: string;
},
direction: 'back' | 'forward' = 'forward',
): boolean => {
if (!element.transitionVideoUrl) {
return false;
}
// For back navigation with separate_video mode, need reverse video
if (
direction === 'back' &&
element.transitionReverseMode === 'separate_video' &&
!element.reverseVideoUrl
) {
return false;
}
return true;
};
/**
* Check if element is a navigation element type.
*
* @param elementType - Element type to check
* @returns true if element is a navigation type
*/
export const isNavigationType = (elementType: string): boolean => {
return (
elementType === 'navigation_next' ||
elementType === 'navigation_prev' ||
elementType === 'navigation'
);
};