153 lines
5.8 KiB
TypeScript
153 lines
5.8 KiB
TypeScript
import { cn } from '@/lib/utils';
|
|
import * as LucideIcons from 'lucide-react';
|
|
|
|
interface FeatureItem {
|
|
title?: string;
|
|
description?: string;
|
|
icon?: string;
|
|
}
|
|
|
|
interface FeatureGridSectionProps {
|
|
id: string;
|
|
layout?: string;
|
|
colorScheme?: string;
|
|
heading?: string;
|
|
items?: FeatureItem[];
|
|
elementStyles?: Record<string, any>;
|
|
}
|
|
|
|
export function FeatureGridSection({
|
|
id,
|
|
layout = 'grid-3',
|
|
colorScheme = 'default',
|
|
heading,
|
|
items = [],
|
|
features = [],
|
|
elementStyles,
|
|
styles,
|
|
}: FeatureGridSectionProps & { features?: FeatureItem[], styles?: Record<string, any> }) {
|
|
// Use items or features (priority to items if both exist, but usually only one comes from props)
|
|
const listItems = items.length > 0 ? items : features;
|
|
const gridCols = {
|
|
'grid-2': 'md:grid-cols-2',
|
|
'grid-3': 'md:grid-cols-3',
|
|
'grid-4': 'md:grid-cols-2 lg:grid-cols-4',
|
|
}[layout] || 'md:grid-cols-3';
|
|
|
|
// Helper to get text styles (including font family)
|
|
const getTextStyles = (elementName: string) => {
|
|
const styles = elementStyles?.[elementName] || {};
|
|
return {
|
|
classNames: cn(
|
|
styles.fontSize,
|
|
styles.fontWeight,
|
|
{
|
|
'font-sans': styles.fontFamily === 'secondary',
|
|
'font-serif': styles.fontFamily === 'primary',
|
|
}
|
|
),
|
|
style: {
|
|
color: styles.color,
|
|
textAlign: styles.textAlign,
|
|
backgroundColor: styles.backgroundColor,
|
|
borderColor: styles.borderColor,
|
|
borderWidth: styles.borderWidth,
|
|
borderRadius: styles.borderRadius,
|
|
}
|
|
};
|
|
};
|
|
|
|
const headingStyle = getTextStyles('heading');
|
|
const featureItemStyle = getTextStyles('feature_item');
|
|
|
|
return (
|
|
<section
|
|
id={id}
|
|
className={cn(
|
|
'wn-section wn-feature-grid',
|
|
`wn-feature-grid--${layout}`,
|
|
`wn-scheme--${colorScheme}`,
|
|
'py-12 md:py-24',
|
|
{
|
|
// 'bg-white': colorScheme === 'default', // Removed for global styling
|
|
'bg-muted': colorScheme === 'muted',
|
|
'bg-primary text-primary-foreground': colorScheme === 'primary',
|
|
}
|
|
)}
|
|
>
|
|
<div className={cn(
|
|
"mx-auto px-4",
|
|
styles?.contentWidth === 'full' ? 'w-full' : 'container'
|
|
)}>
|
|
{heading && (
|
|
<h2
|
|
className={cn(
|
|
"wn-features__heading text-center mb-12",
|
|
!elementStyles?.heading?.fontSize && "text-3xl md:text-4xl",
|
|
!elementStyles?.heading?.fontWeight && "font-bold",
|
|
headingStyle.classNames
|
|
)}
|
|
style={headingStyle.style}
|
|
>
|
|
{heading}
|
|
</h2>
|
|
)}
|
|
|
|
<div className={cn('grid gap-8', gridCols)}>
|
|
{listItems.map((item, index) => (
|
|
<div
|
|
key={index}
|
|
className={cn(
|
|
'wn-feature-grid__item',
|
|
'p-6 rounded-xl',
|
|
!featureItemStyle.style?.backgroundColor && {
|
|
'bg-white shadow-lg': colorScheme !== 'primary',
|
|
'bg-white/10': colorScheme === 'primary',
|
|
},
|
|
featureItemStyle.classNames
|
|
)}
|
|
style={featureItemStyle.style}
|
|
>
|
|
{item.icon && (() => {
|
|
const IconComponent = (LucideIcons as any)[item.icon];
|
|
if (!IconComponent) return null;
|
|
return (
|
|
<div className="wn-feature-grid__icon mb-4 inline-block p-3 rounded-full bg-primary/10 text-primary">
|
|
<IconComponent className="w-8 h-8" />
|
|
</div>
|
|
);
|
|
})()}
|
|
|
|
{item.title && (
|
|
<h3
|
|
className={cn(
|
|
"wn-feature-grid__item-title mb-3",
|
|
!featureItemStyle.classNames && "text-xl font-semibold"
|
|
)}
|
|
style={{ color: featureItemStyle.style?.color }}
|
|
>
|
|
{item.title}
|
|
</h3>
|
|
)}
|
|
|
|
{item.description && (
|
|
<p className={cn(
|
|
'wn-feature-grid__item-desc',
|
|
!featureItemStyle.style?.color && {
|
|
'text-gray-600': colorScheme !== 'primary',
|
|
'text-white/80': colorScheme === 'primary',
|
|
}
|
|
)}
|
|
style={{ color: featureItemStyle.style?.color }}
|
|
>
|
|
{item.description}
|
|
</p>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|