import React, { CSSProperties } from 'react'; import { useNode, UserComponent } from '@craftjs/core'; import { cssPropsToString } from '../../utils/style-helpers'; import { escapeHtml, escapeAttr, safeUrl } from '../../utils/escape'; interface FeatureItem { title: string; description: string; icon: string; image?: string; imageAlt?: string; buttonText?: string; buttonUrl?: string; } interface FeaturesGridProps { features?: FeatureItem[]; style?: CSSProperties; anchorId?: string; } // Keys image/imageAlt/buttonText/buttonUrl are present (blank) on the defaults so // the guided panel's generic array editor (which derives fields from the first // item's keys) exposes inputs for them. An image renders whenever `image` is set. const defaultFeatures: FeatureItem[] = [ { title: 'Fast & Reliable', description: 'Built for performance with optimized loading and rock-solid uptime.', icon: '⚡', image: '', imageAlt: '', buttonText: '', buttonUrl: '' }, { title: 'Easy to Use', description: 'Intuitive drag-and-drop interface that anyone can master in minutes.', icon: '✨', image: '', imageAlt: '', buttonText: '', buttonUrl: '' }, { title: 'Fully Responsive', description: 'Looks great on every device, from phones to ultrawide monitors.', icon: '📱', image: '', imageAlt: '', buttonText: '', buttonUrl: '' }, ]; export const FeaturesGrid: UserComponent = ({ features = defaultFeatures, style = {}, anchorId, }) => { const { connectors: { connect, drag }, selected, } = useNode((node) => ({ selected: node.events.selected, })); return (
{ if (ref) connect(drag(ref)); }} id={anchorId || undefined} style={{ padding: '80px 20px', backgroundColor: '#ffffff', outline: selected ? '2px solid #3b82f6' : 'none', ...style, }} >
{(Array.isArray(features) ? features : []).map((feat, i) => (
{feat.image ? ( {feat.imageAlt ) : (
{feat.icon}
)}

{feat.title}

{feat.description}

{feat.buttonText ? ( e.preventDefault()} style={{ display: 'inline-block', marginTop: '16px', padding: '10px 24px', background: '#3b82f6', color: '#fff', borderRadius: '8px', textDecoration: 'none', fontSize: '14px', fontWeight: 600 }} > {feat.buttonText} ) : null}
))}
); }; /* ---------- Craft config ---------- */ FeaturesGrid.craft = { displayName: 'Features Grid', props: { features: defaultFeatures, style: { backgroundColor: '#ffffff' }, anchorId: '', }, rules: { canDrag: () => true, canMoveIn: () => false, canMoveOut: () => true, }, }; /* ---------- HTML export ---------- */ (FeaturesGrid as any).toHtml = (props: FeaturesGridProps, _childrenHtml: string) => { const sectionStyle = cssPropsToString({ padding: '80px 20px', ...props.style, }); const idAttr = props.anchorId ? ` id="${escapeAttr(props.anchorId)}"` : ''; const cards = (props.features || defaultFeatures).map((feat) => { const media = feat.image ? `${escapeAttr(feat.imageAlt || feat.title || '')}` : `
${escapeHtml(feat.icon)}
`; const button = feat.buttonText ? `\n ${escapeHtml(feat.buttonText)}` : ''; return `
${media}

${escapeHtml(feat.title)}

${escapeHtml(feat.description)}

${button}
`; }).join('\n '); return { html: `
${cards}
`, }; };