quill-table-up 3.0.1 → 3.0.2
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 +3 -2
- package/dist/index.js +29 -29
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +26 -26
- package/dist/index.umd.js.map +1 -1
- package/package.json +18 -18
- package/src/__tests__/e2e/table-keyboard-handler.test.ts +2 -2
- package/src/__tests__/e2e/table-menu.test.ts +4 -5
- package/src/__tests__/unit/table-clipboard.test.ts +39 -39
- package/src/__tests__/unit/table-hack.test.ts +2 -2
- package/src/__tests__/unit/table-insert.test.ts +50 -0
- package/src/__tests__/unit/table-redo-undo.test.ts +217 -25
- package/src/__tests__/unit/table-remove.test.ts +3 -1
- package/src/__tests__/unit/utils.ts +2 -1
- package/src/formats/overrides/block-embed.ts +1 -2
- package/src/formats/overrides/block.ts +7 -2
- package/src/formats/table-cell-inner-format.ts +20 -4
- package/src/formats/table-col-format.ts +26 -30
- package/src/formats/table-wrapper-format.ts +9 -1
- package/src/modules/table-selection.ts +4 -1
- package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/src/table-up.ts +6 -5
- package/src/utils/blot-helper.ts +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Parchment as TypeParchment } from 'quill';
|
|
1
2
|
import type { TableCaptionFormat, TableMainFormat } from '../../formats';
|
|
2
3
|
import type { ToolOption } from '../../utils';
|
|
3
4
|
import Quill from 'quill';
|
|
@@ -1249,37 +1250,119 @@ describe('table undo', () => {
|
|
|
1249
1250
|
{ insert: '\n' },
|
|
1250
1251
|
];
|
|
1251
1252
|
quill.setContents(originDelta);
|
|
1253
|
+
const mergedDelta = [
|
|
1254
|
+
{ insert: '\n' },
|
|
1255
|
+
{ insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100 } } },
|
|
1256
|
+
{ insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100 } } },
|
|
1257
|
+
{ insert: { 'table-up-col': { tableId: '1', colId: '3', full: false, width: 100 } } },
|
|
1258
|
+
{ insert: '1' },
|
|
1259
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 2, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1260
|
+
{ insert: { video: 'some.com' } },
|
|
1261
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 2, tag: 'td', wrapTag: 'tbody' } }, insert: '\n\n' },
|
|
1262
|
+
{ insert: '3' },
|
|
1263
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '3', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1264
|
+
{ insert: '4' },
|
|
1265
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1266
|
+
{ insert: '5' },
|
|
1267
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1268
|
+
{ insert: '6' },
|
|
1269
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '3', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1270
|
+
{ insert: '7' },
|
|
1271
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '1', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1272
|
+
{ insert: '8' },
|
|
1273
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '2', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1274
|
+
{ insert: '9' },
|
|
1275
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '3', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1276
|
+
{ insert: '\n' },
|
|
1277
|
+
];
|
|
1252
1278
|
|
|
1253
1279
|
const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
|
|
1254
1280
|
tableModule.mergeCells([tds[0], tds[1]]);
|
|
1255
1281
|
await vi.runAllTimersAsync();
|
|
1256
1282
|
expectDelta(
|
|
1257
1283
|
quill.getContents(),
|
|
1258
|
-
new Delta(
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1284
|
+
new Delta(mergedDelta),
|
|
1285
|
+
);
|
|
1286
|
+
|
|
1287
|
+
quill.history.undo();
|
|
1288
|
+
await vi.runAllTimersAsync();
|
|
1289
|
+
expectDelta(
|
|
1290
|
+
quill.getContents(),
|
|
1291
|
+
new Delta(originDelta),
|
|
1292
|
+
);
|
|
1293
|
+
|
|
1294
|
+
quill.history.redo();
|
|
1295
|
+
await vi.runAllTimersAsync();
|
|
1296
|
+
expectDelta(
|
|
1297
|
+
quill.getContents(),
|
|
1298
|
+
new Delta(mergedDelta),
|
|
1299
|
+
);
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
it('undo remove cell with BlockEmbed', async () => {
|
|
1303
|
+
const quill = createQuillWithTableModule('<p><br></p>');
|
|
1304
|
+
const originDelta = [
|
|
1305
|
+
{ insert: '\n' },
|
|
1306
|
+
{ insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100 } } },
|
|
1307
|
+
{ insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100 } } },
|
|
1308
|
+
{ insert: { 'table-up-col': { tableId: '1', colId: '3', full: false, width: 100 } } },
|
|
1309
|
+
{ insert: '1' },
|
|
1310
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1311
|
+
{ insert: { video: 'some.com' } },
|
|
1312
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1313
|
+
{ insert: '123' },
|
|
1314
|
+
{ attributes: { 'code-block': 'plain', 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1315
|
+
{ insert: '3' },
|
|
1316
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '3', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1317
|
+
{ insert: '4' },
|
|
1318
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1319
|
+
{ insert: '5' },
|
|
1320
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1321
|
+
{ insert: '6' },
|
|
1322
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '3', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1323
|
+
{ insert: '7' },
|
|
1324
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '1', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1325
|
+
{ insert: '8' },
|
|
1326
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '2', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1327
|
+
{ insert: '9' },
|
|
1328
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '3', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1329
|
+
{ insert: '\n' },
|
|
1330
|
+
];
|
|
1331
|
+
const removedDelta = [
|
|
1332
|
+
{ insert: '\n' },
|
|
1333
|
+
{ insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100 } } },
|
|
1334
|
+
{ insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100 } } },
|
|
1335
|
+
{ insert: { 'table-up-col': { tableId: '1', colId: '3', full: false, width: 100 } } },
|
|
1336
|
+
{ insert: '1' },
|
|
1337
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1338
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1339
|
+
{ insert: '3' },
|
|
1340
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '3', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1341
|
+
{ insert: '4' },
|
|
1342
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1343
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1344
|
+
{ insert: '6' },
|
|
1345
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '3', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1346
|
+
{ insert: '7' },
|
|
1347
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '1', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1348
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '2', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1349
|
+
{ insert: '9' },
|
|
1350
|
+
{ attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '3', rowspan: 1, colspan: 1, tag: 'td', wrapTag: 'tbody' } }, insert: '\n' },
|
|
1351
|
+
{ insert: '\n' },
|
|
1352
|
+
];
|
|
1353
|
+
quill.setContents(originDelta);
|
|
1354
|
+
|
|
1355
|
+
const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
|
|
1356
|
+
for (const td of [tds[1], tds[4], tds[7]]) {
|
|
1357
|
+
const clearTd = td.clone() as TypeParchment.Parent;
|
|
1358
|
+
clearTd.appendChild(td.scroll.create('block'));
|
|
1359
|
+
td.parent.insertBefore(clearTd, td);
|
|
1360
|
+
td.remove();
|
|
1361
|
+
}
|
|
1362
|
+
await vi.runAllTimersAsync();
|
|
1363
|
+
expectDelta(
|
|
1364
|
+
quill.getContents(),
|
|
1365
|
+
new Delta(removedDelta),
|
|
1283
1366
|
);
|
|
1284
1367
|
|
|
1285
1368
|
quill.history.undo();
|
|
@@ -1288,6 +1371,115 @@ describe('table undo', () => {
|
|
|
1288
1371
|
quill.getContents(),
|
|
1289
1372
|
new Delta(originDelta),
|
|
1290
1373
|
);
|
|
1374
|
+
|
|
1375
|
+
quill.history.redo();
|
|
1376
|
+
await vi.runAllTimersAsync();
|
|
1377
|
+
expectDelta(
|
|
1378
|
+
quill.getContents(),
|
|
1379
|
+
new Delta(removedDelta),
|
|
1380
|
+
);
|
|
1381
|
+
});
|
|
1382
|
+
|
|
1383
|
+
it('undo paste table', async () => {
|
|
1384
|
+
const quill = createQuillWithTableModule('<p><br></p>');
|
|
1385
|
+
quill.setContents(
|
|
1386
|
+
quill.clipboard.convert({ html: '<div class="ql-table-wrapper" data-table-id="1" contenteditable="false"><table class="ql-table" data-table-id="1" cellpadding="0" cellspacing="0" style="margin-right: auto; width: 363px;"><colgroup data-table-id="1" contenteditable="false"><col width="121px" data-table-id="1" data-col-id="1"><col width="121px" data-table-id="1" data-col-id="2"><col width="121px" data-table-id="1" data-col-id="3"></colgroup><tbody data-table-id="1"><tr class="ql-table-row" data-table-id="1" data-row-id="1" data-wrap-tag="tbody"><td class="ql-table-cell" data-table-id="1" data-row-id="1" data-col-id="1" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="1" data-col-id="1" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p>1</p></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="1" data-col-id="2" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="1" data-col-id="2" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><h1>2</h1></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="1" data-col-id="3" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="1" data-col-id="3" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><iframe class="ql-video" frameborder="0" allowfullscreen="true" src="some.com"></iframe></div></td></tr><tr class="ql-table-row" data-table-id="1" data-row-id="2" data-wrap-tag="tbody"><td class="ql-table-cell" data-table-id="1" data-row-id="2" data-col-id="1" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="2" data-col-id="1" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p>4</p></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="2" data-col-id="2" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="2" data-col-id="2" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p>5</p></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="2" data-col-id="3" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="2" data-col-id="3" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p>6</p></div></td></tr><tr class="ql-table-row" data-table-id="1" data-row-id="3" data-wrap-tag="tbody"><td class="ql-table-cell" data-table-id="1" data-row-id="3" data-col-id="1" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="3" data-col-id="1" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p>7</p></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="3" data-col-id="2" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="3" data-col-id="2" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p>8</p></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="3" data-col-id="3" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="3" data-col-id="3" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><ol><li data-list="ordered"><span class="ql-ui" contenteditable="false"></span>9</li><li data-list="ordered"><span class="ql-ui" contenteditable="false"></span>end</li></ol></div></td></tr></tbody></table></div>' }),
|
|
1387
|
+
);
|
|
1388
|
+
await vi.runAllTimersAsync();
|
|
1389
|
+
quill.setContents(
|
|
1390
|
+
quill.getContents().insert('\n').concat(
|
|
1391
|
+
quill.clipboard.convert({ html: '<div class="ql-table-wrapper" data-table-id="1" contenteditable="false"><table class="ql-table" data-table-id="1" cellpadding="0" cellspacing="0" style="margin-right: auto; width: 363px;"><colgroup data-table-id="1" contenteditable="false"><col width="121px" data-table-id="1" data-col-id="1"><col width="121px" data-table-id="1" data-col-id="2"><col width="121px" data-table-id="1" data-col-id="3"></colgroup><tbody data-table-id="1"><tr class="ql-table-row" data-table-id="1" data-row-id="1" data-wrap-tag="tbody"><td class="ql-table-cell" data-table-id="1" data-row-id="1" data-col-id="1" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="1" data-col-id="1" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p>1</p></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="1" data-col-id="2" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="1" data-col-id="2" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><h1>2</h1></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="1" data-col-id="3" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="1" data-col-id="3" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><iframe class="ql-video" frameborder="0" allowfullscreen="true" src="some.com"></iframe></div></td></tr><tr class="ql-table-row" data-table-id="1" data-row-id="2" data-wrap-tag="tbody"><td class="ql-table-cell" data-table-id="1" data-row-id="2" data-col-id="1" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="2" data-col-id="1" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p>4</p></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="2" data-col-id="2" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="2" data-col-id="2" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p>5</p></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="2" data-col-id="3" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="2" data-col-id="3" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p>6</p></div></td></tr><tr class="ql-table-row" data-table-id="1" data-row-id="3" data-wrap-tag="tbody"><td class="ql-table-cell" data-table-id="1" data-row-id="3" data-col-id="1" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="3" data-col-id="1" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p>7</p></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="3" data-col-id="2" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="3" data-col-id="2" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><p>8</p></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="3" data-col-id="3" data-wrap-tag="tbody" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="1" data-row-id="3" data-col-id="3" data-rowspan="1" data-colspan="1" data-tag="td" data-wrap-tag="tbody" contenteditable="true"><ol><li data-list="ordered"><span class="ql-ui" contenteditable="false"></span>9</li><li data-list="ordered"><span class="ql-ui" contenteditable="false"></span>end</li></ol></div></td></tr></tbody></table></div>' }),
|
|
1392
|
+
),
|
|
1393
|
+
);
|
|
1394
|
+
await vi.runAllTimersAsync();
|
|
1395
|
+
const pasteTableHTML = `
|
|
1396
|
+
<div>
|
|
1397
|
+
<table cellpadding="0" cellspacing="0" style="margin-right: auto; width: 363px;">
|
|
1398
|
+
${createTaleColHTML(3, { full: false, width: 121 })}
|
|
1399
|
+
<tbody>
|
|
1400
|
+
<tr>
|
|
1401
|
+
<td>
|
|
1402
|
+
<div><p>1</p></div>
|
|
1403
|
+
</td>
|
|
1404
|
+
<td>
|
|
1405
|
+
<div><h1>2</h1></div>
|
|
1406
|
+
</td>
|
|
1407
|
+
<td>
|
|
1408
|
+
<div>
|
|
1409
|
+
<iframe src="some.com" frameborder="0" allowfullscreen="true"></iframe>
|
|
1410
|
+
</div>
|
|
1411
|
+
</td>
|
|
1412
|
+
</tr>
|
|
1413
|
+
<tr>
|
|
1414
|
+
<td>
|
|
1415
|
+
<div><p>4</p></div>
|
|
1416
|
+
</td>
|
|
1417
|
+
<td>
|
|
1418
|
+
<div><p>5</p></div>
|
|
1419
|
+
</td>
|
|
1420
|
+
<td>
|
|
1421
|
+
<div><p>6</p></div>
|
|
1422
|
+
</td>
|
|
1423
|
+
</tr>
|
|
1424
|
+
<tr>
|
|
1425
|
+
<td>
|
|
1426
|
+
<div><p>7</p></div>
|
|
1427
|
+
</td>
|
|
1428
|
+
<td>
|
|
1429
|
+
<div><p>8</p></div>
|
|
1430
|
+
</td>
|
|
1431
|
+
<td>
|
|
1432
|
+
<div>
|
|
1433
|
+
<ol>
|
|
1434
|
+
<li data-list="ordered">9</li>
|
|
1435
|
+
<li data-list="ordered">end</li>
|
|
1436
|
+
</ol>
|
|
1437
|
+
</div>
|
|
1438
|
+
</td>
|
|
1439
|
+
</tr>
|
|
1440
|
+
</tbody>
|
|
1441
|
+
</table>
|
|
1442
|
+
</div>
|
|
1443
|
+
`;
|
|
1444
|
+
expect(quill.root).toEqualHTML(
|
|
1445
|
+
`
|
|
1446
|
+
<p><br></p>
|
|
1447
|
+
${pasteTableHTML}
|
|
1448
|
+
<p><br></p>
|
|
1449
|
+
<p><br></p>
|
|
1450
|
+
${pasteTableHTML}
|
|
1451
|
+
<p><br></p>
|
|
1452
|
+
`,
|
|
1453
|
+
{ ignoreAttrs: ['data-wrap-tag', 'data-tag', 'class', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'rowspan', 'data-colspan', 'colspan', 'contenteditable'] },
|
|
1454
|
+
);
|
|
1455
|
+
|
|
1456
|
+
quill.history.undo();
|
|
1457
|
+
await vi.runAllTimersAsync();
|
|
1458
|
+
expect(quill.root).toEqualHTML(
|
|
1459
|
+
`
|
|
1460
|
+
<p><br></p>
|
|
1461
|
+
${pasteTableHTML}
|
|
1462
|
+
<p><br></p>
|
|
1463
|
+
<p><br></p>
|
|
1464
|
+
${pasteTableHTML}
|
|
1465
|
+
<p><br></p>
|
|
1466
|
+
`,
|
|
1467
|
+
{ ignoreAttrs: ['data-wrap-tag', 'data-tag', 'class', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'rowspan', 'data-colspan', 'colspan', 'contenteditable'] },
|
|
1468
|
+
);
|
|
1469
|
+
|
|
1470
|
+
quill.history.undo();
|
|
1471
|
+
await vi.runAllTimersAsync();
|
|
1472
|
+
expect(quill.root).toEqualHTML(
|
|
1473
|
+
`
|
|
1474
|
+
<p><br></p>
|
|
1475
|
+
${pasteTableHTML}
|
|
1476
|
+
<p><br></p>
|
|
1477
|
+
<p><br></p>
|
|
1478
|
+
${pasteTableHTML}
|
|
1479
|
+
<p><br></p>
|
|
1480
|
+
`,
|
|
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
|
+
);
|
|
1291
1483
|
});
|
|
1292
1484
|
});
|
|
1293
1485
|
|
|
@@ -215,7 +215,9 @@ describe('unusual delete', () => {
|
|
|
215
215
|
await vi.runAllTimersAsync();
|
|
216
216
|
quill.deleteText(0, 16);
|
|
217
217
|
await vi.runAllTimersAsync();
|
|
218
|
-
|
|
218
|
+
// After remove all `col`, `fixUnusuaDeletelTable` will remove whole table.
|
|
219
|
+
// The remaining two rows around
|
|
220
|
+
expect(quill.root).toEqualHTML(`<p><br></p><p><br></p>`);
|
|
219
221
|
});
|
|
220
222
|
|
|
221
223
|
it('delete tail from inside table to outside', async () => {
|
|
@@ -129,6 +129,7 @@ interface TableCaptionCreatorOptions extends Omit<TableCaptionValue, 'tableId'>
|
|
|
129
129
|
text: string;
|
|
130
130
|
}
|
|
131
131
|
export const datasetWrapTag = (tag: string = 'tbody') => `data-wrap-tag="${tag}"`;
|
|
132
|
+
export const datasetTag = (tag: string = 'td') => `data-tag="${tag}"`;
|
|
132
133
|
export const datasetTableId = (id: string) => `data-table-id="${id}"`;
|
|
133
134
|
export const datasetFull = (full: boolean) => full ? ' data-full="true"' : '';
|
|
134
135
|
export const datasetAlign = (align: string) => align === 'left' ? '' : ` data-align="${align}"`;
|
|
@@ -216,7 +217,7 @@ export function createTableBodyHTML(row: number, col: number, options?: Partial<
|
|
|
216
217
|
<tr ${datasetTableId(tableId)} data-row-id="${i + 1}" ${datasetWrapTag('tbody')}>
|
|
217
218
|
${
|
|
218
219
|
new Array(col).fill(0).map((_, j) => `<td rowspan="1" colspan="1" ${datasetTableId(tableId)} data-row-id="${i + 1}" data-col-id="${j + 1}" ${datasetWrapTag('tbody')}>
|
|
219
|
-
<div
|
|
220
|
+
<div ${datasetTag('td')} ${datasetTableId(tableId)} data-rowspan="1" data-colspan="1" data-row-id="${i + 1}" data-col-id="${j + 1}" ${datasetWrapTag('tbody')}${contenteditableString(editable)}>
|
|
220
221
|
<p>
|
|
221
222
|
${isEmpty ? '<br>' : i * row + j + 1}
|
|
222
223
|
</p>
|
|
@@ -18,9 +18,8 @@ export class BlockEmbedOverride extends BlockEmbed {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
length() {
|
|
21
|
-
// because BlockEmbed is the last line of the tableCellInner. need add value in delta, also need add 1 to length
|
|
22
21
|
const formats = bubbleFormats(this);
|
|
23
|
-
if (formats[blotName.tableCellInner]
|
|
22
|
+
if (formats[blotName.tableCellInner]) {
|
|
24
23
|
return super.length() + 1;
|
|
25
24
|
}
|
|
26
25
|
return super.length();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Parchment as TypeParchment } from 'quill';
|
|
2
2
|
import type TypeBlock from 'quill/blots/block';
|
|
3
|
+
import type { BlockEmbed as TypeBlockEmbed } from 'quill/blots/block';
|
|
3
4
|
import type TypeContainer from 'quill/blots/container';
|
|
4
5
|
import type { TableCellInnerFormat } from '../table-cell-inner-format';
|
|
5
6
|
import Quill from 'quill';
|
|
@@ -8,6 +9,7 @@ import { isSameCellValue } from '../utils';
|
|
|
8
9
|
|
|
9
10
|
const Parchment = Quill.import('parchment');
|
|
10
11
|
const Block = Quill.import('blots/block') as typeof TypeBlock;
|
|
12
|
+
const BlockEmbed = Quill.import('blots/block/embed') as typeof TypeBlockEmbed;
|
|
11
13
|
const Container = Quill.import('blots/container') as typeof TypeContainer;
|
|
12
14
|
|
|
13
15
|
export class BlockOverride extends Block {
|
|
@@ -36,6 +38,9 @@ export class BlockOverride extends Block {
|
|
|
36
38
|
// use TableCellInner insertBefore to find the correct position
|
|
37
39
|
currentTableCellInner.insertBefore(replacement, this);
|
|
38
40
|
replacement.appendChild(this);
|
|
41
|
+
if (currentTableCellInner.children.length === 0) {
|
|
42
|
+
currentTableCellInner.remove();
|
|
43
|
+
}
|
|
39
44
|
}
|
|
40
45
|
else {
|
|
41
46
|
// find the first parent not container
|
|
@@ -52,8 +57,7 @@ export class BlockOverride extends Block {
|
|
|
52
57
|
// split current block as a separate "line" and wrap tableCellInner
|
|
53
58
|
const index = this.offset(currentParent);
|
|
54
59
|
const length = this.length();
|
|
55
|
-
currentParent.
|
|
56
|
-
const selfParent = currentParent.split(index);
|
|
60
|
+
const selfParent = currentParent.isolate(index, length);
|
|
57
61
|
if (selfParent && selfParent.parent) {
|
|
58
62
|
selfParent.parent.insertBefore(replacement, selfParent.next);
|
|
59
63
|
}
|
|
@@ -75,6 +79,7 @@ export class BlockOverride extends Block {
|
|
|
75
79
|
|
|
76
80
|
format(name: string, value: any): void {
|
|
77
81
|
if (name === blotName.tableCellInner && this.parent.statics.blotName === name && !value) {
|
|
82
|
+
if (this.prev && this.prev instanceof BlockEmbed) return;
|
|
78
83
|
try {
|
|
79
84
|
const cellInner = findParentBlot(this, blotName.tableCellInner);
|
|
80
85
|
cellInner.unwrap();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Parchment as TypeParchment } from 'quill';
|
|
2
2
|
import type TypeBlock from 'quill/blots/block';
|
|
3
|
+
import type { BlockEmbed as TypeBlockEmbed } from 'quill/blots/block';
|
|
3
4
|
import type TypeScroll from 'quill/blots/scroll';
|
|
4
5
|
import type { TableBodyTag, TableCellValue } from '../utils';
|
|
5
6
|
import type { TableCellFormat } from './table-cell-format';
|
|
@@ -10,7 +11,7 @@ import { TableBodyFormat } from './table-body-format';
|
|
|
10
11
|
import { getValidCellspan, isSameCellValue } from './utils';
|
|
11
12
|
|
|
12
13
|
const Block = Quill.import('blots/block') as TypeParchment.BlotConstructor;
|
|
13
|
-
const BlockEmbed = Quill.import('blots/block/embed') as
|
|
14
|
+
const BlockEmbed = Quill.import('blots/block/embed') as typeof TypeBlockEmbed;
|
|
14
15
|
|
|
15
16
|
export class TableCellInnerFormat extends ContainerFormat {
|
|
16
17
|
static blotName = blotName.tableCellInner;
|
|
@@ -228,9 +229,10 @@ export class TableCellInnerFormat extends ContainerFormat {
|
|
|
228
229
|
|
|
229
230
|
formatAt(index: number, length: number, name: string, value: any) {
|
|
230
231
|
if (this.children.length === 0) {
|
|
231
|
-
this.
|
|
232
|
+
const defaultChild = this.scroll.create(this.statics.defaultChild.blotName);
|
|
233
|
+
this.appendChild(defaultChild);
|
|
232
234
|
// block min length is 1
|
|
233
|
-
length +=
|
|
235
|
+
length += defaultChild.length();
|
|
234
236
|
}
|
|
235
237
|
super.formatAt(index, length, name, value);
|
|
236
238
|
// set style for `td`
|
|
@@ -247,6 +249,14 @@ export class TableCellInnerFormat extends ContainerFormat {
|
|
|
247
249
|
this.appendChild(defaultChild);
|
|
248
250
|
}
|
|
249
251
|
super.insertAt(index, value, def);
|
|
252
|
+
// BlockEmbed will have a \n in delta, this will effect history stack
|
|
253
|
+
// so when insert a BlockEmbed, if current child length <= 1 then remove it
|
|
254
|
+
const blot = def == null
|
|
255
|
+
? this.scroll.create('text', value)
|
|
256
|
+
: this.scroll.create(value, def);
|
|
257
|
+
if (blot instanceof BlockEmbed && child && child.length() <= 1) {
|
|
258
|
+
child.remove();
|
|
259
|
+
}
|
|
250
260
|
}
|
|
251
261
|
|
|
252
262
|
formats(): Record<string, any> {
|
|
@@ -339,10 +349,16 @@ export class TableCellInnerFormat extends ContainerFormat {
|
|
|
339
349
|
cellRef = newCell;
|
|
340
350
|
}
|
|
341
351
|
}
|
|
352
|
+
if (this.tableId !== cellInnerBlot.tableId) {
|
|
353
|
+
const selfTableWrapper = findParentBlot(this, blotName.tableWrapper);
|
|
354
|
+
const index = this.offset(selfTableWrapper);
|
|
355
|
+
const afterSelfTableWrapper = selfTableWrapper.split(index);
|
|
356
|
+
return selfTableWrapper.parent.insertBefore(cellInnerBlot, afterSelfTableWrapper);
|
|
357
|
+
}
|
|
342
358
|
// different rowId. split current row
|
|
343
359
|
if (this.rowId !== cellInnerBlot.rowId) {
|
|
344
360
|
let rowRef: TypeParchment.Blot | null = selfRow;
|
|
345
|
-
const splitRef = ref
|
|
361
|
+
const splitRef = ref;
|
|
346
362
|
if (splitRef) {
|
|
347
363
|
const index = splitRef.offset(selfRow);
|
|
348
364
|
rowRef = selfRow.split(index);
|
|
@@ -3,7 +3,6 @@ import type { BlockEmbed as TypeBlockEmbed } from 'quill/blots/block';
|
|
|
3
3
|
import type { TableColValue } from '../utils';
|
|
4
4
|
import Quill from 'quill';
|
|
5
5
|
import { blotName, findParentBlot, tableUpSize } from '../utils';
|
|
6
|
-
import { TableCellInnerFormat } from './table-cell-inner-format';
|
|
7
6
|
|
|
8
7
|
const BlockEmbed = Quill.import('blots/block/embed') as typeof TypeBlockEmbed;
|
|
9
8
|
|
|
@@ -144,43 +143,40 @@ export class TableColFormat extends BlockEmbed {
|
|
|
144
143
|
|
|
145
144
|
insertAt(index: number, value: string, def?: any): void {
|
|
146
145
|
if (def != null) {
|
|
147
|
-
|
|
146
|
+
if (value === this.statics.blotName && def.tableId !== this.tableId) {
|
|
147
|
+
try {
|
|
148
|
+
const tableWrapperBlot = findParentBlot(this, blotName.tableWrapper);
|
|
149
|
+
const parentBlot = tableWrapperBlot.split(this.offset(tableWrapperBlot)) as TypeParchment.Parent;
|
|
150
|
+
|
|
151
|
+
const blot = this.scroll.create(value, def);
|
|
152
|
+
parentBlot.parent.insertBefore(blot, parentBlot);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// here should not trigger
|
|
156
|
+
console.warn('TableCol not in TableColgroup');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
super.insertAt(index, value, def);
|
|
161
|
+
}
|
|
148
162
|
return;
|
|
149
163
|
}
|
|
150
164
|
try {
|
|
165
|
+
const tableWrapperBlot = findParentBlot(this, blotName.tableWrapper);
|
|
166
|
+
const parentBlot = tableWrapperBlot.split(this.offset(tableWrapperBlot)) as TypeParchment.Parent;
|
|
167
|
+
|
|
151
168
|
const lines = value.split('\n');
|
|
152
169
|
const text = lines.pop();
|
|
153
|
-
const
|
|
154
|
-
|
|
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
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
for (const line of lines) {
|
|
176
|
-
const block = this.scroll.create('block');
|
|
170
|
+
const blocks = lines.map((line) => {
|
|
171
|
+
const block = this.scroll.create('block') as TypeParchment.ParentBlot;
|
|
177
172
|
block.insertAt(0, line);
|
|
178
|
-
|
|
173
|
+
return block;
|
|
174
|
+
});
|
|
175
|
+
for (const block of blocks) {
|
|
176
|
+
parentBlot.parent.insertBefore(block, parentBlot);
|
|
179
177
|
}
|
|
180
178
|
if (text) {
|
|
181
|
-
|
|
182
|
-
lineBlock.appendChild(this.scroll.create('text', text));
|
|
183
|
-
insertBlot.appendChild(lineBlock);
|
|
179
|
+
parentBlot.parent.insertBefore(this.scroll.create('text', text), parentBlot);
|
|
184
180
|
}
|
|
185
181
|
}
|
|
186
182
|
catch {
|
|
@@ -55,11 +55,19 @@ export class TableWrapperFormat extends ContainerFormat {
|
|
|
55
55
|
);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
optimize(context: Record<string, any>) {
|
|
59
|
+
if (this.length() === 0) {
|
|
60
|
+
this.remove();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
super.optimize(context);
|
|
64
|
+
}
|
|
65
|
+
|
|
58
66
|
deleteAt(index: number, length: number) {
|
|
59
67
|
super.deleteAt(index, length);
|
|
60
68
|
const tableBodys = this.descendants(TableBodyFormat);
|
|
61
69
|
const tableColgroups = this.descendants(TableColgroupFormat);
|
|
62
|
-
if (tableBodys.length === 0
|
|
70
|
+
if (tableBodys.length === 0 && tableColgroups.length === 0) {
|
|
63
71
|
this.remove();
|
|
64
72
|
}
|
|
65
73
|
}
|
|
@@ -108,7 +108,10 @@ export class TableSelection extends TableDomSelector {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
for (const td of this.selectedTds) {
|
|
111
|
-
|
|
111
|
+
const clearTd = td.clone() as TypeParchment.Parent;
|
|
112
|
+
clearTd.appendChild(td.scroll.create('block'));
|
|
113
|
+
td.parent.insertBefore(clearTd, td);
|
|
114
|
+
td.remove();
|
|
112
115
|
}
|
|
113
116
|
};
|
|
114
117
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"3.2.3","results":[[":__tests__/unit/utils.test-d.ts",{"duration":0,"failed":false}],[":__tests__/unit/table-clipboard.test.ts",{"duration":
|
|
1
|
+
{"version":"3.2.3","results":[[":__tests__/unit/utils.test-d.ts",{"duration":0,"failed":false}],[":__tests__/unit/table-clipboard.test.ts",{"duration":0,"failed":false}],[":__tests__/unit/table-redo-undo.test.ts",{"duration":0,"failed":false}],[":__tests__/unit/table-hack.test.ts",{"duration":0,"failed":false}],[":__tests__/unit/table-cell-merge.test.ts",{"duration":0,"failed":false}],[":__tests__/unit/table-insert.test.ts",{"duration":0,"failed":false}],[":__tests__/unit/table-blots.test.ts",{"duration":0,"failed":false}],[":__tests__/unit/utils.test.ts",{"duration":0,"failed":false}],[":__tests__/unit/table-caption.test.ts",{"duration":834.3326999999999,"failed":false}],[":__tests__/unit/table-remove.test.ts",{"duration":1954.0650999999998,"failed":false}]]}
|
package/src/table-up.ts
CHANGED
|
@@ -244,10 +244,8 @@ export class TableUp {
|
|
|
244
244
|
const blotName = name.split('formats/')[1];
|
|
245
245
|
return name.startsWith('formats/')
|
|
246
246
|
&& !excludeFormat.has(blotName)
|
|
247
|
-
&& !isSubclassOf(blot, Parchment.Attributor)
|
|
248
247
|
&& (isSubclassOf(blot, Block) || isSubclassOf(blot, BlockEmbed));
|
|
249
|
-
}
|
|
250
|
-
);
|
|
248
|
+
});
|
|
251
249
|
const overrides = overrideFormats.reduce((pre, [name, blot]) => {
|
|
252
250
|
const extendsClass = isSubclassOf(blot, BlockEmbed) ? BlockEmbedOverride : BlockOverride;
|
|
253
251
|
pre[name] = class extends mixinClass(blot, [extendsClass]) {
|
|
@@ -847,8 +845,12 @@ export class TableUp {
|
|
|
847
845
|
|
|
848
846
|
// handle unusual delete cell
|
|
849
847
|
fixUnusuaDeletelTable(tableBlot: TableMainFormat) {
|
|
850
|
-
const bodys = tableBlot.getBodys();
|
|
851
848
|
const tableColIds = tableBlot.getColIds();
|
|
849
|
+
if (tableColIds.length === 0) {
|
|
850
|
+
tableBlot.remove();
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
const bodys = tableBlot.getBodys();
|
|
852
854
|
const tableId = tableBlot.tableId;
|
|
853
855
|
for (const body of bodys) {
|
|
854
856
|
// calculate all cells in body
|
|
@@ -857,7 +859,6 @@ export class TableUp {
|
|
|
857
859
|
body.remove();
|
|
858
860
|
continue;
|
|
859
861
|
}
|
|
860
|
-
if (tableColIds.length === 0) continue;
|
|
861
862
|
// append by col
|
|
862
863
|
const cellSpanMap = new Array(trBlots.length).fill(0).map(() => new Array(tableColIds.length).fill(false));
|
|
863
864
|
for (const [indexTr, tr] of trBlots.entries()) {
|
package/src/utils/blot-helper.ts
CHANGED
|
@@ -101,5 +101,5 @@ export function mixinClass<
|
|
|
101
101
|
return targetClass;
|
|
102
102
|
}
|
|
103
103
|
export function isSubclassOf(childClass: any, parentClass: any): boolean {
|
|
104
|
-
return childClass.prototype instanceof parentClass;
|
|
104
|
+
return childClass.prototype && childClass.prototype instanceof parentClass;
|
|
105
105
|
}
|