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.
- package/README.md +96 -40
- package/dist/index.css +1 -1
- package/dist/index.d.ts +172 -110
- package/dist/index.js +29 -28
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +29 -28
- package/dist/index.umd.js.map +1 -1
- package/dist/table-creator.css +1 -1
- package/package.json +1 -1
- package/src/__tests__/e2e/table-align.test.ts +1 -2
- package/src/__tests__/e2e/table-blots.test.ts +45 -35
- package/src/__tests__/e2e/table-clipboard.test.ts +20 -0
- package/src/__tests__/e2e/table-hack.test.ts +5 -5
- package/src/__tests__/e2e/table-keyboard-handler.test.ts +6 -6
- package/src/__tests__/e2e/table-menu.test.ts +23 -0
- package/src/__tests__/e2e/table-selection.test.ts +3 -3
- package/src/__tests__/unit/table-blots.test.ts +26 -26
- package/src/__tests__/unit/table-caption.test.ts +33 -36
- package/src/__tests__/unit/table-cell-merge.test.ts +114 -114
- package/src/__tests__/unit/table-clipboard.test.ts +383 -19
- package/src/__tests__/unit/table-hack.test.ts +202 -144
- package/src/__tests__/unit/table-insert.test.ts +79 -79
- package/src/__tests__/unit/table-redo-undo.test.ts +495 -64
- package/src/__tests__/unit/table-remove.test.ts +8 -11
- package/src/__tests__/unit/utils.test.ts +4 -4
- package/src/__tests__/unit/utils.ts +4 -3
- package/src/formats/container-format.ts +25 -2
- package/src/formats/index.ts +54 -8
- package/src/formats/overrides/block.ts +35 -30
- package/src/formats/table-body-format.ts +18 -58
- package/src/formats/table-cell-format.ts +296 -286
- package/src/formats/table-cell-inner-format.ts +384 -358
- package/src/formats/table-foot-format.ts +7 -0
- package/src/formats/table-head-format.ts +7 -0
- package/src/formats/table-main-format.ts +84 -5
- package/src/formats/table-row-format.ts +44 -14
- package/src/modules/index.ts +1 -0
- package/src/modules/table-align.ts +59 -53
- package/src/modules/table-clipboard.ts +60 -39
- package/src/modules/table-dom-selector.ts +33 -0
- package/src/modules/table-menu/constants.ts +21 -31
- package/src/modules/table-menu/table-menu-common.ts +72 -51
- package/src/modules/table-menu/table-menu-contextmenu.ts +22 -6
- package/src/modules/table-menu/table-menu-select.ts +75 -12
- package/src/modules/table-resize/table-resize-box.ts +148 -128
- package/src/modules/table-resize/table-resize-common.ts +37 -32
- package/src/modules/table-resize/table-resize-line.ts +53 -36
- package/src/modules/table-resize/table-resize-scale.ts +32 -27
- package/src/modules/table-scrollbar.ts +58 -32
- package/src/modules/table-selection.ts +102 -79
- package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/src/style/index.less +0 -1
- package/src/style/select-box.less +1 -0
- package/src/style/table-menu.less +4 -4
- package/src/style/table-resize.less +1 -0
- package/src/style/table-scrollbar.less +2 -2
- package/src/table-up.ts +161 -194
- package/src/utils/components/table/creator.ts +4 -1
- package/src/utils/components/tooltip.ts +6 -1
- package/src/utils/constants.ts +8 -2
- package/src/utils/index.ts +2 -1
- package/src/utils/scroll.ts +47 -0
- package/src/utils/style-helper.ts +47 -0
- package/src/utils/transformer.ts +0 -25
- package/src/utils/types.ts +15 -15
- 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 {
|
|
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 = [
|
|
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.
|
|
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}
|
|
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
|
-
|
|
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 &&
|
|
479
|
-
const 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
|
-
|
|
575
|
-
|
|
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 =
|
|
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
|
|
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),
|
|
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
|
-
|
|
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
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
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
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
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
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
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
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
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
|
-
|
|
913
|
+
indexCol += 1;
|
|
972
914
|
}
|
|
973
|
-
indexCol += 1;
|
|
974
|
-
}
|
|
975
915
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
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,
|
|
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
|
-
|
|
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>,
|
|
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
|
|
1306
|
-
if (
|
|
1307
|
-
const [tableBlot, baseTr] = findParentBlots(
|
|
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 =
|
|
1310
|
-
const colIds = tableBlot.getColIds().slice(colIndex, colIndex +
|
|
1311
|
-
const
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
let
|
|
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
|
-
|
|
1317
|
-
|
|
1282
|
+
baseCell.colspan = 1;
|
|
1283
|
+
baseCell.rowspan = 1;
|
|
1318
1284
|
while (curTr && rowspan > 0) {
|
|
1319
1285
|
for (const id of colIds) {
|
|
1320
|
-
// keep
|
|
1321
|
-
if (curTr === baseTr && id ===
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
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
|
-
|
|
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');
|
package/src/utils/constants.ts
CHANGED
|
@@ -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
|
}
|
package/src/utils/index.ts
CHANGED
|
@@ -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
|
|
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';
|