quill-table-up 2.4.2 → 3.0.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 (66) hide show
  1. package/README.md +96 -40
  2. package/dist/index.css +1 -1
  3. package/dist/index.d.ts +172 -110
  4. package/dist/index.js +29 -28
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.umd.js +29 -28
  7. package/dist/index.umd.js.map +1 -1
  8. package/dist/table-creator.css +1 -1
  9. package/package.json +1 -1
  10. package/src/__tests__/e2e/table-align.test.ts +1 -2
  11. package/src/__tests__/e2e/table-blots.test.ts +45 -35
  12. package/src/__tests__/e2e/table-clipboard.test.ts +20 -0
  13. package/src/__tests__/e2e/table-hack.test.ts +5 -5
  14. package/src/__tests__/e2e/table-keyboard-handler.test.ts +6 -6
  15. package/src/__tests__/e2e/table-menu.test.ts +23 -0
  16. package/src/__tests__/e2e/table-selection.test.ts +3 -3
  17. package/src/__tests__/unit/table-blots.test.ts +26 -26
  18. package/src/__tests__/unit/table-caption.test.ts +33 -36
  19. package/src/__tests__/unit/table-cell-merge.test.ts +114 -114
  20. package/src/__tests__/unit/table-clipboard.test.ts +383 -19
  21. package/src/__tests__/unit/table-hack.test.ts +202 -144
  22. package/src/__tests__/unit/table-insert.test.ts +79 -79
  23. package/src/__tests__/unit/table-redo-undo.test.ts +495 -64
  24. package/src/__tests__/unit/table-remove.test.ts +8 -11
  25. package/src/__tests__/unit/utils.test.ts +4 -4
  26. package/src/__tests__/unit/utils.ts +4 -3
  27. package/src/formats/container-format.ts +25 -2
  28. package/src/formats/index.ts +54 -8
  29. package/src/formats/overrides/block.ts +35 -30
  30. package/src/formats/table-body-format.ts +18 -58
  31. package/src/formats/table-cell-format.ts +296 -286
  32. package/src/formats/table-cell-inner-format.ts +384 -358
  33. package/src/formats/table-foot-format.ts +7 -0
  34. package/src/formats/table-head-format.ts +7 -0
  35. package/src/formats/table-main-format.ts +84 -5
  36. package/src/formats/table-row-format.ts +44 -14
  37. package/src/modules/index.ts +1 -0
  38. package/src/modules/table-align.ts +59 -53
  39. package/src/modules/table-clipboard.ts +60 -39
  40. package/src/modules/table-dom-selector.ts +33 -0
  41. package/src/modules/table-menu/constants.ts +21 -31
  42. package/src/modules/table-menu/table-menu-common.ts +72 -51
  43. package/src/modules/table-menu/table-menu-contextmenu.ts +22 -6
  44. package/src/modules/table-menu/table-menu-select.ts +75 -12
  45. package/src/modules/table-resize/table-resize-box.ts +148 -128
  46. package/src/modules/table-resize/table-resize-common.ts +37 -32
  47. package/src/modules/table-resize/table-resize-line.ts +53 -36
  48. package/src/modules/table-resize/table-resize-scale.ts +32 -27
  49. package/src/modules/table-scrollbar.ts +58 -32
  50. package/src/modules/table-selection.ts +102 -79
  51. package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  52. package/src/style/index.less +0 -1
  53. package/src/style/select-box.less +1 -0
  54. package/src/style/table-menu.less +4 -4
  55. package/src/style/table-resize.less +1 -0
  56. package/src/style/table-scrollbar.less +2 -2
  57. package/src/table-up.ts +161 -194
  58. package/src/utils/components/table/creator.ts +4 -1
  59. package/src/utils/components/tooltip.ts +6 -1
  60. package/src/utils/constants.ts +8 -2
  61. package/src/utils/index.ts +2 -1
  62. package/src/utils/scroll.ts +47 -0
  63. package/src/utils/style-helper.ts +47 -0
  64. package/src/utils/transformer.ts +0 -25
  65. package/src/utils/types.ts +15 -15
  66. package/src/utils/scroll-event-helper.ts +0 -22
package/src/table-up.ts CHANGED
@@ -4,11 +4,12 @@ import type TypeBlock from 'quill/blots/block';
4
4
  import type { Context } from 'quill/modules/keyboard';
5
5
  import type TypeKeyboard from 'quill/modules/keyboard';
6
6
  import type TypeToolbar from 'quill/modules/toolbar';
7
- import type { Constructor, InternalModule, InternalTableSelectionModule, QuillTheme, QuillThemePicker, TableCellValue, TableConstantsData, TableTextOptions, TableUpOptions } from './utils';
7
+ import type { TableSelection } from './modules';
8
+ import type { Constructor, QuillTheme, QuillThemePicker, TableCellValue, TableConstantsData, TableTextOptions, TableUpOptions } from './utils';
8
9
  import Quill from 'quill';
9
- import { BlockEmbedOverride, BlockOverride, ContainerFormat, ScrollOverride, TableBodyFormat, TableCaptionFormat, TableCellFormat, TableCellInnerFormat, TableColFormat, TableColgroupFormat, TableMainFormat, TableRowFormat, TableWrapperFormat } from './formats';
10
+ import { BlockEmbedOverride, BlockOverride, ContainerFormat, ScrollOverride, TableBodyFormat, TableCaptionFormat, TableCellFormat, TableCellInnerFormat, TableColFormat, TableColgroupFormat, TableFootFormat, TableHeadFormat, TableMainFormat, TableRowFormat, TableWrapperFormat } from './formats';
10
11
  import { TableClipboard } from './modules';
11
- import { blotName, createBEM, createSelectBox, cssTextToObject, debounce, findParentBlot, findParentBlots, isForbidInTable, isFunction, isNumber, isString, isSubclassOf, limitDomInViewPort, mixinClass, objectToCssText, randomId, tableCantInsert, tableUpEvent, tableUpInternal, tableUpSize, toCamelCase } from './utils';
12
+ import { blotName, createBEM, createSelectBox, cssTextToObject, debounce, findParentBlot, findParentBlots, getScrollBarWidth, isForbidInTable, isFunction, isNumber, isString, isSubclassOf, limitDomInViewPort, mixinClass, objectToCssText, randomId, tableCantInsert, tableUpEvent, tableUpInternal, tableUpSize, toCamelCase } from './utils';
12
13
 
13
14
  const Parchment = Quill.import('parchment');
14
15
  const Delta = Quill.import('delta');
@@ -45,11 +46,14 @@ export function updateTableConstants(data: Partial<TableConstantsData>) {
45
46
 
46
47
  TableUp.toolName = blotName.tableWrapper;
47
48
  ContainerFormat.blotName = blotName.container;
49
+ TableCaptionFormat.blotName = blotName.tableCaption;
48
50
  TableWrapperFormat.blotName = blotName.tableWrapper;
49
51
  TableMainFormat.blotName = blotName.tableMain;
50
52
  TableColgroupFormat.blotName = blotName.tableColgroup;
51
53
  TableColFormat.blotName = blotName.tableCol;
54
+ TableHeadFormat.blotName = blotName.tableHead;
52
55
  TableBodyFormat.blotName = blotName.tableBody;
56
+ TableFootFormat.blotName = blotName.tableFoot;
53
57
  TableRowFormat.blotName = blotName.tableRow;
54
58
  TableCellFormat.blotName = blotName.tableCell;
55
59
  TableCellInnerFormat.blotName = blotName.tableCellInner;
@@ -59,7 +63,7 @@ export function updateTableConstants(data: Partial<TableConstantsData>) {
59
63
  export function defaultCustomSelect(tableModule: TableUp, picker: QuillThemePicker) {
60
64
  return createSelectBox({
61
65
  onSelect: (row: number, col: number) => {
62
- tableModule.insertTable(row, col);
66
+ tableModule.insertTable(row, col, Quill.sources.USER);
63
67
  if (picker) {
64
68
  picker.close();
65
69
  }
@@ -212,7 +216,7 @@ export class TableUp {
212
216
  static register() {
213
217
  TableWrapperFormat.allowedChildren = [TableMainFormat];
214
218
 
215
- TableMainFormat.allowedChildren = [TableBodyFormat, TableColgroupFormat, TableCaptionFormat];
219
+ TableMainFormat.allowedChildren = [TableColgroupFormat, TableCaptionFormat, TableHeadFormat, TableBodyFormat, TableFootFormat];
216
220
  TableMainFormat.requiredContainer = TableWrapperFormat;
217
221
 
218
222
  TableCaptionFormat.requiredContainer = TableMainFormat;
@@ -220,11 +224,14 @@ export class TableUp {
220
224
  TableColgroupFormat.allowedChildren = [TableColFormat];
221
225
  TableColgroupFormat.requiredContainer = TableMainFormat;
222
226
 
227
+ TableHeadFormat.allowedChildren = [TableRowFormat];
228
+ TableHeadFormat.requiredContainer = TableMainFormat;
223
229
  TableBodyFormat.allowedChildren = [TableRowFormat];
224
230
  TableBodyFormat.requiredContainer = TableMainFormat;
231
+ TableFootFormat.allowedChildren = [TableRowFormat];
232
+ TableFootFormat.requiredContainer = TableMainFormat;
225
233
 
226
234
  TableRowFormat.allowedChildren = [TableCellFormat];
227
- TableCellFormat.requiredContainer = TableBodyFormat;
228
235
 
229
236
  TableCellFormat.allowedChildren = [TableCellInnerFormat, Break];
230
237
  TableCellFormat.requiredContainer = TableRowFormat;
@@ -258,7 +265,9 @@ export class TableUp {
258
265
  [`formats/${blotName.tableCell}`]: TableCellFormat,
259
266
  [`formats/${blotName.tableCellInner}`]: TableCellInnerFormat,
260
267
  [`formats/${blotName.tableRow}`]: TableRowFormat,
268
+ [`formats/${blotName.tableHead}`]: TableHeadFormat,
261
269
  [`formats/${blotName.tableBody}`]: TableBodyFormat,
270
+ [`formats/${blotName.tableFoot}`]: TableFootFormat,
262
271
  [`formats/${blotName.tableCol}`]: TableColFormat,
263
272
  [`formats/${blotName.tableColgroup}`]: TableColgroupFormat,
264
273
  [`formats/${blotName.tableCaption}`]: TableCaptionFormat,
@@ -273,13 +282,8 @@ export class TableUp {
273
282
  toolBox: HTMLDivElement;
274
283
  fixTableByLisenter = debounce(this.balanceTables, 100);
275
284
  selector?: HTMLElement;
276
- table?: HTMLElement;
277
- tableSelection?: InternalTableSelectionModule;
278
- tableResize?: InternalModule;
279
- tableScrollbar?: InternalModule;
280
- tableAlign?: InternalModule;
281
- tableResizeScale?: InternalModule;
282
285
  resizeOb!: ResizeObserver;
286
+ modules: Record<string, Constructor> = {};
283
287
 
284
288
  get statics(): any {
285
289
  return this.constructor;
@@ -290,15 +294,6 @@ export class TableUp {
290
294
  this.options = this.resolveOptions(options || {});
291
295
  this.toolBox = this.initialContainer();
292
296
 
293
- if (!this.options.scrollbar) {
294
- const scrollbarBEM = createBEM('scrollbar');
295
- this.quill.container.classList.add(scrollbarBEM.bm('origin'));
296
- }
297
-
298
- if (this.options.selection) {
299
- this.tableSelection = new this.options.selection(this, this.quill, this.options.selectionOptions);
300
- }
301
-
302
297
  const toolbar = this.quill.getModule('toolbar') as TypeToolbar;
303
298
  if (toolbar && (this.quill.theme as QuillTheme).pickers) {
304
299
  const [, select] = (toolbar.controls as [string, HTMLElement][] || []).find(([name]) => name === this.statics.toolName) || [];
@@ -334,36 +329,7 @@ export class TableUp {
334
329
  }
335
330
  }
336
331
 
337
- this.quill.root.addEventListener(
338
- 'click',
339
- (evt: MouseEvent) => {
340
- const path = evt.composedPath() as HTMLElement[];
341
- if (!path || path.length <= 0) return;
342
-
343
- const tableNode = path.find(node => node.tagName && node.tagName.toUpperCase() === 'TABLE' && node.classList.contains('ql-table'));
344
- if (tableNode) {
345
- if (this.table === tableNode) {
346
- this.tableSelection && this.tableSelection.show();
347
- this.tableAlign && this.tableAlign.update();
348
- this.tableResize && this.tableResize.update();
349
- this.tableScrollbar && this.tableScrollbar.update();
350
- return;
351
- }
352
- if (this.table) this.hideTableTools();
353
- this.showTableTools(tableNode);
354
- }
355
- else if (this.table) {
356
- this.hideTableTools();
357
- }
358
- },
359
- false,
360
- );
361
- this.quill.on(Quill.events.EDITOR_CHANGE, (type: typeof Quill.events.TEXT_CHANGE | typeof Quill.events.SELECTION_CHANGE) => {
362
- if (type === Quill.events.TEXT_CHANGE && (!this.table || !this.quill.root.contains(this.table))) {
363
- this.hideTableTools();
364
- }
365
- });
366
-
332
+ this.initModules();
367
333
  this.quillHack();
368
334
  this.listenBalanceCells();
369
335
  }
@@ -408,12 +374,8 @@ export class TableUp {
408
374
  full: false,
409
375
  fullSwitch: true,
410
376
  icon: icons.table,
411
- selectionOptions: {},
412
- alignOptions: {},
413
- scrollbarOptions: {},
414
- resizeOptions: {},
415
- resizeScaleOptions: {},
416
377
  autoMergeCell: true,
378
+ modules: [],
417
379
  } as TableUpOptions, options);
418
380
  }
419
381
 
@@ -446,6 +408,16 @@ export class TableUp {
446
408
  }, options);
447
409
  }
448
410
 
411
+ initModules() {
412
+ for (const item of this.options.modules) {
413
+ this.modules[item.module.moduleName] = new item.module(this, this.quill, item.options);
414
+ }
415
+ }
416
+
417
+ getModule<T>(name: string) {
418
+ return this.modules[name] as T | undefined;
419
+ }
420
+
449
421
  quillHack() {
450
422
  const originGetSemanticHTML = this.quill.getSemanticHTML;
451
423
  this.quill.getSemanticHTML = ((index: number = 0, length?: number) => {
@@ -454,7 +426,7 @@ export class TableUp {
454
426
  const tableWrapperFormat = Quill.import(`formats/${blotName.tableWrapper}`) as typeof TableWrapperFormat;
455
427
  const parser = new DOMParser();
456
428
  const doc = parser.parseFromString(html, 'text/html');
457
- for (const node of Array.from(doc.querySelectorAll(`.${tableWrapperFormat.className} caption[contenteditable], .${tableWrapperFormat.className} td > [contenteditable]`))) {
429
+ for (const node of Array.from(doc.querySelectorAll(`.${tableWrapperFormat.className} caption[contenteditable], .${tableWrapperFormat.className} .${TableCellFormat.className} > [contenteditable]`))) {
458
430
  node.removeAttribute('contenteditable');
459
431
  }
460
432
 
@@ -471,12 +443,14 @@ export class TableUp {
471
443
  const range = this.getSelection(true);
472
444
  const formats = this.getFormat(range);
473
445
  // only when selection in cell and selectedTds > 1 can format all cells
474
- if (!formats[blotName.tableCellInner] || range.length > 0 || (tableUpModule && tableUpModule.tableSelection && tableUpModule.tableSelection.selectedTds.length <= 1)) {
446
+ const tableSelection = tableUpModule.getModule<TableSelection>('table-selection');
447
+ console.log(tableSelection?.selectedTds);
448
+ if (!formats[blotName.tableCellInner] || range.length > 0 || (tableUpModule && tableSelection && tableSelection.selectedTds.length <= 1)) {
475
449
  return originFormat.call(this, name, value, source);
476
450
  }
477
451
  // format in selected cells
478
- if (tableUpModule && tableUpModule.tableSelection && tableUpModule.tableSelection.selectedTds.length > 0) {
479
- const selectedTds = tableUpModule.tableSelection.selectedTds;
452
+ if (tableUpModule && tableSelection && tableSelection.selectedTds.length > 0) {
453
+ const selectedTds = tableSelection.selectedTds;
480
454
  // calculate the format value. the format should be canceled when this value exists in all selected cells
481
455
  let setOrigin = false;
482
456
  const tdRanges = [];
@@ -571,13 +545,14 @@ export class TableUp {
571
545
  }
572
546
  // if selection range is not in table, but use the TableSelection selected cells
573
547
  // clean all other formats in cell
574
- if (tableUpModule && tableUpModule.tableSelection && tableUpModule.tableSelection.selectedTds.length > 0 && tableUpModule.tableSelection.table) {
575
- const tableMain = Quill.find(tableUpModule.tableSelection.table) as TableMainFormat;
548
+ const tableSelection = tableUpModule.getModule<TableSelection>('table-selection');
549
+ if (tableUpModule && tableSelection && tableSelection.selectedTds.length > 0 && tableSelection.table) {
550
+ const tableMain = Quill.find(tableSelection.table) as TableMainFormat;
576
551
  if (!tableMain) {
577
552
  console.warn('TableMainFormat not found');
578
553
  return;
579
554
  }
580
- const selectedTds = tableUpModule.tableSelection.selectedTds;
555
+ const selectedTds = tableSelection.selectedTds;
581
556
 
582
557
  // get all need clean style cells. include border-right/border-bottom effect cells
583
558
  const editTds = new Set<TableCellFormat>();
@@ -639,45 +614,6 @@ export class TableUp {
639
614
  }
640
615
  }
641
616
 
642
- showTableTools(table: HTMLElement) {
643
- if (table) {
644
- this.table = table;
645
- this.tableSelection?.show();
646
- if (this.options.align) {
647
- this.tableAlign = new this.options.align(this, table, this.quill, this.options.alignOptions);
648
- }
649
- if (this.options.scrollbar) {
650
- this.tableScrollbar = new this.options.scrollbar(this, table, this.quill, this.options.scrollbarOptions);
651
- }
652
- if (this.options.resize) {
653
- this.tableResize = new this.options.resize(this, table, this.quill, this.options.resizeOptions);
654
- }
655
- if (this.options.resizeScale) {
656
- this.tableResizeScale = new this.options.resizeScale(this, table, this.quill, this.options.resizeScaleOptions);
657
- }
658
- }
659
- }
660
-
661
- hideTableTools() {
662
- this.tableSelection?.hide();
663
- if (this.tableScrollbar) {
664
- this.tableScrollbar.destroy();
665
- this.tableScrollbar = undefined;
666
- }
667
- if (this.tableAlign) {
668
- this.tableAlign.destroy();
669
- this.tableAlign = undefined;
670
- }
671
- if (this.tableResize) {
672
- this.tableResize.destroy();
673
- this.tableResize = undefined;
674
- }
675
- if (this.tableResizeScale) {
676
- this.tableResizeScale.destroy();
677
- }
678
- this.table = undefined;
679
- }
680
-
681
617
  async buildCustomSelect(customSelect: ((module: TableUp, picker: QuillThemePicker) => HTMLElement | Promise<HTMLElement>) | undefined, picker: QuillThemePicker) {
682
618
  if (!customSelect || !isFunction(customSelect)) return;
683
619
  const dom = document.createElement('div');
@@ -771,7 +707,7 @@ export class TableUp {
771
707
  // filter td
772
708
  let rowCount = 0;
773
709
  let lastRowId: string | null = null;
774
- for (const td of Array.from(doc.querySelectorAll('td'))) {
710
+ for (const td of Array.from(doc.querySelectorAll('td, th')) as HTMLElement[]) {
775
711
  if (!cellIds.has(`${td.dataset.rowId}-${td.dataset.colId}`)) {
776
712
  const parent = td.parentElement;
777
713
  td.remove();
@@ -820,7 +756,7 @@ export class TableUp {
820
756
  return doc.body.innerHTML;
821
757
  }
822
758
 
823
- insertTable(rows: number, columns: number) {
759
+ insertTable(rows: number, columns: number, source: EmitterSource = Quill.sources.API) {
824
760
  if (rows >= 30 || columns >= 30) {
825
761
  throw new Error('Both rows and columns must be less than 30.');
826
762
  }
@@ -834,14 +770,15 @@ export class TableUp {
834
770
  throw new Error(`Not supported ${currentBlot.statics.blotName} insert into table.`);
835
771
  }
836
772
 
773
+ const tableId = randomId();
774
+ const colIds = new Array(columns).fill(0).map(() => randomId());
775
+
837
776
  const borderWidth = this.calculateTableCellBorderWidth();
838
777
  const rootStyle = getComputedStyle(this.quill.root);
839
778
  const paddingLeft = Number.parseInt(rootStyle.paddingLeft);
840
779
  const paddingRight = Number.parseInt(rootStyle.paddingRight);
841
- const width = Number.parseInt(rootStyle.width) - paddingLeft - paddingRight - borderWidth;
842
-
843
- const tableId = randomId();
844
- const colIds = new Array(columns).fill(0).map(() => randomId());
780
+ const scrollBarWidth = this.quill.root.scrollHeight > this.quill.root.clientHeight ? getScrollBarWidth({ target: this.quill.root }) : 0;
781
+ const width = Number.parseInt(rootStyle.width) - paddingLeft - paddingRight - borderWidth - scrollBarWidth;
845
782
 
846
783
  // insert delta data to create table
847
784
  const colWidth = !this.options.full ? `${Math.max(Math.floor(width / columns), tableUpSize.colMinWidthPx)}px` : `${Math.max((1 / columns) * 100, tableUpSize.colMinWidthPre)}%`;
@@ -880,7 +817,7 @@ export class TableUp {
880
817
  }
881
818
  }
882
819
 
883
- this.quill.updateContents(new Delta(delta), Quill.sources.USER);
820
+ this.quill.updateContents(new Delta(delta), source);
884
821
  this.quill.setSelection(range.index + columns, Quill.sources.SILENT);
885
822
  this.quill.focus();
886
823
  }
@@ -911,72 +848,76 @@ export class TableUp {
911
848
 
912
849
  // handle unusual delete cell
913
850
  fixUnusuaDeletelTable(tableBlot: TableMainFormat) {
914
- // calculate all cells
915
- const trBlots = tableBlot.getRows();
851
+ const bodys = tableBlot.getBodys();
916
852
  const tableColIds = tableBlot.getColIds();
917
- if (trBlots.length === 0) {
918
- return tableBlot.remove();
919
- }
920
- if (tableColIds.length === 0) return;
921
- // append by col
922
- const cellSpanMap = new Array(trBlots.length).fill(0).map(() => new Array(tableColIds.length).fill(false));
923
853
  const tableId = tableBlot.tableId;
924
- for (const [indexTr, tr] of trBlots.entries()) {
925
- let indexTd = 0;
926
- let indexCol = 0;
927
- const curCellSpan = cellSpanMap[indexTr];
928
- const tds = tr.descendants(TableCellFormat);
929
- // loop every row and column
930
- while (indexCol < tableColIds.length) {
854
+ for (const body of bodys) {
855
+ // calculate all cells in body
856
+ const trBlots = body.getRows();
857
+ if (trBlots.length === 0) {
858
+ body.remove();
859
+ continue;
860
+ }
861
+ if (tableColIds.length === 0) continue;
862
+ // append by col
863
+ const cellSpanMap = new Array(trBlots.length).fill(0).map(() => new Array(tableColIds.length).fill(false));
864
+ for (const [indexTr, tr] of trBlots.entries()) {
865
+ let indexTd = 0;
866
+ let indexCol = 0;
867
+ const curCellSpan = cellSpanMap[indexTr];
868
+ const tds = tr.descendants(TableCellFormat);
869
+ // loop every row and column
870
+ while (indexCol < tableColIds.length) {
931
871
  // skip when rowspan or colspan
932
- if (curCellSpan[indexCol]) {
933
- indexCol += 1;
934
- continue;
935
- }
936
- const curTd = tds[indexTd];
937
- // if colId does not match. insert a new one
938
- if (!curTd || curTd.colId !== tableColIds[indexCol]) {
939
- tr.insertBefore(
940
- createCell(
941
- this.quill.scroll,
942
- {
943
- tableId,
944
- colId: tableColIds[indexCol],
945
- rowId: tr.rowId,
946
- },
947
- ),
948
- curTd,
949
- );
950
- }
951
- else {
952
- if (indexTr + curTd.rowspan - 1 >= trBlots.length) {
953
- curTd.getCellInner().rowspan = trBlots.length - indexTr;
872
+ if (curCellSpan[indexCol]) {
873
+ indexCol += 1;
874
+ continue;
875
+ }
876
+ const curTd = tds[indexTd];
877
+ // if colId does not match. insert a new one
878
+ if (!curTd || curTd.colId !== tableColIds[indexCol]) {
879
+ tr.insertBefore(
880
+ createCell(
881
+ this.quill.scroll,
882
+ {
883
+ tableId,
884
+ colId: tableColIds[indexCol],
885
+ rowId: tr.rowId,
886
+ },
887
+ ),
888
+ curTd,
889
+ );
954
890
  }
891
+ else {
892
+ if (indexTr + curTd.rowspan - 1 >= trBlots.length) {
893
+ curTd.getCellInner().rowspan = trBlots.length - indexTr;
894
+ }
955
895
 
956
- const { colspan, rowspan } = curTd;
957
- // skip next column cell
958
- if (colspan > 1) {
959
- for (let c = 1; c < colspan; c++) {
960
- curCellSpan[indexCol + c] = true;
896
+ const { colspan, rowspan } = curTd;
897
+ // skip next column cell
898
+ if (colspan > 1) {
899
+ for (let c = 1; c < colspan; c++) {
900
+ curCellSpan[indexCol + c] = true;
901
+ }
961
902
  }
962
- }
963
- // skip next rowspan cell
964
- if (rowspan > 1) {
965
- for (let r = indexTr + 1; r < indexTr + rowspan; r++) {
966
- for (let c = 0; c < colspan; c++) {
967
- cellSpanMap[r][indexCol + c] = true;
903
+ // skip next rowspan cell
904
+ if (rowspan > 1) {
905
+ for (let r = indexTr + 1; r < indexTr + rowspan; r++) {
906
+ for (let c = 0; c < colspan; c++) {
907
+ cellSpanMap[r][indexCol + c] = true;
908
+ }
968
909
  }
969
910
  }
911
+ indexTd += 1;
970
912
  }
971
- indexTd += 1;
913
+ indexCol += 1;
972
914
  }
973
- indexCol += 1;
974
- }
975
915
 
976
- // if td not match all exist td. Indicates that a cell has been inserted
977
- if (indexTd < tds.length) {
978
- for (let i = indexTd; i < tds.length; i++) {
979
- tds[i].remove();
916
+ // if td not match all exist td. Indicates that a cell has been inserted
917
+ if (indexTd < tds.length) {
918
+ for (let i = indexTd; i < tds.length; i++) {
919
+ tds[i].remove();
920
+ }
980
921
  }
981
922
  }
982
923
  }
@@ -984,7 +925,6 @@ export class TableUp {
984
925
 
985
926
  balanceTables() {
986
927
  for (const tableBlot of this.quill.scroll.descendants(TableMainFormat)) {
987
- // 同时, 当关闭 autoMerge 时, 如何对 cell 数据的 emptyRow 处理
988
928
  tableBlot.checkEmptyCol(this.options.autoMergeCell);
989
929
  tableBlot.checkEmptyRow(this.options.autoMergeCell);
990
930
  this.fixUnusuaDeletelTable(tableBlot);
@@ -1021,19 +961,18 @@ export class TableUp {
1021
961
  if (selectedTds.length === 0) return;
1022
962
  const tableBlot = findParentBlot(selectedTds[0], blotName.tableMain);
1023
963
  tableBlot && tableBlot.remove();
1024
- this.hideTableTools();
1025
964
  }
1026
965
 
1027
966
  appendRow(selectedTds: TableCellInnerFormat[], isDown: boolean) {
1028
967
  if (selectedTds.length <= 0) return;
1029
968
  // find baseTd and baseTr
1030
969
  const baseTd = selectedTds[isDown ? selectedTds.length - 1 : 0];
1031
- const [tableBlot, tableBodyBlot, baseTdParentTr] = findParentBlots(baseTd, [blotName.tableMain, blotName.tableBody, blotName.tableRow] as const);
970
+ const [tableBlot, baseTdParentTr] = findParentBlots(baseTd, [blotName.tableMain, blotName.tableRow] as const);
1032
971
  const tableTrs = tableBlot.getRows();
1033
972
  const i = tableTrs.indexOf(baseTdParentTr);
1034
973
  const insertRowIndex = i + (isDown ? baseTd.rowspan : 0);
1035
974
 
1036
- tableBodyBlot.insertRow(insertRowIndex);
975
+ tableBlot.insertRow(insertRowIndex);
1037
976
  }
1038
977
 
1039
978
  appendCol(selectedTds: TableCellInnerFormat[], isRight: boolean) {
@@ -1270,6 +1209,25 @@ export class TableUp {
1270
1209
 
1271
1210
  mergeCells(selectedTds: TableCellInnerFormat[]) {
1272
1211
  if (selectedTds.length <= 1) return;
1212
+ const baseCell = selectedTds[0];
1213
+ // move selected cells in same table body
1214
+ const baseCellBody = baseCell.getTableBody();
1215
+ // insert base row
1216
+ let baseRow = baseCell.getTableRow();
1217
+ if (!baseCellBody || !baseRow) return;
1218
+ for (let i = 1; i < selectedTds.length; i++) {
1219
+ const selectTd = selectedTds[i];
1220
+ const currentTdBody = selectTd.getTableBody();
1221
+ if (currentTdBody && currentTdBody !== baseCellBody) {
1222
+ const currentRow = selectTd.getTableRow();
1223
+ if (currentRow) {
1224
+ baseRow.parent.insertBefore(currentRow, baseRow.next);
1225
+ baseRow = currentRow;
1226
+ }
1227
+ }
1228
+ }
1229
+ baseCellBody.convertBody(baseCell.wrapTag);
1230
+
1273
1231
  const counts = selectedTds.reduce(
1274
1232
  (pre, selectTd, index) => {
1275
1233
  // count column span
@@ -1287,7 +1245,7 @@ export class TableUp {
1287
1245
  }
1288
1246
  return pre;
1289
1247
  },
1290
- [{} as Record<string, number>, {} as Record<string, number>, selectedTds[0]] as const,
1248
+ [{} as Record<string, number>, {} as Record<string, number>, baseCell] as const,
1291
1249
  );
1292
1250
 
1293
1251
  const rowCount = Math.max(...Object.values(counts[0]));
@@ -1296,44 +1254,53 @@ export class TableUp {
1296
1254
  baseTd.colspan = colCount;
1297
1255
  baseTd.rowspan = rowCount;
1298
1256
 
1257
+ // selection will move with cursor. make sure selection is in baseTd
1258
+ const index = this.quill.getIndex(baseTd);
1259
+ this.quill.setSelection({ index, length: 0 }, Quill.sources.SILENT);
1260
+
1299
1261
  const tableBlot = findParentBlot(baseTd, blotName.tableMain);
1300
1262
  this.fixTableByRemove(tableBlot);
1301
1263
  }
1302
1264
 
1303
1265
  splitCell(selectedTds: TableCellInnerFormat[]) {
1304
1266
  if (selectedTds.length !== 1) return;
1305
- const baseTd = selectedTds[0];
1306
- if (baseTd.colspan === 1 && baseTd.rowspan === 1) return;
1307
- const [tableBlot, baseTr] = findParentBlots(baseTd, [blotName.tableMain, blotName.tableRow] as const);
1267
+ const baseCell = selectedTds[0];
1268
+ if (baseCell.colspan === 1 && baseCell.rowspan === 1) return;
1269
+ const [tableBlot, baseTr] = findParentBlots(baseCell, [blotName.tableMain, blotName.tableRow] as const);
1270
+ const rows = tableBlot.getRows();
1308
1271
  const tableId = tableBlot.tableId;
1309
- const colIndex = baseTd.getColumnIndex();
1310
- const colIds = tableBlot.getColIds().slice(colIndex, colIndex + baseTd.colspan).reverse();
1311
- const baseTdStyle = (baseTd.formats()[blotName.tableCellInner] as TableCellValue).style;
1312
-
1313
- let curTr = baseTr;
1314
- let rowspan = baseTd.rowspan;
1272
+ const colIndex = baseCell.getColumnIndex();
1273
+ const colIds = tableBlot.getColIds().slice(colIndex, colIndex + baseCell.colspan).reverse();
1274
+ const baseCellValue = baseCell.formats()[blotName.tableCellInner] as TableCellValue;
1275
+ const { emptyRow, ...extendsBaseCellValue } = baseCellValue;
1276
+
1277
+ let rowIndex = rows.indexOf(baseTr);
1278
+ if (rowIndex === -1) return;
1279
+ let curTr = rows[rowIndex];
1280
+ let rowspan = baseCell.rowspan;
1315
1281
  // reset span first. insertCell need colspan to judge insert position
1316
- baseTd.colspan = 1;
1317
- baseTd.rowspan = 1;
1282
+ baseCell.colspan = 1;
1283
+ baseCell.rowspan = 1;
1318
1284
  while (curTr && rowspan > 0) {
1319
1285
  for (const id of colIds) {
1320
- // keep baseTd. baseTr should insert at baseTd's column index + 1
1321
- if (curTr === baseTr && id === baseTd.colId) continue;
1322
- const value: TableCellValue = {
1323
- tableId,
1324
- rowId: curTr.rowId,
1325
- colId: id,
1326
- rowspan: 1,
1327
- colspan: 1,
1328
- };
1329
- if (baseTdStyle) {
1330
- value.style = baseTdStyle;
1331
- }
1332
- curTr.insertCell(colIndex + (curTr === baseTr ? 1 : 0), value);
1286
+ // keep baseCell. baseTr should insert at baseCell's column index + 1
1287
+ if (curTr === baseTr && id === baseCell.colId) continue;
1288
+ curTr.insertCell(
1289
+ colIndex + (curTr === baseTr ? 1 : 0),
1290
+ {
1291
+ ...extendsBaseCellValue,
1292
+ tableId,
1293
+ rowId: curTr.rowId,
1294
+ colId: id,
1295
+ rowspan: 1,
1296
+ colspan: 1,
1297
+ },
1298
+ );
1333
1299
  }
1334
1300
 
1335
1301
  rowspan -= 1;
1336
- curTr = curTr.next as TableRowFormat;
1302
+ rowIndex += 1;
1303
+ curTr = rows[rowIndex];
1337
1304
  }
1338
1305
  }
1339
1306
  }
@@ -82,5 +82,8 @@ export async function showTableCreator(options: Partial<TableCreatorOptions> = {
82
82
  });
83
83
  document.addEventListener('keydown', keyboardClose);
84
84
  cancelBtn.addEventListener('click', close);
85
- });
85
+ })
86
+ .finally(() => {
87
+ document.removeEventListener('keydown', keyboardClose);
88
+ });
86
89
  }
@@ -46,7 +46,6 @@ export function createTooltip(target: HTMLElement, options: ToolTipOptions = {})
46
46
  if (msg || content) {
47
47
  if (!tooltipContainer) {
48
48
  tooltipContainer = document.createElement('div');
49
- document.body.appendChild(tooltipContainer);
50
49
  }
51
50
  const appendTo = container || tooltipContainer;
52
51
  const tooltip = document.createElement('div');
@@ -76,6 +75,9 @@ export function createTooltip(target: HTMLElement, options: ToolTipOptions = {})
76
75
  tooltip.classList.add('hidden');
77
76
  if (appendTo.contains(tooltip)) {
78
77
  appendTo.removeChild(tooltip);
78
+ if (appendTo === tooltipContainer && !tooltipContainer.hasChildNodes()) {
79
+ document.body.removeChild(tooltipContainer);
80
+ }
79
81
  }
80
82
  if (cleanup) cleanup();
81
83
  if (closed) closed();
@@ -89,6 +91,9 @@ export function createTooltip(target: HTMLElement, options: ToolTipOptions = {})
89
91
  const allow = onOpen(force);
90
92
  if (!force && allow) return;
91
93
  }
94
+ if (appendTo === tooltipContainer && !tooltipContainer.parentElement) {
95
+ document.body.appendChild(tooltipContainer);
96
+ }
92
97
  appendTo.appendChild(tooltip);
93
98
  tooltip.removeEventListener('transitionend', transitionendHandler);
94
99
  tooltip.classList.remove('hidden');
@@ -7,7 +7,9 @@ export const blotName = {
7
7
  tableMain: 'table-up-main',
8
8
  tableColgroup: 'table-up-colgroup',
9
9
  tableCol: 'table-up-col',
10
+ tableHead: 'table-up-head',
10
11
  tableBody: 'table-up-body',
12
+ tableFoot: 'table-up-foot',
11
13
  tableRow: 'table-up-row',
12
14
  tableCell: 'table-up-cell',
13
15
  tableCellInner: 'table-up-cell-inner',
@@ -22,6 +24,10 @@ export const tableUpSize = {
22
24
 
23
25
  export const tableUpEvent = {
24
26
  AFTER_TABLE_RESIZE: 'after-table-resize',
27
+ TABLE_SELECTION_DRAG_START: 'table-selection-drag-start',
28
+ TABLE_SELECTION_DRAG_END: 'table-selection-drag-end',
29
+ TABLE_SELECTION_CHANGE: 'table-selection-change',
30
+ TABLE_SELECTION_DISPLAY_CHANGE: 'table-selection-display-change',
25
31
  };
26
32
 
27
33
  export const tableUpInternal = {
@@ -109,10 +115,10 @@ export const cssNamespace = 'table-up';
109
115
  export const tableCantInsert: Set<string> = new Set([blotName.tableCellInner]);
110
116
 
111
117
  export const isForbidInTableBlot = (blot: TypeParchment.Blot) => tableCantInsert.has(blot.statics.blotName);
112
- export function isForbidInTable(current: TypeParchment.Blot): boolean {
118
+ export function isForbidInTable(current: TypeParchment.Blot): boolean {
113
119
  return current && current.parent
114
120
  ? isForbidInTableBlot(current.parent)
115
121
  ? true
116
122
  : isForbidInTable(current.parent)
117
- : false;
123
+ : false;
118
124
  }
@@ -6,7 +6,8 @@ export * from './constants';
6
6
  export * from './is';
7
7
  export * from './position';
8
8
  export * from './resize-observer-helper';
9
- export * from './scroll-event-helper';
9
+ export * from './scroll';
10
+ export * from './style-helper';
10
11
  export * from './transformer';
11
12
  export * from './transition-event-helper';
12
13
  export * from './types';