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.
Files changed (99) hide show
  1. package/dist/index.d.ts +5 -0
  2. package/dist/index.js +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.umd.js +1 -1
  5. package/dist/index.umd.js.map +1 -1
  6. package/dist/table-creator.css +1 -1
  7. package/package.json +9 -14
  8. package/src/__tests__/e2e/custom-creator.test.ts +44 -0
  9. package/src/__tests__/e2e/table-align.test.ts +39 -0
  10. package/src/__tests__/e2e/table-resize.test.ts +152 -0
  11. package/src/__tests__/e2e/table-scrollbar.test.ts +31 -0
  12. package/src/__tests__/e2e/table-selection.test.ts +83 -0
  13. package/src/__tests__/e2e/utils.ts +6 -0
  14. package/src/__tests__/unit/table-insert-blot.test.ts +464 -0
  15. package/src/__tests__/unit/table-insert-remove-merge.test.ts +1270 -0
  16. package/src/__tests__/unit/table-redo-undo.test.ts +909 -0
  17. package/src/__tests__/unit/utils.test-d.ts +49 -0
  18. package/src/__tests__/unit/utils.test.ts +715 -0
  19. package/src/__tests__/unit/utils.ts +216 -0
  20. package/src/__tests__/unit/vitest.d.ts +12 -0
  21. package/src/formats/container-format.ts +52 -0
  22. package/src/formats/index.ts +10 -0
  23. package/src/formats/overrides/block.ts +93 -0
  24. package/src/formats/overrides/blockquote.ts +8 -0
  25. package/src/formats/overrides/code.ts +8 -0
  26. package/src/formats/overrides/header.ts +8 -0
  27. package/src/formats/overrides/index.ts +6 -0
  28. package/src/formats/overrides/list.ts +10 -0
  29. package/src/formats/overrides/scroll.ts +51 -0
  30. package/src/formats/table-body-format.ts +92 -0
  31. package/src/formats/table-cell-format.ts +139 -0
  32. package/src/formats/table-cell-inner-format.ts +251 -0
  33. package/src/formats/table-col-format.ts +174 -0
  34. package/src/formats/table-colgroup-format.ts +133 -0
  35. package/src/formats/table-main-format.ts +143 -0
  36. package/src/formats/table-row-format.ts +147 -0
  37. package/src/formats/table-wrapper-format.ts +55 -0
  38. package/src/formats/utils.ts +3 -0
  39. package/src/index.ts +1157 -0
  40. package/src/modules/index.ts +5 -0
  41. package/src/modules/table-align.ts +116 -0
  42. package/src/modules/table-menu/constants.ts +140 -0
  43. package/src/modules/table-menu/index.ts +3 -0
  44. package/src/modules/table-menu/table-menu-common.ts +249 -0
  45. package/src/modules/table-menu/table-menu-contextmenu.ts +94 -0
  46. package/src/modules/table-menu/table-menu-select.ts +28 -0
  47. package/src/modules/table-resize/index.ts +5 -0
  48. package/src/modules/table-resize/table-resize-box.ts +293 -0
  49. package/src/modules/table-resize/table-resize-common.ts +343 -0
  50. package/src/modules/table-resize/table-resize-line.ts +163 -0
  51. package/src/modules/table-resize/table-resize-scale.ts +154 -0
  52. package/src/modules/table-resize/utils.ts +3 -0
  53. package/src/modules/table-scrollbar.ts +255 -0
  54. package/src/modules/table-selection.ts +262 -0
  55. package/src/style/button.less +45 -0
  56. package/src/style/color-picker.less +134 -0
  57. package/src/style/dialog.less +53 -0
  58. package/src/style/functions.less +9 -0
  59. package/src/style/index.less +89 -0
  60. package/src/style/input.less +64 -0
  61. package/src/style/select-box.less +51 -0
  62. package/src/style/table-creator.less +68 -0
  63. package/src/style/table-menu.less +122 -0
  64. package/src/style/table-resize-scale.less +31 -0
  65. package/src/style/table-resize.less +183 -0
  66. package/src/style/table-scrollbar.less +49 -0
  67. package/src/style/table-selection.less +15 -0
  68. package/src/style/tooltip.less +19 -0
  69. package/src/style/variables.less +1 -0
  70. package/src/svg/background.svg +1 -0
  71. package/src/svg/border.svg +1 -0
  72. package/src/svg/color.svg +1 -0
  73. package/src/svg/insert-bottom.svg +1 -0
  74. package/src/svg/insert-left.svg +1 -0
  75. package/src/svg/insert-right.svg +1 -0
  76. package/src/svg/insert-top.svg +1 -0
  77. package/src/svg/merge-cell.svg +1 -0
  78. package/src/svg/remove-column.svg +1 -0
  79. package/src/svg/remove-row.svg +1 -0
  80. package/src/svg/remove-table.svg +1 -0
  81. package/src/svg/split-cell.svg +1 -0
  82. package/src/types.d.ts +4 -0
  83. package/src/utils/bem.ts +23 -0
  84. package/src/utils/color.ts +109 -0
  85. package/src/utils/components/button.ts +22 -0
  86. package/src/utils/components/color-picker.ts +236 -0
  87. package/src/utils/components/dialog.ts +41 -0
  88. package/src/utils/components/index.ts +6 -0
  89. package/src/utils/components/input.ts +74 -0
  90. package/src/utils/components/table/creator.ts +86 -0
  91. package/src/utils/components/table/index.ts +2 -0
  92. package/src/utils/components/table/select-box.ts +83 -0
  93. package/src/utils/components/tooltip.ts +186 -0
  94. package/src/utils/constants.ts +99 -0
  95. package/src/utils/index.ts +7 -0
  96. package/src/utils/is.ts +6 -0
  97. package/src/utils/position.ts +21 -0
  98. package/src/utils/types.ts +131 -0
  99. 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
+ }
@@ -0,0 +1,3 @@
1
+ import { isValidCellspan } from '../utils';
2
+
3
+ export const getValidCellspan = (value: any) => isValidCellspan(value) ? value : 1;