39948-vm/frontend/src/lib/resolveSlideTransition.ts
2026-05-04 17:30:32 +02:00

154 lines
4.6 KiB
TypeScript

/**
* Resolve Slide Transition Settings
*
* Cascade: Page Transition Settings -> Element Override
*
* Unlike page transitions, slide transitions:
* - Map 'video' type to 'fade' (no video between slides)
* - Support fade through overlay for crossfade effect
*/
import type {
ResolvedTransitionSettings,
EasingFunction,
SlideTransitionType,
} from '../types/transition';
export interface SlideTransitionOverride {
type?: SlideTransitionType | '' | undefined;
durationMs?: number | '' | undefined;
easing?: EasingFunction | '' | undefined;
overlayColor?: string | undefined;
}
export interface ResolvedSlideTransition {
type: SlideTransitionType;
durationMs: number;
easing: EasingFunction;
overlayColor: string;
}
/**
* Resolve slide transition settings with cascade:
* Element override -> Page transition defaults -> Hardcoded fallback
*
* @param pageTransition - Resolved page transition (from useTransitionSettings)
* @param elementOverride - Element-level override (optional)
* @returns Final resolved settings for slide transition
*/
export function resolveSlideTransition(
pageTransition: ResolvedTransitionSettings | null | undefined,
elementOverride?: SlideTransitionOverride | null,
): ResolvedSlideTransition {
// Fallback values if no page transition available
const fallback: ResolvedSlideTransition = {
type: 'fade',
durationMs: 700,
easing: 'ease-in-out',
overlayColor: '#000000',
};
// Helper to check if a value is a non-empty string
const hasValue = <T extends string>(
val: T | '' | undefined,
): val is Exclude<T, ''> => Boolean(val);
if (!pageTransition) {
// No page transition settings - use element override or fallback
return {
type: hasValue(elementOverride?.type)
? elementOverride.type
: fallback.type,
durationMs:
typeof elementOverride?.durationMs === 'number' &&
elementOverride.durationMs > 0
? elementOverride.durationMs
: fallback.durationMs,
easing: hasValue(elementOverride?.easing)
? elementOverride.easing
: fallback.easing,
overlayColor:
elementOverride?.overlayColor && elementOverride.overlayColor !== ''
? elementOverride.overlayColor
: fallback.overlayColor,
};
}
// Cascade: Element override -> Page transition
// Type: 'video' maps to 'fade' for slides
let type: SlideTransitionType = 'fade';
if (hasValue(elementOverride?.type)) {
type = elementOverride.type;
} else if (pageTransition.type === 'none') {
type = 'none';
} else {
type = 'fade'; // 'fade' or 'video' -> 'fade'
}
// Duration: Element override -> Page transition
const durationMs =
typeof elementOverride?.durationMs === 'number' &&
elementOverride.durationMs > 0
? elementOverride.durationMs
: pageTransition.durationMs;
// Easing: Element override -> Page transition
const easing = hasValue(elementOverride?.easing)
? elementOverride.easing
: pageTransition.easing;
// Overlay color: Element override -> Page transition
const overlayColor =
elementOverride?.overlayColor && elementOverride.overlayColor !== ''
? elementOverride.overlayColor
: pageTransition.overlayColor;
return { type, durationMs, easing, overlayColor };
}
/**
* Extract slide transition override from carousel element
*/
export function extractCarouselSlideOverride(
element:
| {
carouselSlideTransitionType?: SlideTransitionType | '';
carouselSlideTransitionDurationMs?: number | '';
carouselSlideTransitionEasing?: EasingFunction | '';
carouselSlideTransitionOverlayColor?: string;
}
| null
| undefined,
): SlideTransitionOverride | null {
if (!element) return null;
return {
type: element.carouselSlideTransitionType,
durationMs: element.carouselSlideTransitionDurationMs,
easing: element.carouselSlideTransitionEasing,
overlayColor: element.carouselSlideTransitionOverlayColor,
};
}
/**
* Extract slide transition override from gallery element
*/
export function extractGallerySlideOverride(
element:
| {
gallerySlideTransitionType?: SlideTransitionType | '';
gallerySlideTransitionDurationMs?: number | '';
gallerySlideTransitionEasing?: EasingFunction | '';
gallerySlideTransitionOverlayColor?: string;
}
| null
| undefined,
): SlideTransitionOverride | null {
if (!element) return null;
return {
type: element.gallerySlideTransitionType,
durationMs: element.gallerySlideTransitionDurationMs,
easing: element.gallerySlideTransitionEasing,
overlayColor: element.gallerySlideTransitionOverlayColor,
};
}