diff --git a/packages/super-editor/src/assets/styles/elements/prosemirror.css b/packages/super-editor/src/assets/styles/elements/prosemirror.css index 086d09f76b..8bace6caab 100644 --- a/packages/super-editor/src/assets/styles/elements/prosemirror.css +++ b/packages/super-editor/src/assets/styles/elements/prosemirror.css @@ -131,7 +131,7 @@ https://github.com/ProseMirror/prosemirror-tables/blob/master/demo/index.html .ProseMirror .tableWrapper { --table-border-width: 1px; - --offset: 1px; + --offset: 2px; overflow-x: auto; scrollbar-width: thin; diff --git a/packages/super-editor/src/core/super-converter/exporter.js b/packages/super-editor/src/core/super-converter/exporter.js index 0908c3e999..356abeae8b 100644 --- a/packages/super-editor/src/core/super-converter/exporter.js +++ b/packages/super-editor/src/core/super-converter/exporter.js @@ -18,6 +18,8 @@ import { TrackDeleteMarkName, TrackInsertMarkName, TrackFormatMarkName } from '@ import { carbonCopy } from '../utilities/carbonCopy.js'; import { baseBulletList, baseOrderedListDef } from './v2/exporter/helpers/base-list.definitions.js'; import { translateCommentNode } from './v2/exporter/commentsExporter.js'; +import { createColGroup } from '@extensions/table/tableHelpers/createColGroup.js'; + /** * @typedef {Object} ExportParams @@ -808,7 +810,7 @@ function translateTable(params) { params.node = preProcessVerticalMergeCells(params.node, params); const elements = translateChildNodes(params); const tableProperties = generateTableProperties(params.node); - const gridProperties = generateTableGrid(params.node); + const gridProperties = generateTableGrid(params.node, params); elements.unshift(tableProperties); elements.unshift(gridProperties); @@ -992,17 +994,32 @@ function generateTableBorders(node) { * @param {SchemaNode} node * @returns {XmlReadyNode} The table grid properties node */ -function generateTableGrid(node) { - const { gridColumnWidths } = node.attrs; - +function generateTableGrid(node, params) { + const { editorSchema } = params; + + let colgroup = []; + + try { + const pmNode = editorSchema.nodeFromJSON(node); + const cellMinWidth = 25; + const { colgroupValues } = createColGroup( + pmNode, + cellMinWidth, + ); + + colgroup = colgroupValues; + } catch(err) { + colgroup = []; + } + const elements = []; - gridColumnWidths?.forEach((width) => { + colgroup?.forEach((width) => { elements.push({ name: 'w:gridCol', - attributes: { 'w:w': inchesToTwips(width) }, + attributes: { 'w:w': pixelsToTwips(width) }, }); }); - + return { name: 'w:tblGrid', elements, diff --git a/packages/super-editor/src/core/super-converter/v2/importer/tableImporter.js b/packages/super-editor/src/core/super-converter/v2/importer/tableImporter.js index 67aefa6fa7..78b251b1df 100644 --- a/packages/super-editor/src/core/super-converter/v2/importer/tableImporter.js +++ b/packages/super-editor/src/core/super-converter/v2/importer/tableImporter.js @@ -118,6 +118,7 @@ export function handleTableCellNode( columnWidth = null, styleTag, params, + cellIndex, ) { const { docx, nodeListHandler } = params; const tcPr = node.elements.find((el) => el.name === 'w:tcPr'); @@ -159,10 +160,33 @@ export function handleTableCellNode( if (width) { attributes['colwidth'] = [width]; attributes['widthUnit'] = 'px'; + + const defaultColWidths = gridColumnWidths; + const hasDefaultColWidths = gridColumnWidths && gridColumnWidths.length > 0; + const colspanNum = parseInt(colspan, 10); + + if (colspanNum && colspanNum > 1 && hasDefaultColWidths) { + let colwidth = []; + + for (let i = 0; i < colspanNum; i++) { + let colwidthValue = defaultColWidths[cellIndex + i]; + let defaultColwidth = 100; + + if (typeof colwidthValue !== 'undefined') { + colwidth.push(colwidthValue); + } else { + colwidth.push(defaultColwidth); + } + } + + if (colwidth.length) { + attributes['colwidth'] = [...colwidth]; + } + } } if (widthType) attributes['widthType'] = widthType; - if (colspan) attributes['colspan'] = Number(colspan); + if (colspan) attributes['colspan'] = parseInt(colspan, 10); if (background) attributes['background'] = background; if (verticalAlign) attributes['verticalAlign'] = verticalAlign; if (fontSize) attributes['fontSize'] = fontSize; @@ -397,7 +421,7 @@ export function handleTableRowNode(node, table, rowBorders, styleTag, params) { const content = cellNodes?.map((n, index) => { const colWidth = cellNodes.length > 1 ? gridColumnWidths[index] : null; - return handleTableCellNode(n, node, table, borders, colWidth, styleTag, params); + return handleTableCellNode(n, node, table, borders, colWidth, styleTag, params, index); }) || []; const newNode = { @@ -448,6 +472,6 @@ const getGridColumnWidths = (tableNode) => { tblGrid?.elements?.flatMap((el) => { if (el.name !== 'w:gridCol') return []; return twipsToPixels(el.attributes['w:w']); - }) || {} + }) || [] ); }; diff --git a/packages/super-editor/src/extensions/table/tableHelpers/createColGroup.js b/packages/super-editor/src/extensions/table/tableHelpers/createColGroup.js index cd1a46c238..3e0d49277f 100644 --- a/packages/super-editor/src/extensions/table/tableHelpers/createColGroup.js +++ b/packages/super-editor/src/extensions/table/tableHelpers/createColGroup.js @@ -5,6 +5,7 @@ export const createColGroup = (node, cellMinWidth, overrideCol, overrideValue) = let fixedWidth = true; const cols = []; + const colsValues = []; const row = node.firstChild; if (!row) return {}; @@ -18,16 +19,19 @@ export const createColGroup = (node, cellMinWidth, overrideCol, overrideValue) = if (!hasWidth) fixedWidth = false; const [prop, value] = getColStyleDeclaration(cellMinWidth, hasWidth); cols.push(['col', { style: `${prop}: ${value}` }]); + colsValues.push(parseInt(value, 10)); } } const tableWidth = fixedWidth ? `${totalWidth}px` : ''; const tableMinWidth = fixedWidth ? '' : `${totalWidth}px`; const colgroup = ['colgroup', {}, ...cols]; + const colgroupValues = [...colsValues]; return { colgroup, tableWidth, - tableMinWidth, + tableMinWidth, + colgroupValues, }; }; diff --git a/packages/super-editor/src/tests/data/table-merged-cells.docx b/packages/super-editor/src/tests/data/table-merged-cells.docx new file mode 100644 index 0000000000..86f3f26fde Binary files /dev/null and b/packages/super-editor/src/tests/data/table-merged-cells.docx differ diff --git a/packages/super-editor/src/tests/export/export-helpers/export-helpers.js b/packages/super-editor/src/tests/export/export-helpers/export-helpers.js index d2e6d4e3a3..e917af00a6 100644 --- a/packages/super-editor/src/tests/export/export-helpers/export-helpers.js +++ b/packages/super-editor/src/tests/export/export-helpers/export-helpers.js @@ -74,6 +74,7 @@ export const getExportedResult = async (name, comments = []) => { const commentDefinitions = processedComments.map((c, index) => getCommentDefinition(c, index)); const [result, params] = exportSchemaToJson({ + editorSchema: editor.schema, node: schema, bodyNode, relationships: [], diff --git a/packages/super-editor/src/tests/export/tableExporter.test.js b/packages/super-editor/src/tests/export/tableExporter.test.js new file mode 100644 index 0000000000..10e9f4b93b --- /dev/null +++ b/packages/super-editor/src/tests/export/tableExporter.test.js @@ -0,0 +1,25 @@ +import { getExportedResult } from './export-helpers/index.js'; +import { twipsToPixels } from '../../core/super-converter/helpers.js'; + +describe('test table export', async () => { + const fileName = 'table-merged-cells.docx'; + const result = await getExportedResult(fileName); + + const body = {}; + + beforeEach(() => { + Object.assign(body, result.elements?.find((el) => el.name === 'w:body')); + }); + + it('correctly gets w:tblGrid', () => { + const tableGrid = body.elements[0].elements[0].elements; + + const gridCol1 = twipsToPixels(tableGrid[0].attributes['w:w']); + const gridCol2 = twipsToPixels(tableGrid[1].attributes['w:w']); + const gridCol3 = twipsToPixels(tableGrid[2].attributes['w:w']); + + expect(gridCol1).toBe(94); + expect(gridCol2).toBe(331); + expect(gridCol3).toBe(176); + }); +}); diff --git a/packages/super-editor/src/tests/import/tableImporter.test.js b/packages/super-editor/src/tests/import/tableImporter.test.js index 099db470ff..e59dd3adc9 100644 --- a/packages/super-editor/src/tests/import/tableImporter.test.js +++ b/packages/super-editor/src/tests/import/tableImporter.test.js @@ -3,6 +3,7 @@ import { handleTrackChangeNode } from '@converter/v2/importer/trackChangesImport import { defaultNodeListHandler } from '@converter/v2/importer/docxImporter.js'; import { TrackInsertMarkName } from '@extensions/track-changes/constants.js'; import { handleAllTableNodes } from '@converter/v2/importer/tableImporter.js'; +import { getTestDataByFileName } from '@tests/helpers/helpers.js'; describe('table live xml test', () => { const simpleTableStyleXml = ``; @@ -200,3 +201,36 @@ describe('table live xml test', () => { expect(result.nodes[0].content[0].content[0].attrs.cellMargins.right).toBe(8); }); }); + +describe('table tests to check colwidth', () => { + + it('correctly gets colwidth for horizontally merged cells', async () => { + const dataName = 'table-merged-cells.docx'; + const docx = await getTestDataByFileName(dataName); + const documentXml = docx['word/document.xml']; + + const doc = documentXml.elements[0]; + const body = doc.elements[0]; + const content = body.elements; + + const result = handleAllTableNodes({ nodes: [content[0]], docx, nodeListHandler: defaultNodeListHandler() }); + const node = result.nodes[0]; + + expect(node.type).toBe('table'); + + const tr1 = node.content[0]; + const tr2 = node.content[1]; + const tr3 = node.content[2]; + + expect(tr1.content[0].attrs.colspan).toBe(2); + expect(tr1.content[0].attrs.colwidth).toEqual([94, 331]); + expect(tr1.content[1].attrs.colwidth).toEqual([176]) + + expect(tr2.content[0].attrs.colwidth).toEqual([94]); + expect(tr2.content[1].attrs.colwidth).toEqual([331]); + expect(tr2.content[2].attrs.colwidth).toEqual([176]); + + expect(tr3.content[0].attrs.colspan).toBe(3); + expect(tr3.content[0].attrs.colwidth).toEqual([94, 331, 176]); + }); +});