quill-table-up 2.1.9 → 2.2.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.
Files changed (79) hide show
  1. package/README.md +41 -34
  2. package/dist/index.css +1 -1
  3. package/dist/index.d.ts +53 -17
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.umd.js +1 -1
  7. package/dist/index.umd.js.map +1 -1
  8. package/package.json +16 -16
  9. package/src/__tests__/e2e/editor-page.ts +6 -0
  10. package/src/__tests__/e2e/table-blots.test.ts +52 -0
  11. package/src/__tests__/e2e/table-caption.test.ts +93 -0
  12. package/src/__tests__/e2e/table-hack.test.ts +43 -0
  13. package/src/__tests__/e2e/table-resize.test.ts +136 -5
  14. package/src/__tests__/e2e/table-selection.test.ts +7 -10
  15. package/src/__tests__/e2e/utils.ts +2 -2
  16. package/src/__tests__/unit/table-blots.test.ts +223 -0
  17. package/src/__tests__/unit/table-caption.test.ts +237 -0
  18. package/src/__tests__/unit/table-clipboard.test.ts +6 -6
  19. package/src/__tests__/unit/table-hack.test.ts +137 -63
  20. package/src/__tests__/unit/table-insert.test.ts +4 -4
  21. package/src/__tests__/unit/table-redo-undo.test.ts +252 -3
  22. package/src/__tests__/unit/utils.test.ts +2 -2
  23. package/src/__tests__/unit/utils.ts +44 -25
  24. package/src/__tests__/unit/vitest.d.ts +0 -1
  25. package/src/formats/index.ts +1 -0
  26. package/src/formats/overrides/scroll.ts +6 -0
  27. package/src/formats/table-caption-format.ts +115 -0
  28. package/src/formats/table-cell-format.ts +63 -33
  29. package/src/formats/table-cell-inner-format.ts +3 -3
  30. package/src/formats/table-col-format.ts +16 -12
  31. package/src/formats/table-colgroup-format.ts +2 -2
  32. package/src/formats/table-main-format.ts +0 -9
  33. package/src/formats/table-wrapper-format.ts +2 -2
  34. package/src/modules/table-clipboard.ts +21 -3
  35. package/src/modules/table-menu/constants.ts +18 -2
  36. package/src/modules/table-menu/table-menu-common.ts +0 -5
  37. package/src/modules/table-resize/table-resize-box.ts +74 -31
  38. package/src/modules/table-resize/table-resize-common.ts +8 -7
  39. package/src/modules/table-resize/table-resize-scale.ts +12 -5
  40. package/src/modules/table-scrollbar.ts +12 -7
  41. package/src/modules/table-selection.ts +16 -19
  42. package/src/style/color-picker.less +4 -2
  43. package/src/style/index.less +19 -0
  44. package/src/style/table-resize.less +16 -1
  45. package/src/svg/arrow-up-down.svg +11 -0
  46. package/src/svg/auto-full.svg +11 -1
  47. package/src/svg/background.svg +10 -1
  48. package/src/svg/border.svg +10 -1
  49. package/src/svg/color.svg +10 -1
  50. package/src/svg/copy.svg +8 -1
  51. package/src/svg/cut.svg +7 -1
  52. package/src/svg/insert-bottom.svg +6 -1
  53. package/src/svg/insert-left.svg +6 -1
  54. package/src/svg/insert-right.svg +6 -1
  55. package/src/svg/insert-top.svg +6 -1
  56. package/src/svg/merge-cell.svg +6 -1
  57. package/src/svg/remove-column.svg +6 -1
  58. package/src/svg/remove-row.svg +6 -1
  59. package/src/svg/remove-table.svg +6 -1
  60. package/src/svg/split-cell.svg +6 -1
  61. package/src/svg/table-head.svg +4 -0
  62. package/src/table-up.ts +149 -112
  63. package/src/utils/bem.ts +2 -2
  64. package/src/utils/blot-helper.ts +12 -0
  65. package/src/utils/color.ts +12 -12
  66. package/src/utils/components/button.ts +2 -2
  67. package/src/utils/components/color-picker.ts +2 -2
  68. package/src/utils/components/dialog.ts +2 -2
  69. package/src/utils/components/input.ts +2 -2
  70. package/src/utils/components/table/creator.ts +2 -2
  71. package/src/utils/components/table/select-box.ts +2 -2
  72. package/src/utils/components/tooltip.ts +2 -2
  73. package/src/utils/constants.ts +5 -3
  74. package/src/utils/position.ts +2 -2
  75. package/src/utils/resize-observer-helper.ts +2 -2
  76. package/src/utils/transformer.ts +5 -0
  77. package/src/utils/transition-event-helper.ts +2 -2
  78. package/src/utils/types.ts +5 -1
  79. package/src/utils/utils.ts +2 -2
@@ -3,7 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
3
  import { TableCellInnerFormat } from '../../formats';
4
4
  import { TableSelection } from '../../modules';
5
5
  import { TableUp } from '../../table-up';
6
- import { createQuillWithTableModule, createTable, createTableBodyHTML, createTableDeltaOps, createTaleColHTML, expectDelta } from './utils';
6
+ import { createQuillWithTableModule, createTable, createTableBodyHTML, createTableCaptionHTML, createTableDeltaOps, createTaleColHTML, expectDelta } from './utils';
7
7
 
8
8
  const Delta = Quill.import('delta');
9
9
  if (!Range.prototype.getBoundingClientRect) {
@@ -30,19 +30,46 @@ afterEach(() => {
30
30
  });
31
31
 
32
32
  describe('hack html convert', () => {
33
- it('getSemanticHTML should not get contenteditable table cell', async () => {
34
- const quill = await createTable(3, 3, { full: false, width: 100, align: 'right' }, { isEmpty: false });
33
+ it('getSemanticHTML should not get contenteditable table cell or caption', async () => {
34
+ const quill = createQuillWithTableModule(`<p><br></p>`);
35
+ quill.setContents([
36
+ { insert: '\nTable Caption' },
37
+ { attributes: { 'table-up-caption': { tableId: '1', side: 'bottom' } }, insert: '\n' },
38
+ { insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100, align: 'right' } } },
39
+ { insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100, align: 'right' } } },
40
+ { insert: { 'table-up-col': { tableId: '1', colId: '3', full: false, width: 100, align: 'right' } } },
41
+ { insert: '1' },
42
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
43
+ { insert: '2' },
44
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
45
+ { insert: '3' },
46
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
47
+ { insert: '4' },
48
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
49
+ { insert: '5' },
50
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
51
+ { insert: '6' },
52
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
53
+ { insert: '7' },
54
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
55
+ { insert: '8' },
56
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
57
+ { insert: '9' },
58
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
59
+ { insert: '\n' },
60
+ ]);
35
61
  await vi.runAllTimersAsync();
36
- const html = quill.getSemanticHTML();
37
- const parser = new DOMParser();
38
- const doc = parser.parseFromString(html, 'text/html');
39
- expect(doc.body).toEqualHTML(
62
+ const html1 = quill.getSemanticHTML();
63
+ const parser1 = new DOMParser();
64
+ const doc1 = parser1.parseFromString(html1, 'text/html');
65
+ expect(doc1.body).toEqualHTML(
40
66
  `
41
67
  <p></p>
42
68
  <div contenteditable="false">
43
- <table cellpadding="0" cellspacing="0" style="margin-left: auto; width: 300px;" data-align="right">
69
+ <table cellpadding="0" cellspacing="0" style="width: 300px; margin-left: auto;" data-align="right">
70
+ ${createTableCaptionHTML({ text: 'Table&nbsp;Caption', side: 'bottom' }, { editable: null })}
44
71
  ${createTaleColHTML(3, { full: false, width: 100, align: 'right' })}
45
- ${createTableBodyHTML(3, 3, { isEmpty: false, editable: false })}
72
+ ${createTableBodyHTML(3, 3, { isEmpty: false, editable: null })}
46
73
  </table>
47
74
  </div>
48
75
  <p></p>
@@ -51,8 +78,34 @@ describe('hack html convert', () => {
51
78
  );
52
79
  });
53
80
 
54
- it('getHTMLByCell should not get contenteditable table cell', async () => {
55
- const quill = await createTable(3, 3, { full: false, width: 100, align: 'right' }, { isEmpty: false });
81
+ it('getHTMLByCell should not get contenteditable table cell or caption', async () => {
82
+ const quill = createQuillWithTableModule(`<p><br></p>`);
83
+ quill.setContents([
84
+ { insert: '\nTable Caption' },
85
+ { attributes: { 'table-up-caption': { tableId: '1', side: 'bottom' } }, insert: '\n' },
86
+ { insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100, align: 'right' } } },
87
+ { insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100, align: 'right' } } },
88
+ { insert: { 'table-up-col': { tableId: '1', colId: '3', full: false, width: 100, align: 'right' } } },
89
+ { insert: '1' },
90
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
91
+ { insert: '2' },
92
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
93
+ { insert: '3' },
94
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
95
+ { insert: '4' },
96
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
97
+ { insert: '5' },
98
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
99
+ { insert: '6' },
100
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
101
+ { insert: '7' },
102
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
103
+ { insert: '8' },
104
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
105
+ { insert: '9' },
106
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
107
+ { insert: '\n' },
108
+ ]);
56
109
  const tableModule = quill.getModule(TableUp.moduleName) as TableUp;
57
110
  const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
58
111
  const html = tableModule.getHTMLByCell([tds[0], tds[1], tds[3], tds[4]]);
@@ -61,7 +114,8 @@ describe('hack html convert', () => {
61
114
  expect(doc.body).toEqualHTML(
62
115
  `
63
116
  <div contenteditable="false">
64
- <table cellpadding="0" cellspacing="0" style="margin-left: auto; width: 200px;" data-align="right">
117
+ <table cellpadding="0" cellspacing="0" style="width: 200px; margin-left: auto;" data-align="right">
118
+ <caption style="caption-side: bottom;">Table&nbsp;Caption</caption>
65
119
  <colgroup contenteditable="false" data-align="right">
66
120
  <col width="100px" data-col-id="1" data-align="right" />
67
121
  <col width="100px" data-col-id="2" data-align="right" />
@@ -98,54 +152,6 @@ describe('hack html convert', () => {
98
152
  { ignoreAttrs: ['class', 'data-table-id'] },
99
153
  );
100
154
  });
101
-
102
- it('getHTMLByCell should not get contenteditable table cell with attribute', async () => {
103
- const quill = await createTable(4, 4, { full: true, width: 100 }, { isEmpty: false });
104
- const tableModule = quill.getModule(TableUp.moduleName) as TableUp;
105
- const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
106
- const html = tableModule.getHTMLByCell([tds[0], tds[1], tds[4], tds[5]]);
107
- const parser = new DOMParser();
108
- const doc = parser.parseFromString(html, 'text/html');
109
- expect(doc.body).toEqualHTML(
110
- `
111
- <div contenteditable="false">
112
- <table cellpadding="0" cellspacing="0" style="margin-right: auto;" data-full="true">
113
- <colgroup contenteditable="false" data-full="true">
114
- <col width="50%" data-col-id="1" data-full="true" />
115
- <col width="50%" data-col-id="2" data-full="true" />
116
- </colgroup>
117
- <tbody>
118
- <tr data-row-id="1">
119
- <td colspan="1" data-col-id="1" data-row-id="1" rowspan="1">
120
- <div data-col-id="1" data-colspan="1" data-row-id="1" data-rowspan="1">
121
- <p>1</p>
122
- </div>
123
- </td>
124
- <td colspan="1" data-col-id="2" data-row-id="1" rowspan="1">
125
- <div data-col-id="2" data-colspan="1" data-row-id="1" data-rowspan="1">
126
- <p>2</p>
127
- </div>
128
- </td>
129
- </tr>
130
- <tr data-row-id="2">
131
- <td colspan="1" data-col-id="1" data-row-id="2" rowspan="1">
132
- <div data-col-id="1" data-colspan="1" data-row-id="2" data-rowspan="1">
133
- <p>5</p>
134
- </div>
135
- </td>
136
- <td colspan="1" data-col-id="2" data-row-id="2" rowspan="1">
137
- <div data-col-id="2" data-colspan="1" data-row-id="2" data-rowspan="1">
138
- <p>6</p>
139
- </div>
140
- </td>
141
- </tr>
142
- </tbody>
143
- </table>
144
- </div>
145
- `,
146
- { ignoreAttrs: ['class', 'data-table-id'] },
147
- );
148
- });
149
155
  });
150
156
 
151
157
  describe('hack format cell', () => {
@@ -177,7 +183,7 @@ describe('hack format cell', () => {
177
183
  });
178
184
 
179
185
  it('select part of text in cell should text like origin', async () => {
180
- const quill = await createTable(2, 2, { full: false }, { isEmpty: false });
186
+ const quill = await createTable(2, 2, { full: false });
181
187
  quill.updateContents(
182
188
  new Delta()
183
189
  .retain(4)
@@ -278,7 +284,7 @@ describe('hack format cell', () => {
278
284
  });
279
285
 
280
286
  it('select length is 0 and not have selecteds should format like origin', async () => {
281
- const quill = await createTable(2, 2, { full: false }, { isEmpty: false });
287
+ const quill = await createTable(2, 2, { full: false });
282
288
  quill.updateContents(
283
289
  new Delta()
284
290
  .retain(4)
@@ -316,7 +322,7 @@ describe('hack format cell', () => {
316
322
 
317
323
  it('select length is 0 and selectedTds not empty should format all text in cell', async () => {
318
324
  const quill = createQuillWithTableModule('<p></p>', { selection: TableSelection });
319
- quill.setContents(createTableDeltaOps(2, 2, { full: false }, { isEmpty: false }));
325
+ quill.setContents(createTableDeltaOps(2, 2, { full: false }));
320
326
  quill.updateContents(
321
327
  new Delta()
322
328
  .retain(4)
@@ -529,6 +535,7 @@ describe('hack toolbar clean handler', () => {
529
535
  { insert: '\n' },
530
536
  ]);
531
537
  const tableUp = quill.getModule(TableUp.moduleName) as TableUp;
538
+ tableUp.tableSelection!.table = quill.root.querySelector('table')!;
532
539
  const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
533
540
  tableUp.tableSelection!.selectedTds = tds;
534
541
 
@@ -574,6 +581,7 @@ describe('hack toolbar clean handler', () => {
574
581
  ]);
575
582
  const tableUp = quill.getModule(TableUp.moduleName) as TableUp;
576
583
  const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
584
+ tableUp.tableSelection!.table = quill.root.querySelector('table')!;
577
585
  tableUp.tableSelection!.selectedTds = tds;
578
586
  quill.setSelection(1, 3, Quill.sources.SILENT);
579
587
 
@@ -680,6 +688,7 @@ describe('hack toolbar clean handler', () => {
680
688
  await vi.runAllTimersAsync();
681
689
  const tableUp = quill.getModule(TableUp.moduleName) as TableUp;
682
690
  const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
691
+ tableUp.tableSelection!.table = quill.root.querySelector('table')!;
683
692
  tableUp.tableSelection!.selectedTds = [tds[1]];
684
693
  quill.theme.modules.toolbar!.handlers!.clean.call(quill.theme.modules.toolbar as any, true);
685
694
  expectDelta(
@@ -822,6 +831,7 @@ describe('hack toolbar clean handler', () => {
822
831
 
823
832
  const tableUp = quill.getModule(TableUp.moduleName) as TableUp;
824
833
  const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
834
+ tableUp.tableSelection!.table = quill.root.querySelector('table')!;
825
835
  tableUp.tableSelection!.selectedTds = tds;
826
836
  quill.theme.modules.toolbar!.handlers!.clean.call(quill.theme.modules.toolbar as any, true);
827
837
 
@@ -883,4 +893,68 @@ describe('hack toolbar clean handler', () => {
883
893
  { ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
884
894
  );
885
895
  });
896
+
897
+ it('clean handler should clean cell selectedTds style', async () => {
898
+ const quill = createQuillWithTableModule('<p></p>', { selection: TableSelection });
899
+ quill.setContents([
900
+ { insert: '\n' },
901
+ { insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100 } } },
902
+ { insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100 } } },
903
+ { insert: { 'table-up-col': { tableId: '1', colId: '3', full: false, width: 100 } } },
904
+ { insert: '1' },
905
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1, style: 'background-color: rgb(201, 16, 16);' } }, insert: '\n' },
906
+ { insert: '2' },
907
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1, style: 'border-bottom-color: rgb(0, 247, 255); background-color: rgb(201, 16, 16);' } }, insert: '\n' },
908
+ { insert: '3' },
909
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '3', rowspan: 1, colspan: 1, style: 'background-color: rgb(201, 16, 16);' } }, insert: '\n' },
910
+ { insert: '4' },
911
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1, style: 'border-right-color: rgb(0, 247, 255); background-color: rgb(201, 16, 16);' } }, insert: '\n' },
912
+ { insert: '5' },
913
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1, style: 'border-color: rgb(0, 247, 255); background-color: rgb(201, 16, 16);' } }, insert: '\n' },
914
+ { insert: '6' },
915
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '3', rowspan: 1, colspan: 1, style: 'background-color: rgb(201, 16, 16);' } }, insert: '\n' },
916
+ { insert: '7' },
917
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '1', rowspan: 1, colspan: 1, style: 'background-color: rgb(201, 16, 16);' } }, insert: '\n' },
918
+ { insert: '8' },
919
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '2', rowspan: 1, colspan: 1, style: 'background-color: rgb(201, 16, 16);' } }, insert: '\n' },
920
+ { insert: '9' },
921
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '3', rowspan: 1, colspan: 1, style: 'background-color: rgb(201, 16, 16);' } }, insert: '\n' },
922
+ { insert: '\n' },
923
+ ]);
924
+
925
+ const tableUp = quill.getModule(TableUp.moduleName) as TableUp;
926
+ const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
927
+ tableUp.tableSelection!.table = quill.root.querySelector('table')!;
928
+ tableUp.tableSelection!.selectedTds = [tds[4]];
929
+ quill.theme.modules.toolbar!.handlers!.clean.call(quill.theme.modules.toolbar as any, true);
930
+
931
+ expectDelta(
932
+ quill.getContents(),
933
+ new Delta([
934
+ { insert: '\n' },
935
+ { insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100 } } },
936
+ { insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100 } } },
937
+ { insert: { 'table-up-col': { tableId: '1', colId: '3', full: false, width: 100 } } },
938
+ { insert: '1' },
939
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1, style: 'background-color: rgb(201, 16, 16);' } }, insert: '\n' },
940
+ { insert: '2' },
941
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1, style: 'background-color: rgb(201, 16, 16);' } }, insert: '\n' },
942
+ { insert: '3' },
943
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '3', rowspan: 1, colspan: 1, style: 'background-color: rgb(201, 16, 16);' } }, insert: '\n' },
944
+ { insert: '4' },
945
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1, style: 'background-color: rgb(201, 16, 16);' } }, insert: '\n' },
946
+ { insert: '5' },
947
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
948
+ { insert: '6' },
949
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '3', rowspan: 1, colspan: 1, style: 'background-color: rgb(201, 16, 16);' } }, insert: '\n' },
950
+ { insert: '7' },
951
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '1', rowspan: 1, colspan: 1, style: 'background-color: rgb(201, 16, 16);' } }, insert: '\n' },
952
+ { insert: '8' },
953
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '2', rowspan: 1, colspan: 1, style: 'background-color: rgb(201, 16, 16);' } }, insert: '\n' },
954
+ { insert: '9' },
955
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '3', rowspan: 1, colspan: 1, style: 'background-color: rgb(201, 16, 16);' } }, insert: '\n' },
956
+ { insert: '\n' },
957
+ ]),
958
+ );
959
+ });
886
960
  });
@@ -269,12 +269,12 @@ describe('set contents', () => {
269
269
 
270
270
  it('should display an empty table', async () => {
271
271
  const quill = createQuillWithTableModule(`<p><br></p>`);
272
- quill.setContents(createTableDeltaOps(2, 2, {}, { isEmpty: true }));
272
+ quill.setContents(createTableDeltaOps(2, 2, {}, {}, { isEmpty: true }));
273
273
  await vi.runAllTimersAsync();
274
274
  expect(quill.root).toEqualHTML(
275
275
  `
276
276
  <p><br></p>
277
- ${createTableHTML(2, 2, {}, { isEmpty: true })}
277
+ ${createTableHTML(2, 2, {}, undefined, { isEmpty: true })}
278
278
  <p><br></p>
279
279
  `,
280
280
  { ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
@@ -666,7 +666,7 @@ describe('set cell attribute', () => {
666
666
  });
667
667
 
668
668
  it('set span cell border color', async () => {
669
- const quill = await createTable(5, 5, {}, { isEmpty: true });
669
+ const quill = await createTable(5, 5, {}, {}, { isEmpty: true });
670
670
  const tableModule = quill.getModule(TableUp.moduleName) as TableUp;
671
671
 
672
672
  const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
@@ -707,7 +707,7 @@ describe('set cell attribute', () => {
707
707
  });
708
708
 
709
709
  it('set rowspan and colspan cell border color', async () => {
710
- const quill = await createTable(6, 4, {}, { isEmpty: true });
710
+ const quill = await createTable(6, 4, {}, {}, { isEmpty: true });
711
711
  const tableModule = quill.getModule(TableUp.moduleName) as TableUp;
712
712
 
713
713
  const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
@@ -1,10 +1,13 @@
1
- import type { TableMainFormat } from '../../formats';
1
+ import type { TableCaptionFormat, TableMainFormat } from '../../formats';
2
+ import type { ToolOption } from '../../utils';
2
3
  import Quill from 'quill';
3
4
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
5
  import { TableCellInnerFormat } from '../../formats';
5
- import { TableSelection } from '../../modules';
6
+ import { tableMenuTools, TableSelection } from '../../modules';
6
7
  import { TableUp } from '../../table-up';
7
- import { createQuillWithTableModule, createTable, createTableBodyHTML, createTableHTML, createTaleColHTML, datasetAlign, datasetFull } from './utils';
8
+ import { createQuillWithTableModule, createTable, createTableBodyHTML, createTableHTML, createTaleColHTML, datasetAlign, datasetFull, expectDelta } from './utils';
9
+
10
+ const Delta = Quill.import('delta');
8
11
 
9
12
  if (!Range.prototype.getBoundingClientRect) {
10
13
  Range.prototype.getBoundingClientRect = function () {
@@ -1008,6 +1011,7 @@ describe('undo cell attribute', () => {
1008
1011
 
1009
1012
  const tableUp = quill.getModule(TableUp.moduleName) as TableUp;
1010
1013
  const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
1014
+ tableUp.tableSelection!.table = quill.root.querySelector('table')!;
1011
1015
  tableUp.tableSelection!.selectedTds = tds;
1012
1016
  quill.theme.modules.toolbar!.handlers!.clean.call(quill.theme.modules.toolbar as any, true);
1013
1017
  expect(quill.root).toEqualHTML(
@@ -1134,3 +1138,248 @@ describe('undo cell attribute', () => {
1134
1138
  );
1135
1139
  });
1136
1140
  });
1141
+
1142
+ describe('table caption', () => {
1143
+ it('undo and redo tableCaption insert', async () => {
1144
+ const quill = await createTable(3, 3, { full: false });
1145
+ const tableModule = quill.getModule(TableUp.moduleName) as TableUp;
1146
+ await vi.runAllTimersAsync();
1147
+ const table = quill.root.querySelector('table')!;
1148
+ tableModule.table = table;
1149
+ (tableMenuTools.InsertCaption as ToolOption).handle(tableModule, [], null);
1150
+ await vi.runAllTimersAsync();
1151
+ const tableCaptionDelta = new Delta([
1152
+ { insert: '\nTable Caption' },
1153
+ { attributes: { 'table-up-caption': { tableId: '1', side: 'top' } }, insert: '\n' },
1154
+ { insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100 } } },
1155
+ { insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100 } } },
1156
+ { insert: { 'table-up-col': { tableId: '1', colId: '3', full: false, width: 100 } } },
1157
+ { insert: '1' },
1158
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1159
+ { insert: '2' },
1160
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1161
+ { insert: '3' },
1162
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1163
+ { insert: '4' },
1164
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1165
+ { insert: '5' },
1166
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1167
+ { insert: '6' },
1168
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1169
+ { insert: '7' },
1170
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1171
+ { insert: '8' },
1172
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1173
+ { insert: '9' },
1174
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1175
+ { insert: '\n' },
1176
+ ]);
1177
+ expectDelta(
1178
+ tableCaptionDelta,
1179
+ quill.getContents(),
1180
+ );
1181
+
1182
+ quill.history.undo();
1183
+ await vi.runAllTimersAsync();
1184
+ expectDelta(
1185
+ new Delta([
1186
+ { insert: '\n' },
1187
+ { insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100 } } },
1188
+ { insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100 } } },
1189
+ { insert: { 'table-up-col': { tableId: '1', colId: '3', full: false, width: 100 } } },
1190
+ { insert: '1' },
1191
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1192
+ { insert: '2' },
1193
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1194
+ { insert: '3' },
1195
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1196
+ { insert: '4' },
1197
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1198
+ { insert: '5' },
1199
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1200
+ { insert: '6' },
1201
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1202
+ { insert: '7' },
1203
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1204
+ { insert: '8' },
1205
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1206
+ { insert: '9' },
1207
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1208
+ { insert: '\n' },
1209
+ ]),
1210
+ quill.getContents(),
1211
+ );
1212
+
1213
+ quill.history.redo();
1214
+ await vi.runAllTimersAsync();
1215
+ expectDelta(
1216
+ tableCaptionDelta,
1217
+ quill.getContents(),
1218
+ );
1219
+ });
1220
+
1221
+ it('undo and redo tableCaption edit', async () => {
1222
+ const quill = await createTable(3, 3, { full: false });
1223
+ const tableModule = quill.getModule(TableUp.moduleName) as TableUp;
1224
+ await vi.runAllTimersAsync();
1225
+ const table = quill.root.querySelector('table')!;
1226
+ tableModule.table = table;
1227
+ (tableMenuTools.InsertCaption as ToolOption).handle(tableModule, [], null);
1228
+ await vi.runAllTimersAsync();
1229
+ quill.deleteText({ index: 2, length: 4 });
1230
+ await vi.runAllTimersAsync();
1231
+ const tableCaptionDelta = new Delta([
1232
+ { insert: '\nT Caption' },
1233
+ { attributes: { 'table-up-caption': { tableId: '1', side: 'top' } }, insert: '\n' },
1234
+ { insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100 } } },
1235
+ { insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100 } } },
1236
+ { insert: { 'table-up-col': { tableId: '1', colId: '3', full: false, width: 100 } } },
1237
+ { insert: '1' },
1238
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1239
+ { insert: '2' },
1240
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1241
+ { insert: '3' },
1242
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1243
+ { insert: '4' },
1244
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1245
+ { insert: '5' },
1246
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1247
+ { insert: '6' },
1248
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1249
+ { insert: '7' },
1250
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1251
+ { insert: '8' },
1252
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1253
+ { insert: '9' },
1254
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1255
+ { insert: '\n' },
1256
+ ]);
1257
+ expectDelta(
1258
+ tableCaptionDelta,
1259
+ quill.getContents(),
1260
+ );
1261
+
1262
+ quill.history.undo();
1263
+ await vi.runAllTimersAsync();
1264
+ expectDelta(
1265
+ new Delta([
1266
+ { insert: '\nTable Caption' },
1267
+ { attributes: { 'table-up-caption': { tableId: '1', side: 'top' } }, insert: '\n' },
1268
+ { insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100 } } },
1269
+ { insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100 } } },
1270
+ { insert: { 'table-up-col': { tableId: '1', colId: '3', full: false, width: 100 } } },
1271
+ { insert: '1' },
1272
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1273
+ { insert: '2' },
1274
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1275
+ { insert: '3' },
1276
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1277
+ { insert: '4' },
1278
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1279
+ { insert: '5' },
1280
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1281
+ { insert: '6' },
1282
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1283
+ { insert: '7' },
1284
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1285
+ { insert: '8' },
1286
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1287
+ { insert: '9' },
1288
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1289
+ { insert: '\n' },
1290
+ ]),
1291
+ quill.getContents(),
1292
+ );
1293
+
1294
+ quill.history.redo();
1295
+ await vi.runAllTimersAsync();
1296
+ expectDelta(
1297
+ tableCaptionDelta,
1298
+ quill.getContents(),
1299
+ );
1300
+ });
1301
+
1302
+ it('undo and redo tableCaption position switch', async () => {
1303
+ const quill = await createTable(3, 3, { full: false });
1304
+ const tableModule = quill.getModule(TableUp.moduleName) as TableUp;
1305
+ await vi.runAllTimersAsync();
1306
+ const table = quill.root.querySelector('table')!;
1307
+ tableModule.table = table;
1308
+ (tableMenuTools.InsertCaption as ToolOption).handle(tableModule, [], null);
1309
+ await vi.runAllTimersAsync();
1310
+
1311
+ const tableCaption = Quill.find(table.querySelector('caption')!)! as TableCaptionFormat;
1312
+ expect(tableCaption).not.toBeNull();
1313
+ tableCaption.side = 'bottom';
1314
+ await vi.runAllTimersAsync();
1315
+ const tableCaptionDelta = new Delta([
1316
+ { insert: '\nTable Caption' },
1317
+ { attributes: { 'table-up-caption': { tableId: '1', side: 'bottom' } }, insert: '\n' },
1318
+ { insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100 } } },
1319
+ { insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100 } } },
1320
+ { insert: { 'table-up-col': { tableId: '1', colId: '3', full: false, width: 100 } } },
1321
+ { insert: '1' },
1322
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1323
+ { insert: '2' },
1324
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1325
+ { insert: '3' },
1326
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1327
+ { insert: '4' },
1328
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1329
+ { insert: '5' },
1330
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1331
+ { insert: '6' },
1332
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1333
+ { insert: '7' },
1334
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1335
+ { insert: '8' },
1336
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1337
+ { insert: '9' },
1338
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1339
+ { insert: '\n' },
1340
+ ]);
1341
+ expectDelta(
1342
+ tableCaptionDelta,
1343
+ quill.getContents(),
1344
+ );
1345
+
1346
+ quill.history.undo();
1347
+ await vi.runAllTimersAsync();
1348
+ expectDelta(
1349
+ new Delta([
1350
+ { insert: '\nTable Caption' },
1351
+ { attributes: { 'table-up-caption': { tableId: '1', side: 'top' } }, insert: '\n' },
1352
+ { insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100 } } },
1353
+ { insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100 } } },
1354
+ { insert: { 'table-up-col': { tableId: '1', colId: '3', full: false, width: 100 } } },
1355
+ { insert: '1' },
1356
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1357
+ { insert: '2' },
1358
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1359
+ { insert: '3' },
1360
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1361
+ { insert: '4' },
1362
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1363
+ { insert: '5' },
1364
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1365
+ { insert: '6' },
1366
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1367
+ { insert: '7' },
1368
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
1369
+ { insert: '8' },
1370
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
1371
+ { insert: '9' },
1372
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
1373
+ { insert: '\n' },
1374
+ ]),
1375
+ quill.getContents(),
1376
+ );
1377
+
1378
+ quill.history.redo();
1379
+ await vi.runAllTimersAsync();
1380
+ expectDelta(
1381
+ tableCaptionDelta,
1382
+ quill.getContents(),
1383
+ );
1384
+ });
1385
+ });
@@ -12,7 +12,7 @@ afterEach(() => {
12
12
  vi.useRealTimers();
13
13
  });
14
14
 
15
- const createOverridesTable = (html: string, options = true, moduleOptions = {}, register = {}) => {
15
+ function createOverridesTable(html: string, options = true, moduleOptions = {}, register = {}) {
16
16
  updateTableConstants({
17
17
  blotName: {
18
18
  tableCol: 'a-col',
@@ -137,7 +137,7 @@ const createOverridesTable = (html: string, options = true, moduleOptions = {},
137
137
  },
138
138
  });
139
139
  return quill;
140
- };
140
+ }
141
141
 
142
142
  describe('test utils', () => {
143
143
  it('test findParentBlot', async () => {