x
This commit is contained in:
1 parent
9922e8a5bc
commit
0144545166
4 files changed
+197
-16
No files matched your search
@@ -0,0 +1,65 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useScrollReveal } from '../hooks/useScrollReveal';
|
||||
|
||||
// Animation style -> initial transform mapping
|
||||
const STYLE_TRANSFORMS: Record<string, string> = {
|
||||
'fade-up': 'translateY(28px)',
|
||||
'fade-in': 'translateY(0)',
|
||||
'slide-left': 'translateX(28px)',
|
||||
'zoom-in': 'scale(0.95)',
|
||||
'none': 'translateY(0)',
|
||||
}
|
||||
|
||||
interface ScrollRevealProps {
|
||||
children: React.ReactNode;
|
||||
delay?: number; // ms
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function ScrollReveal({ children, delay = 0, className = '' }: ScrollRevealProps) {
|
||||
const { ref, isVisible } = useScrollReveal<HTMLDivElement>({
|
||||
threshold: 0.08,
|
||||
rootMargin: '0px 0px -40px 0px',
|
||||
});
|
||||
|
||||
const [clientSettings, setClientSettings] = useState({
|
||||
enabled: true,
|
||||
style: 'fade-up',
|
||||
durationMs: 700
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// Read from CSS variables injected by layout.tsx on the body tag
|
||||
const body = document.body;
|
||||
const enabled = body.style.getPropertyValue('--scroll-reveal-enabled').trim() !== '0';
|
||||
const style = body.style.getPropertyValue('--scroll-reveal-style').trim() || 'fade-up';
|
||||
const durationVar = body.style.getPropertyValue('--scroll-reveal-duration').trim() || '700ms';
|
||||
const durationMs = parseInt(durationVar) || 700;
|
||||
|
||||
setClientSettings({ enabled, style, durationMs });
|
||||
}, []);
|
||||
|
||||
// If animation is disabled or set to none, just render the children normally
|
||||
if (!clientSettings.enabled || clientSettings.style === 'none') {
|
||||
return <div className={className}>{children}</div>
|
||||
}
|
||||
|
||||
const initialTransform = STYLE_TRANSFORMS[clientSettings.style] ?? STYLE_TRANSFORMS['fade-up'];
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={className}
|
||||
style={{
|
||||
opacity: isVisible ? 1 : 0,
|
||||
transform: isVisible ? 'translateY(0) translateX(0) scale(1)' : initialTransform,
|
||||
transition: `opacity ${clientSettings.durationMs}ms ease-out ${delay}ms, transform ${clientSettings.durationMs}ms ease-out ${delay}ms`,
|
||||
willChange: 'opacity, transform',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -865,6 +865,8 @@ function ContactFormSection({ payload, lang }: { payload: Record<string, unknown
|
||||
)
|
||||
}
|
||||
|
||||
import ScrollReveal from '../ScrollReveal'
|
||||
|
||||
export default function DynamicPageRenderer({ sections, categories, lang: langProp }: DynamicPageRendererProps) {
|
||||
const i18nLang = useLang()
|
||||
// Use i18n.language reactively — switches immediately when user changes language
|
||||
@@ -881,37 +883,101 @@ export default function DynamicPageRenderer({ sections, categories, lang: langPr
|
||||
case 'ANNOUNCEMENT_BAR':
|
||||
return <AnnouncementBar key={section.id} payload={payload} lang={lang} />
|
||||
case 'HERO':
|
||||
return <HeroSection key={section.id} payload={payload} lang={lang} />
|
||||
return (
|
||||
<ScrollReveal key={section.id}>
|
||||
<HeroSection payload={payload} lang={lang} />
|
||||
</ScrollReveal>
|
||||
)
|
||||
case 'CATEGORIES':
|
||||
return <CategoriesSection key={section.id} payload={payload} categories={categories} lang={lang} />
|
||||
return (
|
||||
<ScrollReveal key={section.id}>
|
||||
<CategoriesSection payload={payload} categories={categories} lang={lang} />
|
||||
</ScrollReveal>
|
||||
)
|
||||
case 'PRODUCTS':
|
||||
return <ProductsSection key={section.id} payload={payload} lang={lang} />
|
||||
return (
|
||||
<ScrollReveal key={section.id}>
|
||||
<ProductsSection payload={payload} lang={lang} />
|
||||
</ScrollReveal>
|
||||
)
|
||||
case 'PRODUCT_CAROUSEL':
|
||||
return <ProductsSection key={section.id} payload={{ ...payload, layout: 'carousel' }} lang={lang} />
|
||||
return (
|
||||
<ScrollReveal key={section.id}>
|
||||
<ProductsSection payload={{ ...payload, layout: 'carousel' }} lang={lang} />
|
||||
</ScrollReveal>
|
||||
)
|
||||
case 'BANNER':
|
||||
return <BannerSection key={section.id} payload={payload} lang={lang} />
|
||||
return (
|
||||
<ScrollReveal key={section.id}>
|
||||
<BannerSection payload={payload} lang={lang} />
|
||||
</ScrollReveal>
|
||||
)
|
||||
case 'TEXT':
|
||||
return <TextSection key={section.id} payload={payload} lang={lang} />
|
||||
return (
|
||||
<ScrollReveal key={section.id}>
|
||||
<TextSection payload={payload} lang={lang} />
|
||||
</ScrollReveal>
|
||||
)
|
||||
case 'NEWSLETTER':
|
||||
return <NewsletterSection key={section.id} payload={payload} lang={lang} />
|
||||
return (
|
||||
<ScrollReveal key={section.id}>
|
||||
<NewsletterSection payload={payload} lang={lang} />
|
||||
</ScrollReveal>
|
||||
)
|
||||
case 'EDITORIAL_TILE':
|
||||
return <EditorialTileSection key={section.id} payload={payload} lang={lang} />
|
||||
return (
|
||||
<ScrollReveal key={section.id}>
|
||||
<EditorialTileSection payload={payload} lang={lang} />
|
||||
</ScrollReveal>
|
||||
)
|
||||
case 'EDITORIAL_CAROUSEL':
|
||||
return <EditorialCarouselSection key={section.id} payload={payload} lang={lang} />
|
||||
return (
|
||||
<ScrollReveal key={section.id}>
|
||||
<EditorialCarouselSection payload={payload} lang={lang} />
|
||||
</ScrollReveal>
|
||||
)
|
||||
case 'HERO_ARTICLE':
|
||||
return <HeroArticleSection key={section.id} payload={payload} lang={lang} />
|
||||
return (
|
||||
<ScrollReveal key={section.id}>
|
||||
<HeroArticleSection payload={payload} lang={lang} />
|
||||
</ScrollReveal>
|
||||
)
|
||||
case 'RICH_TEXT':
|
||||
return <TextSection key={section.id} payload={payload} lang={lang} />
|
||||
return (
|
||||
<ScrollReveal key={section.id}>
|
||||
<TextSection payload={payload} lang={lang} />
|
||||
</ScrollReveal>
|
||||
)
|
||||
case 'SCENT_NOTES_BREAKDOWN':
|
||||
return <ScentNotesBreakdownSection key={section.id} payload={payload} lang={lang} />
|
||||
return (
|
||||
<ScrollReveal key={section.id}>
|
||||
<ScentNotesBreakdownSection payload={payload} lang={lang} />
|
||||
</ScrollReveal>
|
||||
)
|
||||
case 'QUOTE_CALLOUT':
|
||||
return <QuoteCalloutSection key={section.id} payload={payload} lang={lang} />
|
||||
return (
|
||||
<ScrollReveal key={section.id}>
|
||||
<QuoteCalloutSection payload={payload} lang={lang} />
|
||||
</ScrollReveal>
|
||||
)
|
||||
case 'ACCORDION':
|
||||
return <AccordionSection key={section.id} payload={payload} lang={lang} />
|
||||
return (
|
||||
<ScrollReveal key={section.id}>
|
||||
<AccordionSection payload={payload} lang={lang} />
|
||||
</ScrollReveal>
|
||||
)
|
||||
case 'CONTACT_INFO':
|
||||
return <ContactInfoSection key={section.id} payload={payload} lang={lang} />
|
||||
return (
|
||||
<ScrollReveal key={section.id}>
|
||||
<ContactInfoSection payload={payload} lang={lang} />
|
||||
</ScrollReveal>
|
||||
)
|
||||
case 'CONTACT_FORM':
|
||||
return <ContactFormSection key={section.id} payload={payload} lang={lang} />
|
||||
return (
|
||||
<ScrollReveal key={section.id}>
|
||||
<ContactFormSection payload={payload} lang={lang} />
|
||||
</ScrollReveal>
|
||||
)
|
||||
default:
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.warn(`[DynamicPageRenderer] Unsupported section type: ${section.type}`)
|
||||
@@ -922,3 +988,4 @@ export default function DynamicPageRenderer({ sections, categories, lang: langPr
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user