fixed positioning
This commit is contained in:
parent
08365ca09e
commit
7dd3384a46
@ -10,11 +10,11 @@
|
|||||||
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,
|
type ElementEffectProperties,
|
||||||
} from '../../lib/elementEffects';
|
} from '../../lib/elementEffects';
|
||||||
import type { CanvasElement as CanvasElementType } from '../../types/constructor';
|
import type { CanvasElement as CanvasElementType } from '../../types/constructor';
|
||||||
import type { ResolvedTransitionSettings } from '../../types/transition';
|
import type { ResolvedTransitionSettings } from '../../types/transition';
|
||||||
@ -57,66 +57,67 @@ const CanvasElement: React.FC<CanvasElementProps> = ({
|
|||||||
pageTransitionSettings,
|
pageTransitionSettings,
|
||||||
preloadCache,
|
preloadCache,
|
||||||
}) => {
|
}) => {
|
||||||
// Extract effect properties from element using helper
|
// Extract effect properties from element
|
||||||
const effectProperties = extractEffectProperties(
|
const effectProperties: Partial<ElementEffectProperties> = {
|
||||||
element as unknown as Record<string, unknown>,
|
appearAnimation: element.appearAnimation,
|
||||||
);
|
appearAnimationDuration: element.appearAnimationDuration,
|
||||||
|
appearAnimationEasing: element.appearAnimationEasing,
|
||||||
// Use appear animation hook (removes animation after completion to unlock properties)
|
hoverScale: element.hoverScale,
|
||||||
const { animationStyle, onAnimationEnd, hasAnimationEnded } =
|
hoverOpacity: element.hoverOpacity,
|
||||||
useAppearAnimation(
|
hoverBackgroundColor: element.hoverBackgroundColor,
|
||||||
effectProperties,
|
hoverColor: element.hoverColor,
|
||||||
element.id, // Reset animation when element changes
|
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,
|
||||||
|
// Hover reveal effects
|
||||||
|
hoverReveal: element.hoverReveal,
|
||||||
|
hoverRevealInitialOpacity: element.hoverRevealInitialOpacity,
|
||||||
|
hoverRevealTargetOpacity: element.hoverRevealTargetOpacity,
|
||||||
|
hoverRevealDuration: element.hoverRevealDuration,
|
||||||
|
hoverRevealDelay: element.hoverRevealDelay,
|
||||||
|
hoverRevealPersist: element.hoverRevealPersist,
|
||||||
|
hoverPersistOnClick: element.hoverPersistOnClick,
|
||||||
|
};
|
||||||
|
|
||||||
// Use effects hook - disabled in edit mode to avoid interfering with dragging
|
// Use effects hook - disabled in edit mode to avoid interfering with dragging
|
||||||
const { effectStyle, eventHandlers, onPersistClick } = useElementEffects(
|
const { effectStyle, eventHandlers } = useElementEffects(
|
||||||
isEditMode ? {} : effectProperties,
|
isEditMode ? {} : effectProperties,
|
||||||
isEditMode
|
|
||||||
? undefined
|
|
||||||
: {
|
|
||||||
resetKey: element.id,
|
|
||||||
appearAnimationCompleted:
|
|
||||||
hasAnimationEnded || !effectProperties.appearAnimation,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Combined click handler (only persist click in preview mode)
|
|
||||||
const handleClick = () => {
|
|
||||||
if (!isEditMode) {
|
|
||||||
onPersistClick();
|
|
||||||
}
|
|
||||||
onClick();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Clamp position to canvas bounds (0-100%)
|
// Clamp position to canvas bounds (0-100%)
|
||||||
const clamp = (value: number, min: number, max: number) =>
|
const clamp = (value: number, min: number, max: number) =>
|
||||||
Math.min(Math.max(value, min), max);
|
Math.min(Math.max(value, min), max);
|
||||||
const xClamped = clamp(element.xPercent ?? 50, 0, 100);
|
const xClamped = clamp(element.xPercent ?? 50, 0, 100);
|
||||||
const yClamped = clamp(element.yPercent ?? 50, 0, 100);
|
const yClamped = clamp(element.yPercent ?? 50, 0, 100);
|
||||||
|
|
||||||
// Build base position style
|
// Build base position style (outer div - handles positioning + animation)
|
||||||
let positionStyle: React.CSSProperties = {
|
let positionStyle: React.CSSProperties = {
|
||||||
left: `${xClamped}%`,
|
left: `${xClamped}%`,
|
||||||
top: `${yClamped}%`,
|
top: `${yClamped}%`,
|
||||||
transform: 'translate(-50%, -50%)',
|
transform: 'translate(-50%, -50%)',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Merge interactive effects (preview mode only)
|
// Add appear animation to outer div (ALWAYS - for WYSIWYG)
|
||||||
if (!isEditMode && effectStyle.transform) {
|
// Animation is applied to outer div to keep positioning hack working
|
||||||
// Preserve the translate, add effect transform
|
if (effectProperties.appearAnimation) {
|
||||||
positionStyle.transform = `translate(-50%, -50%) ${effectStyle.transform}`;
|
|
||||||
// Remove transform from effectStyle to avoid double application
|
|
||||||
const { transform, ...restEffectStyle } = effectStyle;
|
|
||||||
positionStyle = { ...positionStyle, ...restEffectStyle };
|
|
||||||
} else if (!isEditMode) {
|
|
||||||
positionStyle = { ...positionStyle, ...effectStyle };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add transition for interactive effects (preview mode only)
|
|
||||||
if (!isEditMode && hasAnyEffects(effectProperties)) {
|
|
||||||
positionStyle = {
|
positionStyle = {
|
||||||
...positionStyle,
|
...positionStyle,
|
||||||
|
...buildAppearAnimationStyle(effectProperties),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build inner wrapper style for hover/focus/active effects (preview mode only)
|
||||||
|
// Using separate div so animation on outer div doesn't block these effects
|
||||||
|
let innerEffectStyle: React.CSSProperties = {};
|
||||||
|
if (!isEditMode && hasAnyEffects(effectProperties)) {
|
||||||
|
innerEffectStyle = {
|
||||||
|
...effectStyle,
|
||||||
...buildTransitionStyle(effectProperties),
|
...buildTransitionStyle(effectProperties),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -129,51 +130,8 @@ const CanvasElement: React.FC<CanvasElementProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if appear animation uses transform (slide/scale need separate wrapper)
|
// Check if we need the inner wrapper for effects
|
||||||
const needsAnimationWrapper =
|
const needsEffectWrapper = !isEditMode && hasAnyEffects(effectProperties);
|
||||||
effectProperties.appearAnimation &&
|
|
||||||
effectProperties.appearAnimation !== 'fade';
|
|
||||||
|
|
||||||
// Render content
|
|
||||||
const content = (
|
|
||||||
<UiElementRenderer
|
|
||||||
element={element}
|
|
||||||
resolveUrl={resolveUrl}
|
|
||||||
isSelected={isSelected}
|
|
||||||
isEditMode={isEditMode}
|
|
||||||
onGalleryCardClick={onGalleryCardClick}
|
|
||||||
onCarouselButtonPositionChange={onCarouselButtonPositionChange}
|
|
||||||
letterboxStyles={letterboxStyles}
|
|
||||||
pageTransitionSettings={pageTransitionSettings}
|
|
||||||
preloadCache={preloadCache}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
// For slide/scale (uses transform), use separate wrapper to avoid conflict with positioning
|
|
||||||
if (needsAnimationWrapper) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
role='button'
|
|
||||||
tabIndex={0}
|
|
||||||
data-constructor-element-id={element.id}
|
|
||||||
className='absolute cursor-pointer'
|
|
||||||
style={positionStyle}
|
|
||||||
onMouseDown={isEditMode ? onMouseDown : undefined}
|
|
||||||
onClick={handleClick}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
{...(!isEditMode ? 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
|
||||||
@ -181,16 +139,40 @@ const CanvasElement: React.FC<CanvasElementProps> = ({
|
|||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
data-constructor-element-id={element.id}
|
data-constructor-element-id={element.id}
|
||||||
className='absolute cursor-pointer'
|
className='absolute cursor-pointer'
|
||||||
style={combinedStyle}
|
style={positionStyle}
|
||||||
onMouseDown={isEditMode ? onMouseDown : undefined}
|
onMouseDown={isEditMode ? onMouseDown : undefined}
|
||||||
onClick={handleClick}
|
onClick={onClick}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
onAnimationEnd={
|
{...(!isEditMode && !needsEffectWrapper ? eventHandlers : {})}
|
||||||
effectProperties.appearAnimation ? onAnimationEnd : undefined
|
|
||||||
}
|
|
||||||
{...(!isEditMode ? eventHandlers : {})}
|
|
||||||
>
|
>
|
||||||
{content}
|
{needsEffectWrapper ? (
|
||||||
|
// Inner wrapper handles hover/focus/active effects independently from animation
|
||||||
|
<div style={innerEffectStyle} {...eventHandlers}>
|
||||||
|
<UiElementRenderer
|
||||||
|
element={element}
|
||||||
|
resolveUrl={resolveUrl}
|
||||||
|
isSelected={isSelected}
|
||||||
|
isEditMode={isEditMode}
|
||||||
|
onGalleryCardClick={onGalleryCardClick}
|
||||||
|
onCarouselButtonPositionChange={onCarouselButtonPositionChange}
|
||||||
|
letterboxStyles={letterboxStyles}
|
||||||
|
pageTransitionSettings={pageTransitionSettings}
|
||||||
|
preloadCache={preloadCache}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<UiElementRenderer
|
||||||
|
element={element}
|
||||||
|
resolveUrl={resolveUrl}
|
||||||
|
isSelected={isSelected}
|
||||||
|
isEditMode={isEditMode}
|
||||||
|
onGalleryCardClick={onGalleryCardClick}
|
||||||
|
onCarouselButtonPositionChange={onCarouselButtonPositionChange}
|
||||||
|
letterboxStyles={letterboxStyles}
|
||||||
|
pageTransitionSettings={pageTransitionSettings}
|
||||||
|
preloadCache={preloadCache}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -277,18 +277,24 @@
|
|||||||
@-webkit-keyframes element-fade-in {
|
@-webkit-keyframes element-fade-in {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
-webkit-transform: translate3d(0, 0, 0);
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
-webkit-transform: translate3d(0, 0, 0);
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes element-fade-in {
|
@keyframes element-fade-in {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user