diff --git a/cypress/fixtures/Table.md b/cypress/fixtures/Table.md index 546ecb189e4..199584abb14 100644 --- a/cypress/fixtures/Table.md +++ b/cypress/fixtures/Table.md @@ -13,8 +13,8 @@ This is a table | Header | other Header | |--------|--------------| -| Cell | other cell | -| Cell | other cell | +| Cell | other cell | +| Cell | other cell | ## Create a table @@ -54,40 +54,40 @@ did insertTable ## Add a new row at the end -| | | | -|--|--|--| -| | | | +| | | | +|--|--|-------------| +| | | | | | | addRowAfter | --- -| | | | -|--|--|--| -| | | | +| | | | +|--|--|-----------------| +| | | | | | | did addRowAfter | -| | | | +| | | | ## Add a new column at the end -| | | | -|--|--|--| -| | | | +| | | | +|--|--|----------------| +| | | | | | | addColumnAfter | --- -| | | | | -|--|--|--|--| -| | | | | +| | | | | +|--|--|--------------------|--| +| | | | | | | | did addColumnAfter | | ## Delete row at the end -| | | | -|--|--|--| -| | | | +| | | | +|--|--|-----------| +| | | | | | | deleteRow | @@ -99,9 +99,9 @@ did insertTable ## Delete column at the end -| | | | -|--|--|--| -| | | | +| | | | +|--|--|--------------| +| | | | | | | deleteColumn | diff --git a/src/nodes/Table/Table.js b/src/nodes/Table/Table.js index 7ee936f5f59..9920680c151 100644 --- a/src/nodes/Table/Table.js +++ b/src/nodes/Table/Table.js @@ -14,6 +14,7 @@ import { selectedRect, selectionCell, } from '@tiptap/pm/tables' +import { Node } from '@tiptap/pm/model' /** * @@ -66,6 +67,49 @@ function findSameCellInNextRow($cell) { } } +/** + * + * @param {Node} node - Table node + */ +function getColumns(node) { + const columns = [] + + node.content.forEach((row) => { + row.content.forEach((cell, offset, columnIndex) => { + if (!columns[columnIndex]) { + columns[columnIndex] = [] + } + columns[columnIndex].push(cell) + }) + }) + + return columns +} + +/** + * + * @param {Array} columns - Columns of table + */ +function calculateColumnWidths(columns) { + const widths = [] + + columns.forEach((column) => { + let maxWidth = 0 + + column.forEach((cell) => { + let cellWidth = 0 + cell.content.forEach(node => { + cellWidth += (node.text?.length || 6) + }) + maxWidth = Math.max(maxWidth, cellWidth) + }) + + widths.push(maxWidth) + }) + + return widths +} + export default Table.extend({ content: 'tableCaption? tableHeadRow tableRow*', @@ -178,6 +222,9 @@ export default Table.extend({ }, toMarkdown(state, node) { + const columns = getColumns(node) + state.options.columnWidths = calculateColumnWidths(columns) + state.options.currentHeaderIndex = 0 state.renderContent(node) state.closeBlock(node) }, diff --git a/src/nodes/Table/TableCell.js b/src/nodes/Table/TableCell.js index 0891633b0ad..ca2dee7a77c 100644 --- a/src/nodes/Table/TableCell.js +++ b/src/nodes/Table/TableCell.js @@ -8,10 +8,29 @@ export default TableCell.extend({ toMarkdown(state, node) { state.write(' ') const backup = state.options?.escapeExtraCharacters + const columnIndex = state.options.currentColumnIndex state.options.escapeExtraCharacters = /\|/ + + let cellRenderedContentLength = 0 + node.content.forEach((childNode, offset, index) => { + cellRenderedContentLength += (childNode.text?.length || 6) + if (childNode.attrs.syntax === ' ') node.child(index).attrs.syntax = 'html' + }) + const columnWidth = state.options.columnWidths[columnIndex] + const align = node.attrs?.textAlign || 'left' + const space = columnWidth - cellRenderedContentLength + const leftPadding = Math.floor(space / 2) + const rightPadding = Math.ceil(space / 2) + + if (align === 'center') state.write(' '.repeat(leftPadding)) + if (align === 'right') state.write(' '.repeat(space)) state.renderInline(node) + if (align === 'center') state.write(' '.repeat(rightPadding)) + if (align === 'left') state.write(' '.repeat(space)) + state.options.escapeExtraCharacters = backup state.write(' |') + state.options.currentColumnIndex++ }, parseHTML() { diff --git a/src/nodes/Table/TableHeadRow.js b/src/nodes/Table/TableHeadRow.js index 677780389d6..f85846fc1da 100644 --- a/src/nodes/Table/TableHeadRow.js +++ b/src/nodes/Table/TableHeadRow.js @@ -9,8 +9,8 @@ export default TableRow.extend({ state.renderInline(node) state.ensureNewLine() state.write('|') - node.forEach(cell => { - let row = state.repeat('-', cell.textContent.length + 2) + node.forEach((cell, offset, index) => { + let row = state.repeat('-', state.options.columnWidths[index] + 2) const align = cell.attrs?.textAlign if (align === 'center' || align === 'left') row = ':' + row.slice(1) if (align === 'center' || align === 'right') row = row.slice(0, -1) + ':' diff --git a/src/nodes/Table/TableHeader.js b/src/nodes/Table/TableHeader.js index a56a7708d1f..e9064dd2129 100644 --- a/src/nodes/Table/TableHeader.js +++ b/src/nodes/Table/TableHeader.js @@ -4,9 +4,21 @@ export default TableHeader.extend({ content: 'inline*', toMarkdown(state, node) { + const headerIndex = state.options.currentHeaderIndex + const columnWidth = state.options.columnWidths[headerIndex] + const align = node.attrs?.textAlign || 'left' + const space = columnWidth - node.content.size + const leftPadding = Math.floor(space / 2) + const rightPadding = Math.ceil(space / 2) + state.write(' ') + if (align === 'center') state.write(' '.repeat(leftPadding)) + if (align === 'right') state.write(' '.repeat(space)) state.renderInline(node) + if (align === 'center') state.write(' '.repeat(rightPadding)) + if (align === 'left') state.write(' '.repeat(space)) state.write(' |') + state.options.currentHeaderIndex++ }, parseHTML() { diff --git a/src/nodes/Table/TableRow.js b/src/nodes/Table/TableRow.js index b6ce01d823b..6525f6ab74a 100644 --- a/src/nodes/Table/TableRow.js +++ b/src/nodes/Table/TableRow.js @@ -5,6 +5,7 @@ export default TableRow.extend({ toMarkdown(state, node) { state.write('|') + state.options.currentColumnIndex = 0 state.renderInline(node) state.ensureNewLine() },