fixed positioning for presentations

This commit is contained in:
Dmitri 2026-05-28 10:54:43 +02:00
parent 7dd3384a46
commit e1ff820629

View File

@ -9,9 +9,9 @@
import React from 'react'; import React from 'react';
import UiElementRenderer from './UiElements/UiElementRenderer'; import UiElementRenderer from './UiElements/UiElementRenderer';
import { useElementEffects } from '../hooks/useElementEffects'; import { useElementEffects } from '../hooks/useElementEffects';
import { useAppearAnimation } from '../hooks/useAppearAnimation';
import { import {
buildTransitionStyle, buildTransitionStyle,
buildAppearAnimationStyle,
hasAnyEffects, hasAnyEffects,
extractEffectProperties, extractEffectProperties,
} from '../lib/elementEffects'; } from '../lib/elementEffects';
@ -57,20 +57,11 @@ const RuntimeElement: React.FC<RuntimeElementProps> = ({
element as unknown as Record<string, unknown>, element as unknown as Record<string, unknown>,
); );
// Use appear animation hook (removes animation after completion to unlock properties) // Use effects hook for interactive states
const { animationStyle, onAnimationEnd, hasAnimationEnded } =
useAppearAnimation(
effectProperties,
element.id, // Reset animation when element changes
);
// Use effects hook for interactive states with animation coordination
const { effectStyle, eventHandlers, onPersistClick } = useElementEffects( const { effectStyle, eventHandlers, onPersistClick } = useElementEffects(
effectProperties, effectProperties,
{ {
resetKey: element.id, // Reset reveal on element change resetKey: element.id, // Reset reveal on element change
appearAnimationCompleted:
hasAnimationEnded || !effectProperties.appearAnimation,
}, },
); );
@ -80,36 +71,33 @@ const RuntimeElement: React.FC<RuntimeElementProps> = ({
onClick(); // Original navigation action onClick(); // Original navigation action
}; };
// Build base position style // Build base position style (outer div - handles positioning + animation)
let positionStyle: React.CSSProperties = { let positionStyle: React.CSSProperties = {
left: `${xPercent}%`, left: `${xPercent}%`,
top: `${yPercent}%`, top: `${yPercent}%`,
transform: `translate(-50%, -50%)${rotation ? ` rotate(${rotation}deg)` : ''}`, transform: `translate(-50%, -50%)${rotation ? ` rotate(${rotation}deg)` : ''}`,
}; };
// Merge transform if effect style has transform // Add appear animation to outer div
if (effectStyle.transform) { // Animation stays on outer div to keep positioning hack working
// Preserve the translate and rotation, add effect transform if (effectProperties.appearAnimation) {
positionStyle.transform = `translate(-50%, -50%)${rotation ? ` rotate(${rotation}deg)` : ''} ${effectStyle.transform}`; positionStyle = {
// Remove transform from effectStyle to avoid double application ...positionStyle,
const { transform, ...restEffectStyle } = effectStyle; ...buildAppearAnimationStyle(effectProperties),
positionStyle = { ...positionStyle, ...restEffectStyle }; };
} else {
positionStyle = { ...positionStyle, ...effectStyle };
} }
// 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)) { if (hasAnyEffects(effectProperties)) {
const transitionStyle = buildTransitionStyle(effectProperties); innerEffectStyle = {
positionStyle = { ...positionStyle, ...transitionStyle }; ...effectStyle,
...buildTransitionStyle(effectProperties),
};
} }
// Check if appear animation uses transform (slide/scale need separate wrapper) // Render content
const needsAnimationWrapper =
effectProperties.appearAnimation &&
effectProperties.appearAnimation !== 'fade';
// Render content (with or without animation wrapper)
const content = ( const content = (
<UiElementRenderer <UiElementRenderer
element={element} element={element}
@ -121,41 +109,25 @@ const RuntimeElement: React.FC<RuntimeElementProps> = ({
/> />
); );
// For fade animation (opacity only), apply to positioning div // Check if we need the inner wrapper for effects
// For slide/scale (uses transform), use separate wrapper to avoid conflict const needsEffectWrapper = hasAnyEffects(effectProperties);
if (needsAnimationWrapper) {
return (
<div
className='absolute cursor-pointer'
style={positionStyle}
onClick={handleClick}
tabIndex={0}
{...eventHandlers}
>
<div style={animationStyle} onAnimationEnd={onAnimationEnd}>
{content}
</div>
</div>
);
}
// Fade or no animation: apply animation to positioning div
const combinedStyle = effectProperties.appearAnimation
? { ...positionStyle, ...animationStyle }
: positionStyle;
return ( return (
<div <div
className='absolute cursor-pointer' className='absolute cursor-pointer'
style={combinedStyle} style={positionStyle}
onClick={handleClick} onClick={handleClick}
tabIndex={0} tabIndex={0}
onAnimationEnd={ {...(!needsEffectWrapper ? eventHandlers : {})}
effectProperties.appearAnimation ? onAnimationEnd : undefined
}
{...eventHandlers}
> >
{content} {needsEffectWrapper ? (
// Inner wrapper handles hover/focus/active effects independently from animation
<div style={innerEffectStyle} {...eventHandlers}>
{content}
</div>
) : (
content
)}
</div> </div>
); );
}; };