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,5 @@
1
+ export * from './table-align';
2
+ export * from './table-menu';
3
+ export * from './table-resize';
4
+ export * from './table-scrollbar';
5
+ export * from './table-selection';
@@ -0,0 +1,116 @@
1
+ import type TableUp from '..';
2
+ import type { TableMainFormat, TableWrapperFormat } from '../formats';
3
+ import { autoUpdate, computePosition, flip, limitShift, offset, shift } from '@floating-ui/dom';
4
+ import Quill from 'quill';
5
+ import { createBEM } from '../utils';
6
+
7
+ export class TableAlign {
8
+ tableBlot: TableMainFormat;
9
+ tableWrapperBlot: TableWrapperFormat;
10
+ alignBox?: HTMLElement;
11
+ cleanup?: () => void;
12
+ bem = createBEM('align');
13
+ resizeObserver = new ResizeObserver(() => this.hide());
14
+ constructor(public tableModule: TableUp, public table: HTMLElement, public quill: Quill) {
15
+ this.tableBlot = Quill.find(table)! as TableMainFormat;
16
+ this.tableWrapperBlot = this.tableBlot.parent as TableWrapperFormat;
17
+
18
+ this.alignBox = this.buildTool();
19
+ }
20
+
21
+ buildTool() {
22
+ const alignBox = this.tableModule.addContainer(this.bem.b());
23
+ const icons = Quill.import('ui/icons') as Record<string, any>;
24
+ const alignIcons = {
25
+ left: icons.align[''],
26
+ center: icons.align.center,
27
+ right: icons.align.right,
28
+ };
29
+ for (const [align, iconStr] of Object.entries(alignIcons)) {
30
+ const item = document.createElement('span');
31
+ item.dataset.align = align;
32
+ item.classList.add(this.bem.be('item'));
33
+ item.innerHTML = `<i class="icon">${iconStr}</i>`;
34
+ item.addEventListener('click', () => {
35
+ const value = item.dataset.align;
36
+ if (value) {
37
+ this.setTableAlign(this.tableBlot, value);
38
+
39
+ this.quill.once(Quill.events.SCROLL_OPTIMIZE, () => {
40
+ if (this.tableModule.tableSelection) {
41
+ this.tableModule.tableSelection.hide();
42
+ }
43
+ if (this.tableModule.tableResize) {
44
+ this.tableModule.tableResize.update();
45
+ }
46
+ if (this.tableModule.tableResizeScale) {
47
+ this.tableModule.tableResizeScale.update();
48
+ }
49
+ if (this.tableModule.tableScrollbar) {
50
+ this.tableModule.tableScrollbar.update();
51
+ }
52
+ });
53
+ }
54
+ });
55
+ alignBox.appendChild(item);
56
+ }
57
+ if (!this.cleanup) {
58
+ this.cleanup = autoUpdate(
59
+ this.tableWrapperBlot.domNode,
60
+ alignBox,
61
+ () => this.update(),
62
+ );
63
+ }
64
+ return alignBox;
65
+ }
66
+
67
+ setTableAlign(tableBlot: TableMainFormat, align: string) {
68
+ const cols = tableBlot.getCols();
69
+ for (const col of cols) {
70
+ col.align = align;
71
+ }
72
+ }
73
+
74
+ show() {
75
+ if (!this.alignBox) return;
76
+ this.alignBox.classList.add(this.bem.bm('active'));
77
+ this.resizeObserver.observe(this.table);
78
+ }
79
+
80
+ hide() {
81
+ if (!this.alignBox) return;
82
+ this.alignBox.classList.remove(this.bem.bm('active'));
83
+ if (this.cleanup) {
84
+ this.cleanup();
85
+ this.cleanup = undefined;
86
+ }
87
+ }
88
+
89
+ update() {
90
+ if (!this.alignBox) return;
91
+ if (this.tableBlot.full || this.tableBlot.domNode.offsetWidth >= this.quill.root.offsetWidth) {
92
+ this.hide();
93
+ return;
94
+ }
95
+
96
+ this.show();
97
+ computePosition(this.tableWrapperBlot.domNode, this.alignBox, {
98
+ placement: 'top',
99
+ middleware: [flip(), shift({ limiter: limitShift() }), offset(16)],
100
+ }).then(({ x, y }) => {
101
+ Object.assign(this.alignBox!.style, {
102
+ left: `${x}px`,
103
+ top: `${y}px`,
104
+ });
105
+ });
106
+ }
107
+
108
+ destroy() {
109
+ this.hide();
110
+ this.resizeObserver.disconnect();
111
+ if (this.alignBox) {
112
+ this.alignBox.remove();
113
+ this.alignBox = undefined;
114
+ }
115
+ }
116
+ }
@@ -0,0 +1,140 @@
1
+ import type { Tool } from '../../utils';
2
+ import Background from '../../svg/background.svg';
3
+ import Border from '../../svg/border.svg';
4
+ import InsertBottom from '../../svg/insert-bottom.svg';
5
+ import InsertLeft from '../../svg/insert-left.svg';
6
+ import InsertRight from '../../svg/insert-right.svg';
7
+ import InsertTop from '../../svg/insert-top.svg';
8
+ import MergeCell from '../../svg/merge-cell.svg';
9
+ import RemoveColumn from '../../svg/remove-column.svg';
10
+ import RemoveRow from '../../svg/remove-row.svg';
11
+ import RemoveTable from '../../svg/remove-table.svg';
12
+ import SplitCell from '../../svg/split-cell.svg';
13
+ import { createBEM } from '../../utils';
14
+
15
+ export const menuColorSelectClassName = 'color-selector';
16
+ export const defaultTools: Tool[] = [
17
+ {
18
+ name: 'InsertTop',
19
+ icon: InsertTop,
20
+ tip: 'Insert row above',
21
+ handle: (tableModule) => {
22
+ tableModule.appendRow(false);
23
+ tableModule.hideTableTools();
24
+ },
25
+ },
26
+ {
27
+ name: 'InsertRight',
28
+ icon: InsertRight,
29
+ tip: 'Insert column right',
30
+ handle: (tableModule) => {
31
+ tableModule.appendCol(true);
32
+ tableModule.hideTableTools();
33
+ },
34
+ },
35
+ {
36
+ name: 'InsertBottom',
37
+ icon: InsertBottom,
38
+ tip: 'Insert row below',
39
+ handle: (tableModule) => {
40
+ tableModule.appendRow(true);
41
+ tableModule.hideTableTools();
42
+ },
43
+ },
44
+ {
45
+ name: 'InsertLeft',
46
+ icon: InsertLeft,
47
+ tip: 'Insert column Left',
48
+ handle: (tableModule) => {
49
+ tableModule.appendCol(false);
50
+ tableModule.hideTableTools();
51
+ },
52
+ },
53
+ {
54
+ name: 'break',
55
+ },
56
+ {
57
+
58
+ name: 'MergeCell',
59
+ icon: MergeCell,
60
+ tip: 'Merge Cell',
61
+ handle: (tableModule) => {
62
+ tableModule.mergeCells();
63
+ tableModule.hideTableTools();
64
+ },
65
+ },
66
+ {
67
+
68
+ name: 'SplitCell',
69
+ icon: SplitCell,
70
+ tip: 'Split Cell',
71
+ handle: (tableModule) => {
72
+ tableModule.splitCell();
73
+ tableModule.hideTableTools();
74
+ },
75
+ },
76
+ {
77
+ name: 'break',
78
+ },
79
+ {
80
+ name: 'DeleteRow',
81
+ icon: RemoveRow,
82
+ tip: 'Delete Row',
83
+ handle: (tableModule) => {
84
+ tableModule.removeRow();
85
+ tableModule.hideTableTools();
86
+ },
87
+ },
88
+ {
89
+ name: 'DeleteColumn',
90
+ icon: RemoveColumn,
91
+ tip: 'Delete Column',
92
+ handle: (tableModule) => {
93
+ tableModule.removeCol();
94
+ tableModule.hideTableTools();
95
+ },
96
+ },
97
+ {
98
+ name: 'DeleteTable',
99
+ icon: RemoveTable,
100
+ tip: 'Delete table',
101
+ handle: (tableModule) => {
102
+ tableModule.deleteTable();
103
+ },
104
+ },
105
+ {
106
+ name: 'break',
107
+ },
108
+ {
109
+ name: 'BackgroundColor',
110
+ icon: Background,
111
+ isColorChoose: true,
112
+ tip: 'Set background color',
113
+ key: 'background-color',
114
+ handle: (tableModule, selectedTds, color) => {
115
+ tableModule.setCellAttrs(selectedTds, 'background-color', color, true);
116
+ },
117
+ },
118
+ {
119
+ name: 'BorderColor',
120
+ icon: Border,
121
+ isColorChoose: true,
122
+ tip: 'Set border color',
123
+ key: 'border-color',
124
+ handle: (tableModule, selectedTds, color) => {
125
+ tableModule.setCellAttrs(selectedTds, 'border-color', color, true);
126
+ },
127
+ },
128
+ ];
129
+
130
+ export const maxSaveColorCount = 10;
131
+ export const usedColors = new Set<string>();
132
+ const bem = createBEM('color-map');
133
+ export const colorClassName = {
134
+ selectWrapper: bem.b(),
135
+ used: bem.bm('used'),
136
+ item: bem.be('item'),
137
+ btn: bem.be('btn'),
138
+ map: bem.be('content'),
139
+ mapRow: bem.be('content-row'),
140
+ };
@@ -0,0 +1,3 @@
1
+ export * from './table-menu-common';
2
+ export * from './table-menu-contextmenu';
3
+ export * from './table-menu-select';
@@ -0,0 +1,249 @@
1
+ import type Quill from 'quill';
2
+ import type { TableUp } from '../..';
3
+ import type { TableMenuOptions, ToolOption, TooltipInstance, ToolTipOptions } from '../../utils';
4
+ import { createBEM, createColorPicker, createTooltip, debounce, defaultColorMap, isArray, isFunction, randomId } from '../../utils';
5
+ import { colorClassName, defaultTools, maxSaveColorCount, menuColorSelectClassName, usedColors } from './constants';
6
+
7
+ export type TableMenuOptionsInput = Partial<Omit<TableMenuOptions, 'texts'>>;
8
+ export class TableMenuCommon {
9
+ options: TableMenuOptions;
10
+ menu: HTMLElement | null = null;
11
+ updateUsedColor: (this: any, color?: string) => void;
12
+ tooltipItem: TooltipInstance[] = [];
13
+ bem = createBEM('menu');
14
+ colorItemClass = `color-${randomId()}`;
15
+ colorChooseTooltipOption: ToolTipOptions = {
16
+ direction: 'top',
17
+ };
18
+
19
+ constructor(public tableModule: TableUp, public quill: Quill, options: TableMenuOptionsInput) {
20
+ this.options = this.resolveOptions(options);
21
+
22
+ try {
23
+ const storageValue = localStorage.getItem(this.options.localstorageKey) || '[]';
24
+ let colorValue = JSON.parse(storageValue);
25
+ if (!isArray(colorValue)) {
26
+ colorValue = [];
27
+ }
28
+ colorValue.slice(-1 * maxSaveColorCount).map((c: string) => usedColors.add(c));
29
+ }
30
+ catch {}
31
+
32
+ this.updateUsedColor = debounce((color?: string) => {
33
+ if (!color) return;
34
+ usedColors.add(color);
35
+ if (usedColors.size > maxSaveColorCount) {
36
+ const saveColors = Array.from(usedColors).slice(-1 * maxSaveColorCount);
37
+ usedColors.clear();
38
+ saveColors.map(v => usedColors.add(v));
39
+ }
40
+
41
+ localStorage.setItem(this.options.localstorageKey, JSON.stringify(Array.from(usedColors)));
42
+ const usedColorWrappers = Array.from(document.querySelectorAll(`.${this.colorItemClass}.${colorClassName.used}`));
43
+ for (const usedColorWrapper of usedColorWrappers) {
44
+ const newColorItem = document.createElement('div');
45
+ newColorItem.classList.add(colorClassName.item);
46
+ newColorItem.style.backgroundColor = String(color);
47
+ // if already have same color item. doesn't need insert
48
+ const sameColorItem = Array.from(usedColorWrapper.querySelectorAll(`.${colorClassName.item}[style*="background-color: ${newColorItem.style.backgroundColor}"]`));
49
+ if (sameColorItem.length <= 0) {
50
+ usedColorWrapper.appendChild(newColorItem);
51
+ }
52
+
53
+ const colorItem = Array.from(usedColorWrapper.querySelectorAll(`.${colorClassName.item}`)).slice(0, -1 * maxSaveColorCount);
54
+ for (const item of colorItem) {
55
+ item.remove();
56
+ }
57
+ }
58
+ }, 1000);
59
+ }
60
+
61
+ resolveOptions(options: TableMenuOptionsInput) {
62
+ const value = Object.assign({
63
+ tipText: true,
64
+ tipTexts: {},
65
+ tools: defaultTools,
66
+ localstorageKey: '__table-bg-used-color',
67
+ defaultColorMap,
68
+ }, options);
69
+ return value as TableMenuOptions;
70
+ };
71
+
72
+ getUsedColors() {
73
+ return usedColors;
74
+ }
75
+
76
+ buildTools(): HTMLElement {
77
+ const toolBox = document.createElement('div');
78
+ toolBox.classList.add(this.bem.b());
79
+ Object.assign(toolBox.style, { display: 'flex' });
80
+ for (const tool of this.options.tools) {
81
+ const { name, icon, handle, isColorChoose, key: attrKey, tip = '' } = tool as ToolOption;
82
+ const item = document.createElement('span');
83
+ item.classList.add(this.bem.be('item'));
84
+ if (name === 'break') {
85
+ item.classList.add(this.bem.is('break'));
86
+ }
87
+ else {
88
+ // add icon
89
+ const iconDom = document.createElement('i');
90
+ iconDom.classList.add('icon');
91
+ if (isFunction(icon)) {
92
+ iconDom.appendChild(icon(this.tableModule));
93
+ }
94
+ else {
95
+ iconDom.innerHTML = icon;
96
+ }
97
+ item.appendChild(iconDom);
98
+
99
+ if (isColorChoose && attrKey) {
100
+ const tooltipItem = this.createColorChoose(item, { name, icon, handle, isColorChoose, key: attrKey, tip });
101
+ this.tooltipItem.push(tooltipItem);
102
+ item.classList.add(menuColorSelectClassName);
103
+ }
104
+ else {
105
+ isFunction(handle) && item.addEventListener('click', (e) => {
106
+ this.quill.focus();
107
+ handle(this.tableModule, this.getSelectedTds(), e);
108
+ }, false);
109
+ }
110
+
111
+ // add text
112
+ const tipText = this.options.tipTexts[name] || tip;
113
+ if (this.options.tipText && tipText && tip) {
114
+ this.createTipText(item, tipText);
115
+ }
116
+ }
117
+ toolBox.appendChild(item);
118
+ }
119
+ return toolBox;
120
+ };
121
+
122
+ createColorChoose(item: HTMLElement, { handle, key }: ToolOption) {
123
+ const colorSelectWrapper = document.createElement('div');
124
+ colorSelectWrapper.classList.add(colorClassName.selectWrapper);
125
+
126
+ if (this.options.defaultColorMap.length > 0) {
127
+ const colorMap = document.createElement('div');
128
+ colorMap.classList.add(colorClassName.map);
129
+ for (const colors of this.options.defaultColorMap) {
130
+ const colorMapRow = document.createElement('div');
131
+ colorMapRow.classList.add(colorClassName.mapRow);
132
+ for (const color of colors) {
133
+ const colorItem = document.createElement('div');
134
+ colorItem.classList.add(colorClassName.item);
135
+ colorItem.style.backgroundColor = color;
136
+ colorMapRow.appendChild(colorItem);
137
+ }
138
+ colorMap.appendChild(colorMapRow);
139
+ }
140
+ colorSelectWrapper.appendChild(colorMap);
141
+ }
142
+
143
+ const colorMapRow = document.createElement('div');
144
+ colorMapRow.classList.add(colorClassName.mapRow);
145
+ Object.assign(colorMapRow.style, {
146
+ marginTop: '4px',
147
+ });
148
+ const transparentColor = document.createElement('div');
149
+ transparentColor.classList.add(colorClassName.btn, 'transparent');
150
+ transparentColor.textContent = this.tableModule.options.texts.transparent;
151
+ transparentColor.addEventListener('click', () => {
152
+ handle(this.tableModule, this.getSelectedTds(), 'transparent');
153
+ });
154
+ const clearColor = document.createElement('div');
155
+ clearColor.classList.add(colorClassName.btn, 'clear');
156
+ clearColor.textContent = this.tableModule.options.texts.clear;
157
+ clearColor.addEventListener('click', () => {
158
+ handle(this.tableModule, this.getSelectedTds(), null);
159
+ });
160
+ const customColor = document.createElement('div');
161
+ customColor.classList.add(colorClassName.btn, 'custom');
162
+ customColor.textContent = this.tableModule.options.texts.custom;
163
+ const colorPicker = createColorPicker({
164
+ onChange: (color) => {
165
+ handle(this.tableModule, this.getSelectedTds(), color);
166
+ this.updateUsedColor(color);
167
+ },
168
+ });
169
+ const { hide: hideColorPicker, destroy: destroyColorPicker } = createTooltip(customColor, {
170
+ direction: 'right',
171
+ type: 'click',
172
+ content: colorPicker,
173
+ container: customColor,
174
+ })!;
175
+
176
+ colorMapRow.appendChild(transparentColor);
177
+ colorMapRow.appendChild(clearColor);
178
+ colorMapRow.appendChild(customColor);
179
+ colorSelectWrapper.appendChild(colorMapRow);
180
+
181
+ if (usedColors.size > 0) {
182
+ const usedColorWrap = document.createElement('div');
183
+ usedColorWrap.classList.add(colorClassName.used, this.colorItemClass);
184
+ for (const recordColor of usedColors) {
185
+ const colorItem = document.createElement('div');
186
+ colorItem.classList.add(colorClassName.item);
187
+ colorItem.style.backgroundColor = recordColor;
188
+ usedColorWrap.appendChild(colorItem);
189
+ }
190
+ colorSelectWrapper.appendChild(usedColorWrap);
191
+ }
192
+
193
+ colorSelectWrapper.addEventListener('click', (e) => {
194
+ e.stopPropagation();
195
+ hideColorPicker();
196
+ const item = e.target as HTMLElement;
197
+ const color = item.style.backgroundColor;
198
+ const selectedTds = this.getSelectedTds();
199
+ if (item && color && selectedTds.length > 0) {
200
+ this.tableModule.setCellAttrs(selectedTds, key!, color, true);
201
+ if (!item.closest(`.${colorClassName.item}`)) return;
202
+ this.updateUsedColor(color);
203
+ }
204
+ });
205
+
206
+ return createTooltip(item, {
207
+ content: colorSelectWrapper,
208
+ onClose(force) {
209
+ const isChild = colorSelectWrapper.contains(colorPicker);
210
+ if (force && isChild) {
211
+ hideColorPicker();
212
+ }
213
+ return isChild;
214
+ },
215
+ onDestroy() {
216
+ destroyColorPicker();
217
+ },
218
+ ...this.colorChooseTooltipOption,
219
+ })!;
220
+ }
221
+
222
+ getSelectedTds() {
223
+ return this.tableModule.tableSelection?.selectedTds || [];
224
+ }
225
+
226
+ createTipText(item: HTMLElement, text: string) {
227
+ const tipTextDom = createTooltip(item, { msg: text });
228
+ tipTextDom && this.tooltipItem.push(tipTextDom);
229
+ }
230
+
231
+ update() {
232
+ if (!this.menu || !this.tableModule.tableSelection || !this.tableModule.tableSelection.boundary) return;
233
+ Object.assign(this.menu.style, { display: 'flex' });
234
+ }
235
+
236
+ hide() {
237
+ this.menu && Object.assign(this.menu.style, { display: 'none' });
238
+ for (const tooltip of this.tooltipItem) {
239
+ tooltip.hide(true);
240
+ }
241
+ }
242
+
243
+ destroy() {
244
+ for (const tooltip of this.tooltipItem) tooltip.destroy();
245
+ if (!this.menu) return;
246
+ this.menu.remove();
247
+ this.menu = null;
248
+ }
249
+ }
@@ -0,0 +1,94 @@
1
+ import type Quill from 'quill';
2
+ import type TableUp from '../..';
3
+ import type { TableMenuOptions, ToolTipOptions } from '../../utils';
4
+ import { limitDomInViewPort } from '../../utils';
5
+ import { menuColorSelectClassName } from './constants';
6
+ import { TableMenuCommon } from './table-menu-common';
7
+
8
+ type TableMenuOptionsInput = Partial<Omit<TableMenuOptions, 'texts'>>;
9
+ export class TableMenuContextmenu extends TableMenuCommon {
10
+ colorChooseTooltipOption: ToolTipOptions = {
11
+ direction: 'right',
12
+ };
13
+
14
+ constructor(public tableModule: TableUp, public quill: Quill, options: TableMenuOptionsInput) {
15
+ super(tableModule, quill, options);
16
+
17
+ this.quill.root.addEventListener('contextmenu', this.listenContextmenu);
18
+ }
19
+
20
+ listenContextmenu = (e: MouseEvent) => {
21
+ e.preventDefault();
22
+
23
+ const path = e.composedPath() as HTMLElement[];
24
+ if (!path || path.length <= 0) return;
25
+
26
+ const tableNode = path.find(node => node.tagName && node.tagName.toUpperCase() === 'TABLE' && node.classList.contains('ql-table'));
27
+
28
+ if (tableNode && this.tableModule.tableSelection?.selectedTds?.length) {
29
+ if (!this.menu) {
30
+ this.menu = this.buildTools();
31
+ }
32
+ this.update({ x: e.clientX, y: e.clientY });
33
+ document.addEventListener('click', () => {
34
+ this.hide();
35
+ }, { once: true });
36
+ }
37
+ else {
38
+ this.hide();
39
+ }
40
+ };
41
+
42
+ buildTools(): HTMLElement {
43
+ const menu = super.buildTools();
44
+ menu.classList.add(this.bem.is('contextmenu'));
45
+ const items = menu.getElementsByClassName(menuColorSelectClassName);
46
+ for (const item of Array.from(items)) {
47
+ item.addEventListener('click', e => e.stopPropagation());
48
+ }
49
+ document.body.appendChild(menu);
50
+ return menu;
51
+ }
52
+
53
+ createTipText(item: HTMLElement, text: string): void {
54
+ const tipTextDom = document.createElement('span');
55
+ tipTextDom.textContent = text;
56
+ item.appendChild(tipTextDom);
57
+ }
58
+
59
+ update(position?: { x: number; y: number }) {
60
+ if (!this.menu || !this.tableModule.tableSelection || !this.tableModule.tableSelection.boundary) return;
61
+ super.update();
62
+ const style: Record<string, any> = {
63
+ display: 'flex',
64
+ left: 0,
65
+ top: 0,
66
+ };
67
+
68
+ if (!position) {
69
+ return this.hide();
70
+ }
71
+ const { x, y } = position;
72
+ style.left = x;
73
+ style.top = y;
74
+
75
+ Object.assign(this.menu.style, {
76
+ ...style,
77
+ left: `${style.left + window.scrollX}px`,
78
+ top: `${style.top + window.scrollY}px`,
79
+ });
80
+
81
+ // limit menu in viewport
82
+ const menuRect = this.menu.getBoundingClientRect();
83
+ const { left: limitLeft, top: limitTop } = limitDomInViewPort(menuRect);
84
+ Object.assign(this.menu.style, {
85
+ left: `${limitLeft + window.scrollX}px`,
86
+ top: `${limitTop + window.scrollY}px`,
87
+ });
88
+ }
89
+
90
+ destroy() {
91
+ this.quill.root.removeEventListener('contextmenu', this.listenContextmenu);
92
+ super.destroy();
93
+ }
94
+ }
@@ -0,0 +1,28 @@
1
+ import type Quill from 'quill';
2
+ import type { TableMenuOptionsInput, TableUp } from '../..';
3
+ import { computePosition, flip, limitShift, offset, shift } from '@floating-ui/dom';
4
+ import { TableMenuCommon } from './table-menu-common';
5
+
6
+ export class TableMenuSelect extends TableMenuCommon {
7
+ constructor(public tableModule: TableUp, public quill: Quill, options: TableMenuOptionsInput) {
8
+ super(tableModule, quill, options);
9
+
10
+ this.menu = this.buildTools();
11
+ this.tableModule.addContainer(this.menu);
12
+ }
13
+
14
+ update() {
15
+ if (!this.menu || !this.tableModule.tableSelection || !this.tableModule.tableSelection.boundary) return;
16
+ super.update();
17
+
18
+ computePosition(this.tableModule.tableSelection.cellSelect, this.menu, {
19
+ placement: 'bottom',
20
+ middleware: [flip(), shift({ limiter: limitShift() }), offset(8)],
21
+ }).then(({ x, y }) => {
22
+ Object.assign(this.menu!.style, {
23
+ left: `${x}px`,
24
+ top: `${y}px`,
25
+ });
26
+ });
27
+ }
28
+ }
@@ -0,0 +1,5 @@
1
+ export * from './table-resize-box';
2
+ export * from './table-resize-common';
3
+ export * from './table-resize-line';
4
+ export * from './table-resize-scale';
5
+ export * from './utils';