diff --git a/apps/admin/src/components/products/ProductStoryScrollForm.tsx b/apps/admin/src/components/products/ProductStoryScrollForm.tsx index a56900f..f737246 100644 --- a/apps/admin/src/components/products/ProductStoryScrollForm.tsx +++ b/apps/admin/src/components/products/ProductStoryScrollForm.tsx @@ -160,6 +160,53 @@ export default function ProductStoryScrollForm({ payload, onChange, onBrowseMedi + {/* Visual Theme Selector (Dark Theater vs Light Luxury) */} +
+
+ +
+
+ + + +
+
+ {/* 1. Core Hero Showcase Image */}
diff --git a/apps/backend/src/routes/pages.ts b/apps/backend/src/routes/pages.ts index 5f9c820..d3381aa 100644 --- a/apps/backend/src/routes/pages.ts +++ b/apps/backend/src/routes/pages.ts @@ -1,6 +1,6 @@ import { Elysia, t } from "elysia"; import prisma from "../lib/prisma"; -// Trigger reload: support story_scroll and apple_watch_tour in ProductLayoutSectionPayloadSchema +// Trigger reload: support storyTheme in ProductLayoutSectionPayloadSchema import { LocalizedStringSchema, HeroSectionPayloadSchema, diff --git a/apps/website/src/app/[lang]/products/[slug]/page.tsx b/apps/website/src/app/[lang]/products/[slug]/page.tsx index 2f85bb4..5754780 100644 --- a/apps/website/src/app/[lang]/products/[slug]/page.tsx +++ b/apps/website/src/app/[lang]/products/[slug]/page.tsx @@ -123,6 +123,7 @@ function ProductMainSection({ const storyScenes = Array.isArray(payload.storyScenes) ? (payload.storyScenes as any[]) : undefined const endCtaTitle = (payload.endCtaTitle as string) || undefined const endCtaButtonText = (payload.endCtaButtonText as string) || undefined + const storyTheme = (payload.storyTheme as string) || 'dark' return (
@@ -161,6 +162,7 @@ function ProductMainSection({ lang="th" endCtaTitle={endCtaTitle} endCtaButtonText={endCtaButtonText} + storyTheme={storyTheme} />
diff --git a/apps/website/src/components/CinematicStoryScroll.tsx b/apps/website/src/components/CinematicStoryScroll.tsx index 3823e12..847c106 100644 --- a/apps/website/src/components/CinematicStoryScroll.tsx +++ b/apps/website/src/components/CinematicStoryScroll.tsx @@ -23,6 +23,7 @@ interface Props { lang: string endCtaTitle?: string endCtaButtonText?: string + storyTheme?: 'dark' | 'light' | string } const DEFAULT_SCENES: StoryScene[] = [ @@ -37,7 +38,7 @@ const DEFAULT_SCENES: StoryScene[] = [ id: 'material', kicker: '02 / MATERIAL', title: 'CERAMIC\nCOMPOUND.', - description: 'สูตรผสมเนื้อเซรามิกเกรดพรีเมียม สึกหรอน้อย เสียงเบียบเงียบ ไร้แรงสั่นสะเทือน และลดฝุ่นผงจับจานเบรกอย่างเห็นได้ชัด', + description: 'สูตรผสมเนื้อเซรามิกเกรดพรีเมียม สึกหรอน้อย เสียงเงียบ ไร้แรงสั่นสะเทือน และลดฝุ่นผงจับจานเบรกอย่างเห็นได้ชัด', chips: ['Low Noise', 'Low Vibration', 'Cleaner Braking'], theme: 'material', }, @@ -87,11 +88,14 @@ export default function CinematicStoryScroll({ lang, endCtaTitle, endCtaButtonText, + storyTheme = 'dark', }: Props) { const [activeScene, setActiveScene] = useState('hero') const [isStoryVisible, setIsStoryVisible] = useState(false) const storyRef = useRef(null) + const isLight = storyTheme === 'light' + const activeScenes = useMemo(() => { if (scenes && scenes.length > 0) return scenes return DEFAULT_SCENES @@ -136,7 +140,10 @@ export default function CinematicStoryScroll({ } }) }, - { threshold: 0.55 } + { + rootMargin: '-20% 0px -20% 0px', + threshold: [0.1, 0.4], + } ) triggers.forEach((t) => observer.observe(t)) @@ -167,25 +174,53 @@ export default function CinematicStoryScroll({ } } - // Active scene index + const scrollToScene = (index: number) => { + const storyEl = storyRef.current + if (!storyEl) return + const triggers = storyEl.querySelectorAll('.story-trigger') + if (triggers[index]) { + triggers[index].scrollIntoView({ behavior: 'smooth' }) + } + } + const activeIndex = Math.max(0, activeScenes.findIndex(s => s.id === activeScene)) return ( -
+
+ {/* Top Atmosphere Gradient Blend (prevents harsh abrupt cut into dark) */} + {!isLight && ( +
+ )} + {/* Side Progress Dots (Desktop) */}