146 lines
5.4 KiB
TypeScript
146 lines
5.4 KiB
TypeScript
'use client'
|
|
import React, { useState, useRef, useEffect } from 'react'
|
|
import type { FDServiceChooserBlock as Props } from '@/payload-types'
|
|
|
|
/* Consistent radius system — same as CardGrid, PricingCard, etc. */
|
|
const cardRadius = 'rounded-[32px] md:rounded-[50px] lg:rounded-[70px]'
|
|
|
|
export const FDServiceChooserBlockComponent: React.FC<Props> = ({
|
|
heading,
|
|
description,
|
|
categories = [],
|
|
sectionBackground = 'gray',
|
|
anchorId,
|
|
}) => {
|
|
const [activeIndex, setActiveIndex] = useState(0)
|
|
const [animating, setAnimating] = useState(false)
|
|
const [prefersReducedMotion, setPrefersReducedMotion] = useState(false)
|
|
const prevIndex = useRef(0)
|
|
|
|
/* Priority #8: Detect prefers-reduced-motion */
|
|
useEffect(() => {
|
|
const mql = window.matchMedia('(prefers-reduced-motion: reduce)')
|
|
setPrefersReducedMotion(mql.matches)
|
|
const handler = (e: MediaQueryListEvent) => setPrefersReducedMotion(e.matches)
|
|
mql.addEventListener('change', handler)
|
|
return () => mql.removeEventListener('change', handler)
|
|
}, [])
|
|
|
|
const isDark = sectionBackground === 'navy'
|
|
const bgClass = isDark ? 'bg-fd-navy' : sectionBackground === 'gray' ? 'bg-fd-surface-alt dark:bg-fd-navy' : 'bg-white dark:bg-fd-navy'
|
|
const titleClass = isDark ? 'text-fd-yellow' : 'text-fd-navy dark:text-fd-yellow'
|
|
const bodyClass = isDark ? 'text-white' : 'text-fd-navy dark:text-white'
|
|
const cardBg = isDark ? 'bg-white/10 border-[5px] border-white/10' : 'bg-white border-[5px] border-gray-200 dark:bg-white/10 dark:border-white/10'
|
|
|
|
const handleTabChange = (i: number) => {
|
|
if (i === activeIndex) return
|
|
|
|
if (prefersReducedMotion) {
|
|
/* Instant switch — no animation */
|
|
setActiveIndex(i)
|
|
return
|
|
}
|
|
|
|
setAnimating(true)
|
|
prevIndex.current = activeIndex
|
|
setTimeout(() => {
|
|
setActiveIndex(i)
|
|
setAnimating(false)
|
|
}, 200)
|
|
}
|
|
|
|
const activeCategory = (categories ?? [])[activeIndex]
|
|
|
|
return (
|
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bgClass}`}>
|
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
|
|
|
<div className="text-center mb-10 md:mb-12">
|
|
{heading && (
|
|
<h2 className={`font-joey-heavy text-fd-h1 mb-4 ${titleClass}`}>
|
|
{heading}
|
|
</h2>
|
|
)}
|
|
{description && (
|
|
<p className={`font-joey text-fd-body-lg max-w-[600px] mx-auto ${bodyClass}`}>
|
|
{description}
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex flex-wrap justify-center gap-2 md:gap-3 mb-10 md:mb-12">
|
|
{(categories ?? []).map((cat, i) => (
|
|
<button
|
|
key={i}
|
|
onClick={() => handleTabChange(i)}
|
|
className={`px-5 py-2 rounded-full font-joey-medium text-fd-body transition-all duration-200 border-2 ${
|
|
activeIndex === i
|
|
? 'bg-fd-yellow border-fd-yellow text-fd-navy shadow-sm'
|
|
: isDark
|
|
? 'border-white/30 text-white hover:border-fd-yellow hover:text-fd-yellow'
|
|
: 'border-fd-navy/20 text-fd-navy hover:border-fd-yellow hover:bg-fd-yellow/10 dark:border-white/30 dark:text-white dark:hover:border-fd-yellow dark:hover:text-fd-yellow'
|
|
}`}
|
|
>
|
|
{cat.label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
<div
|
|
className={
|
|
prefersReducedMotion
|
|
? '' /* No transition when motion is reduced */
|
|
: `transition-all duration-200 ${animating ? 'opacity-0 translate-y-2' : 'opacity-100 translate-y-0'}`
|
|
}
|
|
style={{ minHeight: '200px' }}
|
|
>
|
|
{activeCategory?.intro && (
|
|
<p className={`text-center font-joey text-fd-body mb-6 opacity-70 ${bodyClass}`}>
|
|
{activeCategory.intro}
|
|
</p>
|
|
)}
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 md:gap-6">
|
|
{activeCategory?.services?.map((service, i) => (
|
|
<div
|
|
key={i}
|
|
className={`${cardRadius} p-8 md:p-12 flex flex-col gap-4 ${cardBg}`}
|
|
style={
|
|
prefersReducedMotion
|
|
? undefined
|
|
: {
|
|
transition: 'opacity 0.3s, transform 0.3s',
|
|
transitionDelay: `${i * 60}ms`,
|
|
opacity: animating ? 0 : 1,
|
|
transform: animating ? 'translateY(8px)' : 'translateY(0)',
|
|
}
|
|
}
|
|
>
|
|
<h3 className={`font-joey-bold text-fd-h3 ${isDark ? 'text-fd-yellow' : 'text-fd-navy dark:text-fd-yellow'}`}>
|
|
{service.title}
|
|
</h3>
|
|
{service.description && (
|
|
<p className={`font-joey text-fd-body opacity-80 flex-1 ${bodyClass}`}>
|
|
{service.description}
|
|
</p>
|
|
)}
|
|
{service.ctaText && (
|
|
<div className="mt-auto pt-2">
|
|
<a
|
|
href={service.ctaLink || '/kontakt'}
|
|
className={isDark ? 'fd-btn-secondary-dark' : 'fd-btn-secondary dark:fd-btn-secondary-dark'}
|
|
>
|
|
{service.ctaText}
|
|
</a>
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|