quill-table-up 2.0.1 → 2.0.3
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/dist/index.d.ts +5 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/table-creator.css +1 -1
- package/package.json +9 -14
- package/src/__tests__/e2e/custom-creator.test.ts +44 -0
- package/src/__tests__/e2e/table-align.test.ts +39 -0
- package/src/__tests__/e2e/table-resize.test.ts +152 -0
- package/src/__tests__/e2e/table-scrollbar.test.ts +31 -0
- package/src/__tests__/e2e/table-selection.test.ts +83 -0
- package/src/__tests__/e2e/utils.ts +6 -0
- package/src/__tests__/unit/table-insert-blot.test.ts +464 -0
- package/src/__tests__/unit/table-insert-remove-merge.test.ts +1270 -0
- package/src/__tests__/unit/table-redo-undo.test.ts +909 -0
- package/src/__tests__/unit/utils.test-d.ts +49 -0
- package/src/__tests__/unit/utils.test.ts +715 -0
- package/src/__tests__/unit/utils.ts +216 -0
- package/src/__tests__/unit/vitest.d.ts +12 -0
- package/src/formats/container-format.ts +52 -0
- package/src/formats/index.ts +10 -0
- package/src/formats/overrides/block.ts +93 -0
- package/src/formats/overrides/blockquote.ts +8 -0
- package/src/formats/overrides/code.ts +8 -0
- package/src/formats/overrides/header.ts +8 -0
- package/src/formats/overrides/index.ts +6 -0
- package/src/formats/overrides/list.ts +10 -0
- package/src/formats/overrides/scroll.ts +51 -0
- package/src/formats/table-body-format.ts +92 -0
- package/src/formats/table-cell-format.ts +139 -0
- package/src/formats/table-cell-inner-format.ts +251 -0
- package/src/formats/table-col-format.ts +174 -0
- package/src/formats/table-colgroup-format.ts +133 -0
- package/src/formats/table-main-format.ts +143 -0
- package/src/formats/table-row-format.ts +147 -0
- package/src/formats/table-wrapper-format.ts +55 -0
- package/src/formats/utils.ts +3 -0
- package/src/index.ts +1157 -0
- package/src/modules/index.ts +5 -0
- package/src/modules/table-align.ts +116 -0
- package/src/modules/table-menu/constants.ts +140 -0
- package/src/modules/table-menu/index.ts +3 -0
- package/src/modules/table-menu/table-menu-common.ts +249 -0
- package/src/modules/table-menu/table-menu-contextmenu.ts +94 -0
- package/src/modules/table-menu/table-menu-select.ts +28 -0
- package/src/modules/table-resize/index.ts +5 -0
- package/src/modules/table-resize/table-resize-box.ts +293 -0
- package/src/modules/table-resize/table-resize-common.ts +343 -0
- package/src/modules/table-resize/table-resize-line.ts +163 -0
- package/src/modules/table-resize/table-resize-scale.ts +154 -0
- package/src/modules/table-resize/utils.ts +3 -0
- package/src/modules/table-scrollbar.ts +255 -0
- package/src/modules/table-selection.ts +262 -0
- package/src/style/button.less +45 -0
- package/src/style/color-picker.less +134 -0
- package/src/style/dialog.less +53 -0
- package/src/style/functions.less +9 -0
- package/src/style/index.less +89 -0
- package/src/style/input.less +64 -0
- package/src/style/select-box.less +51 -0
- package/src/style/table-creator.less +68 -0
- package/src/style/table-menu.less +122 -0
- package/src/style/table-resize-scale.less +31 -0
- package/src/style/table-resize.less +183 -0
- package/src/style/table-scrollbar.less +49 -0
- package/src/style/table-selection.less +15 -0
- package/src/style/tooltip.less +19 -0
- package/src/style/variables.less +1 -0
- package/src/svg/background.svg +1 -0
- package/src/svg/border.svg +1 -0
- package/src/svg/color.svg +1 -0
- package/src/svg/insert-bottom.svg +1 -0
- package/src/svg/insert-left.svg +1 -0
- package/src/svg/insert-right.svg +1 -0
- package/src/svg/insert-top.svg +1 -0
- package/src/svg/merge-cell.svg +1 -0
- package/src/svg/remove-column.svg +1 -0
- package/src/svg/remove-row.svg +1 -0
- package/src/svg/remove-table.svg +1 -0
- package/src/svg/split-cell.svg +1 -0
- package/src/types.d.ts +4 -0
- package/src/utils/bem.ts +23 -0
- package/src/utils/color.ts +109 -0
- package/src/utils/components/button.ts +22 -0
- package/src/utils/components/color-picker.ts +236 -0
- package/src/utils/components/dialog.ts +41 -0
- package/src/utils/components/index.ts +6 -0
- package/src/utils/components/input.ts +74 -0
- package/src/utils/components/table/creator.ts +86 -0
- package/src/utils/components/table/index.ts +2 -0
- package/src/utils/components/table/select-box.ts +83 -0
- package/src/utils/components/tooltip.ts +186 -0
- package/src/utils/constants.ts +99 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/is.ts +6 -0
- package/src/utils/position.ts +21 -0
- package/src/utils/types.ts +131 -0
- package/src/utils/utils.ts +139 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { TableValue } from '../utils';
|
|
2
|
+
import type { TableColgroupFormat } from './table-colgroup-format';
|
|
3
|
+
import { blotName } from '../utils';
|
|
4
|
+
import { ContainerFormat } from './container-format';
|
|
5
|
+
import { TableColFormat } from './table-col-format';
|
|
6
|
+
import { TableRowFormat } from './table-row-format';
|
|
7
|
+
|
|
8
|
+
export class TableMainFormat extends ContainerFormat {
|
|
9
|
+
static blotName = blotName.tableMain;
|
|
10
|
+
static tagName = 'table';
|
|
11
|
+
static className = 'ql-table';
|
|
12
|
+
|
|
13
|
+
static create(value: TableValue) {
|
|
14
|
+
const node = super.create() as HTMLElement;
|
|
15
|
+
const { tableId, full, align } = value;
|
|
16
|
+
node.dataset.tableId = tableId;
|
|
17
|
+
if (align === 'right' || align === 'center') {
|
|
18
|
+
node.dataset.align = align;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
node.removeAttribute('date-align');
|
|
22
|
+
}
|
|
23
|
+
full && (node.dataset.full = String(full));
|
|
24
|
+
node.setAttribute('cellpadding', '0');
|
|
25
|
+
node.setAttribute('cellspacing', '0');
|
|
26
|
+
return node;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
constructor(scroll: any, domNode: HTMLElement, _value: any) {
|
|
30
|
+
super(scroll, domNode);
|
|
31
|
+
this.updateAlign();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
colWidthFillTable() {
|
|
35
|
+
if (this.full) return;
|
|
36
|
+
const cols = this.getCols();
|
|
37
|
+
if (!cols) return;
|
|
38
|
+
const colsWidth = cols.reduce((sum, col) => col.width + sum, 0);
|
|
39
|
+
if (colsWidth === 0 || Number.isNaN(colsWidth)) return null;
|
|
40
|
+
this.domNode.style.width = `${colsWidth}px`;
|
|
41
|
+
return colsWidth;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get tableId() {
|
|
45
|
+
return this.domNode.dataset.tableId!;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get full() {
|
|
49
|
+
return Object.hasOwn(this.domNode.dataset, 'full');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get align() {
|
|
53
|
+
return this.domNode.dataset.align || '';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
set align(value: string) {
|
|
57
|
+
if (value === 'right' || value === 'center') {
|
|
58
|
+
this.domNode.dataset.align = value;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
this.domNode.removeAttribute('data-align');
|
|
62
|
+
}
|
|
63
|
+
this.updateAlign();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
cancelFull() {
|
|
67
|
+
if (!this.full) return;
|
|
68
|
+
const cols = this.getCols();
|
|
69
|
+
const tableWidth = this.domNode.getBoundingClientRect().width;
|
|
70
|
+
for (const col of cols) {
|
|
71
|
+
col.domNode.removeAttribute('data-full');
|
|
72
|
+
col.width = col.width / 100 * tableWidth;
|
|
73
|
+
}
|
|
74
|
+
const colgroup = this.children.head as TableColgroupFormat;
|
|
75
|
+
if (colgroup && colgroup.statics.blotName === blotName.tableColgroup) {
|
|
76
|
+
colgroup.full = false;
|
|
77
|
+
}
|
|
78
|
+
this.domNode.removeAttribute('data-full');
|
|
79
|
+
this.colWidthFillTable();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
updateAlign() {
|
|
83
|
+
const value = this.align;
|
|
84
|
+
const style: Record<string, string | null> = {
|
|
85
|
+
marginLeft: null,
|
|
86
|
+
marginRight: null,
|
|
87
|
+
};
|
|
88
|
+
switch (value) {
|
|
89
|
+
case 'center': {
|
|
90
|
+
style.marginLeft = 'auto';
|
|
91
|
+
style.marginRight = 'auto';
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case '':
|
|
95
|
+
case 'left': {
|
|
96
|
+
style.marginRight = 'auto';
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case 'right': {
|
|
100
|
+
style.marginLeft = 'auto';
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
default: {
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
Object.assign(this.domNode.style, style);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
getRows() {
|
|
111
|
+
return this.descendants(TableRowFormat);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
getRowIds() {
|
|
115
|
+
return this.getRows().map(d => d.rowId);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
getCols() {
|
|
119
|
+
return this.descendants(TableColFormat);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
getColIds() {
|
|
123
|
+
return this.getCols().map(d => d.colId);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
checkMerge(): boolean {
|
|
127
|
+
const next = this.next;
|
|
128
|
+
return (
|
|
129
|
+
next !== null
|
|
130
|
+
&& next.statics.blotName === this.statics.blotName
|
|
131
|
+
&& next.domNode.dataset.tableId === this.tableId
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
optimize(context: Record<string, any>) {
|
|
136
|
+
const parent = this.parent;
|
|
137
|
+
if (parent !== null && parent.statics.blotName !== blotName.tableWrapper) {
|
|
138
|
+
this.wrap(blotName.tableWrapper, this.tableId);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
super.optimize(context);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { Parchment as TypeParchment } from 'quill';
|
|
2
|
+
import type { TableCellValue, TableRowValue } from '../utils';
|
|
3
|
+
import type { TableCellFormat } from './table-cell-format';
|
|
4
|
+
import { blotName, findParentBlot } from '../utils';
|
|
5
|
+
import { ContainerFormat } from './container-format';
|
|
6
|
+
import { TableCellInnerFormat } from './table-cell-inner-format';
|
|
7
|
+
|
|
8
|
+
export type SkipRowCount = number[] & { skipRowNum?: number };
|
|
9
|
+
export class TableRowFormat extends ContainerFormat {
|
|
10
|
+
static blotName = blotName.tableRow;
|
|
11
|
+
static tagName = 'tr';
|
|
12
|
+
static className = 'ql-table-row';
|
|
13
|
+
|
|
14
|
+
static create(value: TableRowValue) {
|
|
15
|
+
const node = super.create() as HTMLElement;
|
|
16
|
+
node.dataset.tableId = value.tableId;
|
|
17
|
+
node.dataset.rowId = value.rowId;
|
|
18
|
+
return node;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare children: TypeParchment.LinkedList<TableCellFormat>;
|
|
22
|
+
|
|
23
|
+
get rowId() {
|
|
24
|
+
return this.domNode.dataset.rowId!;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get tableId() {
|
|
28
|
+
return this.domNode.dataset.tableId!;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
setHeight(value: string) {
|
|
32
|
+
this.foreachCellInner((cellInner) => {
|
|
33
|
+
cellInner.setFormatValue('height', value, true);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// insert cell at index
|
|
38
|
+
// return the minus skip column number
|
|
39
|
+
// [2, 3]. means next line should skip 2 columns. next next line skip 3 columns
|
|
40
|
+
insertCell(targetIndex: number, value: TableCellValue) {
|
|
41
|
+
const skip: SkipRowCount = [];
|
|
42
|
+
const next = this.children.iterator();
|
|
43
|
+
let index = 0;
|
|
44
|
+
let cur;
|
|
45
|
+
while ((cur = next())) {
|
|
46
|
+
index += cur.colspan;
|
|
47
|
+
if (index > targetIndex) break;
|
|
48
|
+
if (cur.rowspan !== 1) {
|
|
49
|
+
for (let i = 0; i < cur.rowspan - 1; i++) {
|
|
50
|
+
skip[i] = (skip[i] || 0) + cur.colspan;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (cur && index - cur.colspan < targetIndex) {
|
|
56
|
+
const tableCell = cur.getCellInner();
|
|
57
|
+
tableCell.colspan += 1;
|
|
58
|
+
if (cur.rowspan !== 1) {
|
|
59
|
+
skip.skipRowNum = cur.rowspan - 1;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const tableCell = this.scroll.create(blotName.tableCell, value) as ContainerFormat;
|
|
64
|
+
const tableCellInner = this.scroll.create(blotName.tableCellInner, value) as ContainerFormat;
|
|
65
|
+
const block = this.scroll.create('block') as TypeParchment.BlockBlot;
|
|
66
|
+
block.appendChild(this.scroll.create('break'));
|
|
67
|
+
tableCellInner.appendChild(block);
|
|
68
|
+
tableCell.appendChild(tableCellInner);
|
|
69
|
+
this.insertBefore(tableCell, cur);
|
|
70
|
+
}
|
|
71
|
+
return skip;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
getCellByColumIndex(stopIndex: number): [null | TableCellFormat, number, number[]] {
|
|
75
|
+
const skip: number[] = [];
|
|
76
|
+
let cur: null | TableCellFormat = null;
|
|
77
|
+
let cellEndIndex = 0;
|
|
78
|
+
if (stopIndex < 0) return [cur, cellEndIndex, skip];
|
|
79
|
+
const next = this.children.iterator();
|
|
80
|
+
while ((cur = next())) {
|
|
81
|
+
cellEndIndex += cur.colspan;
|
|
82
|
+
if (cur.rowspan !== 1) {
|
|
83
|
+
for (let i = 0; i < cur.rowspan - 1; i++) {
|
|
84
|
+
skip[i] = (skip[i] || 0) + cur.colspan;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (cellEndIndex > stopIndex) break;
|
|
88
|
+
}
|
|
89
|
+
return [cur, cellEndIndex, skip];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
removeCell(targetIndex: number): SkipRowCount {
|
|
93
|
+
if (targetIndex < 0) return [];
|
|
94
|
+
const columnIndexData = this.getCellByColumIndex(targetIndex);
|
|
95
|
+
const [cur, index] = columnIndexData;
|
|
96
|
+
const skip: SkipRowCount = columnIndexData[2];
|
|
97
|
+
if (!cur) return skip;
|
|
98
|
+
if (index - cur.colspan < targetIndex || cur.colspan > 1) {
|
|
99
|
+
const [tableCell] = cur.descendants(TableCellInnerFormat);
|
|
100
|
+
|
|
101
|
+
if (cur.colspan !== 1 && targetIndex === index - cur.colspan) {
|
|
102
|
+
// if delete index is cell start index. update cell colId to next colId
|
|
103
|
+
const tableBlot = findParentBlot(this, blotName.tableMain);
|
|
104
|
+
const colIds = tableBlot.getColIds();
|
|
105
|
+
tableCell.colId = colIds[colIds.indexOf(tableCell.colId) + 1];
|
|
106
|
+
}
|
|
107
|
+
if (cur.rowspan !== 1) {
|
|
108
|
+
skip.skipRowNum = cur.rowspan - 1;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
tableCell.colspan -= 1;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
cur.remove();
|
|
115
|
+
}
|
|
116
|
+
return skip;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
foreachCellInner(func: (tableCell: TableCellInnerFormat, index: number) => boolean | void) {
|
|
120
|
+
const next = this.children.iterator();
|
|
121
|
+
let i = 0;
|
|
122
|
+
let cur: TableCellFormat | null;
|
|
123
|
+
while ((cur = next())) {
|
|
124
|
+
const [tableCell] = cur.descendants(TableCellInnerFormat);
|
|
125
|
+
if (func(tableCell, i++)) break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
checkMerge(): boolean {
|
|
130
|
+
const next = this.next as TableRowFormat;
|
|
131
|
+
return (
|
|
132
|
+
next !== null
|
|
133
|
+
&& next.statics.blotName === this.statics.blotName
|
|
134
|
+
&& next.rowId === this.rowId
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
optimize(context: Record<string, any>) {
|
|
139
|
+
const parent = this.parent;
|
|
140
|
+
const { tableId } = this;
|
|
141
|
+
if (parent !== null && parent.statics.blotName !== blotName.tableBody) {
|
|
142
|
+
this.wrap(blotName.tableBody, tableId);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
super.optimize(context);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { blotName } from '../utils';
|
|
2
|
+
import { ContainerFormat } from './container-format';
|
|
3
|
+
import { TableBodyFormat } from './table-body-format';
|
|
4
|
+
import { TableColgroupFormat } from './table-colgroup-format';
|
|
5
|
+
|
|
6
|
+
export class TableWrapperFormat extends ContainerFormat {
|
|
7
|
+
static blotName = blotName.tableWrapper;
|
|
8
|
+
static tagName = 'div';
|
|
9
|
+
static className = 'ql-table-wrapper';
|
|
10
|
+
|
|
11
|
+
static create(value: string) {
|
|
12
|
+
const node = super.create() as HTMLElement;
|
|
13
|
+
|
|
14
|
+
node.dataset.tableId = value;
|
|
15
|
+
node.addEventListener(
|
|
16
|
+
'dragstart',
|
|
17
|
+
(e) => {
|
|
18
|
+
e.preventDefault();
|
|
19
|
+
e.stopPropagation();
|
|
20
|
+
},
|
|
21
|
+
true,
|
|
22
|
+
);
|
|
23
|
+
// not allow drop content into table
|
|
24
|
+
node.addEventListener('drop', (e) => {
|
|
25
|
+
e.preventDefault();
|
|
26
|
+
});
|
|
27
|
+
node.addEventListener('dragover', (e) => {
|
|
28
|
+
e.preventDefault();
|
|
29
|
+
e.dataTransfer!.dropEffect = 'none';
|
|
30
|
+
});
|
|
31
|
+
return node;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get tableId() {
|
|
35
|
+
return this.domNode.dataset.tableId!;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
checkMerge(): boolean {
|
|
39
|
+
const next = this.next as TableWrapperFormat;
|
|
40
|
+
return (
|
|
41
|
+
next !== null
|
|
42
|
+
&& next.statics.blotName === this.statics.blotName
|
|
43
|
+
&& next.tableId === this.tableId
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
deleteAt(index: number, length: number) {
|
|
48
|
+
super.deleteAt(index, length);
|
|
49
|
+
const tableBodys = (this.descendants(TableBodyFormat));
|
|
50
|
+
const tableColgroups = (this.descendants(TableColgroupFormat));
|
|
51
|
+
if (tableBodys.length === 0 || tableColgroups.length === 0) {
|
|
52
|
+
this.remove();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|