feat: a cell's vertical alignment survives saving

Import read style:vertical-align but neither exporter wrote it, so a
mid-cell callout label jumped to the row's top on every save. odf-kit's
CellOptions and docx's TableCell both take it natively; DOCX import maps
w:vAlign in as well.
This commit is contained in:
Steffen Becker committed 2026-08-10 12:51:58 +00:00
1 parent 3805654e8a
commit a8dcd9b897
6 files changed
+45 -1

No files matched your search

+3
View File
@@ -927,6 +927,9 @@ function tableToDocx(node: TiptapNode, contentWidthCm: number, num: Numbering):
? { marginUnitType: WidthType.DXA, top: cmToTwip(ownPad[0]), right: cmToTwip(ownPad[1]), bottom: cmToTwip(ownPad[2]), left: cmToTwip(ownPad[3]) }
: undefined,
shading: fill ? { type: ShadingType.CLEAR, fill } : undefined,
// Content set against the middle/bottom of the box → w:vAlign.
verticalAlign: cell.attrs?.verticalAlign === 'middle' ? 'center'
: cell.attrs?.verticalAlign === 'bottom' ? 'bottom' : undefined,
width: widthTwip ? { size: widthTwip, type: WidthType.DXA } : undefined,
borders: {
top: docxCellBorder(cell.attrs, 'borderTop'),
+3
View File
@@ -2669,6 +2669,9 @@ function exportTable(node: TiptapNode, doc: OdtDocument, contentWidthCm: number,
const c = normalizeColor(bg);
if (c) opts.backgroundColor = c;
}
// Content set against the middle/bottom of the box → style:vertical-align.
const va = cell.attrs?.verticalAlign;
if (va === 'middle' || va === 'bottom') opts.verticalAlign = va;
// Per-side borders → fo:border-* (null = the table default border below).
for (const side of BORDER_SIDES) {
const b = parseBorderAttr(cell.attrs?.[side] as string | null);
+5
View File
@@ -1673,6 +1673,11 @@ function buildTable(tbl: Element, ctx: Ctx): Node | null {
const blocks = convertBlocks(Array.from(tc.children), ctx, 'cell', bg === HEADER_SHADE);
const attrs: Record<string, unknown> = { colspan, rowspan: 1 };
if (bg) attrs.backgroundColor = bg;
// w:vAlign — Word's "center" is the editor's "middle"; "top"/"both" stay the default.
const vAlignEl = fc(tcPr, 'vAlign');
const vAlign = vAlignEl ? wVal(vAlignEl) : null;
if (vAlign === 'center') attrs.verticalAlign = 'middle';
else if (vAlign === 'bottom') attrs.verticalAlign = 'bottom';
// The cell's own margins (w:tcMar), kept only where they differ from the table's.
const ownPad = cellPaddingAttr(cellMarginsCm([fc(tcPr, 'tcMar')]), padBase);
if (ownPad) attrs.cellPadding = ownPad;