import React, { useCallback, useState } from 'react'; import { useEditor } from '@craftjs/core'; import { SHADOW_PRESETS } from '../../../constants/presets'; import { StylePanelProps, CollapsibleSection, ColorPickerField, SectionLabel, PresetButtonGrid, labelStyle, inputStyle, smallInputStyle, btnActiveStyle, sectionGap, useNodeProp, SpacingControl, BorderControl, BorderValue, buildBorderShorthand, AnimationControl, VisibilityControl, } from './shared'; /** Parses a border shorthand string ("2px solid #hex") produced by * buildBorderShorthand() back into its parts for round-tripping through * BorderControl. See SectionTypePanel.tsx for the identical helper. */ function parseBorderShorthand(v: string | undefined): BorderValue { if (!v || v === 'none') return { width: '', style: 'none', color: '#000000' }; const m = String(v).match(/^([\d.]+[a-z%]*)\s+(\w+)\s+(.+)$/); if (!m) return { width: '', style: 'none', color: '#000000' }; return { width: m[1], style: m[2], color: m[3] }; } const capSide = (s: string): string => s.charAt(0).toUpperCase() + s.slice(1); const bulletOptions = [ { label: '✓', value: 'check' }, { label: '●', value: 'dot' }, { label: '→', value: 'arrow' }, { label: '★', value: 'star' }, { label: '—', value: 'dash' }, { label: 'None', value: 'none' }, ]; const bulletChar: Record = { check: '✓', dot: '●', arrow: '→', star: '★', dash: '—', none: '', }; export const PricingStylePanel: React.FC = ({ selectedId, nodeProps }) => { const { actions } = useEditor(); const { setProp, setPropStyle } = useNodeProp(selectedId); const [expandedPlan, setExpandedPlan] = useState(0); const plans: any[] = Array.isArray(nodeProps.plans) ? nodeProps.plans : []; const currentBullet = nodeProps.bulletType || 'check'; const style = nodeProps.style || {}; const updatePlan = useCallback((planIndex: number, field: string, value: any) => { actions.setProp(selectedId, (props: any) => { const updated = [...(Array.isArray(props.plans) ? props.plans : [])]; updated[planIndex] = { ...updated[planIndex], [field]: value }; props.plans = updated; }); }, [actions, selectedId]); const addPlan = useCallback(() => { actions.setProp(selectedId, (props: any) => { const updated = [...(Array.isArray(props.plans) ? props.plans : [])]; updated.push({ name: 'New Plan', price: '$0', period: '/month', features: ['Feature 1'], buttonText: 'Choose Plan', buttonHref: '#', isFeatured: false, }); props.plans = updated; }); }, [actions, selectedId]); const removePlan = useCallback((index: number) => { actions.setProp(selectedId, (props: any) => { const updated = [...(Array.isArray(props.plans) ? props.plans : [])]; updated.splice(index, 1); props.plans = updated; }); }, [actions, selectedId]); const addFeature = useCallback((planIndex: number) => { actions.setProp(selectedId, (props: any) => { const updated = [...(Array.isArray(props.plans) ? props.plans : [])]; const features = [...(Array.isArray(updated[planIndex]?.features) ? updated[planIndex].features : [])]; features.push('New feature'); updated[planIndex] = { ...updated[planIndex], features }; props.plans = updated; }); }, [actions, selectedId]); const updateFeature = useCallback((planIndex: number, featureIndex: number, value: string) => { actions.setProp(selectedId, (props: any) => { const updated = [...(Array.isArray(props.plans) ? props.plans : [])]; const features = [...(Array.isArray(updated[planIndex]?.features) ? updated[planIndex].features : [])]; features[featureIndex] = value; updated[planIndex] = { ...updated[planIndex], features }; props.plans = updated; }); }, [actions, selectedId]); const removeFeature = useCallback((planIndex: number, featureIndex: number) => { actions.setProp(selectedId, (props: any) => { const updated = [...(Array.isArray(props.plans) ? props.plans : [])]; const features = [...(Array.isArray(updated[planIndex]?.features) ? updated[planIndex].features : [])]; features.splice(featureIndex, 1); updated[planIndex] = { ...updated[planIndex], features }; props.plans = updated; }); }, [actions, selectedId]); return ( <> {/* Bullet type */}
{bulletOptions.map((b) => ( ))}
{/* Plans */} {plans.map((plan, i) => { const isExpanded = expandedPlan === i; const features: string[] = Array.isArray(plan.features) ? plan.features : []; return (
{/* Plan header - click to expand */}
setExpandedPlan(isExpanded ? -1 : i)} style={{ padding: '8px 10px', cursor: 'pointer', display: 'flex', justifyContent: 'space-between', alignItems: 'center', }}> {plan.name || 'Plan'} {plan.isFeatured && Featured}
{plan.price}
{/* Expanded plan settings */} {isExpanded && (
updatePlan(i, 'name', e.target.value)} style={smallInputStyle} />
updatePlan(i, 'price', e.target.value)} style={smallInputStyle} />
updatePlan(i, 'period', e.target.value)} placeholder="/month" style={smallInputStyle} />
updatePlan(i, 'buttonText', e.target.value)} style={smallInputStyle} />
updatePlan(i, 'buttonHref', e.target.value)} style={smallInputStyle} />
{/* Features list */}
{features.map((feat, fi) => (
{bulletChar[currentBullet] || '✓'} updateFeature(i, fi, e.target.value)} style={{ ...smallInputStyle, flex: 1 }} />
))}
{/* Remove plan */} {plans.length > 1 && ( )}
)}
); })}
{/* Colors */} actions.setProp(selectedId, (p: any) => { p.featuredBg = v; })} /> {/* Regular (non-featured) card colors -- built into PricingTable's render/toHtml but previously hard-coded literals with no control surfacing them. Each falls back to the prior literal when blank. */} setProp('cardBg', v)} /> setProp('textColor', v)} /> setProp('subColor', v)} /> setProp('featColor', v)} /> setProp('checkColor', v)} /> setProp('btnBg', v)} /> setProp('btnColor', v)} /> {/* Box model: margin, padding, border, shadow, opacity */} setPropStyle(`margin${capSide(side)}`, v)} /> setPropStyle(`padding${capSide(side)}`, v)} /> setPropStyle('border', buildBorderShorthand(v))} />
Shadow setPropStyle('boxShadow', v)} />
Opacity setPropStyle('opacity', String(Number(e.target.value) / 100))} style={{ width: '100%' }} />
{/* Entrance animation */} { setProp('animation', v.animation); setProp('animationDelay', v.animationDelay); }} /> {/* Responsive visibility */} { setProp('hideOnDesktop', !!v.hideOnDesktop); setProp('hideOnTablet', !!v.hideOnTablet); setProp('hideOnMobile', !!v.hideOnMobile); }} /> ); };