From e1ff82062908d1ab97e7b274bf91e21314188eac Mon Sep 17 00:00:00 2001 From: Dmitri Date: Thu, 28 May 2026 10:54:43 +0200 Subject: [PATCH] fixed positioning for presentations --- frontend/src/components/RuntimeElement.tsx | 88 ++++++++-------------- 1 file changed, 30 insertions(+), 58 deletions(-) diff --git a/frontend/src/components/RuntimeElement.tsx b/frontend/src/components/RuntimeElement.tsx index ec79131..36f6fc0 100644 --- a/frontend/src/components/RuntimeElement.tsx +++ b/frontend/src/components/RuntimeElement.tsx @@ -9,9 +9,9 @@ import React from 'react'; import UiElementRenderer from './UiElements/UiElementRenderer'; import { useElementEffects } from '../hooks/useElementEffects'; -import { useAppearAnimation } from '../hooks/useAppearAnimation'; import { buildTransitionStyle, + buildAppearAnimationStyle, hasAnyEffects, extractEffectProperties, } from '../lib/elementEffects'; @@ -57,20 +57,11 @@ const RuntimeElement: React.FC = ({ element as unknown as Record, ); - // Use appear animation hook (removes animation after completion to unlock properties) - const { animationStyle, onAnimationEnd, hasAnimationEnded } = - useAppearAnimation( - effectProperties, - element.id, // Reset animation when element changes - ); - - // Use effects hook for interactive states with animation coordination + // Use effects hook for interactive states const { effectStyle, eventHandlers, onPersistClick } = useElementEffects( effectProperties, { resetKey: element.id, // Reset reveal on element change - appearAnimationCompleted: - hasAnimationEnded || !effectProperties.appearAnimation, }, ); @@ -80,36 +71,33 @@ const RuntimeElement: React.FC = ({ onClick(); // Original navigation action }; - // Build base position style + // Build base position style (outer div - handles positioning + animation) 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 appear animation to outer div + // Animation stays on outer div to keep positioning hack working + if (effectProperties.appearAnimation) { + positionStyle = { + ...positionStyle, + ...buildAppearAnimationStyle(effectProperties), + }; } - // Add transition if element has any effects + // Build inner wrapper style for hover/focus/active effects + // Using separate div so animation on outer div doesn't block these effects + let innerEffectStyle: React.CSSProperties = {}; if (hasAnyEffects(effectProperties)) { - const transitionStyle = buildTransitionStyle(effectProperties); - positionStyle = { ...positionStyle, ...transitionStyle }; + innerEffectStyle = { + ...effectStyle, + ...buildTransitionStyle(effectProperties), + }; } - // Check if appear animation uses transform (slide/scale need separate wrapper) - const needsAnimationWrapper = - effectProperties.appearAnimation && - effectProperties.appearAnimation !== 'fade'; - - // Render content (with or without animation wrapper) + // Render content const content = ( = ({ /> ); - // For fade animation (opacity only), apply to positioning div - // For slide/scale (uses transform), use separate wrapper to avoid conflict - if (needsAnimationWrapper) { - return ( -
-
- {content} -
-
- ); - } - - // Fade or no animation: apply animation to positioning div - const combinedStyle = effectProperties.appearAnimation - ? { ...positionStyle, ...animationStyle } - : positionStyle; + // Check if we need the inner wrapper for effects + const needsEffectWrapper = hasAnyEffects(effectProperties); return (
- {content} + {needsEffectWrapper ? ( + // Inner wrapper handles hover/focus/active effects independently from animation +
+ {content} +
+ ) : ( + content + )}
); };