import React, { CSSProperties } from 'react'; import { useNode, UserComponent } from '@craftjs/core'; import { cssPropsToString } from '../../utils/style-helpers'; import { escapeHtml, escapeAttr, safeUrl, cssValue } from '../../utils/escape'; interface PricingPlan { name: string; price: string; period: string; features: string[]; buttonText: string; buttonHref: string; isFeatured: boolean; } interface PricingTableProps { plans?: PricingPlan[]; style?: CSSProperties; featuredBg?: string; bulletType?: string; anchorId?: string; } const bulletChars: Record = { check: '✓', dot: '●', arrow: '→', star: '★', dash: '—', none: '', }; const defaultPlans: PricingPlan[] = [ { name: 'Basic', price: '$9', period: '/month', features: ['1 Website', '10 GB Storage', 'Free SSL Certificate', 'Email Support'], buttonText: 'Get Started', buttonHref: '#', isFeatured: false, }, { name: 'Pro', price: '$29', period: '/month', features: ['10 Websites', '100 GB Storage', 'Free SSL Certificate', 'Priority Support', 'Custom Domain', 'Analytics Dashboard'], buttonText: 'Get Started', buttonHref: '#', isFeatured: true, }, { name: 'Enterprise', price: '$99', period: '/month', features: ['Unlimited Websites', '1 TB Storage', 'Free SSL Certificate', '24/7 Phone Support', 'Custom Domain', 'Advanced Analytics', 'API Access', 'Team Collaboration'], buttonText: 'Contact Sales', buttonHref: '#', isFeatured: false, }, ]; export const PricingTable: UserComponent = ({ plans = defaultPlans, style = {}, featuredBg = '#3b82f6', bulletType = 'check', 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, }} >
{plans.map((plan, i) => (
{plan.isFeatured && (
Most Popular
)}

{plan.name}

{plan.price} {plan.period}
    {(Array.isArray(plan.features) ? plan.features : []).map((feature, fi) => (
  • {bulletChars[bulletType] || '✓'} {feature}
  • ))}
e.preventDefault()} style={{ marginTop: 'auto', display: 'inline-block', padding: '14px 32px', backgroundColor: plan.isFeatured ? '#ffffff' : featuredBg, color: plan.isFeatured ? featuredBg : '#ffffff', textDecoration: 'none', borderRadius: '8px', fontWeight: '600', fontSize: '14px', width: '100%', textAlign: 'center', }} > {plan.buttonText}
))}
); }; /* ---------- Craft config ---------- */ PricingTable.craft = { displayName: 'Pricing Table', props: { plans: defaultPlans, style: { backgroundColor: '#ffffff' }, featuredBg: '#3b82f6', bulletType: 'check', anchorId: '', }, rules: { canDrag: () => true, canMoveIn: () => false, canMoveOut: () => true, }, }; /* ---------- HTML export ---------- */ (PricingTable as any).toHtml = (props: PricingTableProps, _childrenHtml: string) => { const bulletType = props.bulletType || 'check'; const sectionStyle = cssPropsToString({ padding: '80px 20px', ...props.style, }); const idAttr = props.anchorId ? ` id="${escapeAttr(props.anchorId)}"` : ''; const plans = props.plans || defaultPlans; // Sanitized -- featuredBg is a raw string-interpolation sink below (drives // cardBg/btnBg/btnColor, all raw-interpolated into style="..."). const featuredBg = cssValue(props.featuredBg) || '#3b82f6'; const cards = plans.map((plan) => { const cardBg = plan.isFeatured ? featuredBg : '#ffffff'; const cardBorder = plan.isFeatured ? 'border:none;' : 'border:1px solid #e2e8f0;'; const textColor = plan.isFeatured ? '#ffffff' : '#18181b'; const subColor = plan.isFeatured ? 'rgba(255,255,255,0.8)' : '#64748b'; const featColor = plan.isFeatured ? 'rgba(255,255,255,0.9)' : '#4b5563'; const checkColor = plan.isFeatured ? '#bbf7d0' : '#10b981'; const btnBg = plan.isFeatured ? '#ffffff' : featuredBg; const btnColor = plan.isFeatured ? featuredBg : '#ffffff'; const scale = plan.isFeatured ? 'transform:scale(1.05);' : ''; const shadow = plan.isFeatured ? 'box-shadow:0 20px 60px rgba(59,130,246,0.3);' : 'box-shadow:0 1px 3px rgba(0,0,0,0.06);'; const featuresHtml = (Array.isArray(plan.features) ? plan.features : []).map((f) => `
  • ${bulletChars[bulletType] || '✓'}${escapeHtml(f)}
  • ` ).join('\n '); const badge = plan.isFeatured ? `
    Most Popular
    ` : ''; return `
    ${badge}

    ${escapeHtml(plan.name)}

    ${escapeHtml(plan.price)} ${escapeHtml(plan.period)}
      ${featuresHtml}
    ${escapeHtml(plan.buttonText)}
    `; }).join('\n '); return { html: `
    ${cards}
    `, }; };