quill-table-up 2.4.1 → 2.4.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.
@@ -160,7 +160,7 @@ describe('table undo', () => {
160
160
  </div>
161
161
  <p><br></p>
162
162
  `,
163
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'contenteditable'] },
163
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'contenteditable'] },
164
164
  );
165
165
  });
166
166
 
@@ -234,7 +234,7 @@ describe('table undo', () => {
234
234
  </div>
235
235
  <p><br></p>
236
236
  `,
237
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'contenteditable'] },
237
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'contenteditable'] },
238
238
  );
239
239
  });
240
240
 
@@ -374,7 +374,7 @@ describe('table undo', () => {
374
374
  </div>
375
375
  <p><br></p>
376
376
  `,
377
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'contenteditable'] },
377
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'contenteditable'] },
378
378
  );
379
379
  quill.history.undo();
380
380
  await vi.runAllTimersAsync();
@@ -384,7 +384,7 @@ describe('table undo', () => {
384
384
  ${createTableHTML(4, 4)}
385
385
  <p><br></p>
386
386
  `,
387
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'contenteditable'] },
387
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'contenteditable'] },
388
388
  );
389
389
  });
390
390
 
@@ -467,7 +467,7 @@ describe('table undo', () => {
467
467
  </div>
468
468
  <p><br></p>
469
469
  `,
470
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'contenteditable'] },
470
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'contenteditable'] },
471
471
  );
472
472
  quill.history.undo();
473
473
  await vi.runAllTimersAsync();
@@ -477,7 +477,7 @@ describe('table undo', () => {
477
477
  ${createTableHTML(5, 5)}
478
478
  <p><br></p>
479
479
  `,
480
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'contenteditable'] },
480
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'contenteditable'] },
481
481
  );
482
482
  });
483
483
 
@@ -564,7 +564,7 @@ describe('table undo', () => {
564
564
  </div>
565
565
  <p><br></p>
566
566
  `,
567
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'contenteditable'] },
567
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'contenteditable'] },
568
568
  );
569
569
  quill.history.undo();
570
570
  await vi.runAllTimersAsync();
@@ -574,7 +574,7 @@ describe('table undo', () => {
574
574
  ${createTableHTML(5, 5)}
575
575
  <p><br></p>
576
576
  `,
577
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'contenteditable'] },
577
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'contenteditable'] },
578
578
  );
579
579
  });
580
580
 
@@ -654,7 +654,7 @@ describe('table undo', () => {
654
654
  </div>
655
655
  <p><br></p>
656
656
  `,
657
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'contenteditable'] },
657
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'contenteditable'] },
658
658
  );
659
659
  quill.history.undo();
660
660
  await vi.runAllTimersAsync();
@@ -664,7 +664,7 @@ describe('table undo', () => {
664
664
  ${createTableHTML(5, 5)}
665
665
  <p><br></p>
666
666
  `,
667
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'contenteditable'] },
667
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'contenteditable'] },
668
668
  );
669
669
  });
670
670
 
@@ -717,7 +717,7 @@ describe('table undo', () => {
717
717
  `;
718
718
  expect(quill.root).toEqualHTML(
719
719
  afterColHtml,
720
- { ignoreAttrs: ['class', 'style', 'data-full', 'data-table-id', 'data-row-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
720
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-full', 'data-table-id', 'data-row-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
721
721
  );
722
722
  quill.history.undo();
723
723
  await vi.runAllTimersAsync();
@@ -733,7 +733,85 @@ describe('table undo', () => {
733
733
  await vi.runAllTimersAsync();
734
734
  expect(quill.root).toEqualHTML(
735
735
  afterColHtml,
736
- { ignoreAttrs: ['class', 'style', 'data-full', 'data-table-id', 'data-row-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
736
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-full', 'data-table-id', 'data-row-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
737
+ );
738
+ });
739
+
740
+ it('undo and redo cell convert', async () => {
741
+ const quill = await createTable(3, 3, { full: false }, {}, { isEmpty: false });
742
+ const originDelta = [
743
+ { insert: '\n' },
744
+ { insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100 } } },
745
+ { insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100 } } },
746
+ { insert: { 'table-up-col': { tableId: '1', colId: '3', full: false, width: 100 } } },
747
+ { insert: '1' },
748
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
749
+ { insert: '2' },
750
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
751
+ { insert: '3' },
752
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
753
+ { insert: '4' },
754
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
755
+ { insert: '5' },
756
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
757
+ { insert: '6' },
758
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
759
+ { insert: '7' },
760
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '1', rowspan: 1, colspan: 1 } }, insert: '\n' },
761
+ { insert: '8' },
762
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '2', rowspan: 1, colspan: 1 } }, insert: '\n' },
763
+ { insert: '9' },
764
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '3', rowspan: 1, colspan: 1 } }, insert: '\n' },
765
+ { insert: '\n' },
766
+ ];
767
+ const thDelta = [
768
+ { insert: '\n' },
769
+ { insert: { 'table-up-col': { tableId: '1', colId: '1', full: false, width: 100 } } },
770
+ { insert: { 'table-up-col': { tableId: '1', colId: '2', full: false, width: 100 } } },
771
+ { insert: { 'table-up-col': { tableId: '1', colId: '3', full: false, width: 100 } } },
772
+ { insert: '1' },
773
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '1', rowspan: 1, colspan: 1, tag: 'th' } }, insert: '\n' },
774
+ { insert: '2' },
775
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '2', rowspan: 1, colspan: 1, tag: 'th' } }, insert: '\n' },
776
+ { insert: '3' },
777
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '1', colId: '3', rowspan: 1, colspan: 1, tag: 'th' } }, insert: '\n' },
778
+ { insert: '4' },
779
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '1', rowspan: 1, colspan: 1, tag: 'th' } }, insert: '\n' },
780
+ { insert: '5' },
781
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '2', rowspan: 1, colspan: 1, tag: 'th' } }, insert: '\n' },
782
+ { insert: '6' },
783
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '2', colId: '3', rowspan: 1, colspan: 1, tag: 'th' } }, insert: '\n' },
784
+ { insert: '7' },
785
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '1', rowspan: 1, colspan: 1, tag: 'th' } }, insert: '\n' },
786
+ { insert: '8' },
787
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '2', rowspan: 1, colspan: 1, tag: 'th' } }, insert: '\n' },
788
+ { insert: '9' },
789
+ { attributes: { 'table-up-cell-inner': { tableId: '1', rowId: '3', colId: '3', rowspan: 1, colspan: 1, tag: 'th' } }, insert: '\n' },
790
+ { insert: '\n' },
791
+ ];
792
+ quill.setContents(originDelta);
793
+ const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
794
+ for (const td of tds) {
795
+ td.convertTableCell();
796
+ }
797
+ await vi.runAllTimersAsync();
798
+ expectDelta(
799
+ new Delta(thDelta),
800
+ quill.getContents(),
801
+ );
802
+
803
+ quill.history.undo();
804
+ await vi.runAllTimersAsync();
805
+ expectDelta(
806
+ new Delta(originDelta),
807
+ quill.getContents(),
808
+ );
809
+
810
+ quill.history.redo();
811
+ await vi.runAllTimersAsync();
812
+ expectDelta(
813
+ new Delta(thDelta),
814
+ quill.getContents(),
737
815
  );
738
816
  });
739
817
  });
@@ -799,7 +877,7 @@ describe('undo cell attribute', () => {
799
877
  </div>
800
878
  <p><br></p>
801
879
  `,
802
- { ignoreAttrs: ['class', 'data-table-id', 'contenteditable'] },
880
+ { ignoreAttrs: ['data-tag', 'class', 'data-table-id', 'contenteditable'] },
803
881
  );
804
882
  quill.history.undo();
805
883
  await vi.runAllTimersAsync();
@@ -809,7 +887,7 @@ describe('undo cell attribute', () => {
809
887
  ${createTableHTML(2, 2)}
810
888
  <p><br></p>
811
889
  `,
812
- { ignoreAttrs: ['class', 'data-table-id', 'contenteditable'] },
890
+ { ignoreAttrs: ['data-tag', 'class', 'data-table-id', 'contenteditable'] },
813
891
  );
814
892
  });
815
893
 
@@ -953,7 +1031,7 @@ describe('undo cell attribute', () => {
953
1031
  </div>
954
1032
  <p><br></p>
955
1033
  `,
956
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
1034
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
957
1035
  );
958
1036
 
959
1037
  quill.history.undo();
@@ -996,7 +1074,7 @@ describe('undo cell attribute', () => {
996
1074
  </div>
997
1075
  <p><br></p>
998
1076
  `,
999
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
1077
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
1000
1078
  );
1001
1079
 
1002
1080
  quill.history.redo();
@@ -1037,7 +1115,7 @@ describe('undo cell attribute', () => {
1037
1115
  </div>
1038
1116
  <p><br></p>
1039
1117
  `,
1040
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
1118
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
1041
1119
  );
1042
1120
  });
1043
1121
 
@@ -1101,7 +1179,7 @@ describe('undo cell attribute', () => {
1101
1179
  </div>
1102
1180
  <p><br></p>
1103
1181
  `,
1104
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
1182
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
1105
1183
  );
1106
1184
 
1107
1185
  quill.history.undo();
@@ -1144,7 +1222,7 @@ describe('undo cell attribute', () => {
1144
1222
  </div>
1145
1223
  <p><br></p>
1146
1224
  `,
1147
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
1225
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
1148
1226
  );
1149
1227
 
1150
1228
  quill.history.redo();
@@ -1185,7 +1263,7 @@ describe('undo cell attribute', () => {
1185
1263
  </div>
1186
1264
  <p><br></p>
1187
1265
  `,
1188
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
1266
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
1189
1267
  );
1190
1268
  });
1191
1269
 
@@ -40,7 +40,7 @@ describe('remove column from table', () => {
40
40
  </div>
41
41
  <p><br></p>
42
42
  `,
43
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
43
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
44
44
  );
45
45
  });
46
46
 
@@ -70,7 +70,7 @@ describe('remove column from table', () => {
70
70
  </div>
71
71
  <p><br></p>
72
72
  `,
73
- { ignoreAttrs: ['class', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
73
+ { ignoreAttrs: ['data-tag', 'class', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
74
74
  );
75
75
  });
76
76
 
@@ -131,7 +131,7 @@ describe('remove column from table', () => {
131
131
  </div>
132
132
  <p><br></p>
133
133
  `,
134
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
134
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
135
135
  );
136
136
  });
137
137
  });
@@ -162,7 +162,7 @@ describe('remove row from table', () => {
162
162
  </div>
163
163
  <p><br></p>
164
164
  `,
165
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
165
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
166
166
  );
167
167
  });
168
168
 
@@ -197,7 +197,7 @@ describe('remove row from table', () => {
197
197
  </div>
198
198
  <p><br></p>
199
199
  `,
200
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
200
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
201
201
  );
202
202
  });
203
203
  });
@@ -217,7 +217,7 @@ describe('unusual delete', () => {
217
217
  await vi.runAllTimersAsync();
218
218
  expect(quill.root).toEqualHTML(
219
219
  `<p><br></p>`,
220
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
220
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
221
221
  );
222
222
  });
223
223
 
@@ -279,7 +279,7 @@ describe('unusual delete', () => {
279
279
  </div>
280
280
  <p><br></p>
281
281
  `,
282
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
282
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
283
283
  );
284
284
  });
285
285
 
@@ -338,7 +338,7 @@ describe('unusual delete', () => {
338
338
  </div>
339
339
  <p><br></p>
340
340
  `,
341
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
341
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'data-row-id', 'data-col-id', 'data-rowspan', 'data-colspan', 'contenteditable'] },
342
342
  );
343
343
  });
344
344
  });
@@ -283,7 +283,7 @@ describe('test override format', () => {
283
283
  ${createTableHTML(2, 2)}
284
284
  <p><br></p>
285
285
  `,
286
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'contenteditable'] },
286
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'contenteditable'] },
287
287
  );
288
288
  const tableModule = quill.getModule(TableUp.moduleName) as TableUp;
289
289
  const tds = quill.scroll.descendants(TableCellInnerFormat, 0);
@@ -391,7 +391,7 @@ describe('test override format', () => {
391
391
  </div>
392
392
  <p><br></p>
393
393
  `,
394
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'contenteditable'] },
394
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'contenteditable'] },
395
395
  );
396
396
  });
397
397
 
@@ -544,7 +544,7 @@ describe('test override format', () => {
544
544
  </div>
545
545
  <p><br></p>
546
546
  `,
547
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'contenteditable'] },
547
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'contenteditable'] },
548
548
  );
549
549
  });
550
550
 
@@ -705,7 +705,7 @@ describe('test override format', () => {
705
705
  </div>
706
706
  <p><br></p>
707
707
  `,
708
- { ignoreAttrs: ['class', 'style', 'data-table-id', 'contenteditable'] },
708
+ { ignoreAttrs: ['data-tag', 'class', 'style', 'data-table-id', 'contenteditable'] },
709
709
  );
710
710
  });
711
711
  });
@@ -215,7 +215,7 @@ export function createTableBodyHTML(row: number, col: number, options?: Partial<
215
215
  <tr ${datasetTableId(tableId)} data-row-id="${i + 1}">
216
216
  ${
217
217
  new Array(col).fill(0).map((_, j) => `<td rowspan="1" colspan="1" ${datasetTableId(tableId)} data-row-id="${i + 1}" data-col-id="${j + 1}">
218
- <div ${datasetTableId(tableId)} data-rowspan="1" data-colspan="1" data-row-id="${i + 1}" data-col-id="${j + 1}"${contenteditableString(editable)}>
218
+ <div data-tag="td" ${datasetTableId(tableId)} data-rowspan="1" data-colspan="1" data-row-id="${i + 1}" data-col-id="${j + 1}"${contenteditableString(editable)}>
219
219
  <p>
220
220
  ${isEmpty ? '<br>' : i * row + j + 1}
221
221
  </p>
@@ -1,5 +1,5 @@
1
1
  import type { TableCellValue } from '../utils';
2
- import { blotName, findParentBlot, toCamelCase } from '../utils';
2
+ import { blotName, ensureArray, findParentBlot, toCamelCase } from '../utils';
3
3
  import { ContainerFormat } from './container-format';
4
4
  import { TableCellInnerFormat } from './table-cell-inner-format';
5
5
  import { TableRowFormat } from './table-row-format';
@@ -34,8 +34,10 @@ export class TableCellFormat extends ContainerFormat {
34
34
  colspan,
35
35
  style,
36
36
  emptyRow,
37
+ tag = 'td',
37
38
  } = value;
38
- const node = super.create() as HTMLElement;
39
+ const node = document.createElement(tag);
40
+ node.classList.add(...ensureArray(this.className));
39
41
  node.dataset.tableId = tableId;
40
42
  node.dataset.rowId = rowId;
41
43
  node.dataset.colId = colId;
@@ -59,6 +61,7 @@ export class TableCellFormat extends ContainerFormat {
59
61
  colId,
60
62
  rowspan: getValidCellspan(rowspan),
61
63
  colspan: getValidCellspan(colspan),
64
+ tag: domNode.tagName.toLowerCase(),
62
65
  };
63
66
 
64
67
  const inlineStyles: Record<string, any> = {};
@@ -82,6 +85,11 @@ export class TableCellFormat extends ContainerFormat {
82
85
  return value;
83
86
  }
84
87
 
88
+ isChildHeadTableCellInner() {
89
+ const headChild = this.children.head;
90
+ return headChild && headChild.statics.blotName === blotName.tableCellInner;
91
+ }
92
+
85
93
  setFormatValue(name: string, value?: any) {
86
94
  if (this.statics.allowAttrs.has(name) || this.statics.allowDataAttrs.has(name)) {
87
95
  let attrName = name;
@@ -104,15 +112,14 @@ export class TableCellFormat extends ContainerFormat {
104
112
  }
105
113
  }
106
114
 
107
- const headChild = this.children.head;
115
+ const headChild = this.children.head!;
108
116
  if (
109
- this.domNode.style.cssText
110
- && headChild
111
- && headChild.statics.blotName === blotName.tableCellInner
117
+ this.isChildHeadTableCellInner()
118
+ && this.domNode.style.cssText
112
119
  // only update if data not match. avoid optimize circular updates
113
120
  && this.domNode.style.cssText !== (headChild.domNode as HTMLElement).dataset.style
114
121
  ) {
115
- (headChild!.domNode as HTMLElement).dataset.style = this.domNode.style.cssText;
122
+ (headChild.domNode as HTMLElement).dataset.style = this.domNode.style.cssText;
116
123
  }
117
124
  }
118
125
 
@@ -230,6 +237,25 @@ export class TableCellFormat extends ContainerFormat {
230
237
  return this.children.head as TableCellInnerFormat;
231
238
  }
232
239
 
240
+ convertTableCell() {
241
+ const value = this.statics.formats(this.domNode);
242
+ const tag = value.tag === 'td' ? 'th' : 'td';
243
+
244
+ const headChild = this.children.head!;
245
+ if (
246
+ this.isChildHeadTableCellInner()
247
+ // only update if data not match. avoid optimize circular updates
248
+ && (headChild.domNode as HTMLElement).dataset.tag !== tag
249
+ ) {
250
+ (headChild.domNode as HTMLElement).dataset.tag = tag;
251
+ }
252
+
253
+ this.replaceWith(blotName.tableCell, {
254
+ ...value,
255
+ tag,
256
+ });
257
+ }
258
+
233
259
  checkMerge(): boolean {
234
260
  const { colId, rowId, colspan, rowspan } = this;
235
261
  const next = this.next as TableCellFormat;
@@ -40,6 +40,7 @@ export class TableCellInnerFormat extends ContainerFormat {
40
40
  colspan,
41
41
  style,
42
42
  emptyRow,
43
+ tag = 'td',
43
44
  } = value;
44
45
  const node = super.create() as HTMLElement;
45
46
  node.dataset.tableId = tableId;
@@ -47,6 +48,7 @@ export class TableCellInnerFormat extends ContainerFormat {
47
48
  node.dataset.colId = colId;
48
49
  node.dataset.rowspan = String(getValidCellspan(rowspan));
49
50
  node.dataset.colspan = String(getValidCellspan(colspan));
51
+ node.dataset.tag = tag;
50
52
  style && (node.dataset.style = style);
51
53
  try {
52
54
  emptyRow && (node.dataset.emptyRow = JSON.stringify(emptyRow));
@@ -56,13 +58,23 @@ export class TableCellInnerFormat extends ContainerFormat {
56
58
  }
57
59
 
58
60
  static formats(domNode: HTMLElement) {
59
- const { tableId, rowId, colId, rowspan, colspan, style, emptyRow } = domNode.dataset;
61
+ const {
62
+ tableId,
63
+ rowId,
64
+ colId,
65
+ rowspan,
66
+ colspan,
67
+ style,
68
+ emptyRow,
69
+ tag = 'td',
70
+ } = domNode.dataset;
60
71
  const value: Record<string, any> = {
61
72
  tableId: String(tableId),
62
73
  rowId: String(rowId),
63
74
  colId: String(colId),
64
75
  rowspan: Number(getValidCellspan(rowspan)),
65
76
  colspan: Number(getValidCellspan(colspan)),
77
+ tag,
66
78
  };
67
79
  style && (value.style = style);
68
80
  try {
@@ -98,6 +110,10 @@ export class TableCellInnerFormat extends ContainerFormat {
98
110
  }
99
111
  }
100
112
 
113
+ this.clearCache();
114
+ }
115
+
116
+ clearCache() {
101
117
  const blocks = this.descendants(Block, 0);
102
118
  for (const child of blocks) {
103
119
  (child as TypeBlock).cache = {};
@@ -178,6 +194,12 @@ export class TableCellInnerFormat extends ContainerFormat {
178
194
  }
179
195
  }
180
196
 
197
+ convertTableCell() {
198
+ if (this.parent.statics.blotName !== blotName.tableCell) return;
199
+ this.parent.convertTableCell();
200
+ this.clearCache();
201
+ }
202
+
181
203
  formatAt(index: number, length: number, name: string, value: any) {
182
204
  if (this.children.length === 0) {
183
205
  this.appendChild(this.scroll.create(this.statics.defaultChild.blotName));
@@ -4,6 +4,7 @@ import Quill from 'quill';
4
4
  import AutoFull from '../../svg/auto-full.svg';
5
5
  import Background from '../../svg/background.svg';
6
6
  import Border from '../../svg/border.svg';
7
+ import ConvertCell from '../../svg/convert-cell.svg';
7
8
  import Copy from '../../svg/copy.svg';
8
9
  import Cut from '../../svg/cut.svg';
9
10
  import InsertBottom from '../../svg/insert-bottom.svg';
@@ -176,7 +177,7 @@ export const tableMenuTools: Record<string, Tool> = {
176
177
  name: 'InsertCaption',
177
178
  icon: TableHead,
178
179
  tip: 'Insert table caption',
179
- handle(tableModule) {
180
+ handle: (tableModule) => {
180
181
  if (!tableModule.table) return;
181
182
  const tableMainBlot = Quill.find(tableModule.table) as TableMainFormat;
182
183
  if (!tableMainBlot) return;
@@ -187,6 +188,16 @@ export const tableMenuTools: Record<string, Tool> = {
187
188
  tableMainBlot.insertBefore(tableCaption, tableMainBlot.children.head);
188
189
  },
189
190
  },
191
+ ToggleTdBetweenTh: {
192
+ name: 'ToggleTdBetweenTh',
193
+ icon: ConvertCell,
194
+ tip: 'Toggle td between th',
195
+ handle: (tableModule, selectedTds) => {
196
+ for (const td of selectedTds) {
197
+ td.convertTableCell();
198
+ }
199
+ },
200
+ },
190
201
  };
191
202
 
192
203
  export const maxSaveColorCount = 10;
@@ -525,18 +525,20 @@ export class TableSelection {
525
525
 
526
526
  removeCell = (e: KeyboardEvent) => {
527
527
  const range = this.quill.getSelection();
528
- if (range || (e.key !== 'Backspace' && e.key !== 'Delete')) return;
528
+ const activeElement = document.activeElement;
529
+ if (range || (e.key !== 'Backspace' && e.key !== 'Delete') || !this.quill.root.contains(activeElement)) return;
529
530
 
530
- for (const td of this.selectedTds) {
531
- td.deleteAt(0, td.length() - 1);
532
- }
533
531
  if (this.table) {
534
532
  const tableMain = Quill.find(this.table) as TableMainFormat;
535
533
  const cells = tableMain.descendants(TableCellInnerFormat);
536
534
  if (this.selectedTds.length === cells.length) {
537
535
  tableMain.remove();
536
+ return;
538
537
  }
539
538
  }
539
+ for (const td of this.selectedTds) {
540
+ td.deleteAt(0, td.length() - 1);
541
+ }
540
542
  };
541
543
 
542
544
  showDisplay() {
@@ -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":3198.3800000000047,"failed":false}],[":__tests__/unit/table-redo-undo.test.ts",{"duration":8473.984600000002,"failed":false}],[":__tests__/unit/table-hack.test.ts",{"duration":2830.0276,"failed":false}],[":__tests__/unit/table-insert.test.ts",{"duration":4750.2608,"failed":false}],[":__tests__/unit/table-cell-merge.test.ts",{"duration":3491.4125,"failed":false}],[":__tests__/unit/table-blots.test.ts",{"duration":2240.7780000000002,"failed":false}],[":__tests__/unit/utils.test.ts",{"duration":640.7419,"failed":false}],[":__tests__/unit/table-remove.test.ts",{"duration":2719.4262,"failed":false}],[":__tests__/unit/table-caption.test.ts",{"duration":1264.7592,"failed":false}]]}
1
+ {"version":"3.2.3","results":[[":__tests__/unit/utils.test-d.ts",{"duration":0,"failed":false}],[":__tests__/unit/table-clipboard.test.ts",{"duration":7348.2325000000055,"failed":false}],[":__tests__/unit/table-redo-undo.test.ts",{"duration":7314.907299999999,"failed":false}],[":__tests__/unit/table-hack.test.ts",{"duration":2628.3711000000003,"failed":false}],[":__tests__/unit/table-cell-merge.test.ts",{"duration":2576.8806999999997,"failed":false}],[":__tests__/unit/table-insert.test.ts",{"duration":4933.0411,"failed":false}],[":__tests__/unit/table-blots.test.ts",{"duration":2238.9622,"failed":false}],[":__tests__/unit/utils.test.ts",{"duration":1362.8192000000004,"failed":false}],[":__tests__/unit/table-remove.test.ts",{"duration":1996.3753999999994,"failed":false}],[":__tests__/unit/table-caption.test.ts",{"duration":1237.1392,"failed":false}]]}
@@ -69,13 +69,16 @@
69
69
  table-layout: fixed;
70
70
  }
71
71
  tr + tr {
72
- td {
72
+ td,
73
+ th {
73
74
  border-top: none;
74
75
  }
75
76
  }
76
- td {
77
+ td,
78
+ th {
77
79
  border: 1px solid #a1a1aa;
78
- & + td {
80
+ & + td,
81
+ & + th {
79
82
  border-left: none;
80
83
  }
81
84
  }
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
2
+ <!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE -->
3
+ <path
4
+ fill="currentColor"
5
+ d="M11 21V9H3V5q0-.825.588-1.412T5 3h14q.825 0 1.413.588T21 5v14q0 .825-.587 1.413T19 21zm2-2h6v-4h-6zm0-6h6V9h-6zM5 7h14V5H5zM3 22v-2h2.55q-1.2-.575-1.937-1.7t-.738-2.55q0-1.975 1.388-3.363T7.625 11v2q-1.125 0-1.937.8t-.813 1.95q0 .975.6 1.725t1.525.95V16h2v6z"
6
+ />
7
+ </svg>
package/src/utils/is.ts CHANGED
@@ -5,3 +5,4 @@ export const isString = (val: unknown): val is string => typeof val === 'string'
5
5
  export const isNumber = (val: unknown): val is number => typeof val === 'number';
6
6
  export const isObject = (val: unknown): val is Record<any, any> => val !== null && typeof val === 'object';
7
7
  export const isValidCellspan = (val: unknown): boolean => !Number.isNaN(val) && Number(val) > 0;
8
+ export const ensureArray = <T>(val: T | T[]): T[] => isArray(val) ? val : [val];
@@ -88,6 +88,7 @@ export interface TableCellValue {
88
88
  colspan: number;
89
89
  style?: string;
90
90
  emptyRow?: string[];
91
+ tag?: 'td' | 'th';
91
92
  }
92
93
  export interface TableRowValue {
93
94
  tableId: string;