/** * useTransitionSettings Hook * * Resolves transition settings by cascading through three levels: * Element → Project → Global (fallback) * * Video transitions always take precedence over CSS-based transitions. */ import { useMemo } from 'react'; import type { GlobalTransitionDefaults, ProjectTransitionSettings, ElementTransitionSettings, ResolvedTransitionSettings, TransitionType, EasingFunction, DEFAULT_TRANSITION_SETTINGS, } from '../types/transition'; export interface UseTransitionSettingsParams { /** Global defaults (from global_transition_defaults table) */ globalDefaults: GlobalTransitionDefaults | null; /** Project-level settings (from project_transition_settings table, environment-aware) */ projectSettings?: ProjectTransitionSettings | null; /** Element-level settings (from ui_schema_json) */ elementSettings?: ElementTransitionSettings | null; } /** * Default values used when no settings are available at any level */ const FALLBACK_DEFAULTS = { type: 'fade' as TransitionType, durationMs: 700, easing: 'ease-in-out' as EasingFunction, overlayColor: '#000000', }; /** * Hook to resolve transition settings with cascade: * Element → Project → Global → Fallback defaults * * @example * ```tsx * // Project settings are fetched from project_transition_settings store * // and converted using entityToProjectSettings() helper * const transitionSettings = useTransitionSettings({ * globalDefaults, * projectSettings, // from projectTransitionSettingsSlice (environment-aware) * elementSettings: element?.transitionSettings, * }); * * // Use resolved settings * if (transitionSettings.type === 'video') { * // Play video transition * } else { * // Apply CSS transition with transitionSettings.durationMs and transitionSettings.easing * } * ``` */ export function useTransitionSettings({ globalDefaults, projectSettings, elementSettings, }: UseTransitionSettingsParams): ResolvedTransitionSettings { return useMemo(() => { // Video transitions always take precedence if (elementSettings?.transitionVideoUrl) { return { type: 'video' as TransitionType, durationMs: elementSettings.transitionDurationMs ?? FALLBACK_DEFAULTS.durationMs, easing: elementSettings.transitionEasing ?? FALLBACK_DEFAULTS.easing, overlayColor: elementSettings.transitionOverlayColor ?? projectSettings?.overlayColor ?? globalDefaults?.overlay_color ?? FALLBACK_DEFAULTS.overlayColor, videoUrl: elementSettings.transitionVideoUrl, reverseVideoUrl: elementSettings.reverseVideoUrl, }; } // Cascade: Element → Project → Global → Fallback const type: TransitionType = elementSettings?.transitionType ?? projectSettings?.transitionType ?? globalDefaults?.transition_type ?? FALLBACK_DEFAULTS.type; const durationMs: number = elementSettings?.transitionDurationMs ?? projectSettings?.durationMs ?? globalDefaults?.duration_ms ?? FALLBACK_DEFAULTS.durationMs; const easing: EasingFunction = elementSettings?.transitionEasing ?? projectSettings?.easing ?? globalDefaults?.easing ?? FALLBACK_DEFAULTS.easing; const overlayColor: string = elementSettings?.transitionOverlayColor ?? projectSettings?.overlayColor ?? globalDefaults?.overlay_color ?? FALLBACK_DEFAULTS.overlayColor; return { type, durationMs, easing, overlayColor, }; }, [globalDefaults, projectSettings, elementSettings]); } /** * Resolve transition settings without React hook (for non-component contexts) */ export function resolveTransitionSettings({ globalDefaults, projectSettings, elementSettings, }: UseTransitionSettingsParams): ResolvedTransitionSettings { // Video transitions always take precedence if (elementSettings?.transitionVideoUrl) { return { type: 'video' as TransitionType, durationMs: elementSettings.transitionDurationMs ?? FALLBACK_DEFAULTS.durationMs, easing: elementSettings.transitionEasing ?? FALLBACK_DEFAULTS.easing, overlayColor: elementSettings.transitionOverlayColor ?? projectSettings?.overlayColor ?? globalDefaults?.overlay_color ?? FALLBACK_DEFAULTS.overlayColor, videoUrl: elementSettings.transitionVideoUrl, reverseVideoUrl: elementSettings.reverseVideoUrl, }; } // Cascade: Element → Project → Global → Fallback const type: TransitionType = elementSettings?.transitionType ?? projectSettings?.transitionType ?? globalDefaults?.transition_type ?? FALLBACK_DEFAULTS.type; const durationMs: number = elementSettings?.transitionDurationMs ?? projectSettings?.durationMs ?? globalDefaults?.duration_ms ?? FALLBACK_DEFAULTS.durationMs; const easing: EasingFunction = elementSettings?.transitionEasing ?? projectSettings?.easing ?? globalDefaults?.easing ?? FALLBACK_DEFAULTS.easing; const overlayColor: string = elementSettings?.transitionOverlayColor ?? projectSettings?.overlayColor ?? globalDefaults?.overlay_color ?? FALLBACK_DEFAULTS.overlayColor; return { type, durationMs, easing, overlayColor, }; }