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