192 lines
6.1 KiB
TypeScript
192 lines
6.1 KiB
TypeScript
import React from 'react'
|
|
import type { FDSpecCardsBlock as Props } from '@/payload-types'
|
|
import { FDButton } from '@/components/FDButton'
|
|
import {
|
|
sectionBg, isExplicitDark, headingColor, bodyColor,
|
|
bodySubduedColor, fdCardRadius, fdCardRadiusSm,
|
|
} from '@/utilities/fdTheme'
|
|
|
|
const cardStyleMap: Record<string, {
|
|
bg: string; border: string; title: string; body: string
|
|
specLabel: string; specValue: string; divider: string; isDark: boolean
|
|
}> = {
|
|
outlined: {
|
|
bg: 'bg-transparent',
|
|
border: 'border border-white/15 dark:border-white/15',
|
|
title: 'text-white dark:text-white',
|
|
body: 'text-white/70 dark:text-white/70',
|
|
specLabel: 'text-white/50 dark:text-white/50',
|
|
specValue: 'text-white dark:text-white',
|
|
divider: 'border-white/10 dark:border-white/10',
|
|
isDark: true,
|
|
},
|
|
navy: {
|
|
bg: 'bg-fd-navy dark:bg-white/10',
|
|
border: '',
|
|
title: 'text-white',
|
|
body: 'text-white/70',
|
|
specLabel: 'text-white/50',
|
|
specValue: 'text-fd-yellow',
|
|
divider: 'border-white/10',
|
|
isDark: true,
|
|
},
|
|
gray: {
|
|
bg: 'bg-fd-gray-light dark:bg-white/10',
|
|
border: '',
|
|
title: 'text-fd-navy dark:text-white',
|
|
body: 'text-fd-navy/70 dark:text-white/70',
|
|
specLabel: 'text-fd-navy/50 dark:text-white/50',
|
|
specValue: 'text-fd-navy dark:text-white',
|
|
divider: 'border-fd-navy/10 dark:border-white/10',
|
|
isDark: false,
|
|
},
|
|
white: {
|
|
bg: 'bg-white dark:bg-white/10 shadow-fd-card dark:shadow-none',
|
|
border: '',
|
|
title: 'text-fd-navy dark:text-white',
|
|
body: 'text-fd-navy/70 dark:text-white/70',
|
|
specLabel: 'text-fd-navy/50 dark:text-white/50',
|
|
specValue: 'text-fd-navy dark:text-white',
|
|
divider: 'border-fd-navy/10 dark:border-white/10',
|
|
isDark: false,
|
|
},
|
|
}
|
|
|
|
/* Light-section overrides for outlined cards */
|
|
const cardStyleMapLight: Record<string, typeof cardStyleMap.outlined> = {
|
|
outlined: {
|
|
bg: 'bg-transparent',
|
|
border: 'border border-gray-200 dark:border-white/15',
|
|
title: 'text-fd-navy dark:text-white',
|
|
body: 'text-fd-navy/70 dark:text-white/70',
|
|
specLabel: 'text-fd-navy/50 dark:text-white/50',
|
|
specValue: 'text-fd-navy dark:text-white',
|
|
divider: 'border-fd-navy/10 dark:border-white/10',
|
|
isDark: false,
|
|
},
|
|
}
|
|
|
|
export const FDSpecCardsBlockComponent: React.FC<Props> = ({
|
|
heading,
|
|
description,
|
|
ctaText,
|
|
ctaLink,
|
|
secondaryCtaText,
|
|
secondaryCtaLink,
|
|
cards,
|
|
layout = 'sideBySide',
|
|
cardStyle = 'outlined',
|
|
sectionBackground = 'navy',
|
|
anchorId,
|
|
}) => {
|
|
const dark = isExplicitDark(sectionBackground)
|
|
const bg = sectionBg(sectionBackground)
|
|
const hClr = headingColor(dark)
|
|
const bClr = bodySubduedColor(dark)
|
|
|
|
const style = (!dark && cardStyle === 'outlined')
|
|
? cardStyleMapLight.outlined
|
|
: (cardStyleMap[cardStyle || 'outlined'] || cardStyleMap.outlined)
|
|
|
|
const isSideBySide = layout === 'sideBySide' || layout === 'sideBySideReverse'
|
|
const isReversed = layout === 'sideBySideReverse'
|
|
const cardCount = cards?.length || 1
|
|
|
|
/* Cards grid: 1 card = 1 col, 2 = 2-col, 3+ = 2-col wrapping */
|
|
const cardGridCols = cardCount === 1 ? '' : 'sm:grid-cols-2'
|
|
|
|
const renderCards = () => (
|
|
<div className={`grid grid-cols-1 ${cardGridCols} gap-4 md:gap-5`}>
|
|
{cards?.map((card, i) => (
|
|
<div
|
|
key={i}
|
|
className={`${style.bg} ${style.border} ${fdCardRadiusSm} px-7 py-7 md:px-9 md:py-9 flex flex-col gap-4 justify-between`}
|
|
>
|
|
<div className="flex flex-col gap-3">
|
|
<h3 className={`font-joey-bold text-fd-h3 leading-snug ${style.title}`}>
|
|
{card.title}
|
|
</h3>
|
|
{card.description && (
|
|
<p className={`font-joey text-fd-body leading-relaxed ${style.body}`}>
|
|
{card.description}
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
{(card.specLabel || card.specValue) && (
|
|
<div className={`pt-4 mt-auto border-t ${style.divider}`}>
|
|
{card.specLabel && (
|
|
<span className={`font-joey text-fd-small block mb-1 ${style.specLabel}`}>
|
|
{card.specLabel}
|
|
</span>
|
|
)}
|
|
{card.specValue && (
|
|
<span className={`font-joey-bold text-fd-h4 ${style.specValue}`}>
|
|
{card.specValue}
|
|
</span>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
)
|
|
|
|
const renderText = () => (
|
|
<div className="flex flex-col gap-5 md:gap-6 justify-center">
|
|
<h2 className={`font-joey-heavy text-fd-h1 leading-tight ${hClr}`}>
|
|
{heading}
|
|
</h2>
|
|
{description && (
|
|
<p className={`font-joey text-fd-body-lg leading-relaxed ${bClr}`}>
|
|
{description}
|
|
</p>
|
|
)}
|
|
{(ctaText || secondaryCtaText) && (
|
|
<div className="flex flex-wrap gap-4 pt-2">
|
|
{ctaText && (
|
|
<FDButton href={ctaLink || '#'} variant="outline" onDark={dark}>
|
|
{ctaText}
|
|
</FDButton>
|
|
)}
|
|
{secondaryCtaText && (
|
|
<FDButton href={secondaryCtaLink || '#'} variant="primary" onDark={dark}>
|
|
{secondaryCtaText}
|
|
</FDButton>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
|
|
if (!isSideBySide) {
|
|
/* Full-width layout — heading on top, cards below */
|
|
return (
|
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
|
<div className="mb-10 md:mb-14">
|
|
{renderText()}
|
|
</div>
|
|
{renderCards()}
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|
|
|
|
/* Side-by-side layout */
|
|
return (
|
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
|
<div className={`flex flex-col min-[820px]:flex-row gap-10 min-[820px]:gap-14 items-start ${isReversed ? 'min-[820px]:flex-row-reverse' : ''}`}>
|
|
<div className="flex-1 min-w-0 min-[820px]:max-w-[380px]">
|
|
{renderText()}
|
|
</div>
|
|
<div className="flex-[1.5] min-w-0">
|
|
{renderCards()}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|