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, safeUrl } from '../../utils/escape'; interface HeroProps { heading?: string; subtitle?: string; /** New dynamic CTAs. When set (length > 0), legacy primary/secondary fields are ignored. */ ctas?: CtaButton[]; /** Legacy — kept for backwards compatibility with saved projects. */ buttonText?: string; buttonHref?: string; secondaryButtonText?: string; secondaryButtonHref?: string; bgType?: 'color' | 'gradient' | 'image' | 'video'; bgColor?: string; bgGradientFrom?: string; bgGradientTo?: string; bgGradientAngle?: number; bgImage?: string; bgVideo?: string; overlayColor?: string; overlayOpacity?: number; textColor?: string; buttonBgColor?: string; buttonTextColor?: string; minHeight?: string; verticalAlign?: 'top' | 'center' | 'bottom'; textAlign?: 'left' | 'center' | 'right'; anchorId?: string; style?: CSSProperties; } // Helper: build the background CSS value function buildBackground(props: HeroProps): string { switch (props.bgType) { case 'gradient': return `linear-gradient(${props.bgGradientAngle || 135}deg, ${props.bgGradientFrom || '#667eea'}, ${props.bgGradientTo || '#764ba2'})`; case 'image': return props.bgImage ? `url('${props.bgImage}') center/cover no-repeat` : '#1e293b'; case 'color': default: return props.bgColor || '#1e293b'; } } export const HeroSimple: UserComponent = ({ heading = 'Build Something Amazing', subtitle = 'Create beautiful websites without writing a single line of code.', ctas, buttonText, buttonHref, secondaryButtonText, secondaryButtonHref, bgType = 'color', bgColor = '#1e293b', bgGradientFrom = '#667eea', bgGradientTo = '#764ba2', bgGradientAngle = 135, bgImage = '', bgVideo = '', overlayColor = '#000000', overlayOpacity = 0, textColor = '#ffffff', buttonBgColor = '#3b82f6', buttonTextColor = '#ffffff', minHeight = '500px', verticalAlign = 'center', textAlign = 'center', anchorId, style = {}, }) => { const { connectors: { connect, drag } } = useNode(); const bg = buildBackground({ bgType, bgColor, bgGradientFrom, bgGradientTo, bgGradientAngle, bgImage, } as HeroProps); const justifyMap = { top: 'flex-start', center: 'center', bottom: 'flex-end' }; const effectiveCtas = normalizeCtas({ ctas, buttonText, buttonHref, secondaryButtonText, secondaryButtonHref }); const ctaDefaults = { primaryBg: buttonBgColor, primaryText: buttonTextColor, outlineText: textColor, }; return (
{ if (ref) connect(drag(ref)); }} id={anchorId || undefined} style={{ ...style, background: bgType !== 'image' ? bg : undefined, backgroundImage: bgType === 'image' && bgImage ? `url('${bgImage}')` : undefined, backgroundSize: bgType === 'image' ? 'cover' : undefined, backgroundPosition: bgType === 'image' ? 'center' : undefined, minHeight: minHeight === '100vh' ? '100vh' : minHeight, display: 'flex', alignItems: justifyMap[verticalAlign] || 'center', justifyContent: 'center', position: 'relative', overflow: 'hidden', padding: '60px 20px', }} > {/* Video background */} {bgType === 'video' && bgVideo && (
); }; /* ---------- Craft config ---------- */ HeroSimple.craft = { displayName: 'Hero', props: { heading: 'Build Something Amazing', subtitle: 'Create beautiful websites without writing a single line of code.', ctas: [ { text: 'Get Started', href: '#', variant: 'primary' }, ] as CtaButton[], bgType: 'color', bgColor: '#1e293b', bgGradientFrom: '#667eea', bgGradientTo: '#764ba2', bgGradientAngle: 135, bgImage: '', bgVideo: '', overlayColor: '#000000', overlayOpacity: 0, textColor: '#ffffff', buttonBgColor: '#3b82f6', buttonTextColor: '#ffffff', minHeight: '500px', verticalAlign: 'center', textAlign: 'center', anchorId: '', style: {}, }, rules: { canDrag: () => true, canMoveIn: () => false, canMoveOut: () => true, }, }; /* ---------- HTML export ---------- */ (HeroSimple as any).toHtml = (props: HeroProps, _childrenHtml: string) => { const bg = buildBackground(props); const justifyMap: Record = { top: 'flex-start', center: 'center', bottom: 'flex-end' }; const sectionStyle = cssPropsToString({ background: props.bgType !== 'image' ? bg : undefined, backgroundImage: props.bgType === 'image' && props.bgImage ? `url('${props.bgImage}')` : undefined, backgroundSize: props.bgType === 'image' ? 'cover' : undefined, backgroundPosition: props.bgType === 'image' ? 'center' : undefined, minHeight: props.minHeight || '500px', display: 'flex', alignItems: justifyMap[props.verticalAlign || 'center'], justifyContent: 'center', position: 'relative', overflow: 'hidden', padding: '60px 20px', ...props.style, }); let overlayHtml = ''; if ((props.overlayOpacity || 0) > 0) { overlayHtml = `
`; } let videoHtml = ''; if (props.bgType === 'video' && props.bgVideo) { videoHtml = ``; } const textAlign = props.textAlign || 'center'; const justifyBtn = textAlign === 'center' ? 'center' : textAlign === 'right' ? 'flex-end' : 'flex-start'; const ctas = normalizeCtas(props); const buttonsHtml = ctasToHtml(ctas, { primaryBg: props.buttonBgColor || '#3b82f6', primaryText: props.buttonTextColor || '#fff', outlineText: props.textColor || '#fff', }); const idAttr = props.anchorId ? ` id="${escapeAttr(props.anchorId)}"` : ''; return { html: ` ${videoHtml}${overlayHtml}

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

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

${buttonsHtml}
`, }; };