2026-07-12 13:16:53 -07:00
|
|
|
import { CSSProperties } from 'react';
|
2026-07-12 12:05:57 -07:00
|
|
|
import { escapeHtml, escapeAttr, safeUrl } from '../../utils/escape';
|
2026-05-25 12:43:28 -07:00
|
|
|
|
|
|
|
|
export type CtaVariant = 'primary' | 'outline' | 'ghost';
|
|
|
|
|
|
|
|
|
|
export interface CtaButton {
|
|
|
|
|
text: string;
|
|
|
|
|
href: string;
|
|
|
|
|
variant?: CtaVariant;
|
|
|
|
|
target?: '_blank';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface CtaStyleDefaults {
|
|
|
|
|
primaryBg: string;
|
|
|
|
|
primaryText: string;
|
|
|
|
|
outlineText: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read the effective list of CTAs for a section, falling back to legacy
|
|
|
|
|
* primary/secondary props when ctas[] is absent. New sections write ctas[]
|
|
|
|
|
* directly; old sections keep rendering until the user touches the settings.
|
|
|
|
|
*/
|
|
|
|
|
export function normalizeCtas(props: {
|
|
|
|
|
ctas?: CtaButton[];
|
|
|
|
|
buttonText?: string;
|
|
|
|
|
buttonHref?: string;
|
|
|
|
|
secondaryButtonText?: string;
|
|
|
|
|
secondaryButtonHref?: string;
|
|
|
|
|
}): CtaButton[] {
|
|
|
|
|
if (Array.isArray(props.ctas) && props.ctas.length > 0) {
|
|
|
|
|
return props.ctas.filter((c) => c && (c.text || c.href));
|
|
|
|
|
}
|
|
|
|
|
const legacy: CtaButton[] = [];
|
|
|
|
|
if (props.buttonText) legacy.push({ text: props.buttonText, href: props.buttonHref || '#', variant: 'primary' });
|
|
|
|
|
if (props.secondaryButtonText) legacy.push({ text: props.secondaryButtonText, href: props.secondaryButtonHref || '#', variant: 'outline' });
|
|
|
|
|
return legacy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function ctaInlineStyle(cta: CtaButton, defaults: CtaStyleDefaults): CSSProperties {
|
|
|
|
|
const variant = cta.variant || 'primary';
|
|
|
|
|
switch (variant) {
|
|
|
|
|
case 'outline':
|
|
|
|
|
return {
|
|
|
|
|
display: 'inline-block', padding: '14px 36px',
|
|
|
|
|
backgroundColor: 'transparent', color: defaults.outlineText,
|
|
|
|
|
textDecoration: 'none', borderRadius: '8px',
|
|
|
|
|
fontWeight: 600, fontSize: '16px',
|
|
|
|
|
border: `2px solid ${defaults.outlineText}`,
|
|
|
|
|
};
|
|
|
|
|
case 'ghost':
|
|
|
|
|
return {
|
|
|
|
|
display: 'inline-block', padding: '14px 24px',
|
|
|
|
|
backgroundColor: 'transparent', color: defaults.outlineText,
|
|
|
|
|
textDecoration: 'underline', borderRadius: '8px',
|
|
|
|
|
fontWeight: 600, fontSize: '16px',
|
|
|
|
|
};
|
|
|
|
|
case 'primary':
|
|
|
|
|
default:
|
|
|
|
|
return {
|
|
|
|
|
display: 'inline-block', padding: '14px 36px',
|
|
|
|
|
backgroundColor: defaults.primaryBg, color: defaults.primaryText,
|
|
|
|
|
textDecoration: 'none', borderRadius: '8px',
|
|
|
|
|
fontWeight: 600, fontSize: '16px',
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function ctaCssString(cta: CtaButton, defaults: CtaStyleDefaults): string {
|
|
|
|
|
const variant = cta.variant || 'primary';
|
|
|
|
|
switch (variant) {
|
|
|
|
|
case 'outline':
|
|
|
|
|
return `display:inline-block;padding:14px 36px;background-color:transparent;color:${defaults.outlineText};text-decoration:none;border-radius:8px;font-weight:600;font-size:16px;border:2px solid ${defaults.outlineText}`;
|
|
|
|
|
case 'ghost':
|
|
|
|
|
return `display:inline-block;padding:14px 24px;background-color:transparent;color:${defaults.outlineText};text-decoration:underline;border-radius:8px;font-weight:600;font-size:16px`;
|
|
|
|
|
case 'primary':
|
|
|
|
|
default:
|
|
|
|
|
return `display:inline-block;padding:14px 36px;background-color:${defaults.primaryBg};color:${defaults.primaryText};text-decoration:none;border-radius:8px;font-weight:600;font-size:16px`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function ctasToHtml(ctas: CtaButton[], defaults: CtaStyleDefaults): string {
|
|
|
|
|
return ctas.map((c) => {
|
|
|
|
|
const target = c.target === '_blank' ? ' target="_blank" rel="noopener noreferrer"' : '';
|
2026-07-12 12:05:57 -07:00
|
|
|
return `<a href="${escapeAttr(safeUrl(c.href || '#'))}"${target} style="${ctaCssString(c, defaults)}">${escapeHtml(c.text || '')}</a>`;
|
2026-05-25 12:43:28 -07:00
|
|
|
}).join('');
|
|
|
|
|
}
|
|
|
|
|
|