quill-table-up 2.2.4 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +9 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/e2e/table-selection.test.ts +97 -9
- package/src/__tests__/unit/table-clipboard.test.ts +181 -0
- package/src/__tests__/unit/table-hack.test.ts +14 -18
- package/src/__tests__/unit/table-redo-undo.test.ts +51 -0
- package/src/formats/container-format.ts +0 -9
- package/src/formats/table-cell-inner-format.ts +24 -0
- package/src/formats/table-col-format.ts +37 -37
- package/src/modules/table-clipboard.ts +54 -14
- package/src/modules/table-resize/table-resize-common.ts +29 -6
- package/src/modules/table-selection.ts +1 -1
- package/src/table-up.ts +5 -8
package/package.json
CHANGED
|
@@ -115,7 +115,7 @@ test('test TableSelection set indent format', async ({ page }) => {
|
|
|
115
115
|
expect(await page.locator('#editor1 .ql-table-cell-inner p.ql-indent-1').count()).toBe(4);
|
|
116
116
|
});
|
|
117
117
|
|
|
118
|
-
test('test TableSelection set
|
|
118
|
+
test('test TableSelection set format header', async ({ page }) => {
|
|
119
119
|
await createTableBySelect(page, 'container1', 2, 2);
|
|
120
120
|
const cell = page.locator('#editor1 .ql-editor .ql-table td').nth(0);
|
|
121
121
|
const cellBounding = (await cell.boundingBox())!;
|
|
@@ -125,13 +125,38 @@ test('test TableSelection set multiple format', async ({ page }) => {
|
|
|
125
125
|
await page.mouse.move(cellBounding.x + cellBounding.width * 2 - 10, cellBounding.y + cellBounding.height * 2 - 10);
|
|
126
126
|
await page.mouse.up();
|
|
127
127
|
|
|
128
|
-
await page.locator('#
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
128
|
+
await page.locator('#container1 .ql-toolbar .ql-header').nth(0).click();
|
|
129
|
+
await page.locator('#container1 .ql-toolbar .ql-header .ql-picker-options .ql-picker-item').nth(0).click();
|
|
130
|
+
|
|
131
|
+
expect(await page.locator('#editor1 .ql-table-cell-inner h1').count()).toBe(4);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
extendTest('test TableSelection set multiple format', async ({ page, editorPage }) => {
|
|
135
|
+
editorPage.index = 0;
|
|
136
|
+
await editorPage.setContents([
|
|
137
|
+
{ insert: '\n' },
|
|
138
|
+
{ insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100 } } },
|
|
139
|
+
{ insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100 } } },
|
|
140
|
+
{ insert: '1' },
|
|
141
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
142
|
+
{ insert: '2' },
|
|
143
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
144
|
+
{ insert: '3' },
|
|
145
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
146
|
+
{ insert: '4' },
|
|
147
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
148
|
+
{ insert: '\n' },
|
|
149
|
+
]);
|
|
150
|
+
await page.waitForTimeout(1000);
|
|
151
|
+
const cell = page.locator('#editor1 .ql-editor .ql-table td').nth(0);
|
|
152
|
+
const cellBounding = (await cell.boundingBox())!;
|
|
153
|
+
expect(cellBounding).not.toBeNull();
|
|
154
|
+
await cell.click();
|
|
155
|
+
await page.mouse.down();
|
|
156
|
+
await page.mouse.move(cellBounding.x + cellBounding.width * 1.5, cellBounding.y + cellBounding.height * 1.5);
|
|
157
|
+
await page.mouse.up();
|
|
158
|
+
await editorPage.blur();
|
|
159
|
+
await editorPage.focus();
|
|
135
160
|
|
|
136
161
|
await page.locator('.ql-toolbar .ql-bold').nth(0).click();
|
|
137
162
|
await page.locator('.ql-toolbar .ql-italic').nth(0).click();
|
|
@@ -145,7 +170,6 @@ test('test TableSelection set multiple format', async ({ page }) => {
|
|
|
145
170
|
expect(await page.locator('#editor1 .ql-table-cell-inner em').count()).toBe(4);
|
|
146
171
|
expect(await page.locator('#editor1 .ql-table-cell-inner s').count()).toBe(4);
|
|
147
172
|
expect(await page.locator('#editor1 .ql-table-cell-inner u').count()).toBe(4);
|
|
148
|
-
|
|
149
173
|
await strongEl.all().then(async (elements) => {
|
|
150
174
|
for (const element of elements) {
|
|
151
175
|
await expect(element).toHaveCSS('background-color', 'rgb(230, 0, 0)');
|
|
@@ -175,6 +199,53 @@ test('test TableSelection clean format', async ({ page }) => {
|
|
|
175
199
|
expect(await cleanEl.count()).toBe(0);
|
|
176
200
|
});
|
|
177
201
|
|
|
202
|
+
extendTest('test TableSelection set format in part of cell text', async ({ page, editorPage }) => {
|
|
203
|
+
editorPage.index = 0;
|
|
204
|
+
await editorPage.setContents([
|
|
205
|
+
{ insert: '\n' },
|
|
206
|
+
{ insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100 } } },
|
|
207
|
+
{ insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100 } } },
|
|
208
|
+
{ insert: '1' },
|
|
209
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
210
|
+
{ insert: '1' },
|
|
211
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
212
|
+
{ insert: '1' },
|
|
213
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
214
|
+
{ insert: '2' },
|
|
215
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
216
|
+
{ insert: '2' },
|
|
217
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
218
|
+
{ insert: '2' },
|
|
219
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
220
|
+
{ insert: '3' },
|
|
221
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
222
|
+
{ insert: '4' },
|
|
223
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
224
|
+
{ insert: '\n' },
|
|
225
|
+
]);
|
|
226
|
+
await page.waitForTimeout(1000);
|
|
227
|
+
await page.locator('#editor1 .ql-editor .ql-table td').nth(0).click();
|
|
228
|
+
|
|
229
|
+
await editorPage.setSelection(4, 0);
|
|
230
|
+
await page.locator('#container1 .ql-toolbar .ql-header').nth(0).click();
|
|
231
|
+
await page.locator('#container1 .ql-toolbar .ql-header .ql-picker-options .ql-picker-item').nth(0).click();
|
|
232
|
+
const selectionAfterHeader = await editorPage.getSelection();
|
|
233
|
+
expect(selectionAfterHeader).toEqual({ index: 4, length: 0 });
|
|
234
|
+
|
|
235
|
+
await editorPage.setSelection(6, 0);
|
|
236
|
+
await page.locator('.ql-toolbar .ql-list[value="bullet"]').nth(0).click();
|
|
237
|
+
const selectionAfterList = await editorPage.getSelection();
|
|
238
|
+
expect(selectionAfterList).toEqual({ index: 6, length: 0 });
|
|
239
|
+
|
|
240
|
+
await page.locator('#editor1 .ql-editor .ql-table td').nth(1).click();
|
|
241
|
+
await editorPage.setSelection(9, 3);
|
|
242
|
+
await page.locator('.ql-toolbar .ql-bold').nth(0).click();
|
|
243
|
+
await page.locator('.ql-toolbar .ql-italic').nth(0).click();
|
|
244
|
+
|
|
245
|
+
expect(await page.locator('#editor1 .ql-table-cell-inner strong').count()).toBe(2);
|
|
246
|
+
expect(await page.locator('#editor1 .ql-table-cell-inner em').count()).toBe(2);
|
|
247
|
+
});
|
|
248
|
+
|
|
178
249
|
extendTest('test TableSelection should update when text change', async ({ page, editorPage }) => {
|
|
179
250
|
editorPage.index = 0;
|
|
180
251
|
await createTableBySelect(page, 'container1', 3, 3);
|
|
@@ -576,3 +647,20 @@ extendTest('toolbox bounds should same with quill.root', async ({ page, editorPa
|
|
|
576
647
|
expect(quillRootBoundingAfter).not.toBeNull();
|
|
577
648
|
expect(toolboxBoundingAfter).toEqual(quillRootBoundingAfter);
|
|
578
649
|
});
|
|
650
|
+
|
|
651
|
+
extendTest('TableSelection should not update when input composition', async ({ page, editorPage }) => {
|
|
652
|
+
editorPage.index = 0;
|
|
653
|
+
await createTableBySelect(page, 'container1', 3, 3);
|
|
654
|
+
|
|
655
|
+
await page.locator('#editor1 .ql-table .ql-table-cell').nth(0).click();
|
|
656
|
+
const selectionLine = page.locator('#container1 .table-up-selection .table-up-selection__line');
|
|
657
|
+
await expect(selectionLine).toBeVisible();
|
|
658
|
+
const bounding = (await selectionLine.boundingBox())!;
|
|
659
|
+
expect(bounding).not.toBeNull();
|
|
660
|
+
|
|
661
|
+
await page.dispatchEvent('#editor1 .ql-editor .ql-table-cell', 'compositionstart');
|
|
662
|
+
await page.type('#editor1 .ql-editor .ql-table-cell', 'zhongwen');
|
|
663
|
+
|
|
664
|
+
const composingBounding = (await selectionLine.boundingBox())!;
|
|
665
|
+
expect(composingBounding).toEqual(bounding);
|
|
666
|
+
});
|
|
@@ -923,6 +923,187 @@ describe('clipboard cell structure', () => {
|
|
|
923
923
|
quill.getContents(),
|
|
924
924
|
);
|
|
925
925
|
});
|
|
926
|
+
|
|
927
|
+
it('clipboard convert rowspan and struct have empty tr', async () => {
|
|
928
|
+
const quill = createQuillWithTableModule(`<p><br></p>`);
|
|
929
|
+
quill.setContents(
|
|
930
|
+
quill.clipboard.convert({
|
|
931
|
+
html: `
|
|
932
|
+
<body link=blue vlink=purple>
|
|
933
|
+
|
|
934
|
+
<table border=0 cellpadding=0 cellspacing=0 width=126 style='border-collapse:
|
|
935
|
+
collapse;width:94pt'>
|
|
936
|
+
<!--StartFragment-->
|
|
937
|
+
<col width=63 span=2 style='width:47pt'>
|
|
938
|
+
<tr height=19 style='height:14.4pt'>
|
|
939
|
+
<td rowspan=6 height=114 class=xl65 width=63 style='border-bottom:.5pt solid black;
|
|
940
|
+
height:86.4pt;width:47pt'>合并1</td>
|
|
941
|
+
<td rowspan=2 class=xl65 width=63 style='border-bottom:.5pt solid black;
|
|
942
|
+
width:47pt'>合并2</td>
|
|
943
|
+
</tr>
|
|
944
|
+
<tr height=19 style='height:14.4pt'>
|
|
945
|
+
</tr>
|
|
946
|
+
<tr height=19 style='height:14.4pt'>
|
|
947
|
+
<td rowspan=2 height=38 class=xl65 style='border-bottom:.5pt solid black;
|
|
948
|
+
height:28.8pt;border-top:none'>合并3</td>
|
|
949
|
+
</tr>
|
|
950
|
+
<tr height=19 style='height:14.4pt'>
|
|
951
|
+
</tr>
|
|
952
|
+
<tr height=19 style='height:14.4pt'>
|
|
953
|
+
<td rowspan=2 height=38 class=xl65 style='border-bottom:.5pt solid black;
|
|
954
|
+
height:28.8pt;border-top:none'>合并4</td>
|
|
955
|
+
</tr>
|
|
956
|
+
<tr height=19 style='height:14.4pt'>
|
|
957
|
+
</tr>
|
|
958
|
+
<!--EndFragment-->
|
|
959
|
+
</table>
|
|
960
|
+
|
|
961
|
+
</body>
|
|
962
|
+
`,
|
|
963
|
+
}),
|
|
964
|
+
);
|
|
965
|
+
await vi.runAllTimersAsync();
|
|
966
|
+
|
|
967
|
+
expect(quill.root).toEqualHTML(
|
|
968
|
+
`
|
|
969
|
+
<p><br></p>
|
|
970
|
+
<div>
|
|
971
|
+
<table cellpadding="0" cellspacing="0">
|
|
972
|
+
${createTaleColHTML(2, { full: false, width: 63 })}
|
|
973
|
+
<tbody>
|
|
974
|
+
<tr>
|
|
975
|
+
<td rowspan="3" colspan="1">
|
|
976
|
+
<div><p>合并1</p></div>
|
|
977
|
+
</td>
|
|
978
|
+
<td rowspan="1" colspan="1">
|
|
979
|
+
<div><p>合并2</p></div>
|
|
980
|
+
</td>
|
|
981
|
+
</tr>
|
|
982
|
+
<tr>
|
|
983
|
+
<td rowspan="1" colspan="1">
|
|
984
|
+
<div><p>合并3</p></div>
|
|
985
|
+
</td>
|
|
986
|
+
</tr>
|
|
987
|
+
<tr>
|
|
988
|
+
<td rowspan="1" colspan="1">
|
|
989
|
+
<div><p>合并4</p></div>
|
|
990
|
+
</td>
|
|
991
|
+
</tr>
|
|
992
|
+
</tbody>
|
|
993
|
+
</table>
|
|
994
|
+
</div>
|
|
995
|
+
<p><br></p>
|
|
996
|
+
`,
|
|
997
|
+
{ ignoreAttrs: ['class', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'data-style', 'style', 'contenteditable'] },
|
|
998
|
+
);
|
|
999
|
+
expectDelta(
|
|
1000
|
+
new Delta([
|
|
1001
|
+
{ insert: '\n' },
|
|
1002
|
+
{ insert: { 'table-up-col': { full: false, width: 63 } } },
|
|
1003
|
+
{ insert: { 'table-up-col': { full: false, width: 63 } } },
|
|
1004
|
+
{ insert: '合并1' },
|
|
1005
|
+
{ attributes: { 'table-up-cell-inner': { rowspan: 3, colspan: 1 } }, insert: '\n' },
|
|
1006
|
+
{ insert: '合并2' },
|
|
1007
|
+
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
1008
|
+
{ insert: '合并3' },
|
|
1009
|
+
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
1010
|
+
{ insert: '合并4' },
|
|
1011
|
+
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
1012
|
+
{ insert: '\n' },
|
|
1013
|
+
]),
|
|
1014
|
+
quill.getContents(),
|
|
1015
|
+
);
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
it('clipboard convert col with span attribute', async () => {
|
|
1019
|
+
const quill = createQuillWithTableModule(`<p><br></p>`);
|
|
1020
|
+
quill.setContents(
|
|
1021
|
+
quill.clipboard.convert({
|
|
1022
|
+
html: `
|
|
1023
|
+
<body link=blue vlink=purple>
|
|
1024
|
+
|
|
1025
|
+
<table border=0 cellpadding=0 cellspacing=0 width=252 style='border-collapse:
|
|
1026
|
+
collapse;width:188pt'>
|
|
1027
|
+
<!--StartFragment-->
|
|
1028
|
+
<col width=63 span=4 style='width:47pt'>
|
|
1029
|
+
<tr height=19 style='height:14.4pt'>
|
|
1030
|
+
<td colspan=2 rowspan=6 height=114 class=xl65 width=126 style='height:86.4pt;
|
|
1031
|
+
width:94pt'>1</td>
|
|
1032
|
+
<td colspan=2 rowspan=2 class=xl65 width=126 style='width:94pt'>2</td>
|
|
1033
|
+
</tr>
|
|
1034
|
+
<tr height=19 style='height:14.4pt'>
|
|
1035
|
+
</tr>
|
|
1036
|
+
<tr height=19 style='height:14.4pt'>
|
|
1037
|
+
<td colspan=2 rowspan=2 height=38 class=xl65 style='height:28.8pt'>3</td>
|
|
1038
|
+
</tr>
|
|
1039
|
+
<tr height=19 style='height:14.4pt'>
|
|
1040
|
+
</tr>
|
|
1041
|
+
<tr height=19 style='height:14.4pt'>
|
|
1042
|
+
<td colspan=2 rowspan=2 height=38 class=xl65 style='height:28.8pt'>4</td>
|
|
1043
|
+
</tr>
|
|
1044
|
+
<tr height=19 style='height:14.4pt'>
|
|
1045
|
+
</tr>
|
|
1046
|
+
<!--EndFragment-->
|
|
1047
|
+
</table>
|
|
1048
|
+
|
|
1049
|
+
</body>
|
|
1050
|
+
`,
|
|
1051
|
+
}),
|
|
1052
|
+
);
|
|
1053
|
+
await vi.runAllTimersAsync();
|
|
1054
|
+
|
|
1055
|
+
expect(quill.root).toEqualHTML(
|
|
1056
|
+
`
|
|
1057
|
+
<p><br></p>
|
|
1058
|
+
<div>
|
|
1059
|
+
<table cellpadding="0" cellspacing="0">
|
|
1060
|
+
${createTaleColHTML(4, { full: false, width: 63 })}
|
|
1061
|
+
<tbody>
|
|
1062
|
+
<tr>
|
|
1063
|
+
<td rowspan="3" colspan="2">
|
|
1064
|
+
<div><p>1</p></div>
|
|
1065
|
+
</td>
|
|
1066
|
+
<td rowspan="1" colspan="2">
|
|
1067
|
+
<div><p>2</p></div>
|
|
1068
|
+
</td>
|
|
1069
|
+
</tr>
|
|
1070
|
+
<tr>
|
|
1071
|
+
<td rowspan="1" colspan="2">
|
|
1072
|
+
<div><p>3</p></div>
|
|
1073
|
+
</td>
|
|
1074
|
+
</tr>
|
|
1075
|
+
<tr>
|
|
1076
|
+
<td rowspan="1" colspan="2">
|
|
1077
|
+
<div><p>4</p></div>
|
|
1078
|
+
</td>
|
|
1079
|
+
</tr>
|
|
1080
|
+
</tbody>
|
|
1081
|
+
</table>
|
|
1082
|
+
</div>
|
|
1083
|
+
<p><br></p>
|
|
1084
|
+
`,
|
|
1085
|
+
{ ignoreAttrs: ['class', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'data-style', 'style', 'contenteditable'] },
|
|
1086
|
+
);
|
|
1087
|
+
expectDelta(
|
|
1088
|
+
new Delta([
|
|
1089
|
+
{ insert: '\n' },
|
|
1090
|
+
{ insert: { 'table-up-col': { full: false, width: 63 } } },
|
|
1091
|
+
{ insert: { 'table-up-col': { full: false, width: 63 } } },
|
|
1092
|
+
{ insert: { 'table-up-col': { full: false, width: 63 } } },
|
|
1093
|
+
{ insert: { 'table-up-col': { full: false, width: 63 } } },
|
|
1094
|
+
{ insert: '1' },
|
|
1095
|
+
{ attributes: { 'table-up-cell-inner': { rowspan: 3, colspan: 2 } }, insert: '\n' },
|
|
1096
|
+
{ insert: '2' },
|
|
1097
|
+
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 2 } }, insert: '\n' },
|
|
1098
|
+
{ insert: '3' },
|
|
1099
|
+
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 2 } }, insert: '\n' },
|
|
1100
|
+
{ insert: '4' },
|
|
1101
|
+
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 2 } }, insert: '\n' },
|
|
1102
|
+
{ insert: '\n' },
|
|
1103
|
+
]),
|
|
1104
|
+
quill.getContents(),
|
|
1105
|
+
);
|
|
1106
|
+
});
|
|
926
1107
|
});
|
|
927
1108
|
|
|
928
1109
|
describe('clipboard content format', () => {
|
|
@@ -320,7 +320,7 @@ describe('hack format cell', () => {
|
|
|
320
320
|
expect(quill.getSelection()).toEqual({ index: 18, length: 0 });
|
|
321
321
|
});
|
|
322
322
|
|
|
323
|
-
it('
|
|
323
|
+
it('selection not in cell and selectedTds not empty should format all text in cell', async () => {
|
|
324
324
|
const quill = createQuillWithTableModule('<p></p>', { selection: TableSelection });
|
|
325
325
|
quill.setContents(createTableDeltaOps(2, 2, { full: false }));
|
|
326
326
|
quill.updateContents(
|
|
@@ -333,14 +333,15 @@ describe('hack format cell', () => {
|
|
|
333
333
|
.insert('123456789'),
|
|
334
334
|
);
|
|
335
335
|
|
|
336
|
+
quill.setSelection({ index: 3, length: 0 });
|
|
337
|
+
quill.blur();
|
|
338
|
+
quill.focus();
|
|
336
339
|
const tableUp = quill.getModule(TableUp.moduleName) as TableUp;
|
|
337
340
|
const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
|
|
338
341
|
tableUp.tableSelection!.selectedTds = [tds[0], tds[2]];
|
|
339
|
-
quill.setSelection(18, 0, Quill.sources.SILENT);
|
|
340
342
|
quill.format('bold', true);
|
|
341
343
|
// simulate `getBoundingClientRect` will effect selectedTd computed position. need manual set
|
|
342
344
|
tableUp.tableSelection!.selectedTds = [tds[0], tds[2]];
|
|
343
|
-
quill.setSelection(18, 0, Quill.sources.SILENT);
|
|
344
345
|
quill.format('list', 'bullet');
|
|
345
346
|
expectDelta(
|
|
346
347
|
quill.getContents(),
|
|
@@ -363,10 +364,9 @@ describe('hack format cell', () => {
|
|
|
363
364
|
{ insert: '\n' },
|
|
364
365
|
]),
|
|
365
366
|
);
|
|
366
|
-
expect(quill.getSelection()).toBeNull();
|
|
367
367
|
});
|
|
368
368
|
|
|
369
|
-
it('selection
|
|
369
|
+
it('selection can get format tableCellInner. should format like origin', async () => {
|
|
370
370
|
const quill = createQuillWithTableModule('<p></p>', { selection: TableSelection });
|
|
371
371
|
quill.setContents([
|
|
372
372
|
{ insert: '12345\n' },
|
|
@@ -383,33 +383,29 @@ describe('hack format cell', () => {
|
|
|
383
383
|
{ insert: '\n' },
|
|
384
384
|
]);
|
|
385
385
|
|
|
386
|
-
|
|
387
|
-
const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
|
|
388
|
-
tableUp.tableSelection!.selectedTds = [tds[0], tds[1]];
|
|
389
|
-
quill.setSelection(1, 3, Quill.sources.SILENT);
|
|
390
|
-
quill.format('bold', true);
|
|
391
|
-
// simulate `getBoundingClientRect` will effect selectedTd computed position. need manual set
|
|
392
|
-
tableUp.tableSelection!.selectedTds = [tds[0], tds[1]];
|
|
393
|
-
quill.setSelection(1, 3, Quill.sources.SILENT);
|
|
386
|
+
quill.setSelection(9, 0, Quill.sources.SILENT);
|
|
394
387
|
quill.format('list', 'bullet');
|
|
388
|
+
// simulate `getBoundingClientRect` will effect selectedTd computed position. need manual set
|
|
389
|
+
quill.setSelection(14, 1, Quill.sources.SILENT);
|
|
390
|
+
quill.format('bold', true);
|
|
395
391
|
expectDelta(
|
|
396
392
|
quill.getContents(),
|
|
397
393
|
new Delta([
|
|
398
394
|
{ insert: '12345\n' },
|
|
399
395
|
{ insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100 } } },
|
|
400
396
|
{ insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100 } } },
|
|
401
|
-
{ insert: '1'
|
|
397
|
+
{ insert: '1' },
|
|
402
398
|
{ attributes: { 'list': 'bullet', 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
403
|
-
{ insert: '2'
|
|
404
|
-
{ attributes: { '
|
|
399
|
+
{ insert: '2' },
|
|
400
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
405
401
|
{ insert: '3' },
|
|
406
402
|
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
407
|
-
{ insert: '4' },
|
|
403
|
+
{ attributes: { bold: true }, insert: '4' },
|
|
408
404
|
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
409
405
|
{ insert: '\n' },
|
|
410
406
|
]),
|
|
411
407
|
);
|
|
412
|
-
expect(quill.getSelection()).
|
|
408
|
+
expect(quill.getSelection()).toEqual({ index: 14, length: 1 });
|
|
413
409
|
});
|
|
414
410
|
});
|
|
415
411
|
|
|
@@ -685,6 +685,57 @@ describe('table undo', () => {
|
|
|
685
685
|
{ ignoreAttrs: ['class', 'style', 'data-table-id', 'contenteditable'] },
|
|
686
686
|
);
|
|
687
687
|
});
|
|
688
|
+
|
|
689
|
+
it('undo and redo remove last column', async () => {
|
|
690
|
+
const quill = await createTable(2, 2, { full: false });
|
|
691
|
+
const tableModule = quill.getModule(TableUp.moduleName) as TableUp;
|
|
692
|
+
const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
|
|
693
|
+
tableModule.removeCol([tds[1]]);
|
|
694
|
+
await vi.runAllTimersAsync();
|
|
695
|
+
const afterColHtml = `
|
|
696
|
+
<p><br></p>
|
|
697
|
+
<div contenteditable="false">
|
|
698
|
+
<table cellpadding="0" cellspacing="0" style="margin-right: auto; width: 200px;">
|
|
699
|
+
<colgroup contenteditable="false">
|
|
700
|
+
<col width="100px" data-col-id="1">
|
|
701
|
+
</colgroup>
|
|
702
|
+
<tbody>
|
|
703
|
+
<tr>
|
|
704
|
+
<td rowspan="1" colspan="1" data-col-id="1">
|
|
705
|
+
<div data-col-id="1"><p>1</p></div>
|
|
706
|
+
</td>
|
|
707
|
+
</tr>
|
|
708
|
+
<tr>
|
|
709
|
+
<td rowspan="1" colspan="1" data-col-id="1">
|
|
710
|
+
<div data-col-id="1"><p>3</p></div>
|
|
711
|
+
</td>
|
|
712
|
+
</tr>
|
|
713
|
+
</tbody>
|
|
714
|
+
</table>
|
|
715
|
+
</div>
|
|
716
|
+
<p><br></p>
|
|
717
|
+
`;
|
|
718
|
+
expect(quill.root).toEqualHTML(
|
|
719
|
+
afterColHtml,
|
|
720
|
+
{ ignoreAttrs: ['class', 'style', 'data-full', 'data-table-id', 'data-row-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
|
|
721
|
+
);
|
|
722
|
+
quill.history.undo();
|
|
723
|
+
await vi.runAllTimersAsync();
|
|
724
|
+
expect(quill.root).toEqualHTML(
|
|
725
|
+
`
|
|
726
|
+
<p><br></p>
|
|
727
|
+
${createTableHTML(2, 2, { full: false })}
|
|
728
|
+
<p><br></p>
|
|
729
|
+
`,
|
|
730
|
+
{ ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'contenteditable'] },
|
|
731
|
+
);
|
|
732
|
+
quill.history.redo();
|
|
733
|
+
await vi.runAllTimersAsync();
|
|
734
|
+
expect(quill.root).toEqualHTML(
|
|
735
|
+
afterColHtml,
|
|
736
|
+
{ ignoreAttrs: ['class', 'style', 'data-full', 'data-table-id', 'data-row-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
|
|
737
|
+
);
|
|
738
|
+
});
|
|
688
739
|
});
|
|
689
740
|
|
|
690
741
|
describe('undo cell attribute', () => {
|
|
@@ -24,15 +24,6 @@ export class ContainerFormat extends Container {
|
|
|
24
24
|
return node;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
insertAt(index: number, value: string, def?: any): void {
|
|
28
|
-
const [child] = this.children.find(index);
|
|
29
|
-
if (!child) {
|
|
30
|
-
const defaultChild = this.scroll.create(this.statics.defaultChild.blotName || 'block');
|
|
31
|
-
this.appendChild(defaultChild);
|
|
32
|
-
}
|
|
33
|
-
super.insertAt(index, value, def);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
27
|
public optimize(_context: Record<string, any>) {
|
|
37
28
|
if (this.children.length === 0) {
|
|
38
29
|
if (this.statics.defaultChild != null) {
|
|
@@ -150,6 +150,16 @@ export class TableCellInnerFormat extends ContainerFormat {
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
insertAt(index: number, value: string, def?: any): void {
|
|
154
|
+
const [child] = this.children.find(index);
|
|
155
|
+
// always keep TableCellInner not empty
|
|
156
|
+
if (!child && this.statics.defaultChild) {
|
|
157
|
+
const defaultChild = this.scroll.create(this.statics.defaultChild.blotName || 'block');
|
|
158
|
+
this.appendChild(defaultChild);
|
|
159
|
+
}
|
|
160
|
+
super.insertAt(index, value, def);
|
|
161
|
+
}
|
|
162
|
+
|
|
153
163
|
formats(): Record<string, any> {
|
|
154
164
|
const value = this.statics.formats(this.domNode);
|
|
155
165
|
return {
|
|
@@ -258,6 +268,20 @@ export class TableCellInnerFormat extends ContainerFormat {
|
|
|
258
268
|
return this.parent.insertBefore(cellInnerBlot, this.next);
|
|
259
269
|
}
|
|
260
270
|
}
|
|
271
|
+
else if (blot.statics.blotName === blotName.tableCol) {
|
|
272
|
+
try {
|
|
273
|
+
const bodyBlot = findParentBlot(this, blotName.tableBody);
|
|
274
|
+
const index = this.offset(bodyBlot);
|
|
275
|
+
const next = bodyBlot.split(index);
|
|
276
|
+
bodyBlot.parent.insertBefore(blot, next);
|
|
277
|
+
blot.optimize({});
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
// here should not trigger
|
|
281
|
+
console.warn('TableCellInner not in TableBody');
|
|
282
|
+
}
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
261
285
|
super.insertBefore(blot, ref);
|
|
262
286
|
}
|
|
263
287
|
}
|
|
@@ -2,7 +2,7 @@ import type { Parchment as TypeParchment } from 'quill';
|
|
|
2
2
|
import type { BlockEmbed as TypeBlockEmbed } from 'quill/blots/block';
|
|
3
3
|
import type { TableColValue } from '../utils';
|
|
4
4
|
import Quill from 'quill';
|
|
5
|
-
import { blotName, findParentBlot,
|
|
5
|
+
import { blotName, findParentBlot, tableUpSize } from '../utils';
|
|
6
6
|
import { TableCellInnerFormat } from './table-cell-inner-format';
|
|
7
7
|
|
|
8
8
|
const BlockEmbed = Quill.import('blots/block/embed') as typeof TypeBlockEmbed;
|
|
@@ -147,45 +147,45 @@ export class TableColFormat extends BlockEmbed {
|
|
|
147
147
|
super.insertAt(index, value, def);
|
|
148
148
|
return;
|
|
149
149
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const newTableCellInner = newBlock.wrap(blotName.tableCellInner, value);
|
|
174
|
-
const newTableCell = newTableCellInner.wrap(blotName.tableCell, value);
|
|
175
|
-
const newTableRow = newTableCell.wrap(blotName.tableRow, value);
|
|
176
|
-
const newTableBody = newTableRow.wrap(blotName.tableBody, value.tableId);
|
|
177
|
-
tableColgroupBlot.parent.insertBefore(newTableBody, tableColgroupBlot.next);
|
|
178
|
-
|
|
179
|
-
insertBlot = newBlock;
|
|
180
|
-
nextBlotRef = newBlock.next;
|
|
150
|
+
try {
|
|
151
|
+
const lines = value.split('\n');
|
|
152
|
+
const text = lines.pop();
|
|
153
|
+
const tableColgroupBlot = findParentBlot(this, blotName.tableColgroup);
|
|
154
|
+
const tableBodyBlot = tableColgroupBlot.next;
|
|
155
|
+
|
|
156
|
+
// create tbody
|
|
157
|
+
let insertBlot: TypeParchment.Parent = this.scroll;
|
|
158
|
+
// split colgroup
|
|
159
|
+
const nextBlotRef: TypeParchment.Blot | null = tableColgroupBlot.split(this.offset(tableColgroupBlot));
|
|
160
|
+
if (tableBodyBlot) {
|
|
161
|
+
const cellInners = tableBodyBlot.descendants(TableCellInnerFormat);
|
|
162
|
+
if (cellInners.length > 0) {
|
|
163
|
+
const cellInnerBlot = cellInners[0];
|
|
164
|
+
const value = TableCellInnerFormat.formats(cellInnerBlot.domNode);
|
|
165
|
+
const newTableCellInner = this.scroll.create(blotName.tableCellInner, value) as TableCellInnerFormat;
|
|
166
|
+
const newTableCell = newTableCellInner.wrap(blotName.tableCell, value);
|
|
167
|
+
const newTableRow = newTableCell.wrap(blotName.tableRow, value);
|
|
168
|
+
const newTableBody = newTableRow.wrap(blotName.tableBody, value.tableId);
|
|
169
|
+
tableColgroupBlot.parent.insertBefore(newTableBody, nextBlotRef);
|
|
170
|
+
|
|
171
|
+
insertBlot = newTableCellInner;
|
|
172
|
+
}
|
|
181
173
|
}
|
|
182
|
-
}
|
|
183
174
|
|
|
184
|
-
|
|
185
|
-
|
|
175
|
+
for (const line of lines) {
|
|
176
|
+
const block = this.scroll.create('block');
|
|
177
|
+
block.insertAt(0, line);
|
|
178
|
+
insertBlot.appendChild(block);
|
|
179
|
+
}
|
|
180
|
+
if (text) {
|
|
181
|
+
const lineBlock = this.scroll.create('block') as TypeParchment.ParentBlot;
|
|
182
|
+
lineBlock.appendChild(this.scroll.create('text', text));
|
|
183
|
+
insertBlot.appendChild(lineBlock);
|
|
184
|
+
}
|
|
186
185
|
}
|
|
187
|
-
|
|
188
|
-
|
|
186
|
+
catch {
|
|
187
|
+
// here should not trigger
|
|
188
|
+
console.warn('TableCol not in TableColgroup');
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
}
|