137 lines
3.7 KiB
TypeScript
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'
|
|
);
|
|
};
|