/** * RuntimeElement Component * * Renders a single UI element with interactive effects at runtime. * Handles hover, focus, active states and positioning. * Delegates element styling and content to UiElementRenderer. */ import React from 'react'; import UiElementRenderer from './UiElements/UiElementRenderer'; import { useElementEffects } from '../hooks/useElementEffects'; import { buildTransitionStyle, buildAppearAnimationStyle, hasAnyEffects, type ElementEffectProperties, } from '../lib/elementEffects'; interface RuntimeElementProps { element: any; onClick: () => void; /** Optional URL resolver for preloaded blob URLs */ resolveUrl?: (url: string | undefined) => string; /** Gallery card click handler */ onGalleryCardClick?: (cardIndex: number) => void; } const RuntimeElement: React.FC = ({ element, onClick, resolveUrl, onGalleryCardClick, }) => { const xPercent = element.xPercent ?? 0; const yPercent = element.yPercent ?? 0; const rotation = element.rotation ?? 0; // Extract effect properties from element const effectProperties: Partial = { appearAnimation: element.appearAnimation, appearAnimationDuration: element.appearAnimationDuration, appearAnimationEasing: element.appearAnimationEasing, hoverScale: element.hoverScale, hoverOpacity: element.hoverOpacity, hoverBackgroundColor: element.hoverBackgroundColor, hoverColor: element.hoverColor, hoverBoxShadow: element.hoverBoxShadow, hoverTransitionDuration: element.hoverTransitionDuration, focusScale: element.focusScale, focusOpacity: element.focusOpacity, focusOutline: element.focusOutline, focusBoxShadow: element.focusBoxShadow, activeScale: element.activeScale, activeOpacity: element.activeOpacity, activeBackgroundColor: element.activeBackgroundColor, }; // Use effects hook for interactive states const { effectStyle, eventHandlers } = useElementEffects(effectProperties); // Build base position style let positionStyle: React.CSSProperties = { left: `${xPercent}%`, top: `${yPercent}%`, transform: `translate(-50%, -50%)${rotation ? ` rotate(${rotation}deg)` : ''}`, }; // Merge transform if effect style has transform if (effectStyle.transform) { // Preserve the translate and rotation, add effect transform positionStyle.transform = `translate(-50%, -50%)${rotation ? ` rotate(${rotation}deg)` : ''} ${effectStyle.transform}`; // Remove transform from effectStyle to avoid double application const { transform, ...restEffectStyle } = effectStyle; positionStyle = { ...positionStyle, ...restEffectStyle }; } else { positionStyle = { ...positionStyle, ...effectStyle }; } // Add transition if element has any effects if (hasAnyEffects(effectProperties)) { const transitionStyle = buildTransitionStyle(effectProperties); positionStyle = { ...positionStyle, ...transitionStyle }; } // Add appear animation if configured if (effectProperties.appearAnimation) { const animationStyle = buildAppearAnimationStyle(effectProperties); positionStyle = { ...positionStyle, ...animationStyle }; } return (
); }; export default RuntimeElement;