From ee0df52531324495d84529c7d460e7636a08c5de Mon Sep 17 00:00:00 2001 From: zzj3720 <17165520+zzj3720@users.noreply.github.com> Date: Sat, 8 Feb 2025 04:40:11 +0000 Subject: [PATCH] feat(editor): table block supports copy and paste in HTML format (#10020) close: BS-2483 --- .../block-table/src/selection-controller.ts | 62 ++++++++++++++++++- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/blocksuite/affine/block-table/src/selection-controller.ts b/blocksuite/affine/block-table/src/selection-controller.ts index c09c26ddae..59b91ee1bc 100644 --- a/blocksuite/affine/block-table/src/selection-controller.ts +++ b/blocksuite/affine/block-table/src/selection-controller.ts @@ -116,15 +116,38 @@ export class SelectionController implements ReactiveController { this.dataManager.clearCells(deleteCells); } const text = cells.map(row => row.join('\t')).join('\n'); + + const htmlTable = ` + + ${cells + .map( + row => ` + + ${row + .map( + cell => ` + + ` + ) + .join('')} + + ` + ) + .join('')} + +
${cell}
`; + this.clipboard .writeToClipboard(items => ({ ...items, [TEXT]: text, + 'text/html': htmlTable, })) .catch(console.error); }; onCopy = () => { const selection = this.getSelected(); + console.log('selection', selection); if (!selection || selection.type !== 'area') { return false; } @@ -194,9 +217,42 @@ export class SelectionController implements ReactiveController { if (!selection || selection.type !== 'area') { return false; } - const plainText = clipboardData.getData('text/plain'); - this.doPaste(plainText, selection); - return true; + + try { + const html = clipboardData.getData('text/html'); + if (html) { + const parser = new DOMParser(); + const doc = parser.parseFromString(html, 'text/html'); + const table = doc.querySelector('table'); + if (table) { + const rows: string[][] = []; + table.querySelectorAll('tr').forEach(tr => { + const rowData: string[] = []; + tr.querySelectorAll('td,th').forEach(cell => { + rowData.push(cell.textContent?.trim() ?? ''); + }); + if (rowData.length > 0) { + rows.push(rowData); + } + }); + if (rows.length > 0) { + this.doPaste(rows.map(row => row.join('\t')).join('\n'), selection); + return true; + } + } + } + + // If no HTML format or parsing failed, try to read plain text + const plainText = clipboardData.getData('text/plain'); + if (plainText) { + this.doPaste(plainText, selection); + return true; + } + } catch (error) { + console.error('Failed to paste:', error); + } + + return false; }; onDragStart(event: MouseEvent) { const target = event.target;