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 => `
+ | ${cell} |
+ `
+ )
+ .join('')}
+
+ `
+ )
+ .join('')}
+
+
`;
+
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;