quill-table-up 3.2.1 → 3.3.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/LICENSE +21 -21
- package/README.md +16 -3
- package/dist/index.d.ts +45 -37
- package/dist/index.js +11 -11
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +9 -9
- package/dist/index.umd.js.map +1 -1
- package/package.json +19 -18
- package/src/__tests__/e2e/table-blots.test.ts +5 -2
- package/src/__tests__/e2e/table-keyboard-handler.test.ts +32 -24
- package/src/__tests__/e2e/table-menu.test.ts +38 -0
- package/src/__tests__/e2e/table-resize.test.ts +111 -60
- package/src/__tests__/e2e/table-scrollbar.test.ts +2 -2
- package/src/__tests__/e2e/table-selection.test.ts +48 -0
- package/src/__tests__/unit/table-contenteditable.test.ts +222 -0
- package/src/__tests__/unit/utils.ts +1 -2
- package/src/formats/container-format.ts +2 -2
- package/src/formats/table-body-format.ts +1 -1
- package/src/formats/table-caption-format.ts +1 -1
- package/src/formats/table-cell-format.ts +2 -2
- package/src/formats/table-cell-inner-format.ts +1 -1
- package/src/formats/table-row-format.ts +1 -1
- package/src/modules/table-align.ts +1 -0
- package/src/modules/table-clipboard/paste-cell-into-cell.ts +2 -2
- package/src/modules/table-dom-selector.ts +18 -3
- package/src/modules/table-menu/table-menu-common.ts +1 -0
- package/src/modules/table-menu/table-menu-contextmenu.ts +2 -3
- package/src/modules/table-menu/table-menu-select.ts +4 -2
- package/src/modules/table-resize/table-resize-box.ts +1 -0
- package/src/modules/table-resize/table-resize-line.ts +14 -1
- package/src/modules/table-resize/table-resize-scale.ts +34 -53
- package/src/modules/table-scrollbar.ts +26 -31
- package/src/modules/table-selection.ts +5 -3
- package/src/style/table-selection.less +0 -5
- package/src/table-up.ts +44 -2
- package/src/utils/types.ts +1 -0
- package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { TableDomSelector, TableSelection } from '../../modules';
|
|
3
|
+
import { TableUp } from '../../table-up';
|
|
4
|
+
import { createQuillWithTableModule } from './utils';
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
vi.useFakeTimers();
|
|
8
|
+
});
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
vi.useRealTimers();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe('TableUp - contenteditable change', () => {
|
|
14
|
+
describe('when contenteditable becomes false', () => {
|
|
15
|
+
it('should destroy all table modules', async () => {
|
|
16
|
+
const quill = createQuillWithTableModule(`<p><br></p>`, {
|
|
17
|
+
modules: [{ module: TableSelection }],
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const tableUp = quill.getModule(TableUp.moduleName) as TableUp;
|
|
21
|
+
const module = tableUp.getModule<TableSelection>(TableSelection.moduleName);
|
|
22
|
+
|
|
23
|
+
expect(module).toBeDefined();
|
|
24
|
+
|
|
25
|
+
// Disable editor
|
|
26
|
+
quill.enable(false);
|
|
27
|
+
|
|
28
|
+
// Wait for MutationObserver callback
|
|
29
|
+
await vi.runAllTimersAsync();
|
|
30
|
+
|
|
31
|
+
// Verify modules are cleared
|
|
32
|
+
expect(tableUp.modules).not.toBeNullable();
|
|
33
|
+
expect(Object.keys(tableUp.modules).length).toEqual(0);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should handle modules without destroy method gracefully', async () => {
|
|
37
|
+
// Create a module without destroy method
|
|
38
|
+
class MockModuleWithoutDestroy extends TableDomSelector {
|
|
39
|
+
static moduleName = 'mock-module-without-destroy';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const quill = createQuillWithTableModule(`<p><br></p>`, {
|
|
43
|
+
modules: [{ module: MockModuleWithoutDestroy }],
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const tableUp = quill.getModule(TableUp.moduleName) as TableUp;
|
|
47
|
+
|
|
48
|
+
// Should not throw error
|
|
49
|
+
await expect(() => quill.enable(false)).not.toThrow();
|
|
50
|
+
|
|
51
|
+
// Modules should be cleared
|
|
52
|
+
expect(tableUp.modules).not.toBeNullable();
|
|
53
|
+
expect(Object.keys(tableUp.modules).length).toEqual(0);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('when contenteditable becomes true', () => {
|
|
58
|
+
it('should create new module instances', async () => {
|
|
59
|
+
const quill = createQuillWithTableModule(`<p><br></p>`, {
|
|
60
|
+
modules: [{ module: TableSelection }],
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const tableUp = quill.getModule(TableUp.moduleName) as TableUp;
|
|
64
|
+
const oldModule = tableUp.getModule<TableSelection>(TableSelection.moduleName);
|
|
65
|
+
|
|
66
|
+
expect(oldModule).toBeDefined();
|
|
67
|
+
|
|
68
|
+
// Disable editor
|
|
69
|
+
quill.enable(false);
|
|
70
|
+
await vi.runAllTimersAsync();
|
|
71
|
+
|
|
72
|
+
// Re-enable editor
|
|
73
|
+
quill.enable(true);
|
|
74
|
+
await vi.runAllTimersAsync();
|
|
75
|
+
|
|
76
|
+
// Get new module instance
|
|
77
|
+
const newModule = tableUp.getModule<TableSelection>(TableSelection.moduleName);
|
|
78
|
+
|
|
79
|
+
// Verify new instance is created
|
|
80
|
+
expect(newModule).toBeDefined();
|
|
81
|
+
expect(newModule).not.toBe(oldModule);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should work with multiple enable/disable cycles', async () => {
|
|
85
|
+
const quill = createQuillWithTableModule(`<p><br></p>`, {
|
|
86
|
+
modules: [{ module: TableSelection }],
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const tableUp = quill.getModule(TableUp.moduleName) as TableUp;
|
|
90
|
+
|
|
91
|
+
// Multiple toggle cycles
|
|
92
|
+
quill.enable(false);
|
|
93
|
+
await vi.runAllTimersAsync();
|
|
94
|
+
|
|
95
|
+
quill.enable(true);
|
|
96
|
+
await vi.runAllTimersAsync();
|
|
97
|
+
|
|
98
|
+
quill.enable(false);
|
|
99
|
+
await vi.runAllTimersAsync();
|
|
100
|
+
|
|
101
|
+
quill.enable(true);
|
|
102
|
+
await vi.runAllTimersAsync();
|
|
103
|
+
|
|
104
|
+
// Verify modules still work
|
|
105
|
+
const module = tableUp.getModule<TableSelection>(TableSelection.moduleName);
|
|
106
|
+
expect(module).toBeDefined();
|
|
107
|
+
expect(module).toBeInstanceOf(TableSelection);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('error handling', () => {
|
|
112
|
+
it('should continue working if module destroy throws error', async () => {
|
|
113
|
+
// Create a module that throws error on destroy
|
|
114
|
+
class BadModule extends TableDomSelector {
|
|
115
|
+
static moduleName = 'bad-module';
|
|
116
|
+
|
|
117
|
+
destroy() {
|
|
118
|
+
throw new Error('Destroy failed');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
123
|
+
|
|
124
|
+
const quill = createQuillWithTableModule(`<p><br></p>`, {
|
|
125
|
+
modules: [{ module: BadModule }],
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const tableUp = quill.getModule(TableUp.moduleName) as TableUp;
|
|
129
|
+
|
|
130
|
+
// Should not throw error
|
|
131
|
+
await expect(() => quill.enable(false)).not.toThrow();
|
|
132
|
+
|
|
133
|
+
// Should output warning
|
|
134
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
|
135
|
+
expect.stringContaining('Failed to destroy module'),
|
|
136
|
+
expect.any(Error),
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
consoleWarnSpy.mockRestore();
|
|
140
|
+
|
|
141
|
+
// Modules should be cleared
|
|
142
|
+
expect(tableUp.modules).toEqual({});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should handle partial destroy failures', async () => {
|
|
146
|
+
class BadModule extends TableDomSelector {
|
|
147
|
+
static moduleName = 'bad-module';
|
|
148
|
+
|
|
149
|
+
destroy() {
|
|
150
|
+
throw new Error('Bad module destroy failed');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
class GoodModule extends TableDomSelector {
|
|
155
|
+
static moduleName = 'good-module';
|
|
156
|
+
|
|
157
|
+
destroy() {
|
|
158
|
+
// Normal destroy
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
163
|
+
|
|
164
|
+
const quill = createQuillWithTableModule(`<p><br></p>`, {
|
|
165
|
+
modules: [
|
|
166
|
+
{ module: BadModule },
|
|
167
|
+
{ module: GoodModule },
|
|
168
|
+
],
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const tableUp = quill.getModule(TableUp.moduleName) as TableUp;
|
|
172
|
+
|
|
173
|
+
quill.enable(false);
|
|
174
|
+
await vi.runAllTimersAsync();
|
|
175
|
+
|
|
176
|
+
// Should have warning
|
|
177
|
+
expect(consoleWarnSpy).toHaveBeenCalled();
|
|
178
|
+
|
|
179
|
+
consoleWarnSpy.mockRestore();
|
|
180
|
+
|
|
181
|
+
// All modules should be cleared
|
|
182
|
+
expect(tableUp.modules).toEqual({});
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe('MutationObserver behavior', () => {
|
|
187
|
+
it('should only respond to contenteditable attribute changes', async () => {
|
|
188
|
+
const quill = createQuillWithTableModule(`<p><br></p>`, {
|
|
189
|
+
modules: [{ module: TableSelection }],
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const tableUp = quill.getModule(TableUp.moduleName) as TableUp;
|
|
193
|
+
const initialModuleCount = Object.keys(tableUp.modules).length;
|
|
194
|
+
|
|
195
|
+
// Change other attributes, should not trigger destroy
|
|
196
|
+
quill.root.setAttribute('data-test', 'value');
|
|
197
|
+
await vi.runAllTimersAsync();
|
|
198
|
+
|
|
199
|
+
expect(Object.keys(tableUp.modules).length).toBe(initialModuleCount);
|
|
200
|
+
|
|
201
|
+
// Change contenteditable should trigger
|
|
202
|
+
quill.enable(false);
|
|
203
|
+
await vi.runAllTimersAsync();
|
|
204
|
+
|
|
205
|
+
expect(tableUp.modules).toEqual({});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should handle direct contenteditable attribute changes', async () => {
|
|
209
|
+
const quill = createQuillWithTableModule(`<p><br></p>`, {
|
|
210
|
+
modules: [{ module: TableSelection }],
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const tableUp = quill.getModule(TableUp.moduleName) as TableUp;
|
|
214
|
+
|
|
215
|
+
// Directly set contenteditable attribute (instead of using enable method)
|
|
216
|
+
quill.root.setAttribute('contenteditable', 'false');
|
|
217
|
+
await vi.runAllTimersAsync();
|
|
218
|
+
|
|
219
|
+
expect(Object.keys(tableUp.modules).length).toEqual(0);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
});
|
|
@@ -187,8 +187,7 @@ export function createTableDeltaOps(row: number, col: number, colOptions?: ColOp
|
|
|
187
187
|
);
|
|
188
188
|
}
|
|
189
189
|
for (const [i, _] of new Array(col).fill(0).entries()) {
|
|
190
|
-
const value: TableColDeltaValue = { tableId, colId: `${i + 1}`, width: 1 / col * 100 };
|
|
191
|
-
value.full = full;
|
|
190
|
+
const value: TableColDeltaValue = { tableId, colId: `${i + 1}`, width: 1 / col * 100, full };
|
|
192
191
|
if (!full) {
|
|
193
192
|
value.width = width;
|
|
194
193
|
}
|
|
@@ -15,8 +15,8 @@ export class ContainerFormat extends Container {
|
|
|
15
15
|
static requiredContainer: TypeParchment.BlotConstructor;
|
|
16
16
|
static defaultChild?: TypeParchment.BlotConstructor;
|
|
17
17
|
|
|
18
|
-
static allowAttrs = new Set<string>(
|
|
19
|
-
static allowDataAttrs = new Set<string>(
|
|
18
|
+
static allowAttrs = new Set<string>();
|
|
19
|
+
static allowDataAttrs = new Set<string>();
|
|
20
20
|
// handle the attribute change when use `setFormatValue`
|
|
21
21
|
static allowDataAttrsChangeHandler: Record<string, string> = {};
|
|
22
22
|
|
|
@@ -31,7 +31,7 @@ export class TableCaptionFormat extends BlockOverride {
|
|
|
31
31
|
static formats(domNode: HTMLElement) {
|
|
32
32
|
const { tableId } = domNode.dataset;
|
|
33
33
|
const value: TableCaptionValue = {
|
|
34
|
-
tableId:
|
|
34
|
+
tableId: tableId!,
|
|
35
35
|
side: domNode.style.captionSide === 'bottom' ? 'bottom' : 'top',
|
|
36
36
|
};
|
|
37
37
|
return value;
|
|
@@ -87,7 +87,7 @@ export class TableCellFormat extends ContainerFormat {
|
|
|
87
87
|
|
|
88
88
|
isChildHeadTableCellInner() {
|
|
89
89
|
const headChild = this.children.head;
|
|
90
|
-
return headChild
|
|
90
|
+
return headChild?.statics.blotName === blotName.tableCellInner;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
setFormatValue(name: string, value?: any) {
|
|
@@ -122,7 +122,7 @@ export class TableCellFormat extends ContainerFormat {
|
|
|
122
122
|
(headChild.domNode as HTMLElement).dataset.style = this.domNode.style.cssText;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
if (this.parent
|
|
125
|
+
if (this.parent?.statics.blotName === blotName.tableRow) {
|
|
126
126
|
(this.parent as TableRowFormat).setFormatValue(name, value);
|
|
127
127
|
}
|
|
128
128
|
}
|
|
@@ -105,7 +105,7 @@ export class TableCellInnerFormat extends ContainerFormat {
|
|
|
105
105
|
else {
|
|
106
106
|
super.setFormatValue(name, value);
|
|
107
107
|
}
|
|
108
|
-
if (this.parent
|
|
108
|
+
if (this.parent?.statics.blotName === blotName.tableCell) {
|
|
109
109
|
this.parent.setFormatValue(name, value);
|
|
110
110
|
}
|
|
111
111
|
|
|
@@ -64,7 +64,7 @@ export class TableRowFormat extends ContainerFormat {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
// Not found in current row. Means cell is rowspan. Find in prev or next row.
|
|
67
|
-
if (this[direction]
|
|
67
|
+
if (this[direction]?.statics.blotName === blotName.tableRow) {
|
|
68
68
|
return (this[direction] as TableRowFormat).getCellByColId(colId, direction);
|
|
69
69
|
}
|
|
70
70
|
return null;
|
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
import type Quill from 'quill';
|
|
2
2
|
import type { TableUp } from '../table-up';
|
|
3
3
|
|
|
4
|
-
export
|
|
4
|
+
export interface TableModuleLifecycle {
|
|
5
|
+
hide: () => void;
|
|
6
|
+
show: () => void;
|
|
7
|
+
update: () => void;
|
|
8
|
+
destroy: () => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class TableDomSelector implements TableModuleLifecycle {
|
|
5
12
|
table?: HTMLTableElement;
|
|
13
|
+
tableSelectMouseDownHandler: (event: MouseEvent) => void;
|
|
6
14
|
|
|
7
15
|
constructor(public tableModule: TableUp, public quill: Quill) {
|
|
8
|
-
this.
|
|
16
|
+
this.tableSelectMouseDownHandler = this.tableSelectHandler.bind(this);
|
|
17
|
+
this.quill.root.addEventListener('mousedown', this.tableSelectMouseDownHandler);
|
|
9
18
|
}
|
|
10
19
|
|
|
11
20
|
tableSelectHandler(event: MouseEvent) {
|
|
12
21
|
const path = event.composedPath() as HTMLElement[];
|
|
13
22
|
if (event.button !== 0 || !path || path.length <= 0) return;
|
|
14
|
-
const tableNode = path.find(node => node.tagName
|
|
23
|
+
const tableNode = path.find(node => node.tagName?.toUpperCase() === 'TABLE');
|
|
15
24
|
this.setSelectionTable(tableNode as HTMLTableElement);
|
|
16
25
|
}
|
|
17
26
|
|
|
@@ -30,4 +39,10 @@ export class TableDomSelector {
|
|
|
30
39
|
show() {}
|
|
31
40
|
|
|
32
41
|
update() {}
|
|
42
|
+
|
|
43
|
+
destroy() {
|
|
44
|
+
this.quill.root.removeEventListener('mousedown', this.tableSelectMouseDownHandler);
|
|
45
|
+
this.hide();
|
|
46
|
+
this.table = undefined;
|
|
47
|
+
}
|
|
33
48
|
}
|
|
@@ -318,6 +318,7 @@ export class TableMenuCommon extends TableDomSelector {
|
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
destroy() {
|
|
321
|
+
super.destroy();
|
|
321
322
|
this.quill.off(Quill.events.TEXT_CHANGE, this.updateWhenTextChange);
|
|
322
323
|
this.quill.off(tableUpEvent.TABLE_SELECTION_DRAG_START, this.hideWhenSelectionDragStart);
|
|
323
324
|
this.activeTooltip = null;
|
|
@@ -27,15 +27,14 @@ export class TableMenuContextmenu extends TableMenuCommon {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
listenContextmenu = (e: MouseEvent) => {
|
|
30
|
-
e.preventDefault();
|
|
31
|
-
|
|
32
30
|
const path = e.composedPath() as HTMLElement[];
|
|
33
31
|
if (!path || path.length <= 0) return;
|
|
34
32
|
|
|
35
|
-
const tableNode = path.find(node => node.tagName
|
|
33
|
+
const tableNode = path.find(node => node.tagName?.toUpperCase() === 'TABLE' && node.classList.contains('ql-table'));
|
|
36
34
|
|
|
37
35
|
const tableSelection = this.tableModule.getModule<TableSelection>(tableUpInternal.tableSelectionName);
|
|
38
36
|
if (tableNode && tableSelection?.selectedTds?.length) {
|
|
37
|
+
e.preventDefault();
|
|
39
38
|
if (!this.menu) {
|
|
40
39
|
this.menu = this.buildTools();
|
|
41
40
|
}
|
|
@@ -44,7 +44,7 @@ export class TableMenuSelect extends TableMenuCommon {
|
|
|
44
44
|
|
|
45
45
|
buildTools(): HTMLElement {
|
|
46
46
|
const menu = super.buildTools();
|
|
47
|
-
this.
|
|
47
|
+
this.quill.container.appendChild(menu);
|
|
48
48
|
return menu;
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -87,10 +87,12 @@ export class TableMenuSelect extends TableMenuCommon {
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
destroy()
|
|
90
|
+
destroy() {
|
|
91
91
|
super.destroy();
|
|
92
92
|
|
|
93
93
|
this.quill.off(tableUpEvent.TABLE_SELECTION_DRAG_START, this.tableSelectionDragStart);
|
|
94
94
|
this.quill.off(tableUpEvent.TABLE_SELECTION_DRAG_END, this.tableSelectionDragEnd);
|
|
95
|
+
this.quill.off(tableUpEvent.TABLE_SELECTION_CHANGE, this.tableSelectioChange);
|
|
96
|
+
this.quill.off(tableUpEvent.TABLE_SELECTION_DISPLAY_CHANGE, this.tableSelectionDisplayChange);
|
|
95
97
|
}
|
|
96
98
|
}
|
|
@@ -137,6 +137,12 @@ export class TableResizeLine extends TableResizeCommon {
|
|
|
137
137
|
},
|
|
138
138
|
onEnd: ({ position }) => {
|
|
139
139
|
this.dragging = false;
|
|
140
|
+
// update the resizer position to the final position
|
|
141
|
+
if (this.colResizer) {
|
|
142
|
+
const resultX = this.dragXCommon.limitRange(this.tableBlot, position.x, true);
|
|
143
|
+
const rootRect = this.quill.root.getBoundingClientRect();
|
|
144
|
+
this.colResizer.style.left = `${resultX - rootRect.x}px`;
|
|
145
|
+
}
|
|
140
146
|
|
|
141
147
|
this.updateTableCol(position.x);
|
|
142
148
|
this.removeBreak();
|
|
@@ -198,6 +204,12 @@ export class TableResizeLine extends TableResizeCommon {
|
|
|
198
204
|
},
|
|
199
205
|
onEnd: ({ position }) => {
|
|
200
206
|
this.dragging = false;
|
|
207
|
+
// update the resizer position to the final position
|
|
208
|
+
if (this.rowResizer) {
|
|
209
|
+
const resultY = this.dragYCommon.limitRange(this.tableBlot, position.y, true);
|
|
210
|
+
const rootRect = this.quill.root.getBoundingClientRect();
|
|
211
|
+
this.rowResizer.style.left = `${resultY - rootRect.y}px`;
|
|
212
|
+
}
|
|
201
213
|
|
|
202
214
|
this.updateTableRow(position.y);
|
|
203
215
|
this.removeBreak();
|
|
@@ -230,7 +242,8 @@ export class TableResizeLine extends TableResizeCommon {
|
|
|
230
242
|
this.table.removeEventListener('pointermove', this.pointermoveHandler);
|
|
231
243
|
}
|
|
232
244
|
|
|
233
|
-
destroy()
|
|
245
|
+
destroy() {
|
|
246
|
+
super.destroy();
|
|
234
247
|
if (this.colResizer) {
|
|
235
248
|
this.colResizer.remove();
|
|
236
249
|
this.colResizer = undefined;
|
|
@@ -3,7 +3,7 @@ import type { TableUp } from '../../table-up';
|
|
|
3
3
|
import type { TableResizeScaleOptions } from '../../utils';
|
|
4
4
|
import Quill from 'quill';
|
|
5
5
|
import { getTableMainRect } from '../../formats';
|
|
6
|
-
import { addScrollEvent, clearScrollEvent, createBEM, tableUpSize } from '../../utils';
|
|
6
|
+
import { addScrollEvent, clearScrollEvent, createBEM, dragElement, tableUpSize } from '../../utils';
|
|
7
7
|
import { TableDomSelector } from '../table-dom-selector';
|
|
8
8
|
import { isTableAlignRight } from './utils';
|
|
9
9
|
|
|
@@ -14,9 +14,6 @@ export class TableResizeScale extends TableDomSelector {
|
|
|
14
14
|
tableMainBlot?: TableMainFormat;
|
|
15
15
|
tableWrapperBlot?: TableWrapperFormat;
|
|
16
16
|
bem = createBEM('scale');
|
|
17
|
-
startX: number = 0;
|
|
18
|
-
startY: number = 0;
|
|
19
|
-
offset: number = 6;
|
|
20
17
|
options: TableResizeScaleOptions;
|
|
21
18
|
root?: HTMLElement;
|
|
22
19
|
block?: HTMLElement;
|
|
@@ -42,6 +39,7 @@ export class TableResizeScale extends TableDomSelector {
|
|
|
42
39
|
resolveOptions(options: Partial<TableResizeScaleOptions>) {
|
|
43
40
|
return Object.assign({
|
|
44
41
|
blockSize: 12,
|
|
42
|
+
offset: 6,
|
|
45
43
|
}, options);
|
|
46
44
|
}
|
|
47
45
|
|
|
@@ -58,57 +56,38 @@ export class TableResizeScale extends TableDomSelector {
|
|
|
58
56
|
|
|
59
57
|
let originColWidth: { blot: TableColFormat; width: number }[] = [];
|
|
60
58
|
let originRowHeight: { blot: TableRowFormat; height: number }[] = [];
|
|
61
|
-
const handleMouseMove = (e: MouseEvent) => {
|
|
62
|
-
if (!this.tableMainBlot) return;
|
|
63
|
-
// divide equally by col count/row count
|
|
64
|
-
const isRight = isTableAlignRight(this.tableMainBlot) ? -1 : 1;
|
|
65
|
-
const diffX = (e.clientX - this.startX) * isRight;
|
|
66
|
-
const diffY = e.clientY - this.startY;
|
|
67
|
-
const itemWidth = Math.floor(diffX / originColWidth.length);
|
|
68
|
-
const itemHeight = Math.floor(diffY / originRowHeight.length);
|
|
69
59
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
60
|
+
dragElement(this.block, {
|
|
61
|
+
onStart: () => {
|
|
62
|
+
if (!this.tableMainBlot) return;
|
|
63
|
+
// save the origin width and height to calculate result width and height
|
|
64
|
+
originColWidth = this.tableMainBlot.getCols().map(col => ({ blot: col, width: Math.floor(col.width) }));
|
|
65
|
+
originRowHeight = this.tableMainBlot.getRows().map(row => ({ blot: row, height: Math.floor(row.domNode.getBoundingClientRect().height) }));
|
|
66
|
+
},
|
|
67
|
+
onMove: ({ movePosition }) => {
|
|
68
|
+
if (!this.tableMainBlot) return;
|
|
69
|
+
// divide equally by col count/row count
|
|
70
|
+
const isRight = isTableAlignRight(this.tableMainBlot) ? -1 : 1;
|
|
71
|
+
const diffX = movePosition.x * isRight;
|
|
72
|
+
const diffY = movePosition.y;
|
|
73
|
+
const itemWidth = Math.floor(diffX / originColWidth.length);
|
|
74
|
+
const itemHeight = Math.floor(diffY / originRowHeight.length);
|
|
75
|
+
|
|
76
|
+
for (const { blot, width } of originColWidth) {
|
|
77
|
+
blot.width = Math.max(width + itemWidth, tableUpSize.colMinWidthPx);
|
|
78
|
+
}
|
|
79
|
+
for (const { blot, height } of originRowHeight) {
|
|
80
|
+
blot.setHeight(`${Math.max(height + itemHeight, tableUpSize.rowMinHeightPx)}px`);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
onEnd: () => {
|
|
84
|
+
originColWidth = [];
|
|
85
|
+
originRowHeight = [];
|
|
86
|
+
},
|
|
92
87
|
});
|
|
93
88
|
this.block.addEventListener('dragstart', e => e.preventDefault());
|
|
94
89
|
}
|
|
95
90
|
|
|
96
|
-
isTableOutofEditor(): boolean {
|
|
97
|
-
if (!this.tableMainBlot || !this.tableWrapperBlot || this.tableMainBlot.full) return false;
|
|
98
|
-
// if tableMain width larger than tableWrapper. reset tableMain width equal editor width
|
|
99
|
-
const tableRect = this.tableMainBlot.domNode.getBoundingClientRect();
|
|
100
|
-
const tableWrapperRect = this.tableWrapperBlot.domNode.getBoundingClientRect();
|
|
101
|
-
// equal scale
|
|
102
|
-
if (tableRect.width > tableWrapperRect.width) {
|
|
103
|
-
for (const col of this.tableMainBlot.getCols()) {
|
|
104
|
-
col.width = Math.floor((col.width / tableRect.width) * tableWrapperRect.width);
|
|
105
|
-
}
|
|
106
|
-
this.tableMainBlot.colWidthFillTable();
|
|
107
|
-
return true;
|
|
108
|
-
}
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
91
|
update() {
|
|
113
92
|
if (!this.block || !this.root || !this.tableMainBlot || !this.tableWrapperBlot) return;
|
|
114
93
|
if (this.tableMainBlot.full) {
|
|
@@ -120,7 +99,7 @@ export class TableResizeScale extends TableDomSelector {
|
|
|
120
99
|
const tableWrapperRect = this.tableWrapperBlot.domNode.getBoundingClientRect();
|
|
121
100
|
const editorRect = this.quill.root.getBoundingClientRect();
|
|
122
101
|
const { scrollTop, scrollLeft } = this.tableWrapperBlot.domNode;
|
|
123
|
-
const blockSize = this.options.blockSize * 2 + this.offset;
|
|
102
|
+
const blockSize = this.options.blockSize * 2 + this.options.offset;
|
|
124
103
|
const rootWidth = Math.min(tableRect.width, tableWrapperRect.width) + blockSize;
|
|
125
104
|
const rootHeight = Math.min(tableRect.height, tableWrapperRect.height) + blockSize;
|
|
126
105
|
Object.assign(this.root.style, {
|
|
@@ -151,6 +130,7 @@ export class TableResizeScale extends TableDomSelector {
|
|
|
151
130
|
|
|
152
131
|
this.resizeobserver.observe(this.tableMainBlot.domNode);
|
|
153
132
|
addScrollEvent.call(this, this.quill.root, () => this.update());
|
|
133
|
+
addScrollEvent.call(this, this.tableWrapperBlot.domNode, () => this.update());
|
|
154
134
|
}
|
|
155
135
|
this.buildResizer();
|
|
156
136
|
}
|
|
@@ -163,12 +143,13 @@ export class TableResizeScale extends TableDomSelector {
|
|
|
163
143
|
this.root = undefined;
|
|
164
144
|
this.block = undefined;
|
|
165
145
|
}
|
|
146
|
+
clearScrollEvent.call(this);
|
|
166
147
|
}
|
|
167
148
|
|
|
168
149
|
destroy() {
|
|
150
|
+
super.destroy();
|
|
169
151
|
this.hide();
|
|
170
|
-
this.quill.off(Quill.events.
|
|
152
|
+
this.quill.off(Quill.events.EDITOR_CHANGE, this.updateWhenTextChange);
|
|
171
153
|
this.resizeobserver.disconnect();
|
|
172
|
-
clearScrollEvent.call(this);
|
|
173
154
|
}
|
|
174
155
|
}
|