import React, { CSSProperties } from 'react'; import { useNode, UserComponent } from '@craftjs/core'; import { cssPropsToString } from '../../utils/style-helpers'; import { CtaButton, normalizeCtas, ctaInlineStyle, ctasToHtml } from './_cta-helpers'; import { escapeHtml, escapeAttr, cssValue } from '../../utils/escape'; interface CallToActionProps { heading?: string; description?: string; ctas?: CtaButton[]; /** Legacy props kept for backward compat with saved projects. */ buttonText?: string; buttonHref?: string; secondaryButtonText?: string; secondaryButtonHref?: string; bgType?: 'color' | 'gradient' | 'image'; bgValue?: string; overlayColor?: string; overlayOpacity?: number; textColor?: string; buttonColor?: string; anchorId?: string; style?: CSSProperties; animation?: string; animationDelay?: string; hideOnDesktop?: boolean; hideOnTablet?: boolean; hideOnMobile?: boolean; } const defaultGradient = 'linear-gradient(135deg, #2563eb 0%, #7c3aed 100%)'; export const CallToAction: UserComponent = ({ heading = 'Ready to Get Started?', description = 'Join thousands of satisfied users and start building your dream website today.', ctas, buttonText, buttonHref, secondaryButtonText, secondaryButtonHref, bgType = 'gradient', bgValue = defaultGradient, overlayColor = '#000000', overlayOpacity = 0, textColor = '#ffffff', buttonColor = '#ffffff', anchorId, style = {}, }) => { const { connectors: { connect, drag }, selected, } = useNode((node) => ({ selected: node.events.selected, })); const bgStyle: CSSProperties = {}; if (bgType === 'color') { bgStyle.backgroundColor = bgValue; } else if (bgType === 'gradient') { bgStyle.background = bgValue; } else if (bgType === 'image') { bgStyle.backgroundImage = `url(${bgValue})`; bgStyle.backgroundSize = 'cover'; bgStyle.backgroundPosition = 'center'; } const isButtonDark = buttonColor === '#ffffff' || buttonColor === '#f8fafc'; const buttonTextColor = isButtonDark ? '#18181b' : '#ffffff'; const effectiveCtas = normalizeCtas({ ctas, buttonText, buttonHref, secondaryButtonText, secondaryButtonHref }); const ctaDefaults = { primaryBg: buttonColor, primaryText: buttonTextColor, outlineText: textColor }; return (
{ if (ref) connect(drag(ref)); }} id={anchorId || undefined} style={{ position: 'relative', padding: '80px 20px', textAlign: 'center', outline: selected ? '2px solid #3b82f6' : 'none', ...bgStyle, ...style, }} > {/* Overlay */} {bgType === 'image' && overlayOpacity > 0 && (
)}

{heading}

{description}

{effectiveCtas.map((cta, i) => ( e.preventDefault()} style={ctaInlineStyle(cta, ctaDefaults)}> {cta.text} ))}
); }; /* ---------- Craft config ---------- */ CallToAction.craft = { displayName: 'Call to Action', props: { heading: 'Ready to Get Started?', description: 'Join thousands of satisfied users and start building your dream website today.', ctas: [ { text: 'Get Started', href: '#', variant: 'primary' }, { text: 'Learn More', href: '#', variant: 'outline' }, ] as CtaButton[], anchorId: '', bgType: 'gradient', bgValue: defaultGradient, overlayColor: '#000000', overlayOpacity: 0, textColor: '#ffffff', buttonColor: '#ffffff', style: {}, animation: '', animationDelay: '', hideOnDesktop: false, hideOnTablet: false, hideOnMobile: false, }, rules: { canDrag: () => true, canMoveIn: () => false, canMoveOut: () => true, }, }; /* ---------- HTML export ---------- */ (CallToAction as any).toHtml = (props: CallToActionProps, _childrenHtml: string) => { const bgType = props.bgType || 'gradient'; const bgValue = props.bgValue || defaultGradient; // Sanitized -- raw string-interpolation sink in the heading/description // style attributes below. const textColor = cssValue(props.textColor) || '#ffffff'; const buttonColor = props.buttonColor || '#ffffff'; const isButtonDark = buttonColor === '#ffffff' || buttonColor === '#f8fafc'; const buttonTextColor = isButtonDark ? '#18181b' : '#ffffff'; const sectionCss: CSSProperties = { position: 'relative', padding: '80px 20px', textAlign: 'center', ...props.style, }; if (bgType === 'color') { sectionCss.backgroundColor = bgValue; } else if (bgType === 'gradient') { sectionCss.background = bgValue; } else if (bgType === 'image') { sectionCss.backgroundImage = `url(${bgValue})`; sectionCss.backgroundSize = 'cover'; sectionCss.backgroundPosition = 'center'; } const sectionStyle = cssPropsToString(sectionCss); let overlayHtml = ''; if (bgType === 'image' && (props.overlayOpacity || 0) > 0) { const overlayStyle = cssPropsToString({ position: 'absolute', inset: '0', backgroundColor: props.overlayColor || '#000000', opacity: String((props.overlayOpacity || 0) / 100) as any, pointerEvents: 'none', }); overlayHtml = ``; } const ctas = normalizeCtas(props); const buttonsHtml = ctasToHtml(ctas, { primaryBg: buttonColor, primaryText: buttonTextColor, outlineText: textColor }); const idAttr = props.anchorId ? ` id="${escapeAttr(props.anchorId)}"` : ''; return { html: ` ${overlayHtml}

${escapeHtml(props.heading || '')}

${escapeHtml(props.description || '')}

${buttonsHtml}
`, }; };