quill-table-up 2.0.4 → 2.0.6
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 +17 -1
- 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 +3 -3
- package/src/__tests__/unit/table-clipboard.test.ts +608 -0
- package/src/__tests__/unit/table-insert-blot.test.ts +0 -143
- package/src/formats/table-col-format.ts +4 -0
- package/src/index.ts +108 -56
- package/src/modules/user-selection.ts +110 -0
|
@@ -319,146 +319,3 @@ describe('column width calculate', () => {
|
|
|
319
319
|
expect(quill.root.querySelectorAll('table')[0].style.width).toBe('300px');
|
|
320
320
|
});
|
|
321
321
|
});
|
|
322
|
-
|
|
323
|
-
describe('html convert', () => {
|
|
324
|
-
it('should convert html code-block correctly', async () => {
|
|
325
|
-
const quill = createQuillWithTableModule(`<p><br></p>`);
|
|
326
|
-
quill.setContents(
|
|
327
|
-
quill.clipboard.convert({ html: '<div class="ql-table-wrapper" data-table-id="1"><table data-table-id="1" class="ql-table" 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"><td class="ql-table-cell" data-table-id="1" data-row-id="1" data-col-id="1" 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"><p>1</p></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="1" data-col-id="2" 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"><p>2</p></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="1" data-col-id="3" 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"><p>3</p></div></td></tr><tr class="ql-table-row" data-table-id="1" data-row-id="2"><td class="ql-table-cell" data-table-id="1" data-row-id="2" data-col-id="1" 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"><p>4</p></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="2" data-col-id="2" 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"><div class="ql-code-block-container" spellcheck="false"><div class="ql-code-block" data-language="plain">5</div><div class="ql-code-block" data-language="plain">5</div><div class="ql-code-block" data-language="plain">5</div></div><p>5</p><p>5</p><p>5</p></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="2" data-col-id="3" 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"><p>6</p></div></td></tr><tr class="ql-table-row" data-table-id="1" data-row-id="3"><td class="ql-table-cell" data-table-id="1" data-row-id="3" data-col-id="1" 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"><p>7</p></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="3" data-col-id="2" 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"><p>8</p></div></td><td class="ql-table-cell" data-table-id="1" data-row-id="3" data-col-id="3" 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"><p>9</p></div></td></tr></tbody></table></div>' }),
|
|
328
|
-
);
|
|
329
|
-
await vi.runAllTimersAsync();
|
|
330
|
-
|
|
331
|
-
const ops = quill.getContents().ops;
|
|
332
|
-
const resultOps = [
|
|
333
|
-
{ insert: '\n' },
|
|
334
|
-
{ insert: { 'table-up-col': { full: false, width: 121 } } },
|
|
335
|
-
{ insert: { 'table-up-col': { full: false, width: 121 } } },
|
|
336
|
-
{ insert: { 'table-up-col': { full: false, width: 121 } } },
|
|
337
|
-
{ insert: '1' },
|
|
338
|
-
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
339
|
-
{ insert: '2' },
|
|
340
|
-
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
341
|
-
{ insert: '3' },
|
|
342
|
-
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
343
|
-
{ insert: '4' },
|
|
344
|
-
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
345
|
-
{ insert: '5' },
|
|
346
|
-
{ attributes: { 'code-block': 'plain', 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
347
|
-
{ insert: '5' },
|
|
348
|
-
{ attributes: { 'code-block': 'plain', 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
349
|
-
{ insert: '5' },
|
|
350
|
-
{ attributes: { 'code-block': 'plain', 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
351
|
-
{ insert: '5' },
|
|
352
|
-
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
353
|
-
{ insert: '5' },
|
|
354
|
-
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
355
|
-
{ insert: '5' },
|
|
356
|
-
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
357
|
-
{ insert: '6' },
|
|
358
|
-
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
359
|
-
{ insert: '7' },
|
|
360
|
-
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
361
|
-
{ insert: '8' },
|
|
362
|
-
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
363
|
-
{ insert: '9' },
|
|
364
|
-
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
365
|
-
{ insert: '\n' },
|
|
366
|
-
];
|
|
367
|
-
for (const [i, op] of ops.entries()) {
|
|
368
|
-
expect(op).toMatchObject(resultOps[i]);
|
|
369
|
-
}
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
it('should convert html header correctly', async () => {
|
|
373
|
-
const quill = createQuillWithTableModule(`<p><br></p>`);
|
|
374
|
-
quill.setContents(
|
|
375
|
-
quill.clipboard.convert({ html: '<div class="ql-table-wrapper" data-table-id="rlabchug06i"><table data-table-id="rlabchug06i" class="ql-table" cellpadding="0" cellspacing="0" style="margin-right: auto; width: 1166px;"><colgroup data-table-id="rlabchug06i" contenteditable="false"><col width="583px" data-table-id="rlabchug06i" data-col-id="ss993p1sqx"><col width="583px" data-table-id="rlabchug06i" data-col-id="2ixexk4mapx"></colgroup><tbody data-table-id="rlabchug06i"><tr class="ql-table-row" data-table-id="rlabchug06i" data-row-id="fvvkgpv86zw"><td class="ql-table-cell" data-table-id="rlabchug06i" data-row-id="fvvkgpv86zw" data-col-id="ss993p1sqx" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="rlabchug06i" data-row-id="fvvkgpv86zw" data-col-id="ss993p1sqx" data-rowspan="1" data-colspan="1"><h1>header1</h1></div></td><td class="ql-table-cell" data-table-id="rlabchug06i" data-row-id="fvvkgpv86zw" data-col-id="2ixexk4mapx" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="rlabchug06i" data-row-id="fvvkgpv86zw" data-col-id="2ixexk4mapx" data-rowspan="1" data-colspan="1"><h3>header3</h3></div></td></tr></tbody></table></div>' }),
|
|
376
|
-
);
|
|
377
|
-
await vi.runAllTimersAsync();
|
|
378
|
-
|
|
379
|
-
const ops = quill.getContents().ops;
|
|
380
|
-
const resultOps = [
|
|
381
|
-
{ insert: '\n' },
|
|
382
|
-
{ insert: { 'table-up-col': { full: false, width: 583 } } },
|
|
383
|
-
{ insert: { 'table-up-col': { full: false, width: 583 } } },
|
|
384
|
-
{ insert: 'header1' },
|
|
385
|
-
{ attributes: { 'header': 1, 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
386
|
-
{ insert: 'header3' },
|
|
387
|
-
{ attributes: { 'header': 3, 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
388
|
-
{ insert: '\n' },
|
|
389
|
-
];
|
|
390
|
-
for (const [i, op] of ops.entries()) {
|
|
391
|
-
expect(op).toMatchObject(resultOps[i]);
|
|
392
|
-
}
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
it('should convert html image correctly', async () => {
|
|
396
|
-
const quill = createQuillWithTableModule(`<p><br></p>`);
|
|
397
|
-
quill.setContents(
|
|
398
|
-
quill.clipboard.convert({ html: '<div class="ql-table-wrapper" data-table-id="7oymehdtx0k"><table data-table-id="7oymehdtx0k" data-full="true" class="ql-table" cellpadding="0" cellspacing="0" style="margin-right: auto;"><colgroup data-table-id="7oymehdtx0k" data-full="true" contenteditable="false"><col width="100%" data-full="true" data-table-id="7oymehdtx0k" data-col-id="hr7qo4t2dus"></colgroup><tbody data-table-id="7oymehdtx0k"><tr class="ql-table-row" data-table-id="7oymehdtx0k" data-row-id="69gog08ow04"><td class="ql-table-cell" data-table-id="7oymehdtx0k" data-row-id="69gog08ow04" data-col-id="hr7qo4t2dus" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="7oymehdtx0k" data-row-id="69gog08ow04" data-col-id="hr7qo4t2dus" data-rowspan="1" data-colspan="1"><p><img src="https://upload-bbs.miyoushe.com/upload/2024/06/18/5556092/73b7bae28fded7a72d93a35d5559b24c_3979852353547906724.png"></p></div></td></tr></tbody></table></div>' }),
|
|
399
|
-
);
|
|
400
|
-
await vi.runAllTimersAsync();
|
|
401
|
-
|
|
402
|
-
const ops = quill.getContents().ops;
|
|
403
|
-
const resultOps = [
|
|
404
|
-
{ insert: '\n' },
|
|
405
|
-
{ insert: { 'table-up-col': { full: true, width: 100 } } },
|
|
406
|
-
{ insert: { image: 'https://upload-bbs.miyoushe.com/upload/2024/06/18/5556092/73b7bae28fded7a72d93a35d5559b24c_3979852353547906724.png' } },
|
|
407
|
-
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
408
|
-
{ insert: '\n' },
|
|
409
|
-
];
|
|
410
|
-
for (const [i, op] of ops.entries()) {
|
|
411
|
-
expect(op).toMatchObject(resultOps[i]);
|
|
412
|
-
}
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
it('should convert html video correctly', async () => {
|
|
416
|
-
const quill = createQuillWithTableModule(`<p><br></p>`);
|
|
417
|
-
quill.setContents(
|
|
418
|
-
quill.clipboard.convert({ html: '<div class="ql-table-wrapper" data-table-id="1"><table data-table-id="1" data-full="true" class="ql-table" cellpadding="0" cellspacing="0" style="margin-right: auto;"><colgroup data-table-id="1" data-full="true" contenteditable="false"><col width="100%" data-full="true" data-table-id="1" data-col-id="1"></colgroup><tbody data-table-id="1"><tr class="ql-table-row" data-table-id="1" data-row-id="1"><td class="ql-table-cell" data-table-id="1" data-row-id="1" data-col-id="1" 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"><iframe class="ql-video" frameborder="0" allowfullscreen="true" src="http://127.0.0.1:5500/docs/index.html"></iframe><p><br></p></div></td></tr></tbody></table></div>' }),
|
|
419
|
-
);
|
|
420
|
-
await vi.runAllTimersAsync();
|
|
421
|
-
|
|
422
|
-
const ops = quill.getContents().ops;
|
|
423
|
-
const resultOps = [
|
|
424
|
-
{ insert: '\n' },
|
|
425
|
-
{ insert: { 'table-up-col': { full: true, width: 100 } } },
|
|
426
|
-
{ insert: { video: 'http://127.0.0.1:5500/docs/index.html' } },
|
|
427
|
-
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
428
|
-
{ insert: '\n' },
|
|
429
|
-
];
|
|
430
|
-
for (const [i, op] of ops.entries()) {
|
|
431
|
-
expect(op).toMatchObject(resultOps[i]);
|
|
432
|
-
}
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
it('should convert html list correctly', async () => {
|
|
436
|
-
const quill = createQuillWithTableModule(`<p><br></p>`);
|
|
437
|
-
quill.setContents(
|
|
438
|
-
quill.clipboard.convert({ html: '<div class="ql-table-wrapper" data-table-id="ls2bw9dtr6m"><table data-table-id="ls2bw9dtr6m" class="ql-table" cellpadding="0" cellspacing="0" style="margin-right: auto; width: 1166px;"><colgroup data-table-id="ls2bw9dtr6m" contenteditable="false"><col width="583px" data-table-id="ls2bw9dtr6m" data-col-id="p3imc1n7xlo"><col width="583px" data-table-id="ls2bw9dtr6m" data-col-id="ndvtber87yq"></colgroup><tbody data-table-id="ls2bw9dtr6m"><tr class="ql-table-row" data-table-id="ls2bw9dtr6m" data-row-id="bztm23s128n"><td class="ql-table-cell" data-table-id="ls2bw9dtr6m" data-row-id="bztm23s128n" data-col-id="p3imc1n7xlo" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="ls2bw9dtr6m" data-row-id="bztm23s128n" data-col-id="p3imc1n7xlo" data-rowspan="1" data-colspan="1"><ol><li data-list="ordered"><span class="ql-ui" contenteditable="false"></span>list order</li><li data-list="ordered" class="ql-indent-1"><span class="ql-ui" contenteditable="false"></span>aaa</li></ol></div></td><td class="ql-table-cell" data-table-id="ls2bw9dtr6m" data-row-id="bztm23s128n" data-col-id="ndvtber87yq" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="ls2bw9dtr6m" data-row-id="bztm23s128n" data-col-id="ndvtber87yq" data-rowspan="1" data-colspan="1"><ol><li data-list="bullet"><span class="ql-ui" contenteditable="false"></span>list bullet</li></ol></div></td></tr><tr class="ql-table-row" data-table-id="ls2bw9dtr6m" data-row-id="3kx2tsgoao5"><td class="ql-table-cell" data-table-id="ls2bw9dtr6m" data-row-id="3kx2tsgoao5" data-col-id="p3imc1n7xlo" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="ls2bw9dtr6m" data-row-id="3kx2tsgoao5" data-col-id="p3imc1n7xlo" data-rowspan="1" data-colspan="1"><ol><li data-list="unchecked"><span class="ql-ui" contenteditable="false"></span>list checkbox</li><li data-list="checked"><span class="ql-ui" contenteditable="false"></span>checkbox checked</li></ol></div></td><td class="ql-table-cell" data-table-id="ls2bw9dtr6m" data-row-id="3kx2tsgoao5" data-col-id="ndvtber87yq" rowspan="1" colspan="1"><div class="ql-table-cell-inner" data-table-id="ls2bw9dtr6m" data-row-id="3kx2tsgoao5" data-col-id="ndvtber87yq" data-rowspan="1" data-colspan="1"><p><br></p></div></td></tr></tbody></table></div>' }),
|
|
439
|
-
);
|
|
440
|
-
await vi.runAllTimersAsync();
|
|
441
|
-
|
|
442
|
-
const ops = quill.getContents().ops;
|
|
443
|
-
const resultOps = [
|
|
444
|
-
{ insert: '\n' },
|
|
445
|
-
{ insert: { 'table-up-col': { full: false, width: 583 } } },
|
|
446
|
-
{ insert: { 'table-up-col': { full: false, width: 583 } } },
|
|
447
|
-
{ insert: 'list order' },
|
|
448
|
-
{ attributes: { 'list': 'ordered', 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
449
|
-
{ insert: 'aaa' },
|
|
450
|
-
{ attributes: { 'indent': 1, 'list': 'ordered', 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
451
|
-
{ insert: 'list bullet' },
|
|
452
|
-
{ attributes: { 'list': 'bullet', 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
453
|
-
{ insert: 'list checkbox' },
|
|
454
|
-
{ attributes: { 'list': 'unchecked', 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
455
|
-
{ insert: 'checkbox checked' },
|
|
456
|
-
{ attributes: { 'list': 'checked', 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
457
|
-
{ attributes: { 'table-up-cell-inner': { rowspan: 1, colspan: 1 } }, insert: '\n' },
|
|
458
|
-
{ insert: '\n' },
|
|
459
|
-
];
|
|
460
|
-
for (const [i, op] of ops.entries()) {
|
|
461
|
-
expect(op).toMatchObject(resultOps[i]);
|
|
462
|
-
}
|
|
463
|
-
});
|
|
464
|
-
});
|
|
@@ -122,6 +122,10 @@ export class TableColFormat extends BlockEmbed {
|
|
|
122
122
|
const tableColgroup = findParentBlot(this, blotName.tableColgroup);
|
|
123
123
|
tableColgroup.align = this.align;
|
|
124
124
|
|
|
125
|
+
if (this.next != null && this.checkMerge()) {
|
|
126
|
+
this.next.remove();
|
|
127
|
+
}
|
|
128
|
+
|
|
125
129
|
super.optimize(context);
|
|
126
130
|
}
|
|
127
131
|
|
package/src/index.ts
CHANGED
|
@@ -272,7 +272,7 @@ export class TableUp {
|
|
|
272
272
|
},
|
|
273
273
|
false,
|
|
274
274
|
);
|
|
275
|
-
this.quill.on(Quill.events.EDITOR_CHANGE, (event: string, range: Range,
|
|
275
|
+
this.quill.on(Quill.events.EDITOR_CHANGE, (event: string, range: Range, _oldRange: Range) => {
|
|
276
276
|
if (event === Quill.events.SELECTION_CHANGE && range) {
|
|
277
277
|
const [startBlot] = this.quill.getLine(range.index);
|
|
278
278
|
const [endBlot] = this.quill.getLine(range.index + range.length);
|
|
@@ -287,29 +287,30 @@ export class TableUp {
|
|
|
287
287
|
}
|
|
288
288
|
catch {}
|
|
289
289
|
|
|
290
|
-
//
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
290
|
+
// // TODO: selection some times will be reject mouse drag select
|
|
291
|
+
// // only can select inside table or select all table
|
|
292
|
+
// if (startBlot instanceof TableColFormat) {
|
|
293
|
+
// if (!oldRange) {
|
|
294
|
+
// oldRange = { index: 0, length: 0 };
|
|
295
|
+
// }
|
|
296
|
+
// return this.quill.setSelection(
|
|
297
|
+
// range.index + (oldRange.index > range.index ? -1 : 1),
|
|
298
|
+
// range.length + (oldRange.length === range.length ? 0 : oldRange.length > range.length ? -1 : 1),
|
|
299
|
+
// Quill.sources.USER,
|
|
300
|
+
// );
|
|
301
|
+
// }
|
|
302
|
+
// else if (endBlot instanceof TableColFormat) {
|
|
303
|
+
// return this.quill.setSelection(range.index + 1, range.length + 1, Quill.sources.USER);
|
|
304
|
+
// }
|
|
305
|
+
|
|
306
|
+
// if (range.length > 0) {
|
|
307
|
+
// if (startTableBlot && !endTableBlot) {
|
|
308
|
+
// this.quill.setSelection(range.index - 1, range.length + 1, Quill.sources.USER);
|
|
309
|
+
// }
|
|
310
|
+
// else if (endTableBlot && !startTableBlot) {
|
|
311
|
+
// this.quill.setSelection(range.index, range.length + 1, Quill.sources.USER);
|
|
312
|
+
// }
|
|
313
|
+
// }
|
|
313
314
|
|
|
314
315
|
// if range is not in table. hide table tools
|
|
315
316
|
if (!startTableBlot || !endTableBlot) {
|
|
@@ -375,6 +376,7 @@ export class TableUp {
|
|
|
375
376
|
let tableId = randomId();
|
|
376
377
|
let rowId = randomId();
|
|
377
378
|
let colIds: string[] = [];
|
|
379
|
+
let rowspanCount: { rowspan: number; colspan: number }[] = [];
|
|
378
380
|
let cellCount = 0;
|
|
379
381
|
let colCount = 0;
|
|
380
382
|
|
|
@@ -396,51 +398,70 @@ export class TableUp {
|
|
|
396
398
|
|
|
397
399
|
this.quill.clipboard.addMatcher('table', (node, delta) => {
|
|
398
400
|
if (delta.ops.length === 0) return delta;
|
|
399
|
-
|
|
401
|
+
|
|
400
402
|
const format = this.quill.getFormat();
|
|
401
|
-
|
|
402
|
-
// remove quill origin table format
|
|
403
|
+
const currentCellFormat = format[blotName.tableCellInner];
|
|
403
404
|
const ops: Record<string, any>[] = [];
|
|
404
405
|
const cols: Record<string, any>[] = [];
|
|
405
406
|
for (let i = 0; i < delta.ops.length; i++) {
|
|
406
407
|
const { attributes, insert } = delta.ops[i];
|
|
408
|
+
// remove quill origin table format and tableCell format
|
|
407
409
|
const { table, [blotName.tableCell]: tableCell, ...attrs } = attributes || {};
|
|
408
|
-
|
|
409
|
-
|
|
410
|
+
const hasCol = insert && (insert as Record<string, any>)[blotName.tableCol];
|
|
411
|
+
if (currentCellFormat) {
|
|
412
|
+
// if current in cell. no need add col. but need replace paste cell format with current cell format
|
|
413
|
+
if (hasCol) continue;
|
|
414
|
+
const { [blotName.tableCellInner]: tableCellInner, ...keepAtttrs } = attrs;
|
|
415
|
+
ops.push({
|
|
416
|
+
attributes: {
|
|
417
|
+
...keepAtttrs,
|
|
418
|
+
[blotName.tableCellInner]: currentCellFormat,
|
|
419
|
+
},
|
|
420
|
+
insert,
|
|
421
|
+
});
|
|
410
422
|
}
|
|
411
423
|
else {
|
|
412
|
-
|
|
424
|
+
if (hasCol) {
|
|
425
|
+
cols.push({ insert });
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
ops.push({ attributes: attrs, insert });
|
|
429
|
+
}
|
|
413
430
|
}
|
|
414
431
|
}
|
|
415
432
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
433
|
+
// if current in cell. no need add col
|
|
434
|
+
if (!currentCellFormat) {
|
|
435
|
+
const colWidths = calculateCols(node as HTMLElement, colIds.length);
|
|
436
|
+
const newCols = colWidths.reduce((colOps, width, i) => {
|
|
437
|
+
if (!cols[i]) {
|
|
438
|
+
colOps.push({
|
|
439
|
+
insert: {
|
|
440
|
+
[blotName.tableCol]: {
|
|
441
|
+
tableId,
|
|
442
|
+
colId: colIds[i],
|
|
443
|
+
width,
|
|
444
|
+
full: false,
|
|
445
|
+
},
|
|
426
446
|
},
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
colOps.push(cols[i]);
|
|
451
|
+
}
|
|
452
|
+
return colOps;
|
|
453
|
+
}, [] as Record<string, any>[]);
|
|
454
|
+
ops.unshift(...newCols);
|
|
455
|
+
// insert break line before table and after table
|
|
456
|
+
ops.unshift({ insert: '\n' });
|
|
457
|
+
ops.push({ insert: '\n' });
|
|
458
|
+
}
|
|
436
459
|
// reset variable to avoid conflict with other table
|
|
437
460
|
tableId = randomId();
|
|
438
461
|
colIds = [];
|
|
462
|
+
rowspanCount = [];
|
|
439
463
|
cellCount = 0;
|
|
440
464
|
colCount = 0;
|
|
441
|
-
// insert break line before table and after table
|
|
442
|
-
ops.unshift({ insert: '\n' });
|
|
443
|
-
ops.push({ insert: '\n' });
|
|
444
465
|
return new Delta(ops);
|
|
445
466
|
});
|
|
446
467
|
|
|
@@ -482,17 +503,39 @@ export class TableUp {
|
|
|
482
503
|
(op.attributes[blotName.tableCellInner] as Record<string, any>).style = `background:${op.attributes.background};${cellAttrs.style}`;
|
|
483
504
|
}
|
|
484
505
|
}
|
|
506
|
+
// minus rowspan
|
|
507
|
+
for (const [i, span] of rowspanCount.entries()) {
|
|
508
|
+
if (span.rowspan > 0) {
|
|
509
|
+
span.rowspan -= 1;
|
|
510
|
+
}
|
|
511
|
+
if (span.rowspan <= 0) {
|
|
512
|
+
rowspanCount[i] = { rowspan: 0, colspan: 0 };
|
|
513
|
+
}
|
|
514
|
+
}
|
|
485
515
|
return delta;
|
|
486
516
|
});
|
|
487
517
|
|
|
488
518
|
const matchCell = (node: Node, delta: TypeDelta) => {
|
|
489
519
|
const cell = node as HTMLElement;
|
|
490
520
|
const cellFormat = TableCellFormat.formats(cell);
|
|
491
|
-
if (!colIds[cellCount]) {
|
|
521
|
+
if (!colIds[cellCount] || !rowspanCount[cellCount]) {
|
|
492
522
|
for (let i = cellCount; i >= 0; i--) {
|
|
493
|
-
if (!colIds[i])
|
|
523
|
+
if (!colIds[i]) {
|
|
524
|
+
colIds[i] = randomId();
|
|
525
|
+
}
|
|
526
|
+
if (!rowspanCount[i]) {
|
|
527
|
+
rowspanCount[i] = { rowspan: 0, colspan: 0 };
|
|
528
|
+
}
|
|
494
529
|
}
|
|
495
530
|
}
|
|
531
|
+
// skip the colspan of the cell in the previous row
|
|
532
|
+
const { colspan } = rowspanCount[cellCount];
|
|
533
|
+
cellCount += colspan;
|
|
534
|
+
// add current cell rowspan in `rowspanCount` to calculate next row cell
|
|
535
|
+
if (cellFormat.rowspan > 1) {
|
|
536
|
+
rowspanCount[cellCount] = { rowspan: cellFormat.rowspan, colspan: cellFormat.colspan };
|
|
537
|
+
}
|
|
538
|
+
|
|
496
539
|
const colId = colIds[cellCount];
|
|
497
540
|
cellCount += cellFormat.colspan;
|
|
498
541
|
|
|
@@ -520,7 +563,6 @@ export class TableUp {
|
|
|
520
563
|
}
|
|
521
564
|
return new Delta(ops);
|
|
522
565
|
};
|
|
523
|
-
|
|
524
566
|
this.quill.clipboard.addMatcher('td', matchCell);
|
|
525
567
|
this.quill.clipboard.addMatcher('th', matchCell);
|
|
526
568
|
}
|
|
@@ -1153,5 +1195,15 @@ export function defaultCustomSelect(tableModule: TableUp, picker: QuillThemePick
|
|
|
1153
1195
|
export default TableUp;
|
|
1154
1196
|
export * from './formats';
|
|
1155
1197
|
export * from './modules';
|
|
1156
|
-
export {
|
|
1198
|
+
export {
|
|
1199
|
+
blotName,
|
|
1200
|
+
createColorPicker,
|
|
1201
|
+
createSelectBox,
|
|
1202
|
+
createTooltip,
|
|
1203
|
+
findParentBlot,
|
|
1204
|
+
findParentBlots,
|
|
1205
|
+
randomId,
|
|
1206
|
+
tableUpEvent,
|
|
1207
|
+
tableUpSize,
|
|
1208
|
+
} from './utils';
|
|
1157
1209
|
export * from './utils/types';
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { Parchment as TypeParchment, Range as TypeRange } from 'quill';
|
|
2
|
+
import type TableUp from '..';
|
|
3
|
+
import type { TableCellInnerFormat } from '../formats';
|
|
4
|
+
import Quill from 'quill';
|
|
5
|
+
import { blotName, findParentBlot } from '../utils';
|
|
6
|
+
|
|
7
|
+
export class UserSelection {
|
|
8
|
+
limitKeyboardSelection: boolean = false;
|
|
9
|
+
selectionDirection: number | null = null;
|
|
10
|
+
lastValidSelection: {
|
|
11
|
+
startContainer: Node;
|
|
12
|
+
startOffset: number;
|
|
13
|
+
endContainer: Node;
|
|
14
|
+
endOffset: number;
|
|
15
|
+
} | null = null;
|
|
16
|
+
|
|
17
|
+
constructor(public quill: Quill, public tableModule: TableUp) {
|
|
18
|
+
document.addEventListener('selectionchange', () => {
|
|
19
|
+
const selection = document.getSelection();
|
|
20
|
+
if (!selection) return;
|
|
21
|
+
const { anchorNode, focusNode } = selection;
|
|
22
|
+
if (!anchorNode || !focusNode) return;
|
|
23
|
+
const anchorBlot = Quill.find(anchorNode);
|
|
24
|
+
const focusBlot = Quill.find(focusNode);
|
|
25
|
+
if (!anchorBlot || !focusBlot) return;
|
|
26
|
+
let anchorCellBlot: TableCellInnerFormat | null = null;
|
|
27
|
+
let focusCellBlot: TableCellInnerFormat | null = null;
|
|
28
|
+
try {
|
|
29
|
+
anchorCellBlot = findParentBlot(anchorBlot as TypeParchment.Blot, blotName.tableCellInner);
|
|
30
|
+
}
|
|
31
|
+
catch {}
|
|
32
|
+
try {
|
|
33
|
+
focusCellBlot = findParentBlot(focusBlot as TypeParchment.Blot, blotName.tableCellInner);
|
|
34
|
+
}
|
|
35
|
+
catch {}
|
|
36
|
+
|
|
37
|
+
if ((anchorCellBlot && !focusCellBlot) || (focusCellBlot && !anchorCellBlot)) {
|
|
38
|
+
// TODO: select whole table
|
|
39
|
+
this.quill.blur();
|
|
40
|
+
}
|
|
41
|
+
else if (anchorCellBlot !== focusCellBlot) {
|
|
42
|
+
this.limitKeyboardSelection = true;
|
|
43
|
+
this.selectionDirection = this.getSelectionDirection();
|
|
44
|
+
// blur and select cell
|
|
45
|
+
if (this.lastValidSelection) {
|
|
46
|
+
const range = document.createRange();
|
|
47
|
+
range.setStart(this.lastValidSelection.startContainer, this.lastValidSelection.startOffset);
|
|
48
|
+
range.setEnd(this.lastValidSelection.endContainer, this.lastValidSelection.endOffset);
|
|
49
|
+
selection.removeAllRanges();
|
|
50
|
+
selection.addRange(range);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
this.lastValidSelection = {
|
|
55
|
+
startContainer: selection.anchorNode!,
|
|
56
|
+
startOffset: selection.anchorOffset,
|
|
57
|
+
endContainer: selection.focusNode!,
|
|
58
|
+
endOffset: selection.focusOffset,
|
|
59
|
+
};
|
|
60
|
+
this.limitKeyboardSelection = false;
|
|
61
|
+
this.selectionDirection = null;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
document.addEventListener('keydown', (event) => {
|
|
65
|
+
if (event.shiftKey && this.limitKeyboardSelection) {
|
|
66
|
+
// 从上到下选区:阻止向右和向下的选区扩展
|
|
67
|
+
if (this.selectionDirection === Node.DOCUMENT_POSITION_FOLLOWING) {
|
|
68
|
+
if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {
|
|
69
|
+
event.preventDefault();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// 从下到上选区:阻止向左和向上的选区扩展
|
|
73
|
+
else if (
|
|
74
|
+
this.selectionDirection === Node.DOCUMENT_POSITION_PRECEDING
|
|
75
|
+
&& (event.key === 'ArrowLeft' || event.key === 'ArrowUp')
|
|
76
|
+
) {
|
|
77
|
+
event.preventDefault();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
this.quill.on(Quill.events.EDITOR_CHANGE, (event: string, range: TypeRange) => {
|
|
82
|
+
if (event === Quill.events.SELECTION_CHANGE && range) {
|
|
83
|
+
const [startBlot] = this.quill.getLine(range.index);
|
|
84
|
+
const [endBlot] = this.quill.getLine(range.index + range.length);
|
|
85
|
+
let startTableBlot;
|
|
86
|
+
let endTableBlot;
|
|
87
|
+
try {
|
|
88
|
+
startTableBlot = findParentBlot(startBlot!, blotName.tableMain);
|
|
89
|
+
}
|
|
90
|
+
catch {}
|
|
91
|
+
try {
|
|
92
|
+
endTableBlot = findParentBlot(endBlot!, blotName.tableMain);
|
|
93
|
+
}
|
|
94
|
+
catch {}
|
|
95
|
+
|
|
96
|
+
// if range is not in table. hide table tools
|
|
97
|
+
if (!startTableBlot || !endTableBlot) {
|
|
98
|
+
this.tableModule.hideTableTools();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
getSelectionDirection() {
|
|
105
|
+
const selection = document.getSelection();
|
|
106
|
+
if (!selection || selection.type === 'Caret') return null;
|
|
107
|
+
if (!selection.anchorNode || !selection.focusNode) return null;
|
|
108
|
+
return selection.anchorNode.compareDocumentPosition(selection.focusNode);
|
|
109
|
+
}
|
|
110
|
+
}
|