From 1addd17d6401f027d81064e55c5d96bde08cf816 Mon Sep 17 00:00:00 2001 From: zzj3720 Date: Tue, 25 Feb 2025 18:52:13 +0800 Subject: [PATCH] fix(editor): table block supports parsing rich text --- .../affine/block-table/src/adapters/html.ts | 5 +- .../block-table/src/adapters/markdown.ts | 5 +- .../block-table/src/adapters/plain-text.ts | 9 ++- .../affine/block-table/src/adapters/utils.ts | 66 +++++++++---------- 4 files changed, 45 insertions(+), 40 deletions(-) diff --git a/blocksuite/affine/block-table/src/adapters/html.ts b/blocksuite/affine/block-table/src/adapters/html.ts index 4a70f98ecf..3d28af2928 100644 --- a/blocksuite/affine/block-table/src/adapters/html.ts +++ b/blocksuite/affine/block-table/src/adapters/html.ts @@ -30,7 +30,10 @@ export const tableBlockHtmlAdapterMatcher: BlockHtmlAdapterMatcher = { } const { walkerContext } = context; if (o.node.tagName === 'table') { - const tableProps = parseTableFromHtml(o.node); + const astToDelta = context.deltaConverter.astToDelta.bind( + context.deltaConverter + ); + const tableProps = parseTableFromHtml(o.node, astToDelta); walkerContext.openNode( { type: 'block', diff --git a/blocksuite/affine/block-table/src/adapters/markdown.ts b/blocksuite/affine/block-table/src/adapters/markdown.ts index c26136cfc9..7577084aa1 100644 --- a/blocksuite/affine/block-table/src/adapters/markdown.ts +++ b/blocksuite/affine/block-table/src/adapters/markdown.ts @@ -25,12 +25,15 @@ export const tableBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher = { enter: (o, context) => { const { walkerContext } = context; if (o.node.type === 'table') { + const astToDelta = context.deltaConverter.astToDelta.bind( + context.deltaConverter + ); walkerContext.openNode( { type: 'block', id: nanoid(), flavour: TableModelFlavour, - props: parseTableFromMarkdown(o.node), + props: parseTableFromMarkdown(o.node, astToDelta), children: [], }, 'children' diff --git a/blocksuite/affine/block-table/src/adapters/plain-text.ts b/blocksuite/affine/block-table/src/adapters/plain-text.ts index 47f005d4dd..dbb7bbdd2d 100644 --- a/blocksuite/affine/block-table/src/adapters/plain-text.ts +++ b/blocksuite/affine/block-table/src/adapters/plain-text.ts @@ -7,6 +7,7 @@ import { BlockPlainTextAdapterExtension, type BlockPlainTextAdapterMatcher, } from '@blocksuite/affine-shared/adapters'; +import type { DeltaInsert } from '@blocksuite/inline'; import { nanoid } from '@blocksuite/store'; import { createTableProps, formatTable, processTable } from './utils.js'; @@ -21,10 +22,14 @@ export const tableBlockPlainTextAdapterMatcher: BlockPlainTextAdapterMatcher = { const text = o.node.content; const rowTexts = text.split('\n'); if (rowTexts.length <= 1) return; - const rowTextLists: string[][] = []; + const rowTextLists: DeltaInsert[][][] = []; let columnCount: number | null = null; for (const row of rowTexts) { - const cells = row.split('\t'); + const cells = row.split('\t').map(text => [ + { + insert: text, + }, + ]); if (cells.length <= 1) return; if (columnCount == null) { columnCount = cells.length; diff --git a/blocksuite/affine/block-table/src/adapters/utils.ts b/blocksuite/affine/block-table/src/adapters/utils.ts index f0beb110e3..179e073ed3 100644 --- a/blocksuite/affine/block-table/src/adapters/utils.ts +++ b/blocksuite/affine/block-table/src/adapters/utils.ts @@ -5,14 +5,23 @@ import type { TableRow, } from '@blocksuite/affine-model'; import { - AdapterTextUtils, - HastUtils, + type HtmlAST, + type MarkdownAST, } from '@blocksuite/affine-shared/adapters'; +import { HastUtils } from '@blocksuite/affine-shared/adapters'; import { generateFractionalIndexingKeyBetween } from '@blocksuite/affine-shared/utils'; import type { DeltaInsert } from '@blocksuite/inline'; import { nanoid } from '@blocksuite/store'; -import type { Element, ElementContent } from 'hast'; -import type { PhrasingContent, Table as MarkdownTable, TableCell } from 'mdast'; +import type { Element } from 'hast'; +import type { Table as MarkdownTable } from 'mdast'; + +type RichTextType = DeltaInsert[]; +const createRichText = (text: RichTextType) => { + return { + '$blocksuite:internal:text$': true, + delta: text, + }; +}; function calculateColumnWidths(rows: string[][]): number[] { return ( rows[0]?.map((_, colIndex) => @@ -92,15 +101,6 @@ export const processTable = ( }); return table; }; -const getTextFromElement = (element: ElementContent): string => { - if (element.type === 'text') { - return element.value.trim(); - } - if (element.type === 'element') { - return element.children.map(child => getTextFromElement(child)).join(''); - } - return ''; -}; const getAllTag = (node: Element | undefined, tagName: string): Element[] => { if (!node) { @@ -120,7 +120,7 @@ const getAllTag = (node: Element | undefined, tagName: string): Element[] => { return []; }; -export const createTableProps = (rowTextLists: string[][]) => { +export const createTableProps = (deltasLists: RichTextType[][]) => { const createIdAndOrder = (count: number) => { const result: { id: string; order: string }[] = Array.from({ length: count, @@ -135,8 +135,8 @@ export const createTableProps = (rowTextLists: string[][]) => { } return result; }; - const columnCount = Math.max(...rowTextLists.map(row => row.length)); - const rowCount = rowTextLists.length; + const columnCount = Math.max(...deltasLists.map(row => row.length)); + const rowCount = deltasLists.length; const columns: TableColumn[] = createIdAndOrder(columnCount).map(v => ({ columnId: v.id, @@ -156,9 +156,9 @@ export const createTableProps = (rowTextLists: string[][]) => { continue; } const cellId = `${row.rowId}:${column.columnId}`; - const text = rowTextLists[i]?.[j]; + const text = deltasLists[i]?.[j]; cells[cellId] = { - text: AdapterTextUtils.createText(text ?? ''), + text: createRichText(text ?? []), }; } } @@ -172,7 +172,8 @@ export const createTableProps = (rowTextLists: string[][]) => { }; export const parseTableFromHtml = ( - element: Element + element: Element, + astToDelta: (ast: HtmlAST) => RichTextType ): TableBlockPropsSerialized => { const headerRows = getAllTag(element, 'thead').flatMap(node => getAllTag(node, 'tr').map(tr => getAllTag(tr, 'th')) @@ -184,33 +185,26 @@ export const parseTableFromHtml = ( getAllTag(node, 'tr').map(tr => getAllTag(tr, 'td')) ); const allRows = [...headerRows, ...bodyRows, ...footerRows]; - const rowTextLists: string[][] = []; + const rowTextLists: RichTextType[][] = []; allRows.forEach(cells => { - const row: string[] = []; + const row: RichTextType[] = []; cells.forEach(cell => { - row.push(getTextFromElement(cell)); + row.push(astToDelta(cell)); }); rowTextLists.push(row); }); return createTableProps(rowTextLists); }; -const getTextFromTableCell = (node: TableCell) => { - const getTextFromPhrasingContent = (node: PhrasingContent) => { - if (node.type === 'text') { - return node.value; - } - return ''; - }; - return node.children.map(child => getTextFromPhrasingContent(child)).join(''); -}; - -export const parseTableFromMarkdown = (node: MarkdownTable) => { - const rowTextLists: string[][] = []; +export const parseTableFromMarkdown = ( + node: MarkdownTable, + astToDelta: (ast: MarkdownAST) => RichTextType +) => { + const rowTextLists: RichTextType[][] = []; node.children.forEach(row => { - const rowText: string[] = []; + const rowText: RichTextType[] = []; row.children.forEach(cell => { - rowText.push(getTextFromTableCell(cell)); + rowText.push(astToDelta(cell)); }); rowTextLists.push(rowText); });