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 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<RuntimeElementProps> = ({
element as unknown as Record<string, unknown>,
);
// 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<RuntimeElementProps> = ({
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 = (
<UiElementRenderer
element={element}
@ -121,41 +109,25 @@ const RuntimeElement: React.FC<RuntimeElementProps> = ({
/>
);
// For fade animation (opacity only), apply to positioning div
// For slide/scale (uses transform), use separate wrapper to avoid conflict
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;
// Check if we need the inner wrapper for effects
const needsEffectWrapper = hasAnyEffects(effectProperties);
return (
<div
className='absolute cursor-pointer'
style={combinedStyle}
style={positionStyle}
onClick={handleClick}
tabIndex={0}
onAnimationEnd={
effectProperties.appearAnimation ? onAnimationEnd : undefined
}
{...eventHandlers}
{...(!needsEffectWrapper ? eventHandlers : {})}
>
{content}
{needsEffectWrapper ? (
// Inner wrapper handles hover/focus/active effects independently from animation
<div style={innerEffectStyle} {...eventHandlers}>
{content}
</div>
) : (
content
)}
</div>
);
};