import {
Block,
TextBlock,
MetricsBlock,
DividerBlock,
CTABlock,
ImageBlock,
SpacerBlock,
SocialBlock,
HeaderBlock,
FooterBlock,
GlobalStyles,
} from '../types'
// Render a single block to email-safe HTML (table-based, inline CSS)
export function renderBlockToHtml(block: Block, globalStyles: GlobalStyles): string {
switch (block.type) {
case 'text':
return renderTextBlock(block, globalStyles)
case 'metrics':
return renderMetricsBlock(block)
case 'divider':
return renderDividerBlock(block)
case 'cta':
return renderButtonBlock(block)
case 'image':
return renderImageBlock(block)
case 'spacer':
return renderSpacerBlock(block)
case 'social':
return renderSocialBlock(block)
case 'header':
return renderHeaderBlock(block)
case 'footer':
return renderFooterBlock(block)
default:
return ''
}
}
function blockPaddingStyle(block: Block): string {
const s = block.style
if (!s) return ''
const parts: string[] = []
if (s.paddingTop) parts.push(`padding-top:${s.paddingTop}px`)
if (s.paddingBottom) parts.push(`padding-bottom:${s.paddingBottom}px`)
if (s.paddingLeft) parts.push(`padding-left:${s.paddingLeft}px`)
if (s.paddingRight) parts.push(`padding-right:${s.paddingRight}px`)
if (s.marginTop) parts.push(`margin-top:${s.marginTop}px`)
if (s.marginBottom) parts.push(`margin-bottom:${s.marginBottom}px`)
if (s.backgroundColor) parts.push(`background-color:${s.backgroundColor}`)
return parts.join(';')
}
function renderTextBlock(block: TextBlock, globalStyles: GlobalStyles): string {
const fontFamily = block.fontFamily || globalStyles.fontFamily
const fontSize = block.fontSize ? `${block.fontSize}px` : '16px'
const lineHeight = block.lineHeight ? `${block.lineHeight}` : '1.6'
const color = block.textColor || '#1f2937'
const extraStyle = blockPaddingStyle(block)
return `
${block.content}
`
}
function renderMetricsBlock(block: MetricsBlock): string {
const cols = block.columns || 2
const extraStyle = blockPaddingStyle(block)
const title = block.title
? `${block.title}
`
: ''
const cells = block.metrics
.map((m) => {
const changeColor =
m.changeType === 'positive'
? '#16a34a'
: m.changeType === 'negative'
? '#dc2626'
: '#6b7280'
const changeHtml = m.change
? `${m.change}
`
: ''
return `
${m.value}
${m.label}
${changeHtml}
| `
})
.join(' | ')
return `${title}`
}
function renderDividerBlock(block: DividerBlock): string {
const extraStyle = blockPaddingStyle(block)
if (block.dividerStyle === 'space') {
return ``
}
const color = block.color || '#d1d5db'
const thickness = block.thickness || 1
const width = block.width || 100
return ``
}
function renderButtonBlock(block: CTABlock): string {
const bg = block.buttonColor || '#2563eb'
const color = block.textColor || '#ffffff'
const radius = block.borderRadius ?? 6
const paddingH = block.paddingH ?? 24
const paddingV = block.paddingV ?? 12
const extraStyle = blockPaddingStyle(block)
const align = block.alignment || 'center'
return ``
}
function renderImageBlock(block: ImageBlock): string {
if (!block.url) return ''
const width = block.width || 100
const align = block.alignment || 'center'
const extraStyle = blockPaddingStyle(block)
const captionHtml = block.caption
? `${block.caption}
`
: ''
const imgTag = `
`
const linked = block.linkUrl
? `${imgTag}`
: imgTag
return ``
}
function renderSpacerBlock(block: SpacerBlock): string {
const height = block.height || 32
return `
`
}
function renderSocialBlock(block: SocialBlock): string {
const align = block.alignment || 'center'
const size = block.iconSize || 24
const extraStyle = blockPaddingStyle(block)
// Using text-based fallback icons for email compatibility
const platformLabels: Record = {
linkedin: 'LinkedIn',
twitter: 'Twitter',
facebook: 'Facebook',
instagram: 'Instagram',
youtube: 'YouTube',
github: 'GitHub',
website: 'Website',
}
const platformColors: Record = {
linkedin: '#0A66C2',
twitter: '#1DA1F2',
facebook: '#1877F2',
instagram: '#E4405F',
youtube: '#FF0000',
github: '#333333',
website: '#6b7280',
}
const links = block.links
.filter((l) => l.url)
.map((l) => {
const label = platformLabels[l.platform] || l.platform
const color = platformColors[l.platform] || '#6b7280'
return `${label} | `
})
.join('')
if (!links) return ''
return ``
}
function renderHeaderBlock(block: HeaderBlock): string {
const align = block.alignment || 'center'
const extraStyle = blockPaddingStyle(block)
const logo = block.logoUrl
? `
`
: ''
return `|
${logo}${block.companyName}
|
`
}
function renderFooterBlock(block: FooterBlock): string {
const align = block.alignment || 'center'
const extraStyle = blockPaddingStyle(block)
const addressHtml = block.address
? `${block.address}
`
: ''
const unsubHtml = block.showUnsubscribe
? ``
: ''
return `|
${block.companyName}
${addressHtml}
${unsubHtml}
|
`
}