138 lines
4.4 KiB
TypeScript
138 lines
4.4 KiB
TypeScript
/**
|
|
* UiElementRenderer Component
|
|
*
|
|
* Unified UI Element Renderer - single source of truth for element rendering.
|
|
* Used by both CanvasElement (constructor) and RuntimeElement (presentation)
|
|
* to ensure WYSIWYG consistency.
|
|
*
|
|
* Renders any UI element with consistent styling by delegating to per-type components.
|
|
*/
|
|
|
|
import React from 'react';
|
|
import type { CanvasElement } from '../../types/constructor';
|
|
import type { ResolvedTransitionSettings } from '../../types/transition';
|
|
import type { PreloadCacheProvider } from '../../hooks/video';
|
|
import { useElementWrapperStyle } from './shared/useElementWrapperStyle';
|
|
import {
|
|
isNavigationElementType,
|
|
isTooltipElementType,
|
|
isDescriptionElementType,
|
|
isGalleryElementType,
|
|
isCarouselElementType,
|
|
isVideoPlayerElementType,
|
|
isAudioPlayerElementType,
|
|
isLogoElementType,
|
|
isSpotElementType,
|
|
isPopupElementType,
|
|
} from '../../lib/elementDefaults';
|
|
|
|
// Import per-type components
|
|
import NavigationElement from './elements/NavigationElement';
|
|
import GalleryElement from './elements/GalleryElement';
|
|
import TooltipElement from './elements/TooltipElement';
|
|
import DescriptionElement from './elements/DescriptionElement';
|
|
import CarouselElement from './elements/CarouselElement';
|
|
import LogoElement from './elements/LogoElement';
|
|
import SpotElement from './elements/SpotElement';
|
|
import VideoPlayerElement from './elements/VideoPlayerElement';
|
|
import AudioPlayerElement from './elements/AudioPlayerElement';
|
|
import PopupElement from './elements/PopupElement';
|
|
|
|
export interface UiElementRendererProps {
|
|
element: CanvasElement;
|
|
resolveUrl?: (url: string | undefined) => string;
|
|
// Constructor-specific props (optional)
|
|
isSelected?: boolean;
|
|
isEditMode?: boolean;
|
|
// Gallery carousel callback
|
|
onGalleryCardClick?: (cardIndex: number) => void;
|
|
// Carousel-specific callback for button position changes (constructor only)
|
|
onCarouselButtonPositionChange?: (
|
|
button: 'prev' | 'next',
|
|
x: number,
|
|
y: number,
|
|
) => void;
|
|
// Letterbox styles for constraining fullscreen elements to canvas bounds
|
|
letterboxStyles?: React.CSSProperties;
|
|
// Page transition settings (for slide transition cascade in carousel/gallery)
|
|
pageTransitionSettings?: ResolvedTransitionSettings;
|
|
// Preload cache provider for video elements
|
|
preloadCache?: PreloadCacheProvider;
|
|
}
|
|
|
|
/**
|
|
* Unified UI Element Renderer
|
|
*
|
|
* Renders any UI element with consistent styling.
|
|
* Used by both CanvasElement (constructor) and RuntimeElement (presentation).
|
|
*/
|
|
export const UiElementRenderer: React.FC<UiElementRendererProps> = ({
|
|
element,
|
|
resolveUrl,
|
|
isSelected = false,
|
|
isEditMode = false,
|
|
onGalleryCardClick,
|
|
onCarouselButtonPositionChange,
|
|
letterboxStyles,
|
|
pageTransitionSettings,
|
|
preloadCache,
|
|
}) => {
|
|
const { className, style } = useElementWrapperStyle({
|
|
element,
|
|
isSelected,
|
|
isEditMode,
|
|
});
|
|
|
|
// Common props for all element types
|
|
const commonProps = { element, resolveUrl, className, style };
|
|
|
|
// Delegate to type-specific component
|
|
if (isNavigationElementType(element.type)) {
|
|
return <NavigationElement {...commonProps} />;
|
|
}
|
|
if (isGalleryElementType(element.type)) {
|
|
return <GalleryElement {...commonProps} onCardClick={onGalleryCardClick} />;
|
|
}
|
|
if (isTooltipElementType(element.type)) {
|
|
return <TooltipElement {...commonProps} />;
|
|
}
|
|
if (isDescriptionElementType(element.type)) {
|
|
return <DescriptionElement {...commonProps} />;
|
|
}
|
|
if (isCarouselElementType(element.type)) {
|
|
return (
|
|
<CarouselElement
|
|
{...commonProps}
|
|
isEditMode={isEditMode}
|
|
onButtonPositionChange={onCarouselButtonPositionChange}
|
|
letterboxStyles={letterboxStyles}
|
|
pageTransitionSettings={pageTransitionSettings}
|
|
/>
|
|
);
|
|
}
|
|
if (isVideoPlayerElementType(element.type)) {
|
|
return <VideoPlayerElement {...commonProps} preloadCache={preloadCache} />;
|
|
}
|
|
if (isAudioPlayerElementType(element.type)) {
|
|
return <AudioPlayerElement {...commonProps} />;
|
|
}
|
|
if (isLogoElementType(element.type)) {
|
|
return <LogoElement {...commonProps} />;
|
|
}
|
|
if (isSpotElementType(element.type)) {
|
|
return <SpotElement {...commonProps} />;
|
|
}
|
|
if (isPopupElementType(element.type)) {
|
|
return <PopupElement {...commonProps} />;
|
|
}
|
|
|
|
// Fallback for unknown types
|
|
return (
|
|
<div className={className} style={style}>
|
|
<span className='px-4 py-2 text-sm'>{element.label || element.type}</span>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default UiElementRenderer;
|