feat(builder): surface hidden section/pricing/social props + box-model rollout

SectionTypePanel (Accordion/Tabs/Testimonials/Countdown/NumberCounter/
CTASection/CallToAction/FeaturesGrid), PricingStylePanel, and
SocialStylePanel all gain a Spacing & Border section (margin/padding
per-side, border, box-shadow, opacity), an Animation section, and a
Visibility section, wired to the shared SpacingControl/BorderControl/
AnimationControl/VisibilityControl.

PricingTable's per-card colors (cardBg/textColor/subColor/featColor/
checkColor/btnBg/btnColor) were previously hard-coded literals computed
from featuredBg inside toHtml -- promoted to real optional props (each
falling back to the exact prior literal when unset) and exposed via
ColorPickerField in PricingStylePanel.

SocialStylePanel now exposes SocialLinks' iconShape/gap (already-built
props with no control), plus Icon's bgColor/bgShape/bgSize/link and
StarRating's filledColor/emptyColor, which the panel's generic
iconBgColor/starColor checks never matched since those aren't Icon's or
StarRating's real prop names.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-14 06:47:58 -07:00
co-authored by Claude Opus 4.8
parent 1d9460c173
commit 8bf525c600
27 changed files with 873 additions and 16 deletions
+71 -15
View File
@@ -19,6 +19,23 @@ interface PricingTableProps {
featuredBg?: string;
bulletType?: string;
anchorId?: string;
/* ---- Regular (non-featured) card colors ----
All optional; each falls back to the exact literal the card was
previously hard-coded to (or, for the button, to featuredBg -- the
button's original derivation) when left unset, so existing saved
projects render pixel-identical until a color is explicitly picked. */
cardBg?: string;
textColor?: string;
subColor?: string;
featColor?: string;
checkColor?: string;
btnBg?: string;
btnColor?: string;
animation?: string;
animationDelay?: string;
hideOnDesktop?: boolean;
hideOnTablet?: boolean;
hideOnMobile?: boolean;
}
const bulletChars: Record<string, string> = {
@@ -61,6 +78,13 @@ export const PricingTable: UserComponent<PricingTableProps> = ({
featuredBg = '#3b82f6',
bulletType = 'check',
anchorId,
cardBg,
textColor,
subColor,
featColor,
checkColor,
btnBg,
btnColor,
}) => {
const {
connectors: { connect, drag },
@@ -69,6 +93,14 @@ export const PricingTable: UserComponent<PricingTableProps> = ({
selected: node.events.selected,
}));
const regCardBg = cardBg || '#ffffff';
const regTextColor = textColor || '#18181b';
const regSubColor = subColor || '#64748b';
const regFeatColor = featColor || '#4b5563';
const regCheckColor = checkColor || '#10b981';
const regBtnBg = btnBg || featuredBg;
const regBtnColor = btnColor || '#ffffff';
return (
<section
ref={(ref: HTMLElement | null): void => { if (ref) connect(drag(ref)); }}
@@ -95,7 +127,7 @@ export const PricingTable: UserComponent<PricingTableProps> = ({
style={{
flex: '1 1 280px',
maxWidth: '360px',
backgroundColor: plan.isFeatured ? featuredBg : '#ffffff',
backgroundColor: plan.isFeatured ? featuredBg : regCardBg,
border: plan.isFeatured ? 'none' : '1px solid #e2e8f0',
borderRadius: '16px',
padding: '40px 32px',
@@ -127,7 +159,7 @@ export const PricingTable: UserComponent<PricingTableProps> = ({
<h3 style={{
fontSize: '20px',
fontWeight: '600',
color: plan.isFeatured ? '#ffffff' : '#18181b',
color: plan.isFeatured ? '#ffffff' : regTextColor,
marginBottom: '8px',
marginTop: plan.isFeatured ? '8px' : '0',
}}>
@@ -137,14 +169,14 @@ export const PricingTable: UserComponent<PricingTableProps> = ({
<span style={{
fontSize: '48px',
fontWeight: '700',
color: plan.isFeatured ? '#ffffff' : '#18181b',
color: plan.isFeatured ? '#ffffff' : regTextColor,
lineHeight: '1',
}}>
{plan.price}
</span>
<span style={{
fontSize: '16px',
color: plan.isFeatured ? 'rgba(255,255,255,0.8)' : '#64748b',
color: plan.isFeatured ? 'rgba(255,255,255,0.8)' : regSubColor,
}}>
{plan.period}
</span>
@@ -161,12 +193,12 @@ export const PricingTable: UserComponent<PricingTableProps> = ({
{(Array.isArray(plan.features) ? plan.features : []).map((feature, fi) => (
<li key={fi} style={{
fontSize: '14px',
color: plan.isFeatured ? 'rgba(255,255,255,0.9)' : '#4b5563',
color: plan.isFeatured ? 'rgba(255,255,255,0.9)' : regFeatColor,
display: 'flex',
alignItems: 'center',
gap: '8px',
}}>
<span style={{ color: plan.isFeatured ? '#bbf7d0' : '#10b981', fontWeight: '700' }}>{bulletChars[bulletType] || '✓'}</span>
<span style={{ color: plan.isFeatured ? '#bbf7d0' : regCheckColor, fontWeight: '700' }}>{bulletChars[bulletType] || '✓'}</span>
{feature}
</li>
))}
@@ -178,8 +210,8 @@ export const PricingTable: UserComponent<PricingTableProps> = ({
marginTop: 'auto',
display: 'inline-block',
padding: '14px 32px',
backgroundColor: plan.isFeatured ? '#ffffff' : featuredBg,
color: plan.isFeatured ? featuredBg : '#ffffff',
backgroundColor: plan.isFeatured ? '#ffffff' : regBtnBg,
color: plan.isFeatured ? featuredBg : regBtnColor,
textDecoration: 'none',
borderRadius: '8px',
fontWeight: '600',
@@ -207,6 +239,18 @@ PricingTable.craft = {
featuredBg: '#3b82f6',
bulletType: 'check',
anchorId: '',
cardBg: '',
textColor: '',
subColor: '',
featColor: '',
checkColor: '',
btnBg: '',
btnColor: '',
animation: '',
animationDelay: '',
hideOnDesktop: false,
hideOnTablet: false,
hideOnMobile: false,
},
rules: {
canDrag: () => true,
@@ -228,16 +272,28 @@ PricingTable.craft = {
// Sanitized -- featuredBg is a raw string-interpolation sink below (drives
// cardBg/btnBg/btnColor, all raw-interpolated into style="...").
const featuredBg = cssValue(props.featuredBg) || '#3b82f6';
// Sanitized -- regular (non-featured) card color overrides, all raw
// string-interpolation sinks into style="..." below. Each falls back to
// the exact literal the card was previously hard-coded to (or, for the
// button, to featuredBg) when unset, so unmodified pricing tables render
// identically to before these props existed.
const regCardBg = cssValue(props.cardBg) || '#ffffff';
const regTextColor = cssValue(props.textColor) || '#18181b';
const regSubColor = cssValue(props.subColor) || '#64748b';
const regFeatColor = cssValue(props.featColor) || '#4b5563';
const regCheckColor = cssValue(props.checkColor) || '#10b981';
const regBtnBg = cssValue(props.btnBg) || featuredBg;
const regBtnColor = cssValue(props.btnColor) || '#ffffff';
const cards = plans.map((plan) => {
const cardBg = plan.isFeatured ? featuredBg : '#ffffff';
const cardBg = plan.isFeatured ? featuredBg : regCardBg;
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 textColor = plan.isFeatured ? '#ffffff' : regTextColor;
const subColor = plan.isFeatured ? 'rgba(255,255,255,0.8)' : regSubColor;
const featColor = plan.isFeatured ? 'rgba(255,255,255,0.9)' : regFeatColor;
const checkColor = plan.isFeatured ? '#bbf7d0' : regCheckColor;
const btnBg = plan.isFeatured ? '#ffffff' : regBtnBg;
const btnColor = plan.isFeatured ? featuredBg : regBtnColor;
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);';