fixed carousel z-indexes issue
This commit is contained in:
parent
fc624c0700
commit
4a61fd1a69
@ -35,7 +35,7 @@ const ConstructorControlsPanel: React.FC<ConstructorControlsPanelProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='fixed z-40 w-[min(92vw,460px)] rounded-lg border border-gray-200 bg-white shadow-xl'
|
className='fixed z-[1000] w-[min(92vw,460px)] rounded-lg border border-gray-200 bg-white shadow-xl'
|
||||||
style={{
|
style={{
|
||||||
left: position.x,
|
left: position.x,
|
||||||
top: position.y,
|
top: position.y,
|
||||||
|
|||||||
@ -66,7 +66,7 @@ const ConstructorMenu = forwardRef<HTMLDivElement, ConstructorMenuProps>(
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className='fixed z-40 w-60 border border-gray-200 rounded-lg bg-white shadow-xl'
|
className='fixed z-[1000] w-60 border border-gray-200 rounded-lg bg-white shadow-xl'
|
||||||
style={{ left: position.x, top: position.y }}
|
style={{ left: position.x, top: position.y }}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -181,7 +181,7 @@ export function ElementEditorPanel({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={elementEditorRef}
|
ref={elementEditorRef}
|
||||||
className={`fixed z-40 ${isCollapsed ? 'w-[260px]' : 'w-[380px]'} max-h-[calc(100vh-2rem)] overflow-auto rounded-lg border border-gray-200 bg-white/95 p-3 shadow-xl`}
|
className={`fixed z-[1000] ${isCollapsed ? 'w-[260px]' : 'w-[380px]'} max-h-[calc(100vh-2rem)] overflow-auto rounded-lg border border-gray-200 bg-white/95 p-3 shadow-xl`}
|
||||||
style={{ left: position.x, top: position.y }}
|
style={{ left: position.x, top: position.y }}
|
||||||
>
|
>
|
||||||
<ElementEditorHeader
|
<ElementEditorHeader
|
||||||
|
|||||||
@ -574,9 +574,10 @@ export default function RuntimePresentation({
|
|||||||
|
|
||||||
{/* Outer container: full viewport with black background for letterbox bars */}
|
{/* Outer container: full viewport with black background for letterbox bars */}
|
||||||
<div className='relative w-screen h-screen overflow-hidden bg-black'>
|
<div className='relative w-screen h-screen overflow-hidden bg-black'>
|
||||||
{/* Inner canvas: maintains aspect ratio centered in viewport */}
|
{/* Inner canvas: maintains aspect ratio centered in viewport.
|
||||||
|
z-[46] creates stacking context above carousel (z-10 bg, z-45 controls) portaled to body. */}
|
||||||
<div
|
<div
|
||||||
className='overflow-hidden'
|
className='relative z-[46] overflow-hidden'
|
||||||
style={{
|
style={{
|
||||||
...cssVars,
|
...cssVars,
|
||||||
...letterboxStyles,
|
...letterboxStyles,
|
||||||
@ -630,19 +631,17 @@ export default function RuntimePresentation({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* New page content wrapper - fades in for non-transition navigation.
|
{/* Page background wrapper - z-5 keeps it BELOW carousel slide (z-10).
|
||||||
z-1 ensures it's above previous backgrounds (z-0) during fade.
|
Fades in for non-transition navigation.
|
||||||
onAnimationEnd resets isFadingIn when CSS animation completes. */}
|
onAnimationEnd resets isFadingIn when CSS animation completes. */}
|
||||||
<div
|
<div
|
||||||
data-testid='page-content-wrapper'
|
data-testid='page-background-wrapper'
|
||||||
className={`absolute inset-0 z-1 ${isFadingIn ? 'animate-crossfade-in' : ''}`}
|
className={`absolute inset-0 z-5 ${isFadingIn ? 'animate-crossfade-in' : ''}`}
|
||||||
onAnimationEnd={onFadeInAnimationEnd}
|
onAnimationEnd={onFadeInAnimationEnd}
|
||||||
>
|
>
|
||||||
{/* Background image element - z-1 keeps it below backdrop blur (z-5).
|
{/* Background image element */}
|
||||||
CSS backgroundImage provides instant display.
|
|
||||||
Use native img for blob URLs to prevent repeated fetch requests from Next.js Image. */}
|
|
||||||
{backgroundImageUrl && !backgroundVideoUrl && (
|
{backgroundImageUrl && !backgroundVideoUrl && (
|
||||||
<div className='absolute inset-0 z-1 pointer-events-none'>
|
<div className='absolute inset-0 pointer-events-none'>
|
||||||
{backgroundImageUrl.startsWith('blob:') ? (
|
{backgroundImageUrl.startsWith('blob:') ? (
|
||||||
// eslint-disable-next-line @next/next/no-img-element
|
// eslint-disable-next-line @next/next/no-img-element
|
||||||
<img
|
<img
|
||||||
@ -682,12 +681,12 @@ export default function RuntimePresentation({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Background video - z-1 keeps it below backdrop blur (z-5) */}
|
{/* Background video */}
|
||||||
{backgroundVideoUrl && (
|
{backgroundVideoUrl && (
|
||||||
<video
|
<video
|
||||||
ref={bgVideoRef}
|
ref={bgVideoRef}
|
||||||
key={backgroundVideoUrl}
|
key={backgroundVideoUrl}
|
||||||
className='absolute inset-0 z-1 h-full w-full object-contain'
|
className='absolute inset-0 h-full w-full object-contain'
|
||||||
src={backgroundVideoUrl}
|
src={backgroundVideoUrl}
|
||||||
autoPlay={videoAutoplay}
|
autoPlay={videoAutoplay}
|
||||||
loop={useNativeLoop}
|
loop={useNativeLoop}
|
||||||
@ -695,23 +694,29 @@ export default function RuntimePresentation({
|
|||||||
playsInline
|
playsInline
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Page elements - z-40 ensures they appear above carousel background (z-10) and carousel controls (z-30) */}
|
|
||||||
<div className='absolute inset-0 z-40'>
|
|
||||||
{pageElements.map((element: CanvasElement) => (
|
|
||||||
<RuntimeElement
|
|
||||||
key={element.id}
|
|
||||||
element={element}
|
|
||||||
onClick={() => handleElementClick(element)}
|
|
||||||
resolveUrl={resolveUrlWithBlob}
|
|
||||||
onGalleryCardClick={(cardIndex) =>
|
|
||||||
handleGalleryCardClick(element, cardIndex)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{/* End new page content wrapper */}
|
{/* End page background wrapper */}
|
||||||
|
|
||||||
|
{/* Page elements wrapper - z-[46] keeps it ABOVE carousel slide (z-10) AND carousel controls (z-45).
|
||||||
|
UI controls (z-50) remain on top.
|
||||||
|
Fades in together with background. */}
|
||||||
|
<div
|
||||||
|
data-testid='page-elements-wrapper'
|
||||||
|
className={`absolute inset-0 z-[46] ${isFadingIn ? 'animate-crossfade-in' : ''}`}
|
||||||
|
>
|
||||||
|
{pageElements.map((element: CanvasElement) => (
|
||||||
|
<RuntimeElement
|
||||||
|
key={element.id}
|
||||||
|
element={element}
|
||||||
|
onClick={() => handleElementClick(element)}
|
||||||
|
resolveUrl={resolveUrlWithBlob}
|
||||||
|
onGalleryCardClick={(cardIndex) =>
|
||||||
|
handleGalleryCardClick(element, cardIndex)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{/* End page elements wrapper */}
|
||||||
|
|
||||||
{/* Controls: Offline toggle and Fullscreen button */}
|
{/* Controls: Offline toggle and Fullscreen button */}
|
||||||
<div className='absolute top-4 right-4 z-50 flex items-center gap-2'>
|
<div className='absolute top-4 right-4 z-50 flex items-center gap-2'>
|
||||||
|
|||||||
@ -340,8 +340,8 @@ const CarouselElement: React.FC<CarouselElementProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Full-width carousel - two layers:
|
// Full-width carousel - two layers:
|
||||||
// 1. Background image layer at z-10 (behind canvas elements)
|
// 1. Background image layer at z-10 (behind canvas z-[46])
|
||||||
// 2. Navigation/caption layer at z-30 (above everything for clickability)
|
// 2. Navigation/caption layer at z-[47] (above canvas z-[46], below UI controls z-50)
|
||||||
const fullWidthBackground = (
|
const fullWidthBackground = (
|
||||||
<div className='fixed inset-0 z-10 overflow-hidden bg-black pointer-events-none'>
|
<div className='fixed inset-0 z-10 overflow-hidden bg-black pointer-events-none'>
|
||||||
{currentSlide?.imageUrl && (
|
{currentSlide?.imageUrl && (
|
||||||
@ -359,7 +359,7 @@ const CarouselElement: React.FC<CarouselElementProps> = ({
|
|||||||
const fullWidthControls = (
|
const fullWidthControls = (
|
||||||
<div
|
<div
|
||||||
ref={overlayRef}
|
ref={overlayRef}
|
||||||
className='fixed inset-0 z-30 pointer-events-none'
|
className='fixed inset-0 z-[47] pointer-events-none'
|
||||||
onTouchStart={handleTouchStart}
|
onTouchStart={handleTouchStart}
|
||||||
onTouchEnd={handleTouchEnd}
|
onTouchEnd={handleTouchEnd}
|
||||||
>
|
>
|
||||||
@ -400,7 +400,7 @@ const CarouselElement: React.FC<CarouselElementProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Full-width mode: use portal to render outside transform hierarchy
|
// Full-width mode: use portal to render outside transform hierarchy
|
||||||
// Background at z-10, controls at z-30 for clickability
|
// Background at z-10, controls at z-[47] for clickability (above canvas z-[46])
|
||||||
if (isFullWidth) {
|
if (isFullWidth) {
|
||||||
// SSR safety: only use portal when mounted in browser
|
// SSR safety: only use portal when mounted in browser
|
||||||
if (!isMounted) {
|
if (!isMounted) {
|
||||||
|
|||||||
@ -1426,7 +1426,7 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
|
|||||||
</title>
|
</title>
|
||||||
</Head>
|
</Head>
|
||||||
<div className='relative w-screen h-screen bg-black overflow-hidden'>
|
<div className='relative w-screen h-screen bg-black overflow-hidden'>
|
||||||
<div className='absolute top-4 left-4 z-30 flex max-w-[80vw] flex-col gap-2'>
|
<div className='absolute top-4 left-4 z-[1000] flex max-w-[80vw] flex-col gap-2'>
|
||||||
<p className='text-xs font-semibold text-gray-700'>
|
<p className='text-xs font-semibold text-gray-700'>
|
||||||
{projectName || 'Loading project...'}
|
{projectName || 'Loading project...'}
|
||||||
</p>
|
</p>
|
||||||
@ -1476,10 +1476,11 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Canvas container: z-[46] creates stacking context above carousel (z-10 bg, z-45 controls) portaled to body */}
|
||||||
<div
|
<div
|
||||||
ref={canvasRef}
|
ref={canvasRef}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
className={`z-20 overflow-clip ${hasFullWidthCarousel ? 'bg-transparent' : 'bg-black'}`}
|
className={`relative z-[46] overflow-clip ${hasFullWidthCarousel ? 'bg-transparent' : 'bg-black'}`}
|
||||||
style={{
|
style={{
|
||||||
...canvasCssVars,
|
...canvasCssVars,
|
||||||
...letterboxStyles,
|
...letterboxStyles,
|
||||||
@ -1487,29 +1488,33 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<BackdropPortalProvider>
|
<BackdropPortalProvider>
|
||||||
<CanvasBackground
|
{/* Background wrapper - z-5 keeps it BELOW carousel slide (z-10) */}
|
||||||
backgroundImageUrl={backgroundImageSrc}
|
<div className={`absolute inset-0 z-5 ${isFadingIn ? 'animate-crossfade-in' : ''}`}>
|
||||||
backgroundVideoUrl={backgroundVideoSrc}
|
<CanvasBackground
|
||||||
backgroundAudioUrl={backgroundAudioSrc}
|
backgroundImageUrl={backgroundImageSrc}
|
||||||
previousBgImageUrl={pageSwitch.previousBgImageUrl}
|
backgroundVideoUrl={backgroundVideoSrc}
|
||||||
previousBgVideoUrl={pageSwitch.previousBgVideoUrl}
|
backgroundAudioUrl={backgroundAudioSrc}
|
||||||
isSwitching={pageSwitch.isSwitching}
|
previousBgImageUrl={pageSwitch.previousBgImageUrl}
|
||||||
isNewBgReady={pageSwitch.isNewBgReady}
|
previousBgVideoUrl={pageSwitch.previousBgVideoUrl}
|
||||||
isFadingIn={isFadingIn}
|
isSwitching={pageSwitch.isSwitching}
|
||||||
onBackgroundReady={() => {
|
isNewBgReady={pageSwitch.isNewBgReady}
|
||||||
pageSwitch.markBackgroundReady();
|
isFadingIn={isFadingIn}
|
||||||
setIsBackgroundReady(true);
|
onBackgroundReady={() => {
|
||||||
}}
|
pageSwitch.markBackgroundReady();
|
||||||
videoAutoplay={backgroundVideoAutoplay}
|
setIsBackgroundReady(true);
|
||||||
videoLoop={backgroundVideoLoop}
|
}}
|
||||||
videoMuted={backgroundVideoMuted}
|
videoAutoplay={backgroundVideoAutoplay}
|
||||||
videoStartTime={backgroundVideoStartTime}
|
videoLoop={backgroundVideoLoop}
|
||||||
videoEndTime={backgroundVideoEndTime}
|
videoMuted={backgroundVideoMuted}
|
||||||
/>
|
videoStartTime={backgroundVideoStartTime}
|
||||||
|
videoEndTime={backgroundVideoEndTime}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Elements container - z-10 ensures they appear above backdrop layer */}
|
{/* Elements container - z-[46] keeps it ABOVE carousel slide (z-10) AND carousel controls (z-45).
|
||||||
|
UI controls (z-50) remain on top. */}
|
||||||
<div
|
<div
|
||||||
className={`absolute inset-0 z-10 ${isFadingIn ? 'animate-crossfade-in' : ''}`}
|
className={`absolute inset-0 z-[46] ${isFadingIn ? 'animate-crossfade-in' : ''}`}
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className='absolute inset-0 flex items-center justify-center'>
|
<div className='absolute inset-0 flex items-center justify-center'>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user