chore: add table markdown transformer to super

This commit is contained in:
Aman Harwara
2023-10-18 22:11:30 +05:30
parent 2e3ac3ce57
commit 68e5dbc523

View File

@@ -5,14 +5,27 @@ import {
TEXT_FORMAT_TRANSFORMERS,
TEXT_MATCH_TRANSFORMERS,
TextMatchTransformer,
$convertToMarkdownString,
$convertFromMarkdownString,
} from '@lexical/markdown'
import {
$createTableCellNode,
$createTableNode,
$createTableRowNode,
$isTableCellNode,
$isTableNode,
$isTableRowNode,
TableCellHeaderStates,
TableCellNode,
TableNode,
TableRowNode,
} from '@lexical/table'
import {
HorizontalRuleNode,
$createHorizontalRuleNode,
$isHorizontalRuleNode,
} from '@lexical/react/LexicalHorizontalRuleNode'
import { LexicalNode } from 'lexical'
import { $isParagraphNode, $isTextNode, LexicalNode } from 'lexical'
import {
$createRemoteImageNode,
$isRemoteImageNode,
@@ -59,7 +72,156 @@ const IMAGE: TextMatchTransformer = {
type: 'text-match',
}
// Table transformer, taken from Lexical Playground
const TABLE_ROW_REG_EXP = /^(?:\|)(.+)(?:\|)\s?$/
const TABLE_ROW_DIVIDER_REG_EXP = /^(\| ?:?-*:? ?)+\|\s?$/
function getTableColumnsSize(table: TableNode) {
const row = table.getFirstChild()
return $isTableRowNode(row) ? row.getChildrenSize() : 0
}
function createTableCell(textContent: string): TableCellNode {
textContent = textContent.replace(/\\n/g, '\n')
const cell = $createTableCellNode(TableCellHeaderStates.NO_STATUS)
$convertFromMarkdownString(textContent, MarkdownTransformers, cell)
return cell
}
function mapToTableCells(textContent: string): Array<TableCellNode> | null {
const match = textContent.match(TABLE_ROW_REG_EXP)
if (!match || !match[1]) {
return null
}
return match[1].split('|').map((text) => createTableCell(text))
}
export const TABLE: ElementTransformer = {
dependencies: [TableNode, TableRowNode, TableCellNode],
export: (node: LexicalNode) => {
if (!$isTableNode(node)) {
return null
}
const output: string[] = []
for (const row of node.getChildren()) {
const rowOutput = []
if (!$isTableRowNode(row)) {
continue
}
let isHeaderRow = false
for (const cell of row.getChildren()) {
// It's TableCellNode so it's just to make flow happy
if ($isTableCellNode(cell)) {
rowOutput.push($convertToMarkdownString(MarkdownTransformers, cell).replace(/\n/g, '\\n'))
if (cell.__headerState === TableCellHeaderStates.ROW) {
isHeaderRow = true
}
}
}
output.push(`| ${rowOutput.join(' | ')} |`)
if (isHeaderRow) {
output.push(`| ${rowOutput.map((_) => '---').join(' | ')} |`)
}
}
return output.join('\n')
},
regExp: TABLE_ROW_REG_EXP,
replace: (parentNode, _1, match) => {
// Header row
if (TABLE_ROW_DIVIDER_REG_EXP.test(match[0])) {
const table = parentNode.getPreviousSibling()
if (!table || !$isTableNode(table)) {
return
}
const rows = table.getChildren()
const lastRow = rows[rows.length - 1]
if (!lastRow || !$isTableRowNode(lastRow)) {
return
}
// Add header state to row cells
lastRow.getChildren().forEach((cell) => {
if (!$isTableCellNode(cell)) {
return
}
cell.toggleHeaderStyle(TableCellHeaderStates.ROW)
})
// Remove line
parentNode.remove()
return
}
const matchCells = mapToTableCells(match[0])
if (matchCells == null) {
return
}
const rows = [matchCells]
let sibling = parentNode.getPreviousSibling()
let maxCells = matchCells.length
while (sibling) {
if (!$isParagraphNode(sibling)) {
break
}
if (sibling.getChildrenSize() !== 1) {
break
}
const firstChild = sibling.getFirstChild()
if (!$isTextNode(firstChild)) {
break
}
const cells = mapToTableCells(firstChild.getTextContent())
if (cells == null) {
break
}
maxCells = Math.max(maxCells, cells.length)
rows.unshift(cells)
const previousSibling = sibling.getPreviousSibling()
sibling.remove()
sibling = previousSibling
}
const table = $createTableNode()
for (const cells of rows) {
const tableRow = $createTableRowNode()
table.append(tableRow)
for (let i = 0; i < maxCells; i++) {
tableRow.append(i < cells.length ? cells[i] : createTableCell(''))
}
}
const previousSibling = parentNode.getPreviousSibling()
if ($isTableNode(previousSibling) && getTableColumnsSize(previousSibling) === maxCells) {
previousSibling.append(...table.getChildren())
parentNode.remove()
} else {
parentNode.replace(table)
}
table.selectEnd()
},
type: 'element',
}
export const MarkdownTransformers = [
TABLE,
CHECK_LIST,
IMAGE,
...ELEMENT_TRANSFORMERS,