fix(builder): ColumnLayout.toHtml derives column widths from split (D2b)

toHtml emitted only the wrapper's flex/gap style and passed childrenHtml
through untouched, so a non-default split percentage never showed up in
the export -- each column's width silently reverted to whatever was baked
into its own inline style at node-creation time. toHtml has no direct
handle on individual children to rewrite their styles (it only receives
the already-concatenated childrenHtml string), so emit a scoped <style>
block with nth-child rules (!important, to win over any stale per-child
inline flex) computed via the same getWidths(split, columns) mapping the
editor render uses. Precedent: Menu/Navbar toHtml already emit scoped
<style> blocks for hover CSS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-12 13:46:44 -07:00
parent 2887cc9bdf
commit cdcc3969bc
2 changed files with 49 additions and 1 deletions
+18 -1
View File
@@ -115,7 +115,11 @@ ColumnLayout.craft = {
/* ---------- HTML export ---------- */
(ColumnLayout as any).toHtml = (props: ColumnLayoutProps, childrenHtml: string) => {
const columns = props.columns || 2;
const split = props.split || '50-50';
const gap = props.gap || '16px';
const widths = getWidths(split, columns);
const outerStyle = cssPropsToString({
display: 'flex',
flexWrap: 'wrap',
@@ -124,7 +128,20 @@ ColumnLayout.craft = {
...props.style,
});
const idAttr = props.anchorId ? ` id="${escapeAttr(props.anchorId)}"` : '';
// Each column is exported as an independently-serialized child node, so
// toHtml has no direct handle on individual children to rewrite their
// inline flex-basis. Instead, scope an nth-child CSS rule (with
// !important, to win over any stale inline flex baked into a child at
// creation time) to a generated class -- same width mapping (getWidths)
// the editor render uses. Precedent: Menu/Navbar toHtml already emit
// scoped <style> blocks for hover CSS.
const scopeId = `cols-${Math.random().toString(36).slice(2, 8)}`;
const widthCss = widths
.map((w, i) => `.${scopeId} > :nth-child(${i + 1}) { flex: 0 0 calc(${w} - ${gap}) !important; }`)
.join('\n ');
return {
html: `<div${idAttr}${outerStyle ? ` style="${outerStyle}"` : ''}>${childrenHtml}</div>`,
html: `<style>\n ${widthCss}\n</style>\n<div class="${scopeId}"${idAttr}${outerStyle ? ` style="${outerStyle}"` : ''}>${childrenHtml}</div>`,
};
};