@oix1987/yjd 1.0.0 → 1.0.2
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 +73 -22
- package/dist/rich-editor.esm.js +2 -0
- package/dist/rich-editor.esm.js.map +1 -0
- package/dist/rich-editor.min.js +2 -0
- package/dist/rich-editor.min.js.map +1 -0
- package/package.json +12 -7
- package/index.js +0 -221
- package/lib/core/editor.js +0 -1175
- package/lib/core/format.js +0 -542
- package/lib/core/module.js +0 -81
- package/lib/core/registry.js +0 -152
- package/lib/formats/background.js +0 -212
- package/lib/formats/bold.js +0 -67
- package/lib/formats/capitalization.js +0 -563
- package/lib/formats/color.js +0 -165
- package/lib/formats/emoji.js +0 -282
- package/lib/formats/font-family.js +0 -547
- package/lib/formats/heading.js +0 -502
- package/lib/formats/image.js +0 -344
- package/lib/formats/import.js +0 -385
- package/lib/formats/indent.js +0 -297
- package/lib/formats/italic.js +0 -27
- package/lib/formats/line-height.js +0 -558
- package/lib/formats/link.js +0 -251
- package/lib/formats/list.js +0 -635
- package/lib/formats/strike.js +0 -31
- package/lib/formats/subscript.js +0 -36
- package/lib/formats/superscript.js +0 -35
- package/lib/formats/table.js +0 -288
- package/lib/formats/tag.js +0 -304
- package/lib/formats/text-align.js +0 -421
- package/lib/formats/text-size.js +0 -497
- package/lib/formats/underline.js +0 -30
- package/lib/formats/video.js +0 -372
- package/lib/modules/block-toolbar.js +0 -628
- package/lib/modules/code-view.js +0 -434
- package/lib/modules/history.js +0 -410
- package/lib/modules/resize-handles.js +0 -677
- package/lib/modules/table-toolbar.js +0 -618
- package/lib/modules/toolbar.js +0 -424
- package/lib/styles-loader.js +0 -144
- package/lib/styles.css +0 -2123
- package/lib/ui/color-picker.js +0 -296
- package/lib/ui/customselect.js +0 -319
- package/lib/ui/emoji-picker.js +0 -196
- package/lib/ui/icons.js +0 -413
- package/lib/ui/image-popup.js +0 -444
- package/lib/ui/import-popup.js +0 -288
- package/lib/ui/link-popup.js +0 -191
- package/lib/ui/list-picker.js +0 -307
- package/lib/ui/select-button.js +0 -61
- package/lib/ui/table-popup.js +0 -171
- package/lib/ui/tag-popup.js +0 -249
- package/lib/ui/text-align-picker.js +0 -281
- package/lib/ui/video-popup.js +0 -422
- package/lib/utils/history-helper.js +0 -50
- package/lib/utils/popup-helper.js +0 -219
- package/lib/utils/popup-positioning.js +0 -231
|
@@ -1,618 +0,0 @@
|
|
|
1
|
-
import Module from '../core/module.js';
|
|
2
|
-
import IconUtils from '../ui/icons.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Table Toolbar Module - Floating toolbar hiện lên khi click vào table
|
|
6
|
-
*/
|
|
7
|
-
class TableToolbar extends Module {
|
|
8
|
-
static DEFAULTS = {
|
|
9
|
-
fadeDelay: 3000, // Auto hide after 3 seconds of inactivity
|
|
10
|
-
buttons: ['tableProfile', 'deleteTable', 'insertRowAbove', 'insertRowBelow', 'deleteRow', 'insertColRight', 'insertColLeft', 'deleteCol']
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
constructor(editor, options = {}) {
|
|
14
|
-
super(editor, options);
|
|
15
|
-
this.tableToolbar = null;
|
|
16
|
-
this.currentTable = null;
|
|
17
|
-
this.currentCell = null;
|
|
18
|
-
this.hideTimeout = null;
|
|
19
|
-
this.isVisible = false;
|
|
20
|
-
|
|
21
|
-
this.init();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async init() {
|
|
25
|
-
await this.createTableToolbar();
|
|
26
|
-
this.setupEventListeners();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Tạo table toolbar element
|
|
33
|
-
*/
|
|
34
|
-
async createTableToolbar() {
|
|
35
|
-
this.tableToolbar = document.createElement('div');
|
|
36
|
-
this.tableToolbar.className = 'table-toolbar';
|
|
37
|
-
|
|
38
|
-
// Create toolbar container
|
|
39
|
-
const toolbarContainer = document.createElement('div');
|
|
40
|
-
toolbarContainer.className = 'table-toolbar-container';
|
|
41
|
-
|
|
42
|
-
// Define button groups
|
|
43
|
-
const buttonGroups = [
|
|
44
|
-
{
|
|
45
|
-
name: 'table-actions',
|
|
46
|
-
buttons: [
|
|
47
|
-
{ cmd: 'tableProfile', icon: 'icon-table-profile', title: 'Table Profile' },
|
|
48
|
-
{ cmd: 'deleteTable', icon: 'icon-delete-table', title: 'Delete Table' }
|
|
49
|
-
]
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
name: 'row-actions',
|
|
53
|
-
buttons: [
|
|
54
|
-
{ cmd: 'insertRowAbove', icon: 'icon-add-row-above', title: 'Add Row Above' },
|
|
55
|
-
{ cmd: 'insertRowBelow', icon: 'icon-add-row-below', title: 'Add Row Below' },
|
|
56
|
-
{ cmd: 'deleteRow', icon: 'icon-delete-row', title: 'Delete Selected Row' }
|
|
57
|
-
]
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
name: 'col-actions',
|
|
61
|
-
buttons: [
|
|
62
|
-
{ cmd: 'insertColRight', icon: 'icon-add-col-right', title: 'Add Column Right' },
|
|
63
|
-
{ cmd: 'insertColLeft', icon: 'icon-add-col-left', title: 'Add Column Left' },
|
|
64
|
-
{ cmd: 'deleteCol', icon: 'icon-delete-col', title: 'Delete Selected Column' }
|
|
65
|
-
]
|
|
66
|
-
}
|
|
67
|
-
];
|
|
68
|
-
|
|
69
|
-
// Create groups
|
|
70
|
-
for (const group of buttonGroups) {
|
|
71
|
-
const groupDiv = document.createElement('div');
|
|
72
|
-
groupDiv.className = `table-toolbar-group ${group.name}`;
|
|
73
|
-
|
|
74
|
-
// Create buttons in this group
|
|
75
|
-
for (const { cmd, icon, title } of group.buttons) {
|
|
76
|
-
const button = document.createElement('button');
|
|
77
|
-
button.className = 'table-toolbar-btn';
|
|
78
|
-
button.title = title;
|
|
79
|
-
button.dataset.command = cmd;
|
|
80
|
-
|
|
81
|
-
// Load and set SVG icon using IconUtils
|
|
82
|
-
const svgContent = IconUtils.getIcon(icon.replace('icon-', ''));
|
|
83
|
-
if (svgContent) {
|
|
84
|
-
button.innerHTML = svgContent;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
button.addEventListener('click', (e) => {
|
|
88
|
-
e.preventDefault();
|
|
89
|
-
e.stopPropagation();
|
|
90
|
-
this.handleCommand(cmd, button);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
groupDiv.appendChild(button);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
toolbarContainer.appendChild(groupDiv);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Create arrow element
|
|
100
|
-
const arrow = document.createElement('div');
|
|
101
|
-
arrow.className = 'table-toolbar-arrow';
|
|
102
|
-
|
|
103
|
-
// Add container and arrow to toolbar
|
|
104
|
-
this.tableToolbar.appendChild(toolbarContainer);
|
|
105
|
-
this.tableToolbar.appendChild(arrow);
|
|
106
|
-
|
|
107
|
-
// Add to editor wrapper
|
|
108
|
-
this.editor.wrapper.appendChild(this.tableToolbar);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Setup event listeners
|
|
113
|
-
*/
|
|
114
|
-
setupEventListeners() {
|
|
115
|
-
// Listen for clicks on table cells
|
|
116
|
-
this.editor.editor.addEventListener('click', (e) => {
|
|
117
|
-
const clickedCell = e.target.closest('td, th');
|
|
118
|
-
const clickedTable = e.target.closest('table');
|
|
119
|
-
|
|
120
|
-
if (clickedTable && clickedCell) {
|
|
121
|
-
// Check if the clicked table is within the editable area
|
|
122
|
-
const isInEditableArea = this.editor.isNodeInEditableArea ?
|
|
123
|
-
this.editor.isNodeInEditableArea(clickedTable) : true;
|
|
124
|
-
|
|
125
|
-
if (!isInEditableArea) {
|
|
126
|
-
this.hide();
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
this.currentTable = clickedTable;
|
|
131
|
-
this.currentCell = clickedCell;
|
|
132
|
-
this.showAtTable(clickedTable);
|
|
133
|
-
} else {
|
|
134
|
-
this.hide();
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// Hide on outside click
|
|
139
|
-
document.addEventListener('mousedown', (e) => {
|
|
140
|
-
if (!e.target.closest('.table-toolbar') && !e.target.closest('table')) {
|
|
141
|
-
this.hide();
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// Update scroll event listeners to track position instead of hiding
|
|
146
|
-
window.addEventListener('scroll', () => {
|
|
147
|
-
if (this.isVisible && this.currentTable) {
|
|
148
|
-
this.updateToolbarPosition();
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
// Add editor scroll listener
|
|
153
|
-
this.editor.editor.addEventListener('scroll', () => {
|
|
154
|
-
if (this.isVisible && this.currentTable) {
|
|
155
|
-
this.updateToolbarPosition();
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// Update when selection changes within table
|
|
160
|
-
this.editor.editor.addEventListener('keyup', () => {
|
|
161
|
-
if (this.isVisible && this.currentTable) {
|
|
162
|
-
const selection = window.getSelection();
|
|
163
|
-
if (selection && selection.rangeCount > 0) {
|
|
164
|
-
const currentCell = selection.getRangeAt(0).startContainer;
|
|
165
|
-
const tableCell = currentCell.nodeType === Node.TEXT_NODE
|
|
166
|
-
? currentCell.parentElement.closest('td, th')
|
|
167
|
-
: currentCell.closest('td, th');
|
|
168
|
-
|
|
169
|
-
if (tableCell && tableCell !== this.currentCell) {
|
|
170
|
-
// Verify the table cell is still in editable area
|
|
171
|
-
const isInEditableArea = this.editor.isNodeInEditableArea ?
|
|
172
|
-
this.editor.isNodeInEditableArea(tableCell) : true;
|
|
173
|
-
|
|
174
|
-
if (!isInEditableArea) {
|
|
175
|
-
this.hide();
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
this.currentCell = tableCell;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Update toolbar position based on current table
|
|
188
|
-
*/
|
|
189
|
-
updateToolbarPosition() {
|
|
190
|
-
if (!this.isVisible || !this.currentTable) return;
|
|
191
|
-
|
|
192
|
-
// Check if table is still in DOM and in editable area
|
|
193
|
-
if (!document.body.contains(this.currentTable)) {
|
|
194
|
-
this.hide();
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const isInEditableArea = this.editor.isNodeInEditableArea ?
|
|
199
|
-
this.editor.isNodeInEditableArea(this.currentTable) : true;
|
|
200
|
-
|
|
201
|
-
if (!isInEditableArea) {
|
|
202
|
-
this.hide();
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Update position based on current table position
|
|
207
|
-
const rect = this.currentTable.getBoundingClientRect();
|
|
208
|
-
const editorRect = this.editor.wrapper.getBoundingClientRect();
|
|
209
|
-
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
|
210
|
-
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
|
|
211
|
-
|
|
212
|
-
this.updateToolbarAt(
|
|
213
|
-
rect.left - editorRect.left + scrollLeft,
|
|
214
|
-
rect.top - editorRect.top + scrollTop,
|
|
215
|
-
rect.width,
|
|
216
|
-
rect.height
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Update toolbar position at specific coordinates
|
|
222
|
-
*/
|
|
223
|
-
updateToolbarAt(x, y, width, height) {
|
|
224
|
-
if (!this.tableToolbar) return;
|
|
225
|
-
|
|
226
|
-
this.ensureToolbarInViewport(x, y, width, height);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Show toolbar at table position
|
|
231
|
-
*/
|
|
232
|
-
showAtTable(table) {
|
|
233
|
-
if (!table || !this.tableToolbar) return;
|
|
234
|
-
|
|
235
|
-
const rect = table.getBoundingClientRect();
|
|
236
|
-
const editorRect = this.editor.wrapper.getBoundingClientRect();
|
|
237
|
-
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
|
238
|
-
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
|
|
239
|
-
this.showAt(
|
|
240
|
-
rect.left - editorRect.left + scrollLeft,
|
|
241
|
-
rect.top - editorRect.top + scrollTop,
|
|
242
|
-
rect.width,
|
|
243
|
-
rect.height
|
|
244
|
-
);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Show toolbar at specific position
|
|
249
|
-
*/
|
|
250
|
-
showAt(x, y, width, height) {
|
|
251
|
-
if (!this.tableToolbar) return;
|
|
252
|
-
|
|
253
|
-
this.tableToolbar.classList.add('visible');
|
|
254
|
-
this.isVisible = true;
|
|
255
|
-
|
|
256
|
-
this.ensureToolbarInViewport(x, y, width, height);
|
|
257
|
-
this.clearHideTimeout();
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Ensure toolbar stays within viewport and editor bounds
|
|
262
|
-
*/
|
|
263
|
-
ensureToolbarInViewport(x, y, width, height) {
|
|
264
|
-
if (!this.tableToolbar) return;
|
|
265
|
-
|
|
266
|
-
// Lấy thông tin về editor-area
|
|
267
|
-
const editorArea = this.editor.editor;
|
|
268
|
-
const editorRect = editorArea.getBoundingClientRect();
|
|
269
|
-
const toolbarRect = this.tableToolbar.getBoundingClientRect();
|
|
270
|
-
const toolbarContainer = this.editor.wrapper.querySelector('.rich-editor-toolbar-container');
|
|
271
|
-
const toolbarRect2 = toolbarContainer ? toolbarContainer.getBoundingClientRect() : null;
|
|
272
|
-
let left = x+width/2 - this.tableToolbar.offsetWidth/2;
|
|
273
|
-
let top = y - 60- document.documentElement.scrollTop;
|
|
274
|
-
let arrowLeft = '50%';
|
|
275
|
-
let arrowDirection = 'down'; // mũi tên hướng xuống
|
|
276
|
-
|
|
277
|
-
// Trường hợp 1: Vượt quá lề trái của editor
|
|
278
|
-
if (left < 0) {
|
|
279
|
-
left = (x - (this.tableToolbar.offsetWidth * (10/100)));
|
|
280
|
-
if(left < 0) left = 0;
|
|
281
|
-
arrowLeft = '10%'; // Mũi tên ở 10%
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Trường hợp 2: Vượt quá lề phải của editor
|
|
285
|
-
if (left + this.tableToolbar.offsetWidth > (this.editor.wrapper.offsetWidth - 2)) {
|
|
286
|
-
left = x - this.tableToolbar.offsetWidth*0.9;
|
|
287
|
-
arrowLeft = '90%'; // Mũi tên ở 90%
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Trường hợp 3: Vượt quá lề trên của editor
|
|
291
|
-
if (top < (toolbarRect2 ? toolbarRect2.height : 48)) {
|
|
292
|
-
top = y + height +10 - document.documentElement.scrollTop;
|
|
293
|
-
arrowDirection = 'up'; // Mũi tên hướng lên
|
|
294
|
-
if(top < (toolbarRect2 ? toolbarRect2.height : 48)){
|
|
295
|
-
this.hide();
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
if(top > editorRect.height){
|
|
300
|
-
this.hide();
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
// Cập nhật vị trí mũi tên (nếu có)
|
|
304
|
-
const arrow = this.tableToolbar.querySelector('.table-toolbar-arrow');
|
|
305
|
-
if (arrow) {
|
|
306
|
-
arrow.style.left = arrowLeft;
|
|
307
|
-
|
|
308
|
-
if (arrowDirection === 'up') {
|
|
309
|
-
// Mũi tên hướng lên
|
|
310
|
-
arrow.style.bottom = 'auto';
|
|
311
|
-
arrow.style.top = '-8px';
|
|
312
|
-
arrow.style.borderTop = 'none';
|
|
313
|
-
arrow.style.borderBottom = '8px solid #fff';
|
|
314
|
-
arrow.style.borderLeft = '6px solid transparent';
|
|
315
|
-
arrow.style.borderRight = '6px solid transparent';
|
|
316
|
-
} else {
|
|
317
|
-
// Mũi tên hướng xuống (mặc định)
|
|
318
|
-
arrow.style.top = 'auto';
|
|
319
|
-
arrow.style.bottom = '-8px';
|
|
320
|
-
arrow.style.borderBottom = 'none';
|
|
321
|
-
arrow.style.borderTop = '8px solid #fff';
|
|
322
|
-
arrow.style.borderLeft = '6px solid transparent';
|
|
323
|
-
arrow.style.borderRight = '6px solid transparent';
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
// Áp dụng vị trí cuối cùng
|
|
327
|
-
this.tableToolbar.style.left = left + 'px';
|
|
328
|
-
this.tableToolbar.style.top = top + 'px';
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Hide toolbar
|
|
333
|
-
*/
|
|
334
|
-
hide() {
|
|
335
|
-
if (!this.tableToolbar || !this.isVisible) return;
|
|
336
|
-
|
|
337
|
-
this.tableToolbar.classList.remove('visible');
|
|
338
|
-
this.isVisible = false;
|
|
339
|
-
this.currentTable = null;
|
|
340
|
-
this.currentCell = null;
|
|
341
|
-
this.clearHideTimeout();
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Clear hide timeout
|
|
347
|
-
*/
|
|
348
|
-
clearHideTimeout() {
|
|
349
|
-
if (this.hideTimeout) {
|
|
350
|
-
clearTimeout(this.hideTimeout);
|
|
351
|
-
this.hideTimeout = null;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Handle command execution - only if table is in editable area
|
|
357
|
-
*/
|
|
358
|
-
handleCommand(command, button) {
|
|
359
|
-
|
|
360
|
-
if (!this.currentTable || !this.currentCell) {
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// Double check that the table is still in editable area before executing command
|
|
365
|
-
const isInEditableArea = this.editor.isNodeInEditableArea ?
|
|
366
|
-
this.editor.isNodeInEditableArea(this.currentTable) : true;
|
|
367
|
-
|
|
368
|
-
if (!isInEditableArea) {
|
|
369
|
-
this.hide();
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
switch (command) {
|
|
374
|
-
case 'tableProfile':
|
|
375
|
-
this.showTableProfile();
|
|
376
|
-
break;
|
|
377
|
-
case 'deleteTable':
|
|
378
|
-
this.deleteTable();
|
|
379
|
-
break;
|
|
380
|
-
case 'insertRowAbove':
|
|
381
|
-
this.insertRowAbove();
|
|
382
|
-
break;
|
|
383
|
-
case 'insertRowBelow':
|
|
384
|
-
this.insertRowBelow();
|
|
385
|
-
break;
|
|
386
|
-
case 'deleteRow':
|
|
387
|
-
this.deleteRow();
|
|
388
|
-
break;
|
|
389
|
-
case 'insertColRight':
|
|
390
|
-
this.insertColumnRight();
|
|
391
|
-
break;
|
|
392
|
-
case 'insertColLeft':
|
|
393
|
-
this.insertColumnLeft();
|
|
394
|
-
break;
|
|
395
|
-
case 'deleteCol':
|
|
396
|
-
this.deleteColumn();
|
|
397
|
-
break;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
this.editor.focus();
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
/**
|
|
404
|
-
* Insert row above current cell
|
|
405
|
-
*/
|
|
406
|
-
insertRowAbove() {
|
|
407
|
-
const currentRow = this.currentCell.parentElement;
|
|
408
|
-
const newRow = this.createNewRow(currentRow.cells.length);
|
|
409
|
-
currentRow.parentElement.insertBefore(newRow, currentRow);
|
|
410
|
-
|
|
411
|
-
// Update resize handles if they exist
|
|
412
|
-
if (this.editor.resizeHandles) {
|
|
413
|
-
this.editor.resizeHandles.checkAndUpdateHandles();
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Insert row below current cell
|
|
419
|
-
*/
|
|
420
|
-
insertRowBelow() {
|
|
421
|
-
const currentRow = this.currentCell.parentElement;
|
|
422
|
-
const newRow = this.createNewRow(currentRow.cells.length);
|
|
423
|
-
|
|
424
|
-
if (currentRow.nextElementSibling) {
|
|
425
|
-
currentRow.parentElement.insertBefore(newRow, currentRow.nextElementSibling);
|
|
426
|
-
} else {
|
|
427
|
-
currentRow.parentElement.appendChild(newRow);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// Update resize handles if they exist
|
|
431
|
-
if (this.editor.resizeHandles) {
|
|
432
|
-
this.editor.resizeHandles.checkAndUpdateHandles();
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* Insert column left of current cell
|
|
438
|
-
*/
|
|
439
|
-
insertColumnLeft() {
|
|
440
|
-
const cellIndex = Array.from(this.currentCell.parentElement.children).indexOf(this.currentCell);
|
|
441
|
-
const tbody = this.currentTable.querySelector('tbody') || this.currentTable;
|
|
442
|
-
const rows = tbody.querySelectorAll('tr');
|
|
443
|
-
|
|
444
|
-
rows.forEach(row => {
|
|
445
|
-
const newCell = this.createNewCell();
|
|
446
|
-
const targetCell = row.children[cellIndex];
|
|
447
|
-
if (targetCell) {
|
|
448
|
-
row.insertBefore(newCell, targetCell);
|
|
449
|
-
} else {
|
|
450
|
-
row.appendChild(newCell);
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
// Update resize handles if they exist
|
|
455
|
-
if (this.editor.resizeHandles) {
|
|
456
|
-
this.editor.resizeHandles.checkAndUpdateHandles();
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
/**
|
|
461
|
-
* Insert column right of current cell
|
|
462
|
-
*/
|
|
463
|
-
insertColumnRight() {
|
|
464
|
-
const cellIndex = Array.from(this.currentCell.parentElement.children).indexOf(this.currentCell);
|
|
465
|
-
const tbody = this.currentTable.querySelector('tbody') || this.currentTable;
|
|
466
|
-
const rows = tbody.querySelectorAll('tr');
|
|
467
|
-
|
|
468
|
-
rows.forEach(row => {
|
|
469
|
-
const newCell = this.createNewCell();
|
|
470
|
-
const targetCell = row.children[cellIndex + 1];
|
|
471
|
-
if (targetCell) {
|
|
472
|
-
row.insertBefore(newCell, targetCell);
|
|
473
|
-
} else {
|
|
474
|
-
row.appendChild(newCell);
|
|
475
|
-
}
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
// Update resize handles if they exist
|
|
479
|
-
if (this.editor.resizeHandles) {
|
|
480
|
-
this.editor.resizeHandles.checkAndUpdateHandles();
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
/**
|
|
485
|
-
* Delete current row
|
|
486
|
-
*/
|
|
487
|
-
deleteRow() {
|
|
488
|
-
const currentRow = this.currentCell.parentElement;
|
|
489
|
-
const tbody = currentRow.parentElement;
|
|
490
|
-
|
|
491
|
-
// Don't delete if it's the only row
|
|
492
|
-
if (tbody.children.length <= 1) {
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
currentRow.remove();
|
|
497
|
-
|
|
498
|
-
// Update resize handles if they exist
|
|
499
|
-
if (this.editor.resizeHandles) {
|
|
500
|
-
this.editor.resizeHandles.checkAndUpdateHandles();
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
this.hide();
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* Delete current column
|
|
508
|
-
*/
|
|
509
|
-
deleteColumn() {
|
|
510
|
-
const cellIndex = Array.from(this.currentCell.parentElement.children).indexOf(this.currentCell);
|
|
511
|
-
const tbody = this.currentTable.querySelector('tbody') || this.currentTable;
|
|
512
|
-
const rows = tbody.querySelectorAll('tr');
|
|
513
|
-
|
|
514
|
-
// Don't delete if it's the only column
|
|
515
|
-
if (this.currentCell.parentElement.children.length <= 1) {
|
|
516
|
-
console.warn('Cannot delete the only column in table');
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
rows.forEach(row => {
|
|
521
|
-
if (row.children[cellIndex]) {
|
|
522
|
-
row.children[cellIndex].remove();
|
|
523
|
-
}
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
// Update resize handles if they exist
|
|
527
|
-
if (this.editor.resizeHandles) {
|
|
528
|
-
this.editor.resizeHandles.checkAndUpdateHandles();
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
this.hide();
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
/**
|
|
535
|
-
* Show table profile/properties
|
|
536
|
-
*/
|
|
537
|
-
showTableProfile() {
|
|
538
|
-
if (!this.currentTable) return;
|
|
539
|
-
|
|
540
|
-
const rows = this.currentTable.querySelectorAll('tr').length;
|
|
541
|
-
const cols = this.currentTable.querySelector('tr') ?
|
|
542
|
-
this.currentTable.querySelector('tr').querySelectorAll('td, th').length : 0;
|
|
543
|
-
|
|
544
|
-
const tableInfo = {
|
|
545
|
-
rows: rows,
|
|
546
|
-
columns: cols,
|
|
547
|
-
totalCells: rows * cols,
|
|
548
|
-
tableWidth: this.currentTable.offsetWidth,
|
|
549
|
-
tableHeight: this.currentTable.offsetHeight
|
|
550
|
-
};
|
|
551
|
-
|
|
552
|
-
// You can customize this to show a modal or popup with table information
|
|
553
|
-
alert(`Table Profile:\n` +
|
|
554
|
-
`Rows: ${tableInfo.rows}\n` +
|
|
555
|
-
`Columns: ${tableInfo.columns}\n` +
|
|
556
|
-
`Total Cells: ${tableInfo.totalCells}\n` +
|
|
557
|
-
`Width: ${tableInfo.tableWidth}px\n` +
|
|
558
|
-
`Height: ${tableInfo.tableHeight}px`);
|
|
559
|
-
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/**
|
|
563
|
-
* Delete entire table
|
|
564
|
-
*/
|
|
565
|
-
deleteTable() {
|
|
566
|
-
// Hide resize handles before removing table
|
|
567
|
-
if (this.editor.resizeHandles) {
|
|
568
|
-
this.editor.resizeHandles.hideHandles();
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
this.currentTable.remove();
|
|
572
|
-
this.hide();
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
/**
|
|
576
|
-
* Create new row with specified number of cells
|
|
577
|
-
*/
|
|
578
|
-
createNewRow(cellCount) {
|
|
579
|
-
const row = document.createElement('tr');
|
|
580
|
-
for (let i = 0; i < cellCount; i++) {
|
|
581
|
-
row.appendChild(this.createNewCell());
|
|
582
|
-
}
|
|
583
|
-
return row;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
/**
|
|
587
|
-
* Create new cell
|
|
588
|
-
*/
|
|
589
|
-
createNewCell() {
|
|
590
|
-
const cell = document.createElement('td');
|
|
591
|
-
cell.innerHTML = ' ';
|
|
592
|
-
cell.style.minWidth = '50px';
|
|
593
|
-
cell.style.minHeight = '24px';
|
|
594
|
-
cell.style.padding = '4px 8px';
|
|
595
|
-
cell.style.border = '1px solid #ddd';
|
|
596
|
-
cell.style.verticalAlign = 'top';
|
|
597
|
-
cell.contentEditable = 'true';
|
|
598
|
-
return cell;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
/**
|
|
602
|
-
* Destroy module
|
|
603
|
-
*/
|
|
604
|
-
destroy() {
|
|
605
|
-
if (this.tableToolbar && this.tableToolbar.parentNode) {
|
|
606
|
-
this.tableToolbar.parentNode.removeChild(this.tableToolbar);
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
this.clearHideTimeout();
|
|
610
|
-
this.tableToolbar = null;
|
|
611
|
-
this.currentTable = null;
|
|
612
|
-
this.currentCell = null;
|
|
613
|
-
this.isVisible = false;
|
|
614
|
-
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
export default TableToolbar;
|