quill-table-up 2.3.1 → 2.4.1

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 (38) hide show
  1. package/README.md +1 -0
  2. package/dist/index.d.ts +35 -12
  3. package/dist/index.js +26 -25
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.umd.js +35 -31
  6. package/dist/index.umd.js.map +1 -1
  7. package/package.json +2 -2
  8. package/src/__tests__/e2e/table-blots.test.ts +22 -0
  9. package/src/__tests__/e2e/table-keyboard-handler.test.ts +218 -0
  10. package/src/__tests__/e2e/table-selection.test.ts +137 -131
  11. package/src/__tests__/unit/table-cell-merge.test.ts +261 -1
  12. package/src/__tests__/unit/table-clipboard.test.ts +62 -44
  13. package/src/__tests__/unit/table-insert.test.ts +39 -2
  14. package/src/__tests__/unit/table-redo-undo.test.ts +69 -0
  15. package/src/__tests__/unit/table-remove.test.ts +4 -2
  16. package/src/__tests__/unit/utils.ts +22 -4
  17. package/src/__tests__/unit/vitest.d.ts +4 -1
  18. package/src/formats/index.ts +6 -0
  19. package/src/formats/overrides/block-embed.ts +54 -0
  20. package/src/formats/overrides/block.ts +23 -4
  21. package/src/formats/overrides/index.ts +1 -0
  22. package/src/formats/table-cell-format.ts +39 -6
  23. package/src/formats/table-cell-inner-format.ts +70 -21
  24. package/src/formats/table-main-format.ts +92 -1
  25. package/src/formats/table-row-format.ts +13 -2
  26. package/src/modules/table-clipboard.ts +30 -35
  27. package/src/modules/table-resize/table-resize-box.ts +2 -2
  28. package/src/modules/table-resize/table-resize-common.ts +3 -5
  29. package/src/modules/table-resize/table-resize-line.ts +1 -1
  30. package/src/modules/table-resize/table-resize-scale.ts +1 -1
  31. package/src/modules/table-scrollbar.ts +9 -10
  32. package/src/modules/table-selection.ts +52 -31
  33. package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  34. package/src/table-up.ts +57 -23
  35. package/src/utils/blot-helper.ts +7 -4
  36. package/src/utils/index.ts +1 -1
  37. package/src/utils/{scroll-event-handle.ts → scroll-event-helper.ts} +7 -0
  38. package/src/utils/types.ts +2 -0
@@ -2,6 +2,7 @@ import type TypeScroll from 'quill/blots/scroll';
2
2
  import type { TableValue } from '../utils';
3
3
  import { blotName, tableUpSize } from '../utils';
4
4
  import { ContainerFormat } from './container-format';
5
+ import { TableCellInnerFormat } from './table-cell-inner-format';
5
6
  import { TableColFormat } from './table-col-format';
6
7
  import { TableRowFormat } from './table-row-format';
7
8
 
@@ -128,7 +129,9 @@ export class TableMainFormat extends ContainerFormat {
128
129
  }
129
130
 
130
131
  getRows() {
131
- return this.descendants(TableRowFormat);
132
+ return Array.from(this.domNode.querySelectorAll(`${TableRowFormat.tagName}`))
133
+ .map(el => this.scroll.find(el) as TableRowFormat)
134
+ .filter(Boolean);
132
135
  }
133
136
 
134
137
  getRowIds() {
@@ -159,6 +162,94 @@ export class TableMainFormat extends ContainerFormat {
159
162
  }
160
163
 
161
164
  super.optimize(context);
165
+ this.mergeRow();
166
+ }
167
+
168
+ // ensure row id unique in same table
169
+ mergeRow() {
170
+ if (!this.parent) return;
171
+ const rows = this.getRows();
172
+ const rowGroup: Record<string, TableRowFormat[]> = {};
173
+ for (const row of rows) {
174
+ if (!rowGroup[row.rowId]) rowGroup[row.rowId] = [];
175
+ rowGroup[row.rowId].push(row);
176
+ }
177
+
178
+ for (const rowList of Object.values(rowGroup)) {
179
+ for (let i = 1; i < rowList.length; i++) {
180
+ const row = rowList[i];
181
+ row.moveChildren(rowList[0]);
182
+ row.remove();
183
+ }
184
+ }
185
+ }
186
+
187
+ checkEmptyCol(autoMerge: boolean) {
188
+ if (autoMerge) {
189
+ const rowCount = this.getRows().length;
190
+ const cols = this.getCols();
191
+ const cells = this.descendants(TableCellInnerFormat);
192
+ for (const cell of cells) {
193
+ if (cell.colspan > 1 && cell.rowspan >= rowCount) {
194
+ const index = cols.findIndex(col => col.colId === cell.colId);
195
+ const currentCol = cols[index];
196
+ for (let i = index + 1; i < index + cell.colspan; i++) {
197
+ cols[i].remove();
198
+ currentCol.width += cols[i].width;
199
+ }
200
+ cell.colspan = 1;
201
+ }
202
+ }
203
+ }
204
+ }
205
+
206
+ checkEmptyRow(autoMerge: boolean) {
207
+ const rows = this.getRows();
208
+ const rowIds = new Set(rows.map(row => row.rowId));
209
+ for (let i = rows.length - 1; i >= 0; i--) {
210
+ const row = rows[i];
211
+ if (autoMerge) {
212
+ // reduce rowspan previou row
213
+ if (row.children.length === 0) {
214
+ for (let gap = 1, j = i - 1; j >= 0; j--, gap++) {
215
+ const prev = rows[j];
216
+ prev.foreachCellInner((cell) => {
217
+ if (cell.rowspan > gap) {
218
+ cell.rowspan -= 1;
219
+ const emptyRow = new Set(cell.emptyRow);
220
+ emptyRow.delete(row.rowId);
221
+ cell.emptyRow = Array.from(emptyRow);
222
+ }
223
+ });
224
+ }
225
+ row.remove();
226
+ }
227
+ }
228
+ else {
229
+ if (row.children.length === 0 && row.prev) {
230
+ // find the not empty row
231
+ let prev: TableRowFormat = row.prev as TableRowFormat;
232
+ while (prev && prev.children.length === 0) {
233
+ prev = prev.prev as TableRowFormat;
234
+ }
235
+ prev.foreachCellInner((cell) => {
236
+ const emptyRowIds = new Set(cell.emptyRow);
237
+ // prevent order change. like currnet emptyRow ['1', '2'] add rowId '2' will be ['2', '1']
238
+ if (!emptyRowIds.has(row.rowId)) {
239
+ // the loop is from back to front, and the rowId should be added to the head
240
+ cell.emptyRow = [row.rowId, ...emptyRowIds];
241
+ }
242
+ });
243
+ }
244
+ row.foreachCellInner((cell) => {
245
+ for (const emptyRow of cell.emptyRow) {
246
+ if (!rowIds.has(emptyRow)) {
247
+ row.parent.insertBefore(this.scroll.create(blotName.tableRow, { tableId: this.tableId, rowId: emptyRow }), row.next);
248
+ }
249
+ }
250
+ });
251
+ }
252
+ }
162
253
  }
163
254
 
164
255
  sortMergeChildren() {
@@ -157,13 +157,24 @@ export class TableRowFormat extends ContainerFormat {
157
157
  );
158
158
  }
159
159
 
160
- optimize(context: Record<string, any>) {
160
+ optimize(_context: Record<string, any>) {
161
161
  const parent = this.parent;
162
162
  const { tableId } = this;
163
163
  if (parent !== null && parent.statics.blotName !== blotName.tableBody) {
164
164
  this.wrap(blotName.tableBody, tableId);
165
165
  }
166
166
 
167
- super.optimize(context);
167
+ if (
168
+ this.statics.requiredContainer
169
+ && !(this.parent instanceof this.statics.requiredContainer)
170
+ ) {
171
+ this.wrap(this.statics.requiredContainer.blotName);
172
+ }
173
+
174
+ this.enforceAllowedChildren();
175
+ if (this.children.length > 0 && this.next != null && this.checkMerge()) {
176
+ this.next.moveChildren(this);
177
+ this.next.remove();
178
+ }
168
179
  }
169
180
  }
@@ -46,9 +46,7 @@ function calculateCols(tableNode: HTMLElement, colNums: number): number[] {
46
46
  export class TableClipboard extends Clipboard {
47
47
  tableId = randomId();
48
48
  rowId = randomId();
49
- rowIds: string[] = [];
50
49
  colIds: string[] = [];
51
- emptyRowCount: number[] = [];
52
50
  rowspanCount: { rowspan: number; colspan: number }[] = [];
53
51
  cellCount = 0;
54
52
  colCount = 0;
@@ -69,16 +67,17 @@ export class TableClipboard extends Clipboard {
69
67
  matchTable(node: Node, delta: TypeDelta) {
70
68
  if (delta.ops.length === 0) return delta;
71
69
 
72
- const preSumEmptyRowCount = this.emptyRowCount.reduce((acc, cur) => {
73
- acc.push((acc[acc.length - 1] ?? 0) + cur);
74
- return acc;
75
- }, [] as number[]);
76
-
77
70
  const ops: Record<string, any>[] = [];
78
71
  const cols: Record<string, any>[] = [];
79
72
  let bodyStartIndex = -1;
80
73
  for (let i = 0; i < delta.ops.length; i++) {
81
74
  const { attributes, insert } = delta.ops[i];
75
+ // if attribute doesn't have tableCellInner, treat it as a blank line(emptyRow)
76
+ if (!isObject(insert) && attributes && !attributes[blotName.tableCellInner] && !attributes[blotName.tableCaption]) {
77
+ delta.ops.splice(i, 1);
78
+ i -= 1;
79
+ continue;
80
+ }
82
81
  // remove quill origin table format and tableCell format
83
82
  const { table, [blotName.tableCell]: tableCell, ...attrs } = attributes || {};
84
83
  const hasCol = isObject(insert) && insert[blotName.tableCol];
@@ -86,19 +85,7 @@ export class TableClipboard extends Clipboard {
86
85
  cols.push({ insert });
87
86
  }
88
87
  else {
89
- if (attrs[blotName.tableCellInner]) {
90
- const tableCellInner = attrs[blotName.tableCellInner] as TableCellValue;
91
- const rowNum = this.rowIds.indexOf(tableCellInner.rowId);
92
- // reduce cell rowspan by counting empty rows
93
- if (rowNum !== -1) {
94
- const rowspan = tableCellInner.rowspan;
95
- tableCellInner.rowspan -= (preSumEmptyRowCount[rowNum + rowspan - 1] - preSumEmptyRowCount[rowNum]);
96
- }
97
- ops.push({ attributes: { ...attrs, [blotName.tableCellInner]: tableCellInner }, insert });
98
- }
99
- else {
100
- ops.push({ attributes: attrs, insert });
101
- }
88
+ ops.push({ attributes: attrs, insert });
102
89
  }
103
90
  // record col insert index
104
91
  if (
@@ -135,9 +122,7 @@ export class TableClipboard extends Clipboard {
135
122
 
136
123
  // reset variable to avoid conflict with other table
137
124
  this.tableId = randomId();
138
- this.rowIds = [];
139
125
  this.colIds = [];
140
- this.emptyRowCount = [];
141
126
  this.rowspanCount = [];
142
127
  this.cellCount = 0;
143
128
  this.colCount = 0;
@@ -158,6 +143,28 @@ export class TableClipboard extends Clipboard {
158
143
  }
159
144
  }
160
145
  }
146
+ // add `emptyRow`
147
+ let emptyRows = [];
148
+ for (let i = delta.ops.length - 1; i >= 0; i--) {
149
+ const op = delta.ops[i];
150
+ if (op.attributes) {
151
+ if (!op.attributes[blotName.tableCellInner]) {
152
+ emptyRows = [];
153
+ emptyRows.push(randomId());
154
+ }
155
+ else {
156
+ if ((op.attributes[blotName.tableCellInner] as TableCellValue).rowspan === 1) {
157
+ emptyRows = [];
158
+ }
159
+ else if (emptyRows.length > 0) {
160
+ if (!(op.attributes[blotName.tableCellInner] as TableCellValue).emptyRow) {
161
+ (op.attributes[blotName.tableCellInner] as TableCellValue).emptyRow = [];
162
+ }
163
+ (op.attributes[blotName.tableCellInner] as TableCellValue).emptyRow!.push(...emptyRows);
164
+ }
165
+ }
166
+ }
167
+ }
161
168
  return delta;
162
169
  }
163
170
 
@@ -173,6 +180,7 @@ export class TableClipboard extends Clipboard {
173
180
  }
174
181
 
175
182
  matchCol(node: Node, _delta: TypeDelta) {
183
+ // split col by span
176
184
  let span = Number((node as HTMLElement).getAttribute('span') || 1);
177
185
  if (Number.isNaN(span)) span = 1;
178
186
 
@@ -194,7 +202,6 @@ export class TableClipboard extends Clipboard {
194
202
  }
195
203
 
196
204
  matchTr(node: Node, delta: TypeDelta) {
197
- this.rowIds.push(this.rowId);
198
205
  this.rowId = randomId();
199
206
  this.cellCount = 0;
200
207
  // minus rowspan
@@ -206,11 +213,9 @@ export class TableClipboard extends Clipboard {
206
213
  this.rowspanCount[i] = { rowspan: 0, colspan: 0 };
207
214
  }
208
215
  }
209
- let hasTd = true;
210
216
  for (const op of delta.ops) {
211
217
  if (op.attributes) {
212
218
  const { background, [blotName.tableCellInner]: tableCellInner } = op.attributes;
213
- if (!tableCellInner) hasTd = false;
214
219
  if (tableCellInner && background) {
215
220
  const { style = '' } = tableCellInner as TableCellValue;
216
221
  const styleObj = cssTextToObject(style);
@@ -218,17 +223,7 @@ export class TableClipboard extends Clipboard {
218
223
  (op.attributes![blotName.tableCellInner] as TableCellValue).style = objectToCssText(styleObj);
219
224
  }
220
225
  }
221
- else {
222
- hasTd = false;
223
- }
224
- }
225
- // record if row doesn't have any cell. It's happen in excel. https://github.com/zzxming/quill-table-up/issues/165
226
- let rowspanCount = 0;
227
- if (!hasTd) {
228
- rowspanCount = 1;
229
- delta = new Delta();
230
226
  }
231
- this.emptyRowCount.push(rowspanCount);
232
227
  return delta;
233
228
  }
234
229
 
@@ -187,7 +187,7 @@ export class TableResizeBox extends TableResizeCommon {
187
187
 
188
188
  update() {
189
189
  const { rect: tableRect, body: tableBodyBlot } = getTableMainRect(this.tableMain);
190
- if (!tableBodyBlot) return;
190
+ if (!tableBodyBlot || !tableRect) return;
191
191
  const tableWrapperRect = this.tableWrapper.domNode.getBoundingClientRect();
192
192
  const rootRect = this.quill.root.getBoundingClientRect();
193
193
  Object.assign(this.root.style, {
@@ -238,7 +238,7 @@ export class TableResizeBox extends TableResizeCommon {
238
238
  this.tableRows = this.tableMain.getRows();
239
239
  this.root.innerHTML = '';
240
240
  const { rect: tableRect, body: tableBodyBlot } = getTableMainRect(this.tableMain);
241
- if (!tableBodyBlot) return;
241
+ if (!tableBodyBlot || !tableRect) return;
242
242
  const tableWrapperRect = this.tableWrapper.domNode.getBoundingClientRect();
243
243
 
244
244
  if (this.tableCols.length > 0 && this.tableRows.length > 0) {
@@ -255,7 +255,7 @@ export class TableResizeCommon {
255
255
  e.preventDefault();
256
256
  if (!this.tableMain) return;
257
257
  const { rect: tableRect, body: tableBodyBlot } = getTableMainRect(this.tableMain);
258
- if (!tableBodyBlot) return;
258
+ if (!tableBodyBlot || !tableRect) return;
259
259
  // set drag init width
260
260
  const cols = this.tableMain.getCols();
261
261
  this.colIndex = this.findCurrentColIndex(e);
@@ -269,8 +269,7 @@ export class TableResizeCommon {
269
269
  this.dragging = true;
270
270
 
271
271
  const divDom = document.createElement('div');
272
- divDom.classList.add(this.dragBEM.b());
273
- divDom.classList.add(this.dragBEM.is('col'));
272
+ divDom.classList.add(this.dragBEM.b(), this.dragBEM.is('col'));
274
273
  divDom.dataset.w = String(width);
275
274
 
276
275
  const styleValue = {
@@ -350,8 +349,7 @@ export class TableResizeCommon {
350
349
  const tableMainRect = this.tableMain.domNode.getBoundingClientRect();
351
350
 
352
351
  const divDom = document.createElement('div');
353
- divDom.classList.add(this.dragBEM.b());
354
- divDom.classList.add(this.dragBEM.is('row'));
352
+ divDom.classList.add(this.dragBEM.b(), this.dragBEM.is('row'));
355
353
  divDom.dataset.h = String(height);
356
354
 
357
355
  const styleValue = {
@@ -64,7 +64,7 @@ export class TableResizeLine extends TableResizeCommon {
64
64
  }
65
65
 
66
66
  findCurrentColIndex() {
67
- return this.curColIndex;
67
+ return this.curColIndex + (this.tableCellBlot?.colspan || 1) - 1;
68
68
  }
69
69
 
70
70
  handleColMouseUpFunc = async function (this: TableResizeLine) {
@@ -116,7 +116,7 @@ export class TableResizeScale {
116
116
  return;
117
117
  }
118
118
  const { rect: tableRect, body: tableBodyBlot } = getTableMainRect(this.tableMainBlot);
119
- if (!tableBodyBlot) return;
119
+ if (!tableBodyBlot || !tableRect) return;
120
120
  const tableWrapperRect = this.tableWrapperBlot.domNode.getBoundingClientRect();
121
121
  const editorRect = this.quill.root.getBoundingClientRect();
122
122
  const { scrollTop, scrollLeft } = this.tableWrapperBlot.domNode;
@@ -19,9 +19,9 @@ export class Scrollbar {
19
19
  X: number;
20
20
  Y: number;
21
21
  } = {
22
- X: 0,
23
- Y: 0,
24
- };
22
+ X: 0,
23
+ Y: 0,
24
+ };
25
25
 
26
26
  ob: ResizeObserver;
27
27
  container: HTMLElement;
@@ -70,8 +70,8 @@ export class Scrollbar {
70
70
  }
71
71
 
72
72
  setScrollbarPosition() {
73
- const { rect: { width: tableWidth, height: tableHeight }, body: tableBodyBlot } = getTableMainRect(this.tableMainBlot);
74
- if (!tableBodyBlot) return;
73
+ const { rect: tableRect, body: tableBodyBlot } = getTableMainRect(this.tableMainBlot);
74
+ if (!tableBodyBlot || !tableRect) return;
75
75
  const { scrollLeft: editorScrollX, scrollTop: editorScrollY, offsetLeft: rootOffsetLeft, offsetTop: rootOffsetTop } = this.quill.root;
76
76
  const { offsetLeft: containerOffsetLeft, offsetTop: containerOffsetTop } = this.container;
77
77
  const { offsetLeft: bodyOffsetLeft, offsetTop: bodyOffsetTop } = tableBodyBlot.domNode;
@@ -80,10 +80,10 @@ export class Scrollbar {
80
80
  let x = containerOffsetLeft + bodyOffsetLeft - rootOffsetLeft;
81
81
  let y = containerOffsetTop + bodyOffsetTop - rootOffsetTop;
82
82
  if (this.isVertical) {
83
- x += Math.min(containerWidth, tableWidth);
83
+ x += Math.min(containerWidth, tableRect.width);
84
84
  }
85
85
  else {
86
- y += Math.min(containerHeight, tableHeight);
86
+ y += Math.min(containerHeight, tableRect.height);
87
87
  }
88
88
 
89
89
  // table align right effect
@@ -92,7 +92,7 @@ export class Scrollbar {
92
92
  }
93
93
 
94
94
  Object.assign(this.scrollbar.style, {
95
- [this.propertyMap.size]: `${this.isVertical ? Math.min(containerHeight, tableHeight) : containerWidth}px`,
95
+ [this.propertyMap.size]: `${this.isVertical ? Math.min(containerHeight, tableRect.height) : containerWidth}px`,
96
96
  transform: `translate(${x - editorScrollX}px, ${y - editorScrollY}px)`,
97
97
  });
98
98
  this.containerScrollHandler(this.container);
@@ -115,8 +115,7 @@ export class Scrollbar {
115
115
 
116
116
  createScrollbar() {
117
117
  const scrollbar = document.createElement('div');
118
- scrollbar.classList.add(this.bem.b());
119
- scrollbar.classList.add(this.isVertical ? this.bem.is('vertical') : this.bem.is('horizontal'), this.bem.is('transparent'));
118
+ scrollbar.classList.add(this.bem.b(), this.isVertical ? this.bem.is('vertical') : this.bem.is('horizontal'), this.bem.is('transparent'));
120
119
  Object.assign(scrollbar.style, {
121
120
  display: 'none',
122
121
  });
@@ -4,7 +4,7 @@ import type { TableUp } from '../table-up';
4
4
  import type { InternalTableMenuModule, RelactiveRect, TableSelectionOptions } from '../utils';
5
5
  import Quill from 'quill';
6
6
  import { getTableMainRect, TableBodyFormat, TableCellFormat, TableCellInnerFormat } from '../formats';
7
- import { addScrollEvent, blotName, clearScrollEvent, createBEM, createResizeObserver, findAllParentBlot, findParentBlot, getRelativeRect, isRectanglesIntersect, tableUpEvent } from '../utils';
7
+ import { addScrollEvent, blotName, clearScrollEvent, createBEM, createResizeObserver, findAllParentBlot, findParentBlot, getElementScroll, getRelativeRect, isRectanglesIntersect, tableUpEvent } from '../utils';
8
8
 
9
9
  const ERROR_LIMIT = 0;
10
10
 
@@ -18,8 +18,8 @@ export interface SelectionData {
18
18
  export class TableSelection {
19
19
  options: TableSelectionOptions;
20
20
  boundary: RelactiveRect | null = null;
21
- startScrollX: number = 0;
22
- startScrollY: number = 0;
21
+ scrollRecordEls: HTMLElement[] = [];
22
+ startScrollRecordPosition: { x: number; y: number }[] = [];
23
23
  selectedTableScrollX: number = 0;
24
24
  selectedTableScrollY: number = 0;
25
25
  selectedEditorScrollX: number = 0;
@@ -43,6 +43,7 @@ export class TableSelection {
43
43
 
44
44
  constructor(public tableModule: TableUp, public quill: Quill, options: Partial<TableSelectionOptions> = {}) {
45
45
  this.options = this.resolveOptions(options);
46
+ this.scrollRecordEls = [this.quill.root, document.documentElement];
46
47
 
47
48
  this.cellSelectWrap = tableModule.addContainer(this.bem.b());
48
49
  this.cellSelect = this.helpLinesInitial();
@@ -310,17 +311,12 @@ export class TableSelection {
310
311
  }),
311
312
  );
312
313
 
313
- const { x: tableScrollX, y: tableScrollY } = this.getTableViewScroll();
314
- const { x: editorScrollX, y: editorScrollY } = this.getQuillViewScroll();
315
- this.selectedTableScrollX = tableScrollX;
316
- this.selectedTableScrollY = tableScrollY;
317
- this.selectedEditorScrollX = editorScrollX;
318
- this.selectedEditorScrollY = editorScrollY;
319
-
314
+ const scrollDiff = this.getScrollPositionDiff();
320
315
  // set boundary to initially mouse move rectangle
321
316
  const { rect: tableRect } = getTableMainRect(tableMainBlot);
322
- const startPointX = startPoint.x - tableScrollX + this.startScrollX;
323
- const startPointY = startPoint.y - tableScrollY + this.startScrollY;
317
+ if (!tableRect) return [];
318
+ const startPointX = startPoint.x + scrollDiff.x;
319
+ const startPointY = startPoint.y + scrollDiff.y;
324
320
  let boundary = {
325
321
  x: Math.max(tableRect.left, Math.min(endPoint.x, startPointX)),
326
322
  y: Math.max(tableRect.top, Math.min(endPoint.y, startPointY)),
@@ -379,15 +375,44 @@ export class TableSelection {
379
375
  });
380
376
  }
381
377
 
378
+ getScrollPositionDiff() {
379
+ const { x: tableScrollX, y: tableScrollY } = this.getTableViewScroll();
380
+ const { x: editorScrollX, y: editorScrollY } = getElementScroll(this.quill.root);
381
+ this.selectedTableScrollX = tableScrollX;
382
+ this.selectedTableScrollY = tableScrollY;
383
+ this.selectedEditorScrollX = editorScrollX;
384
+ this.selectedEditorScrollY = editorScrollY;
385
+
386
+ return this.startScrollRecordPosition.reduce((pre, { x, y }, i) => {
387
+ const { x: currentX, y: currentY } = getElementScroll(this.scrollRecordEls[i]);
388
+ pre.x += x - currentX;
389
+ pre.y += y - currentY;
390
+ return pre;
391
+ }, { x: 0, y: 0 });
392
+ }
393
+
394
+ recordScrollPosition() {
395
+ this.clearRecordScrollPosition();
396
+ for (const el of this.scrollRecordEls) {
397
+ this.startScrollRecordPosition.push(getElementScroll(el));
398
+ }
399
+ }
400
+
401
+ clearRecordScrollPosition() {
402
+ this.startScrollRecordPosition = [];
403
+ }
404
+
382
405
  mouseDownHandler = (mousedownEvent: MouseEvent) => {
383
406
  const { button, target, clientX, clientY } = mousedownEvent;
384
407
  const closestTable = (target as HTMLElement).closest<HTMLTableElement>('.ql-table');
385
- const closestTableBody = (target as HTMLElement).closest<HTMLTableSectionElement>('tbody');
386
- if (button !== 0 || !closestTable || !closestTableBody) return;
408
+ const closestTableCaption = (target as HTMLElement).closest('caption');
409
+ if (button !== 0 || !closestTable || closestTableCaption) return;
387
410
 
388
411
  this.setSelectionTable(closestTable);
389
412
  const startTableId = closestTable.dataset.tableId;
390
413
  const startPoint = { x: clientX, y: clientY };
414
+
415
+ this.recordScrollPosition();
391
416
  this.selectedTds = this.computeSelectedTds(startPoint, startPoint);
392
417
  this.dragging = true;
393
418
  this.show();
@@ -400,11 +425,12 @@ export class TableSelection {
400
425
  this.tableModule.tableResize.hide();
401
426
  }
402
427
  const { button, target, clientX, clientY } = mousemoveEvent;
403
- const closestTableBody = (target as HTMLElement).closest<HTMLTableSectionElement>('tbody');
428
+ const closestTable = (target as HTMLElement).closest<HTMLTableElement>('.ql-table');
429
+ const closestTableCaption = (target as HTMLElement).closest('caption');
404
430
  if (
405
431
  button !== 0
406
- || !closestTableBody
407
- || closestTableBody.dataset.tableId !== startTableId
432
+ || closestTableCaption
433
+ || !closestTable || closestTable.dataset.tableId !== startTableId
408
434
  ) {
409
435
  return;
410
436
  }
@@ -420,6 +446,7 @@ export class TableSelection {
420
446
  document.body.removeEventListener('mousemove', mouseMoveHandler, false);
421
447
  document.body.removeEventListener('mouseup', mouseUpHandler, false);
422
448
  this.dragging = false;
449
+ this.clearRecordScrollPosition();
423
450
  if (this.tableMenu && this.selectedTds.length > 0) {
424
451
  this.tableMenu.show();
425
452
  }
@@ -451,14 +478,12 @@ export class TableSelection {
451
478
 
452
479
  update() {
453
480
  if (this.selectedTds.length === 0 || !this.boundary || !this.table) return;
454
- const { x: editorScrollX, y: editorScrollY } = this.getQuillViewScroll();
481
+ const { x: editorScrollX, y: editorScrollY } = getElementScroll(this.quill.root);
455
482
  const { x: tableScrollX, y: tableScrollY } = this.getTableViewScroll();
456
483
  const tableWrapperRect = this.table.parentElement!.getBoundingClientRect();
457
484
  const rootRect = this.quill.root.getBoundingClientRect();
458
485
  const wrapLeft = tableWrapperRect.x - rootRect.x;
459
486
  const wrapTop = tableWrapperRect.y - rootRect.y;
460
- this.startScrollX = tableScrollX;
461
- this.startScrollY = tableScrollY;
462
487
 
463
488
  Object.assign(this.cellSelect.style, {
464
489
  left: `${this.selectedEditorScrollX * 2 - editorScrollX + this.boundary.x + this.selectedTableScrollX - tableScrollX - wrapLeft}px`,
@@ -477,13 +502,6 @@ export class TableSelection {
477
502
  }
478
503
  }
479
504
 
480
- getQuillViewScroll() {
481
- return {
482
- x: this.quill.root.scrollLeft,
483
- y: this.quill.root.scrollTop,
484
- };
485
- }
486
-
487
505
  getTableViewScroll() {
488
506
  if (!this.table) {
489
507
  return {
@@ -491,15 +509,18 @@ export class TableSelection {
491
509
  y: 0,
492
510
  };
493
511
  }
494
- return {
495
- x: this.table.parentElement!.scrollLeft,
496
- y: this.table.parentElement!.scrollTop,
497
- };
512
+ return getElementScroll(this.table.parentElement!);
498
513
  }
499
514
 
500
515
  setSelectionTable(table: HTMLTableElement | undefined) {
501
516
  if (this.table === table) return;
502
517
  this.table = table;
518
+ if (this.table) {
519
+ this.scrollRecordEls.push(this.table.parentElement!);
520
+ }
521
+ else {
522
+ this.scrollRecordEls.pop();
523
+ }
503
524
  }
504
525
 
505
526
  removeCell = (e: KeyboardEvent) => {
@@ -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":4809.0661,"failed":false}],[":__tests__/unit/table-redo-undo.test.ts",{"duration":7201.539700000008,"failed":false}],[":__tests__/unit/table-hack.test.ts",{"duration":2535.698,"failed":false}],[":__tests__/unit/table-insert.test.ts",{"duration":4301.950499999992,"failed":false}],[":__tests__/unit/table-cell-merge.test.ts",{"duration":2150.3714999999997,"failed":false}],[":__tests__/unit/table-blots.test.ts",{"duration":1530.4263,"failed":false}],[":__tests__/unit/utils.test.ts",{"duration":606.7640000000001,"failed":false}],[":__tests__/unit/table-remove.test.ts",{"duration":1883.1297999999997,"failed":false}],[":__tests__/unit/table-caption.test.ts",{"duration":863.3289,"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":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}]]}