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 64469fe9571..aa4d6548508 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,50 @@ 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)
+ if (node.text?.includes('|')) cellWidth += 1
+ })
+ maxWidth = Math.max(maxWidth, cellWidth)
+ })
+
+ widths.push(maxWidth)
+ })
+
+ return widths
+}
+
export default Table.extend({
content: 'tableCaption? tableHeadRow tableRow*',
@@ -178,6 +223,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 aa622a8de03..9e5139388f8 100644
--- a/src/nodes/Table/TableCell.js
+++ b/src/nodes/Table/TableCell.js
@@ -8,10 +8,30 @@ 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.text?.includes('|')) cellRenderedContentLength += 1
+ 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()
},
diff --git a/src/tests/fixtures/tables/basic/table.md b/src/tests/fixtures/tables/basic/table.md
index d26d08b4c8a..6bdd34bb7b9 100644
--- a/src/tests/fixtures/tables/basic/table.md
+++ b/src/tests/fixtures/tables/basic/table.md
@@ -1,3 +1,3 @@
-| heading | heading 2 | heading 3 |
-|:-------:|----------:|-----------|
-| center | right | left cell
with line break |
+| heading | heading 2 | heading 3 |
+|:-------:|----------:|---------------------------------|
+| center | right | left cell
with line break |
diff --git a/src/tests/markdown.spec.js b/src/tests/markdown.spec.js
index 61c6375dc51..05f288d49ab 100644
--- a/src/tests/markdown.spec.js
+++ b/src/tests/markdown.spec.js
@@ -200,17 +200,17 @@ describe('Markdown serializer from html', () => {
})
test('table', () => {
- expect(markdownThroughEditorHtml('
')).toBe('| greetings |\n|-----------|\n| hello |\n')
+ expect(markdownThroughEditorHtml('')).toBe('| greetings |\n|-----------|\n| hello |\n')
})
test('table cell escaping', () => {
// while '|' has no special meaning in commonmark is has to be escaped for GFM tables
- expect(markdownThroughEditorHtml('')).toBe('| greetings |\n|-----------|\n| hello \\| hallo |\n')
+ expect(markdownThroughEditorHtml('')).toBe('| greetings |\n|----------------|\n| hello \\| hallo |\n')
})
test('table pastes (#2708)', () => {
// while '|' has no special meaning in commonmark is has to be escaped for GFM tables
- expect(markdownFromPaste('')).toBe('| greetings |\n|-----------|\n| hello |\n')
+ expect(markdownFromPaste('')).toBe('| greetings |\n|-----------|\n| hello |\n')
})
test('front matter', () => {