quill-table-up 3.0.2 → 3.1.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/README.md +5 -7
- package/dist/index.d.ts +71 -18
- package/dist/index.js +26 -25
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +33 -32
- package/dist/index.umd.js.map +1 -1
- package/package.json +16 -12
- package/src/__tests__/e2e/editor-page.ts +3 -2
- package/src/__tests__/e2e/table-keyboard-handler.test.ts +610 -372
- package/src/__tests__/e2e/table-menu.test.ts +0 -24
- package/src/__tests__/e2e/utils.ts +37 -1
- package/src/__tests__/unit/table-blots.test.ts +341 -2
- package/src/__tests__/unit/table-cell-merge.test.ts +22 -1
- package/src/__tests__/unit/table-clipboard.test.ts +187 -21
- package/src/__tests__/unit/table-redo-undo.test.ts +54 -2
- package/src/__tests__/unit/utils.ts +14 -9
- package/src/formats/table-cell-format.ts +11 -3
- package/src/formats/table-row-format.ts +1 -1
- package/src/modules/table-clipboard/index.ts +2 -0
- package/src/modules/table-clipboard/paste-cell-into-cell.ts +329 -0
- package/src/modules/{table-clipboard.ts → table-clipboard/table-clipboard.ts} +375 -360
- package/src/modules/table-menu/constants.ts +38 -17
- package/src/modules/table-resize/table-resize-box.ts +4 -1
- package/src/modules/table-selection.ts +52 -11
- package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/src/table-up.ts +61 -7
- package/src/utils/components/table/select-box.ts +1 -6
- package/src/utils/is.ts +1 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import Quill from 'quill';
|
|
2
2
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { TableCellInnerFormat } from '../../formats';
|
|
3
4
|
import { TableUp } from '../../table-up';
|
|
4
|
-
import { createQuillWithTableModule, createTableBodyHTML, createTableCaptionHTML, createTableDeltaOps, createTableHTML, createTaleColHTML, datasetTag, expectDelta, simulatePasteHTML } from './utils';
|
|
5
|
+
import { createQuillWithTableModule, createTableBodyHTML, createTableCaptionHTML, createTableDeltaOps, createTableHTML, createTaleColHTML, datasetTag, expectDelta, replaceAttrEmptyRow, simulatePasteHTML } from './utils';
|
|
5
6
|
|
|
6
7
|
const Delta = Quill.import('delta');
|
|
7
8
|
|
|
@@ -956,14 +957,172 @@ describe('clipboard cell structure', () => {
|
|
|
956
957
|
);
|
|
957
958
|
});
|
|
958
959
|
|
|
959
|
-
it('clipboard convert empty tr to `emptyRow`', async () => {
|
|
960
|
+
it('clipboard convert empty multiple tr to `emptyRow`', async () => {
|
|
961
|
+
const quill = createQuillWithTableModule('<p><br></p>', { autoMergeCell: false });
|
|
962
|
+
quill.setContents(
|
|
963
|
+
quill.clipboard.convert({
|
|
964
|
+
html: `<table><tbody><tr><td rowspan="3" colspan="3">1</td></tr><tr></tr><tr></tr><tr><td>2</td><td>3</td><td>4</td></tr></tbody></table>`,
|
|
965
|
+
}),
|
|
966
|
+
);
|
|
967
|
+
await vi.runAllTimersAsync();
|
|
968
|
+
|
|
969
|
+
expect(quill.root).toEqualHTML(
|
|
970
|
+
`
|
|
971
|
+
<p><br></p>
|
|
972
|
+
<div>
|
|
973
|
+
<table cellpadding="0" cellspacing="0">
|
|
974
|
+
${createTaleColHTML(3, { full: false, width: 100 })}
|
|
975
|
+
<tbody>
|
|
976
|
+
<tr>
|
|
977
|
+
<td rowspan="3" colspan="3" data-empty-row="length:2">
|
|
978
|
+
<div data-empty-row="length:2"><p>1</p></div>
|
|
979
|
+
</td>
|
|
980
|
+
</tr>
|
|
981
|
+
<tr>
|
|
982
|
+
</tr>
|
|
983
|
+
<tr>
|
|
984
|
+
</tr>
|
|
985
|
+
<tr>
|
|
986
|
+
<td rowspan="1" colspan="1">
|
|
987
|
+
<div><p>2</p></div>
|
|
988
|
+
</td>
|
|
989
|
+
<td rowspan="1" colspan="1">
|
|
990
|
+
<div><p>3</p></div>
|
|
991
|
+
</td>
|
|
992
|
+
<td rowspan="1" colspan="1">
|
|
993
|
+
<div><p>4</p></div>
|
|
994
|
+
</td>
|
|
995
|
+
</tr>
|
|
996
|
+
</tbody>
|
|
997
|
+
</table>
|
|
998
|
+
</div>
|
|
999
|
+
<p><br></p>
|
|
1000
|
+
`,
|
|
1001
|
+
{
|
|
1002
|
+
ignoreAttrs: ['data-wrap-tag', 'data-tag', 'class', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'data-style', 'style', 'contenteditable'],
|
|
1003
|
+
replaceAttrs: {
|
|
1004
|
+
'data-empty-row': replaceAttrEmptyRow,
|
|
1005
|
+
},
|
|
1006
|
+
},
|
|
1007
|
+
);
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
it('clipboard convert `emptyRow` cell', async () => {
|
|
1011
|
+
const quill = createQuillWithTableModule('<p><br></p>', { autoMergeCell: false });
|
|
1012
|
+
quill.setContents(
|
|
1013
|
+
quill.clipboard.convert({
|
|
1014
|
+
html: `<div class="ql-table-wrapper" data-table-id="4rxokyjlh63" contenteditable="false"><table class="ql-table" data-table-id="4rxokyjlh63" cellpadding="0" cellspacing="0" style="margin-right: auto; width: 300px;"><colgroup data-table-id="4rxokyjlh63" contenteditable="false"><col width="100px" data-table-id="4rxokyjlh63" data-col-id="y5a4z42ufdr"><col width="100px" data-table-id="4rxokyjlh63" data-col-id="ukr081pgksl"><col width="100px" data-table-id="4rxokyjlh63" data-col-id="0kg1x92lxzv"></colgroup><tbody data-table-id="4rxokyjlh63"><tr class="ql-table-row" data-table-id="4rxokyjlh63" data-row-id="uezdr0no6w8" data-wrap-tag="tbody"><td class="ql-table-cell" data-table-id="4rxokyjlh63" data-row-id="uezdr0no6w8" data-col-id="y5a4z42ufdr" data-wrap-tag="tbody" rowspan="3" colspan="3" data-empty-row="["xghllrjrcar","j2cc6owtd9l"]"><div class="ql-table-cell-inner" data-table-id="4rxokyjlh63" data-row-id="uezdr0no6w8" data-col-id="y5a4z42ufdr" data-rowspan="3" data-colspan="3" data-tag="td" data-wrap-tag="tbody" data-empty-row="["xghllrjrcar","j2cc6owtd9l"]" contenteditable="true"><p><span>1</span></p></div></td></tr><tr class="ql-table-row" data-table-id="4rxokyjlh63" data-row-id="j2cc6owtd9l" data-wrap-tag="tbody"></tr><tr class="ql-table-row" data-table-id="4rxokyjlh63" data-row-id="xghllrjrcar" data-wrap-tag="tbody"></tr><tr class="ql-table-row" data-table-id="4rxokyjlh63" data-row-id="afwfmtyccci" data-wrap-tag="tbody"><td class="ql-table-cell" data-table-id="4rxokyjlh63" data-row-id="afwfmtyccci" data-col-id="y5a4z42ufdr" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="4rxokyjlh63" data-row-id="afwfmtyccci" data-col-id="y5a4z42ufdr" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p><span>2</span></p></div></td><td class="ql-table-cell" data-table-id="4rxokyjlh63" data-row-id="afwfmtyccci" data-col-id="ukr081pgksl" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="4rxokyjlh63" data-row-id="afwfmtyccci" data-col-id="ukr081pgksl" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p><span>3</span></p></div></td><td class="ql-table-cell" data-table-id="4rxokyjlh63" data-row-id="afwfmtyccci" data-col-id="0kg1x92lxzv" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="4rxokyjlh63" data-row-id="afwfmtyccci" data-col-id="0kg1x92lxzv" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p><span>4</span></p></div></td></tr></tbody></table></div>`,
|
|
1015
|
+
}),
|
|
1016
|
+
);
|
|
1017
|
+
await vi.runAllTimersAsync();
|
|
1018
|
+
|
|
1019
|
+
expect(quill.root).toEqualHTML(
|
|
1020
|
+
`
|
|
1021
|
+
<p><br></p>
|
|
1022
|
+
<div>
|
|
1023
|
+
<table cellpadding="0" cellspacing="0">
|
|
1024
|
+
${createTaleColHTML(3, { full: false, width: 100 })}
|
|
1025
|
+
<tbody>
|
|
1026
|
+
<tr>
|
|
1027
|
+
<td rowspan="3" colspan="3" data-empty-row="length:2">
|
|
1028
|
+
<div data-empty-row="length:2"><p>1</p></div>
|
|
1029
|
+
</td>
|
|
1030
|
+
</tr>
|
|
1031
|
+
<tr>
|
|
1032
|
+
</tr>
|
|
1033
|
+
<tr>
|
|
1034
|
+
</tr>
|
|
1035
|
+
<tr>
|
|
1036
|
+
<td rowspan="1" colspan="1">
|
|
1037
|
+
<div><p>2</p></div>
|
|
1038
|
+
</td>
|
|
1039
|
+
<td rowspan="1" colspan="1">
|
|
1040
|
+
<div><p>3</p></div>
|
|
1041
|
+
</td>
|
|
1042
|
+
<td rowspan="1" colspan="1">
|
|
1043
|
+
<div><p>4</p></div>
|
|
1044
|
+
</td>
|
|
1045
|
+
</tr>
|
|
1046
|
+
</tbody>
|
|
1047
|
+
</table>
|
|
1048
|
+
</div>
|
|
1049
|
+
<p><br></p>
|
|
1050
|
+
`,
|
|
1051
|
+
{
|
|
1052
|
+
ignoreAttrs: ['data-wrap-tag', 'data-tag', 'class', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'data-style', 'style', 'contenteditable'],
|
|
1053
|
+
replaceAttrs: {
|
|
1054
|
+
'data-empty-row': replaceAttrEmptyRow,
|
|
1055
|
+
},
|
|
1056
|
+
},
|
|
1057
|
+
);
|
|
1058
|
+
});
|
|
1059
|
+
|
|
1060
|
+
it('clipboard convert continuous `emptyRow`', async () => {
|
|
1061
|
+
const quill = createQuillWithTableModule('<p><br></p>', { autoMergeCell: false });
|
|
1062
|
+
quill.setContents(
|
|
1063
|
+
quill.clipboard.convert({
|
|
1064
|
+
html: `<div class="ql-table-wrapper" data-table-id="c0o77jksufl" contenteditable="false"><table class="ql-table" data-table-id="c0o77jksufl" cellpadding="0" cellspacing="0" style="margin-right: auto; width: 300px;"><colgroup data-table-id="c0o77jksufl" contenteditable="false"><col width="100px" data-table-id="c0o77jksufl" data-col-id="a49uy7ts9x"><col width="100px" data-table-id="c0o77jksufl" data-col-id="9rvrf9ve5dp"><col width="100px" data-table-id="c0o77jksufl" data-col-id="vi4hyjmdii"></colgroup><tbody data-table-id="c0o77jksufl"><tr class="ql-table-row" data-table-id="c0o77jksufl" data-row-id="0oi5ud6p8jne" data-wrap-tag="tbody"><td class="ql-table-cell" data-table-id="c0o77jksufl" data-row-id="0oi5ud6p8jne" data-col-id="a49uy7ts9x" data-wrap-tag="tbody" rowspan="3" colspan="3" data-empty-row="["xdpparkt6l","ghbvv1sg1pu"]"><div class="ql-table-cell-inner" data-table-id="c0o77jksufl" data-row-id="0oi5ud6p8jne" data-col-id="a49uy7ts9x" data-rowspan="3" data-colspan="3" data-tag="td" data-wrap-tag="tbody" data-empty-row="["xdpparkt6l","ghbvv1sg1pu"]" contenteditable="true"><p><span>1</span></p></div></td></tr><tr class="ql-table-row" data-table-id="c0o77jksufl" data-row-id="ghbvv1sg1pu" data-wrap-tag="tbody"></tr><tr class="ql-table-row" data-table-id="c0o77jksufl" data-row-id="xdpparkt6l" data-wrap-tag="tbody"></tr><tr class="ql-table-row" data-table-id="c0o77jksufl" data-row-id="3mnrj0uu4rh" data-wrap-tag="tbody"><td class="ql-table-cell" data-table-id="c0o77jksufl" data-row-id="3mnrj0uu4rh" data-col-id="a49uy7ts9x" data-wrap-tag="tbody" rowspan="3" colspan="3" data-empty-row="["7au43sfdyh2","a2u11wlu63"]"><div class="ql-table-cell-inner" data-table-id="c0o77jksufl" data-row-id="3mnrj0uu4rh" data-col-id="a49uy7ts9x" data-rowspan="3" data-colspan="3" data-tag="td" data-wrap-tag="tbody" data-empty-row="["7au43sfdyh2","a2u11wlu63"]" contenteditable="true"><p><span>1</span></p></div></td></tr><tr class="ql-table-row" data-table-id="c0o77jksufl" data-row-id="a2u11wlu63" data-wrap-tag="tbody"></tr><tr class="ql-table-row" data-table-id="c0o77jksufl" data-row-id="7au43sfdyh2" data-wrap-tag="tbody"></tr><tr class="ql-table-row" data-table-id="c0o77jksufl" data-row-id="qhrdlqjpdeq" data-wrap-tag="tbody"><td class="ql-table-cell" data-table-id="c0o77jksufl" data-row-id="qhrdlqjpdeq" data-col-id="a49uy7ts9x" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="c0o77jksufl" data-row-id="qhrdlqjpdeq" data-col-id="a49uy7ts9x" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p><span>2</span></p></div></td><td class="ql-table-cell" data-table-id="c0o77jksufl" data-row-id="qhrdlqjpdeq" data-col-id="9rvrf9ve5dp" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="c0o77jksufl" data-row-id="qhrdlqjpdeq" data-col-id="9rvrf9ve5dp" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p><span>3</span></p></div></td><td class="ql-table-cell" data-table-id="c0o77jksufl" data-row-id="qhrdlqjpdeq" data-col-id="vi4hyjmdii" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="c0o77jksufl" data-row-id="qhrdlqjpdeq" data-col-id="vi4hyjmdii" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p><span>4</span></p></div></td></tr></tbody></table></div>`,
|
|
1065
|
+
}),
|
|
1066
|
+
);
|
|
1067
|
+
await vi.runAllTimersAsync();
|
|
1068
|
+
|
|
1069
|
+
expect(quill.root).toEqualHTML(
|
|
1070
|
+
`
|
|
1071
|
+
<p><br></p>
|
|
1072
|
+
<div>
|
|
1073
|
+
<table cellpadding="0" cellspacing="0">
|
|
1074
|
+
${createTaleColHTML(3, { full: false, width: 100 })}
|
|
1075
|
+
<tbody>
|
|
1076
|
+
<tr>
|
|
1077
|
+
<td rowspan="3" colspan="3" data-empty-row="length:2">
|
|
1078
|
+
<div data-empty-row="length:2"><p>1</p></div>
|
|
1079
|
+
</td>
|
|
1080
|
+
</tr>
|
|
1081
|
+
<tr>
|
|
1082
|
+
</tr>
|
|
1083
|
+
<tr>
|
|
1084
|
+
</tr>
|
|
1085
|
+
<tr>
|
|
1086
|
+
<td rowspan="3" colspan="3" data-empty-row="length:2">
|
|
1087
|
+
<div data-empty-row="length:2"><p>1</p></div>
|
|
1088
|
+
</td>
|
|
1089
|
+
</tr>
|
|
1090
|
+
<tr>
|
|
1091
|
+
</tr>
|
|
1092
|
+
<tr>
|
|
1093
|
+
</tr>
|
|
1094
|
+
<tr>
|
|
1095
|
+
<td rowspan="1" colspan="1">
|
|
1096
|
+
<div><p>2</p></div>
|
|
1097
|
+
</td>
|
|
1098
|
+
<td rowspan="1" colspan="1">
|
|
1099
|
+
<div><p>3</p></div>
|
|
1100
|
+
</td>
|
|
1101
|
+
<td rowspan="1" colspan="1">
|
|
1102
|
+
<div><p>4</p></div>
|
|
1103
|
+
</td>
|
|
1104
|
+
</tr>
|
|
1105
|
+
</tbody>
|
|
1106
|
+
</table>
|
|
1107
|
+
</div>
|
|
1108
|
+
<p><br></p>
|
|
1109
|
+
`,
|
|
1110
|
+
{
|
|
1111
|
+
ignoreAttrs: ['data-wrap-tag', 'data-tag', 'class', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'data-style', 'style', 'contenteditable'],
|
|
1112
|
+
replaceAttrs: {
|
|
1113
|
+
'data-empty-row': replaceAttrEmptyRow,
|
|
1114
|
+
},
|
|
1115
|
+
},
|
|
1116
|
+
);
|
|
1117
|
+
});
|
|
1118
|
+
|
|
1119
|
+
it('clipboard convert empty tr to `emptyRow` in thead', async () => {
|
|
960
1120
|
const quill = createQuillWithTableModule(`<p><br></p>`, { autoMergeCell: false });
|
|
961
1121
|
quill.setContents(
|
|
962
1122
|
quill.clipboard.convert({
|
|
963
1123
|
html: `<table><thead><tr><td rowspan="2" colspan="2">1</td></tr><tr></tr></thead><tbody><tr><td>1</td><td>2</td></tr><tr><td>1</td><td>2</td></tr></tbody></table>`,
|
|
964
1124
|
}),
|
|
965
1125
|
);
|
|
966
|
-
|
|
967
1126
|
await vi.runAllTimersAsync();
|
|
968
1127
|
|
|
969
1128
|
expect(quill.root).toEqualHTML(
|
|
@@ -1006,15 +1165,7 @@ describe('clipboard cell structure', () => {
|
|
|
1006
1165
|
{
|
|
1007
1166
|
ignoreAttrs: ['data-wrap-tag', 'data-tag', 'class', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'data-style', 'style', 'contenteditable'],
|
|
1008
1167
|
replaceAttrs: {
|
|
1009
|
-
'data-empty-row':
|
|
1010
|
-
try {
|
|
1011
|
-
const emptyRow = JSON.parse(value);
|
|
1012
|
-
return `length:${emptyRow.length}`;
|
|
1013
|
-
}
|
|
1014
|
-
catch {
|
|
1015
|
-
return value;
|
|
1016
|
-
}
|
|
1017
|
-
},
|
|
1168
|
+
'data-empty-row': replaceAttrEmptyRow,
|
|
1018
1169
|
},
|
|
1019
1170
|
},
|
|
1020
1171
|
);
|
|
@@ -1112,15 +1263,7 @@ describe('clipboard cell structure', () => {
|
|
|
1112
1263
|
{
|
|
1113
1264
|
ignoreAttrs: ['data-wrap-tag', 'data-tag', 'class', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'data-style', 'style', 'contenteditable'],
|
|
1114
1265
|
replaceAttrs: {
|
|
1115
|
-
'data-empty-row':
|
|
1116
|
-
try {
|
|
1117
|
-
const emptyRow = JSON.parse(value);
|
|
1118
|
-
return `length:${emptyRow.length}`;
|
|
1119
|
-
}
|
|
1120
|
-
catch {
|
|
1121
|
-
return value;
|
|
1122
|
-
}
|
|
1123
|
-
},
|
|
1266
|
+
'data-empty-row': replaceAttrEmptyRow,
|
|
1124
1267
|
},
|
|
1125
1268
|
},
|
|
1126
1269
|
);
|
|
@@ -2008,3 +2151,26 @@ describe('clipboard cell in cell', () => {
|
|
|
2008
2151
|
);
|
|
2009
2152
|
});
|
|
2010
2153
|
});
|
|
2154
|
+
|
|
2155
|
+
describe('test TableUp `getHTMLByCell`', () => {
|
|
2156
|
+
it('getHTMLByCell return cell html', async () => {
|
|
2157
|
+
const quill = createQuillWithTableModule(`<p><br></p>`);
|
|
2158
|
+
quill.setContents(createTableDeltaOps(4, 4, { full: true }, {}, { isEmpty: false }));
|
|
2159
|
+
|
|
2160
|
+
const tableModule = quill.getModule(TableUp.moduleName) as TableUp;
|
|
2161
|
+
const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
|
|
2162
|
+
tableModule.mergeCells([tds[0], tds[1], tds[4], tds[5]]);
|
|
2163
|
+
await vi.runAllTimersAsync();
|
|
2164
|
+
|
|
2165
|
+
const html = tableModule.getHTMLByCell([tds[0], tds[2], tds[6]]);
|
|
2166
|
+
const parser = new DOMParser();
|
|
2167
|
+
const doc = parser.parseFromString(html, 'text/html');
|
|
2168
|
+
const htmlCols = Array.from(doc.querySelectorAll('col'));
|
|
2169
|
+
const htmlTds = Array.from(doc.querySelectorAll('td'));
|
|
2170
|
+
expect(htmlCols.length).toBe(3);
|
|
2171
|
+
expect(htmlTds.length).toBe(3);
|
|
2172
|
+
for (const col of htmlCols) {
|
|
2173
|
+
expect(col.getAttribute('width')).toBe('33%');
|
|
2174
|
+
}
|
|
2175
|
+
});
|
|
2176
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Parchment as TypeParchment } from 'quill';
|
|
1
|
+
import type { Op, Parchment as TypeParchment } from 'quill';
|
|
2
2
|
import type { TableCaptionFormat, TableMainFormat } from '../../formats';
|
|
3
3
|
import type { ToolOption } from '../../utils';
|
|
4
4
|
import Quill from 'quill';
|
|
@@ -6,7 +6,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
6
6
|
import { TableCellInnerFormat } from '../../formats';
|
|
7
7
|
import { tableMenuTools, TableSelection } from '../../modules';
|
|
8
8
|
import { TableUp } from '../../table-up';
|
|
9
|
-
import { createQuillWithTableModule, createTable, createTableBodyHTML, createTableHTML, createTaleColHTML, datasetAlign, datasetFull, expectDelta } from './utils';
|
|
9
|
+
import { createQuillWithTableModule, createTable, createTableBodyHTML, createTableDeltaOps, createTableHTML, createTaleColHTML, datasetAlign, datasetFull, expectDelta } from './utils';
|
|
10
10
|
|
|
11
11
|
const Delta = Quill.import('delta');
|
|
12
12
|
|
|
@@ -1481,6 +1481,58 @@ describe('table undo', () => {
|
|
|
1481
1481
|
{ ignoreAttrs: ['data-wrap-tag', 'data-tag', 'class', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'rowspan', 'data-colspan', 'colspan', 'contenteditable'] },
|
|
1482
1482
|
);
|
|
1483
1483
|
});
|
|
1484
|
+
|
|
1485
|
+
it('table convert body undo and redo', async () => {
|
|
1486
|
+
function resetWrapTag(skip: number, count: number, ops: Op[], wrapTag: string) {
|
|
1487
|
+
return [...ops].map((op) => {
|
|
1488
|
+
if (op.attributes?.['table-up-cell-inner']) {
|
|
1489
|
+
if (skip > 0) {
|
|
1490
|
+
skip -= 1;
|
|
1491
|
+
}
|
|
1492
|
+
else if (count > 0) {
|
|
1493
|
+
count -= 1;
|
|
1494
|
+
return { ...op, attributes: { ...op.attributes, 'table-up-cell-inner': { ...op.attributes['table-up-cell-inner'], wrapTag } } };
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
return op;
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
const quill = createQuillWithTableModule('<p><br></p>');
|
|
1502
|
+
const originDelta = createTableDeltaOps(5, 5, { full: false }, {}, { isEmpty: false });
|
|
1503
|
+
quill.setContents(originDelta);
|
|
1504
|
+
await vi.runAllTimersAsync();
|
|
1505
|
+
const table = quill.root.querySelector('table')!;
|
|
1506
|
+
const tableModule = quill.getModule(TableUp.moduleName) as TableUp;
|
|
1507
|
+
const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
|
|
1508
|
+
|
|
1509
|
+
(tableMenuTools.ConvertTothead as ToolOption).handle.call({ quill, table } as any, tableModule, tds.slice(0, 10), null);
|
|
1510
|
+
await vi.runAllTimersAsync();
|
|
1511
|
+
const headDelta = new Delta(resetWrapTag(0, 10, originDelta, 'thead'));
|
|
1512
|
+
expectDelta(quill.getContents(), headDelta);
|
|
1513
|
+
|
|
1514
|
+
(tableMenuTools.ConvertTotfoot as ToolOption).handle.call({ quill, table } as any, tableModule, tds.slice(15, 25), null);
|
|
1515
|
+
await vi.runAllTimersAsync();
|
|
1516
|
+
const footDelta = new Delta(resetWrapTag(15, 10, headDelta.ops, 'tfoot'));
|
|
1517
|
+
expectDelta(quill.getContents(), footDelta);
|
|
1518
|
+
|
|
1519
|
+
const mergedTds = quill.scroll.descendants(TableCellInnerFormat, 0);
|
|
1520
|
+
(tableMenuTools.ConvertTothead as ToolOption).handle.call({ quill, table } as any, tableModule, mergedTds.slice(0, 20), null);
|
|
1521
|
+
await vi.runAllTimersAsync();
|
|
1522
|
+
expectDelta(
|
|
1523
|
+
quill.getContents(),
|
|
1524
|
+
new Delta(resetWrapTag(0, 20, footDelta.ops, 'thead')),
|
|
1525
|
+
);
|
|
1526
|
+
|
|
1527
|
+
quill.history.undo();
|
|
1528
|
+
expectDelta(quill.getContents(), footDelta);
|
|
1529
|
+
|
|
1530
|
+
quill.history.undo();
|
|
1531
|
+
expectDelta(quill.getContents(), headDelta);
|
|
1532
|
+
|
|
1533
|
+
quill.history.redo();
|
|
1534
|
+
expectDelta(quill.getContents(), footDelta);
|
|
1535
|
+
});
|
|
1484
1536
|
});
|
|
1485
1537
|
|
|
1486
1538
|
describe('undo cell attribute', () => {
|
|
@@ -9,9 +9,7 @@ const Delta = Quill.import('delta');
|
|
|
9
9
|
export const normalizeHTML = (html: string | { html: string }) => typeof html === 'object' ? html.html : html.replaceAll(/\n\s*/g, '');
|
|
10
10
|
export function sortAttributes(element: HTMLElement) {
|
|
11
11
|
const attributes = Array.from(element.attributes);
|
|
12
|
-
const sortedAttributes = attributes.
|
|
13
|
-
a.name.localeCompare(b.name),
|
|
14
|
-
);
|
|
12
|
+
const sortedAttributes = attributes.toSorted((a, b) => a.name.localeCompare(b.name));
|
|
15
13
|
|
|
16
14
|
while (element.attributes.length > 0) {
|
|
17
15
|
element.removeAttribute(element.attributes[0].name);
|
|
@@ -51,6 +49,15 @@ export function createQuillWithTableModule(html: string, tableOptions: Partial<T
|
|
|
51
49
|
});
|
|
52
50
|
return quill;
|
|
53
51
|
}
|
|
52
|
+
export function replaceAttrEmptyRow(value: string) {
|
|
53
|
+
try {
|
|
54
|
+
const emptyRow = JSON.parse(value);
|
|
55
|
+
return `length:${emptyRow.length}`;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
54
61
|
|
|
55
62
|
expect.extend({
|
|
56
63
|
toEqualHTML(
|
|
@@ -117,7 +124,7 @@ export function expectDelta(received: TypeDelta, expected: TypeDelta) {
|
|
|
117
124
|
|
|
118
125
|
interface TableColDeltaValue extends Omit<TableColValue, 'width' | 'full'> {
|
|
119
126
|
width: number;
|
|
120
|
-
full?:
|
|
127
|
+
full?: boolean;
|
|
121
128
|
}
|
|
122
129
|
interface TableCreatorOptions {
|
|
123
130
|
isEmpty: boolean;
|
|
@@ -158,10 +165,8 @@ export function createTableDeltaOps(row: number, col: number, colOptions?: ColOp
|
|
|
158
165
|
}
|
|
159
166
|
for (const [i, _] of new Array(col).fill(0).entries()) {
|
|
160
167
|
const value: TableColDeltaValue = { tableId, colId: `${i + 1}`, width: 1 / col * 100 };
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
168
|
+
value.full = full;
|
|
169
|
+
if (!full) {
|
|
165
170
|
value.width = width;
|
|
166
171
|
}
|
|
167
172
|
if (align !== 'left') {
|
|
@@ -176,7 +181,7 @@ export function createTableDeltaOps(row: number, col: number, colOptions?: ColOp
|
|
|
176
181
|
}
|
|
177
182
|
table.push(
|
|
178
183
|
{
|
|
179
|
-
attributes: { 'table-up-cell-inner': { tableId, rowId: i + 1
|
|
184
|
+
attributes: { 'table-up-cell-inner': { tableId, rowId: `${i + 1}`, colId: `${j + 1}`, rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } },
|
|
180
185
|
insert: '\n',
|
|
181
186
|
},
|
|
182
187
|
);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { Parchment as TypeParchment } from 'quill';
|
|
1
2
|
import type { TableBodyTag, TableCellValue } from '../utils';
|
|
2
3
|
import { blotName, ensureArray, findParentBlot, getInlineStyles, toCamelCase } from '../utils';
|
|
3
4
|
import { ContainerFormat } from './container-format';
|
|
5
|
+
import { TableBodyFormat } from './table-body-format';
|
|
4
6
|
import { TableCellInnerFormat } from './table-cell-inner-format';
|
|
5
7
|
import { TableRowFormat } from './table-row-format';
|
|
6
8
|
import { getValidCellspan } from './utils';
|
|
@@ -282,12 +284,18 @@ export class TableCellFormat extends ContainerFormat {
|
|
|
282
284
|
}
|
|
283
285
|
// when `replaceWith` called to replace cell. wrapTag may change. so row wrapTag also need to update
|
|
284
286
|
if (this.parent.statics.blotName === blotName.tableRow && (this.parent as TableRowFormat).wrapTag !== wrapTag) {
|
|
285
|
-
(this.parent as TableRowFormat).setFormatValue('wrap-tag',
|
|
287
|
+
(this.parent as TableRowFormat).setFormatValue('wrap-tag', wrapTag);
|
|
286
288
|
}
|
|
287
289
|
|
|
288
290
|
if (this.emptyRow.length > 0) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
+
const tableBody = this.parent.parent;
|
|
292
|
+
if (tableBody instanceof TableBodyFormat) {
|
|
293
|
+
let insertBefore: TypeParchment.Blot | null = this.parent.next;
|
|
294
|
+
for (const rowId of this.emptyRow) {
|
|
295
|
+
const row = this.scroll.create(blotName.tableRow, { tableId, rowId, wrapTag }) as TableRowFormat;
|
|
296
|
+
tableBody.insertBefore(row, insertBefore);
|
|
297
|
+
insertBefore = row.next;
|
|
298
|
+
}
|
|
291
299
|
}
|
|
292
300
|
}
|
|
293
301
|
|
|
@@ -186,7 +186,7 @@ export class TableRowFormat extends ContainerFormat {
|
|
|
186
186
|
if (newParent && newParent.length() <= 0) {
|
|
187
187
|
newParent.remove();
|
|
188
188
|
}
|
|
189
|
-
const afterParent = (this.parent as
|
|
189
|
+
const afterParent = (this.parent as TypeParchment.ParentBlot).splitAfter(this);
|
|
190
190
|
if (afterParent && afterParent.length() <= 0) {
|
|
191
191
|
afterParent.remove();
|
|
192
192
|
}
|