Skip to content

Commit 7f2dcf8

Browse files
boris-wCopilot
andauthored
feat: add normal html parsing support for copy and paste (#1675)
* feat: add normal html parsing support for copy and paste * Update apps/nextjs-app/src/features/app/utils/clipboard.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Boris <boris2code@outlook.com> * Update packages/core/src/utils/clipboard.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Boris <boris2code@outlook.com> --------- Signed-off-by: Boris <boris2code@outlook.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 5bdc832 commit 7f2dcf8

5 files changed

Lines changed: 67 additions & 4 deletions

File tree

apps/nestjs-backend/test/selection.e2e-spec.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ describe('OpenAPI SelectionController (e2e)', () => {
5454
});
5555

5656
afterEach(async () => {
57-
const result = await permanentDeleteTable(baseId, table.id);
58-
console.log('clear table: ', result);
57+
await permanentDeleteTable(baseId, table.id);
5958
});
6059

6160
afterAll(async () => {
@@ -726,4 +725,31 @@ describe('OpenAPI SelectionController (e2e)', () => {
726725
).toEqual([user2Info.id, user1Info.id]);
727726
});
728727
});
728+
729+
it('paste content end with newline', async () => {
730+
await apiPaste(table.id, {
731+
viewId: table.defaultViewId!,
732+
content: 'test\ntest2',
733+
ranges: [
734+
[0, 0],
735+
[0, 0],
736+
],
737+
});
738+
await apiPaste(table.id, {
739+
viewId: table.defaultViewId!,
740+
content: 'test3\n',
741+
ranges: [
742+
[0, 0],
743+
[0, 0],
744+
],
745+
});
746+
const records = await getRecords(table.id, {
747+
viewId: table.defaultViewId!,
748+
});
749+
expect(records.data.records.map((r) => r.fields[table.fields[0].name])).toEqual([
750+
'test3',
751+
'test2',
752+
undefined,
753+
]);
754+
});
729755
});

apps/nextjs-app/src/features/app/blocks/view/grid/utils/copyAndPaste.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
extractHtmlHeader,
1010
serializerHtml,
1111
isTeableHTML,
12+
parseNormalHtml,
1213
} from '@/features/app/utils/clipboard';
1314
import { uploadFiles } from '@/features/app/utils/uploadFile';
1415
import { getSelectionCell } from './selection';
@@ -112,14 +113,18 @@ export const textPasteHandler = async (
112113
? e.clipboardData.getData(ClipboardTypes.text)
113114
: '';
114115

115-
const cellValues = extractTableContent(html);
116+
const cellValues = hasHtml
117+
? isTeableHTML(html)
118+
? extractTableContent(html)
119+
: parseNormalHtml(html)
120+
: [];
116121

117122
if (header.error) {
118123
throw new Error(header.error);
119124
}
120125

121126
await requestPaste(
122-
isTeableHTML(html) ? cellValues : text,
127+
hasHtml ? cellValues : text,
123128
rangeTypes[selection.type],
124129
selection.serialize(),
125130
header.result

apps/nextjs-app/src/features/app/utils/clipboard.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,14 @@ export const extractTableContent = (html: string) => {
112112
});
113113
return content;
114114
};
115+
116+
export const parseNormalHtml = (html: string) => {
117+
const parser = new DOMParser();
118+
const doc = parser.parseFromString(html, 'text/html');
119+
const table = doc.querySelector('table');
120+
const rows = Array.from(table?.rows || []);
121+
return rows.map((row) => {
122+
const cells = Array.from(row.cells);
123+
return cells.map((cell) => cell.textContent || '');
124+
});
125+
};

packages/core/src/utils/clipboard.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,20 @@ describe('clipboard', () => {
7979
const data = parseClipboardText('text1"\r\ntext2');
8080
expect(data).toEqual([['text1"'], ['text2']]);
8181
});
82+
83+
it('content start or end with newline', () => {
84+
const data = parseClipboardText('text1\n');
85+
expect(data).toEqual([['text1']]);
86+
87+
const data2 = parseClipboardText('\ntext1');
88+
expect(data2).toEqual([['text1']]);
89+
90+
const data3 = parseClipboardText('tex"t1\n');
91+
expect(data3).toEqual([['tex"t1']]);
92+
93+
const data4 = parseClipboardText('\ntex"t1');
94+
expect(data4).toEqual([['tex"t1']]);
95+
});
8296
});
8397

8498
describe('stringify', () => {

packages/core/src/utils/clipboard.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ const windowsNewline = '\r\n';
55
// eslint-disable-next-line sonarjs/cognitive-complexity
66
export const parseClipboardText = (content: string) => {
77
const _newline = content.includes(windowsNewline) ? windowsNewline : newline;
8+
// remove the last newline or windows newline
9+
if (content.endsWith(_newline)) {
10+
content = content.slice(0, -1 * _newline.length);
11+
}
12+
if (content.startsWith(_newline)) {
13+
content = content.slice(_newline.length);
14+
}
815
if (!content.includes('"')) {
916
return content.split(_newline).map((row) => row.split(delimiter));
1017
}

0 commit comments

Comments
 (0)