Shumiland/src/components/sections/WhyParents.tsx
Vadym Samoilenko ef693589bc fix(header,accordion): liquid-glass header + progress bar animation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 16:51:24 +01:00

230 lines
9.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
/* eslint-disable @next/next/no-img-element */
import { useState, useRef, useEffect } from 'react'
import type { HomePageWhyParentsItem, Media } from '@/types/globals'
const VIDEO_THUMBNAIL = '/images/figma/why-parents-video-v2.webp'
const STATIC_ITEMS: HomePageWhyParentsItem[] = [
{
title: 'Подорож кількома світами за один день',
description:
'ДиноПарк, Диво Ліс, Дзеркальний лабіринт — кожна локація це окремий всесвіт пригод для дітей і батьків.',
},
{
title: 'Свіже повітря та затишок лісу',
description:
"Ми оновлюємо тематику та декорації до кожного сезону, тому тут буде цікаво кожного візиту. А у вас з'являться нові яскраві фото у сімейному альбомі.",
},
{
title: 'Нова казка кожної пори року',
description:
'Зима, весна, літо, осінь — кожен сезон у парку неповторний. Святкові декорації, сезонні активності та тематичні заходи чекають на вас.',
},
{
title: 'Безпека понад усе',
description:
'Всі атракції та зони проходять регулярну перевірку. Охоронці, медичний персонал та чіткі правила безпеки забезпечують спокій для батьків.',
},
{
title: 'Все необхідне — поруч і без пошуків',
description:
'Паркування, вбиральні, зона для годування немовлят, укриття, фудкорт — все на місці, щоб ваш відпочинок був справді комфортним.',
},
{
title: 'Фудкорт — смачно для всієї родини',
description:
'Хот-доги, піца, кава, лимонади та багато іншого. Є дитяче меню та здорові перекуси — ніхто не залишиться голодним.',
},
]
interface WhyParentsProps {
items?: HomePageWhyParentsItem[] | null
sideGallery?: { image?: Media | string | null }[] | null
title?: string
}
const INTERVAL = 4000
export function WhyParents({ items, sideGallery: _sideGallery, title }: WhyParentsProps) {
const [openIndex, setOpenIndex] = useState<number>(0)
const [progressKey, setProgressKey] = useState<number>(0)
const autoTimer = useRef<ReturnType<typeof setInterval> | null>(null)
const lenRef = useRef(0)
const activeItems = items && items.length > 0 ? items : STATIC_ITEMS
useEffect(() => {
lenRef.current = activeItems.length
})
useEffect(() => {
const tick = () => {
setOpenIndex((prev) => (prev + 1) % lenRef.current)
setProgressKey((k) => k + 1)
}
autoTimer.current = setInterval(tick, INTERVAL)
return () => {
if (autoTimer.current) clearInterval(autoTimer.current)
}
}, [])
function handleItemClick(i: number) {
setOpenIndex(i)
setProgressKey((k) => k + 1)
if (autoTimer.current) clearInterval(autoTimer.current)
autoTimer.current = setInterval(() => {
setOpenIndex((prev) => (prev + 1) % lenRef.current)
setProgressKey((k) => k + 1)
}, INTERVAL)
}
return (
<section className="py-[20px] md:py-[60px] lg:py-[40px]">
<div className="mx-auto max-w-[1204px] px-8">
<h2
className="mb-[40px] text-[24px] font-bold text-[#272727] uppercase md:mb-[60px] md:text-[32px]"
style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}
>
{title ?? 'Чому батьки обирають Шуміленд'}
</h2>
<div className="flex flex-col items-start gap-16 lg:flex-row lg:items-stretch">
<div className="relative w-full flex-none lg:w-auto">
<div className="absolute top-8 left-0 hidden h-[488px] w-[333px] rounded-[30px] bg-[#396817] lg:block" />
<div className="relative flex flex-col gap-6 lg:ml-[76px] lg:min-h-[600px]">
{activeItems.map((item, i) => {
const isOpen = openIndex === i
return (
<button
key={i}
onClick={() => handleItemClick(i)}
className="relative flex w-full flex-col gap-2.5 overflow-hidden rounded-[10px] bg-[#f1fbeb] px-[50px] py-[20px] text-left shadow-[0_4px_60px_0_rgba(242,139,74,0.25)] transition-all duration-200 lg:w-[628px]"
aria-expanded={isOpen}
>
<div className="flex items-center gap-5">
<span
className="flex-1 text-[20px] leading-tight font-bold text-[#272727]"
style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}
>
{item.title}
</span>
<svg
width="19"
height="10"
viewBox="0 0 19 10"
fill="none"
className="flex-none transition-transform duration-200"
style={{ transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)' }}
aria-hidden="true"
>
<path
d="M1 1L9.5 9L18 1"
stroke="#f28b4a"
strokeWidth="2"
strokeLinecap="round"
/>
</svg>
</div>
<div
className={`grid transition-[grid-template-rows] duration-300 ease-out ${isOpen ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'}`}
>
<div className="overflow-hidden">
<p
className="pt-2 text-[16px] leading-[1.6] font-light text-[#272727]"
style={{ fontFamily: 'var(--font-poppins, Poppins), sans-serif' }}
>
{item.description}
</p>
</div>
</div>
{/* Progress bar — CSS animation, keyed to reset on each advance */}
{isOpen && (
<div className="absolute bottom-0 left-0 h-[3px] w-full">
<div
key={progressKey}
className="h-full bg-[#f28b4a]"
style={{
animation: `progress-fill ${INTERVAL}ms linear forwards`,
}}
/>
</div>
)}
</button>
)
})}
</div>
</div>
{/* Desktop: 3 video thumbnail cards — Figma Gallery_holder */}
<div className="hidden flex-1 overflow-hidden lg:flex lg:items-start lg:gap-5">
{(
[
{ h: 546, mt: 0 },
{ h: 488, mt: 29 },
{ h: 488, mt: 29 },
] as { h: number; mt: number }[]
).map(({ h, mt }, i) => (
<div
key={i}
className="relative flex-none overflow-hidden rounded-[20px] bg-[#d6f2c0]"
style={{ width: 505, height: h, marginTop: mt }}
>
<img
src={VIDEO_THUMBNAIL}
alt=""
aria-hidden="true"
className="h-full w-full object-cover"
loading={i === 0 ? 'eager' : 'lazy'}
/>
<div className="absolute inset-0 flex items-center justify-center">
<div className="flex h-[100px] w-[100px] items-center justify-center rounded-full bg-white/80 shadow-[0_4px_125px_0_rgba(23,27,36,0.6)]">
<svg
width="36"
height="36"
viewBox="0 0 24 24"
fill="none"
aria-hidden="true"
className="ml-1"
>
<path d="M5 3L19 12L5 21V3Z" fill="#396817" />
</svg>
</div>
</div>
</div>
))}
</div>
{/* Mobile: horizontal scroll with video thumbnails */}
<div
className="flex w-full gap-5 overflow-x-auto pb-2 lg:hidden"
style={{ scrollbarWidth: 'none' }}
>
{[
'/images/figma/why-parents-1.webp',
'/images/figma/why-parents-2.webp',
'/images/figma/why-parents-3.webp',
'/images/figma/why-parents-4.webp',
].map((src, i) => (
<div
key={i}
className="flex-none overflow-hidden rounded-[20px]"
style={{ width: '280px', height: '200px' }}
>
<img
src={src}
alt=""
aria-hidden="true"
className="h-full w-full object-cover"
loading="lazy"
/>
</div>
))}
</div>
</div>
</div>
</section>
)
}