@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,701 @@
1
+ import Module from '../core/module.js';
2
+
3
+ /**
4
+ * Resize Handles Module - Adds resize functionality to images, videos, and tables
5
+ * Creates 4 corner handles for dragging to resize elements
6
+ */
7
+ class ResizeHandles extends Module {
8
+ static DEFAULTS = {
9
+ minWidth: 50,
10
+ minHeight: 50,
11
+ maxWidth: 800,
12
+ maxHeight: 600,
13
+ maintainAspectRatio: true, // For images and videos
14
+ snapToGrid: false,
15
+ gridSize: 10
16
+ };
17
+
18
+ constructor(editor, options = {}) {
19
+ super(editor, options);
20
+ this.activeElement = null;
21
+ this.handles = [];
22
+ this.isResizing = false;
23
+ this.startX = 0;
24
+ this.startY = 0;
25
+ this.startWidth = 0;
26
+ this.startHeight = 0;
27
+ this.currentHandle = null;
28
+ this.aspectRatio = 1;
29
+
30
+ this.init();
31
+ }
32
+
33
+ init() {
34
+ this.createHandles();
35
+ this.setupEventListeners();
36
+ }
37
+
38
+ /**
39
+ * Create resize handles container
40
+ */
41
+ createHandles() {
42
+ this.handlesContainer = document.createElement('div');
43
+ this.handlesContainer.className = 'resize-handles-container';
44
+ this.handlesContainer.style.position = 'absolute';
45
+ this.handlesContainer.style.pointerEvents = 'none';
46
+ this.handlesContainer.style.zIndex = '997'; // Lower than all toolbar elements
47
+ this.handlesContainer.style.display = 'none';
48
+
49
+ // Create 4 corner handles
50
+ const handlePositions = [
51
+ { name: 'nw', cursor: 'nw-resize', position: { top: -4, left: -4 } },
52
+ { name: 'ne', cursor: 'ne-resize', position: { top: -4, right: -4 } },
53
+ { name: 'sw', cursor: 'sw-resize', position: { bottom: -4, left: -4 } },
54
+ { name: 'se', cursor: 'se-resize', position: { bottom: -4, right: -4 } }
55
+ ];
56
+
57
+ handlePositions.forEach(config => {
58
+ const handle = this.createHandle(config);
59
+ this.handles.push(handle);
60
+ this.handlesContainer.appendChild(handle);
61
+ });
62
+
63
+ // Add to editor wrapper but ensure it's behind toolbars
64
+ this.editor.wrapper.appendChild(this.handlesContainer);
65
+ }
66
+
67
+ /**
68
+ * Create individual resize handle
69
+ */
70
+ createHandle(config) {
71
+ const handle = document.createElement('div');
72
+ handle.className = `resize-handle resize-handle-${config.name}`;
73
+ handle.style.position = 'absolute';
74
+ handle.style.width = '8px';
75
+ handle.style.height = '8px';
76
+ handle.style.backgroundColor = '#3b82f6';
77
+ handle.style.border = '1px solid #fff';
78
+ handle.style.borderRadius = '50%';
79
+ handle.style.cursor = config.cursor;
80
+ handle.style.pointerEvents = 'auto';
81
+ handle.style.boxShadow = '0 2px 4px rgba(0,0,0,0.2)';
82
+ handle.style.zIndex = '999'; // Lower than toolbars
83
+ handle.dataset.handle = config.name;
84
+
85
+ // Position handle
86
+ Object.entries(config.position).forEach(([key, value]) => {
87
+ handle.style[key] = value + 'px';
88
+ });
89
+
90
+ // Add event listeners
91
+ handle.addEventListener('mousedown', (e) => this.handleMouseDown(e, config.name));
92
+
93
+ return handle;
94
+ }
95
+
96
+ /**
97
+ * Setup event listeners
98
+ */
99
+ setupEventListeners() {
100
+ // Keep references so listeners can be removed in destroy() (the document/
101
+ // window handlers would otherwise leak across editor create/destroy cycles).
102
+ this._onEditorClick = (e) => this.handleElementClick(e);
103
+ this._onDocClick = (e) => {
104
+ if (!this.isClickOnResizableElement(e) && !this.isClickOnHandle(e)) {
105
+ this.hideHandles();
106
+ }
107
+ };
108
+ this._onDocMousemove = (e) => this.handleMouseMove(e);
109
+ this._onDocMouseup = (e) => this.handleMouseUp(e);
110
+ this._onWindowScroll = () => {
111
+ if (this.activeElement) this.updateHandlePosition();
112
+ };
113
+ this._onEditorScroll = () => {
114
+ if (this.activeElement) this.updateHandlePosition();
115
+ };
116
+
117
+ this.editor.editor.addEventListener('click', this._onEditorClick);
118
+ document.addEventListener('click', this._onDocClick);
119
+ document.addEventListener('mousemove', this._onDocMousemove);
120
+ document.addEventListener('mouseup', this._onDocMouseup);
121
+ window.addEventListener('scroll', this._onWindowScroll);
122
+ this.editor.editor.addEventListener('scroll', this._onEditorScroll);
123
+
124
+ // Listen for DOM changes to update handles
125
+ this.setupMutationObserver();
126
+ }
127
+
128
+ /**
129
+ * Handle click on resizable elements
130
+ */
131
+ handleElementClick(e) {
132
+ const target = e.target;
133
+
134
+ // Debug logging
135
+
136
+
137
+ // Find the actual resizable element
138
+ let resizableElement = this.findResizableElement(target);
139
+
140
+ if (resizableElement) {
141
+ e.preventDefault();
142
+ e.stopPropagation();
143
+ this.showHandles(resizableElement);
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Find the actual resizable element from a clicked target
149
+ */
150
+ findResizableElement(target) {
151
+ // If target is already resizable, return it
152
+ if (this.isResizableElement(target)) {
153
+ return target;
154
+ }
155
+
156
+ // If target is inside a table (td, th, tr, tbody), find the table
157
+ if (target.tagName === 'TD' || target.tagName === 'TH' ||
158
+ target.tagName === 'TR' || target.tagName === 'TBODY') {
159
+ let parent = target.parentElement;
160
+ while (parent && parent.tagName !== 'TABLE') {
161
+ parent = parent.parentElement;
162
+ }
163
+ if (parent && this.isResizableElement(parent)) {
164
+ return parent;
165
+ }
166
+ }
167
+
168
+ // Check if any parent is resizable
169
+ let parent = target.parentElement;
170
+ while (parent && parent !== this.editor.wrapper) {
171
+ if (this.isResizableElement(parent)) {
172
+ return parent;
173
+ }
174
+ parent = parent.parentElement;
175
+ }
176
+
177
+ return null;
178
+ }
179
+
180
+ /**
181
+ * Check if element is resizable
182
+ */
183
+ isResizableElement(element) {
184
+ // Debug logging
185
+
186
+
187
+ const isImage = element.classList.contains('inserted-image');
188
+ const isVideo = element.classList.contains('inserted-video');
189
+ const isTable = element.classList.contains('rich-editor-table');
190
+
191
+
192
+
193
+ return isImage || isVideo || isTable;
194
+ }
195
+
196
+ /**
197
+ * Check if click is on resizable element
198
+ */
199
+ isClickOnResizableElement(e) {
200
+ return this.isResizableElement(e.target);
201
+ }
202
+
203
+ /**
204
+ * Check if click is on resize handle
205
+ */
206
+ isClickOnHandle(e) {
207
+ return e.target.classList.contains('resize-handle');
208
+ }
209
+
210
+ /**
211
+ * Show resize handles for element
212
+ */
213
+ showHandles(element) {
214
+
215
+
216
+ this.activeElement = element;
217
+ this.updateHandlePosition();
218
+ this.handlesContainer.style.display = 'block';
219
+
220
+ // Store aspect ratio for images and videos
221
+ if (element.classList.contains('inserted-image') || element.classList.contains('inserted-video')) {
222
+ this.aspectRatio = element.offsetWidth / element.offsetHeight;
223
+ }
224
+
225
+ // For tables, ensure they have proper positioning and setup size monitoring
226
+ if (element.classList.contains('rich-editor-table')) {
227
+ element.style.position = 'relative';
228
+ element.style.display = 'table';
229
+
230
+ // Store initial dimensions for comparison
231
+ this.lastTableWidth = element.offsetWidth;
232
+ this.lastTableHeight = element.offsetHeight;
233
+
234
+ // Setup periodic size checking for tables
235
+ this.setupTableSizeMonitoring(element);
236
+ }
237
+
238
+
239
+ }
240
+
241
+ /**
242
+ * Hide resize handles
243
+ */
244
+ hideHandles() {
245
+ // Clear table size monitoring
246
+ if (this.tableSizeInterval) {
247
+ clearInterval(this.tableSizeInterval);
248
+ this.tableSizeInterval = null;
249
+ }
250
+
251
+ this.activeElement = null;
252
+ this.handlesContainer.style.display = 'none';
253
+ }
254
+
255
+ /**
256
+ * Update handle position based on active element
257
+ */
258
+ updateHandlePosition() {
259
+ if (!this.activeElement) return;
260
+
261
+ // Check if element still exists in DOM
262
+ if (!document.body.contains(this.activeElement)) {
263
+ this.hideHandles();
264
+ return;
265
+ }
266
+
267
+ const elementRect = this.activeElement.getBoundingClientRect();
268
+ const editorRect = this.editor.wrapper.getBoundingClientRect();
269
+ const scrollTop = this.editor.wrapper.scrollTop || 0;
270
+ const scrollLeft = this.editor.wrapper.scrollLeft || 0;
271
+
272
+ // Position handles container
273
+ const top = elementRect.top - editorRect.top + scrollTop;
274
+ const left = elementRect.left - editorRect.left + scrollLeft;
275
+ const width = elementRect.width;
276
+ const height = elementRect.height;
277
+ const bottom = top + height;
278
+ this.handlesContainer.style.top = top + 'px';
279
+ this.handlesContainer.style.left = left + 'px';
280
+ this.handlesContainer.style.width = width + 'px';
281
+ this.handlesContainer.style.height = height + 'px';
282
+
283
+ if(bottom < 0){
284
+ this.hideHandles();
285
+ return;
286
+ }
287
+ if(top > editorRect.height){
288
+ this.hideHandles();
289
+ return;
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Handle mouse down on resize handle
295
+ */
296
+ handleMouseDown(e, handleName) {
297
+ e.preventDefault();
298
+ e.stopPropagation();
299
+
300
+ this.isResizing = true;
301
+ this.currentHandle = handleName;
302
+ this.startX = e.clientX;
303
+ this.startY = e.clientY;
304
+ this.startWidth = this.activeElement.offsetWidth;
305
+ this.startHeight = this.activeElement.offsetHeight;
306
+
307
+ // Store initial position
308
+ const elementRect = this.activeElement.getBoundingClientRect();
309
+ this.startLeft = elementRect.left;
310
+ this.startTop = elementRect.top;
311
+
312
+ // Add resizing class for styling
313
+ this.activeElement.classList.add('resizing');
314
+ document.body.style.cursor = e.target.style.cursor;
315
+ document.body.style.userSelect = 'none';
316
+ }
317
+
318
+ /**
319
+ * Handle mouse move during resize
320
+ */
321
+ handleMouseMove(e) {
322
+ if (!this.isResizing || !this.activeElement) return;
323
+
324
+ const deltaX = e.clientX - this.startX;
325
+ const deltaY = e.clientY - this.startY;
326
+
327
+ let newWidth = this.startWidth;
328
+ let newHeight = this.startHeight;
329
+
330
+ // Calculate new dimensions based on handle position
331
+ switch (this.currentHandle) {
332
+ case 'nw':
333
+ newWidth = this.startWidth - deltaX;
334
+ newHeight = this.startHeight - deltaY;
335
+ break;
336
+ case 'ne':
337
+ newWidth = this.startWidth + deltaX;
338
+ newHeight = this.startHeight - deltaY;
339
+ break;
340
+ case 'sw':
341
+ newWidth = this.startWidth - deltaX;
342
+ newHeight = this.startHeight + deltaY;
343
+ break;
344
+ case 'se':
345
+ newWidth = this.startWidth + deltaX;
346
+ newHeight = this.startHeight + deltaY;
347
+ break;
348
+ }
349
+
350
+ // Never let an element grow past the editor's content width (its inner
351
+ // width minus padding) — otherwise tables/images overflow the editor.
352
+ let maxW = this.options.maxWidth;
353
+ const area = this.editor && this.editor.editor;
354
+ if (area) {
355
+ const cs = getComputedStyle(area);
356
+ const avail = area.clientWidth
357
+ - (parseFloat(cs.paddingLeft) || 0)
358
+ - (parseFloat(cs.paddingRight) || 0);
359
+ if (avail > 0) maxW = Math.min(maxW, avail);
360
+ }
361
+
362
+ // Apply constraints
363
+ newWidth = Math.max(this.options.minWidth, Math.min(maxW, newWidth));
364
+ newHeight = Math.max(this.options.minHeight, Math.min(this.options.maxHeight, newHeight));
365
+
366
+ // Maintain aspect ratio for images and videos
367
+ if ((this.activeElement.classList.contains('inserted-image') ||
368
+ this.activeElement.classList.contains('inserted-video')) &&
369
+ this.options.maintainAspectRatio) {
370
+
371
+ const ratioByWidth = newWidth / this.aspectRatio;
372
+ const ratioByHeight = newHeight * this.aspectRatio;
373
+
374
+ if (Math.abs(newWidth - ratioByHeight) < Math.abs(newHeight - ratioByWidth)) {
375
+ newWidth = ratioByHeight;
376
+ } else {
377
+ newHeight = ratioByWidth;
378
+ }
379
+ // Aspect-ratio math can push width back over the limit — re-clamp.
380
+ if (newWidth > maxW) {
381
+ newWidth = maxW;
382
+ newHeight = newWidth / this.aspectRatio;
383
+ }
384
+ }
385
+
386
+ // Snap to grid if enabled
387
+ if (this.options.snapToGrid) {
388
+ newWidth = Math.round(newWidth / this.options.gridSize) * this.options.gridSize;
389
+ newHeight = Math.round(newHeight / this.options.gridSize) * this.options.gridSize;
390
+ }
391
+
392
+ // Apply new dimensions
393
+ this.applyDimensions(newWidth, newHeight);
394
+ this.updateHandlePosition();
395
+
396
+ // Emit resize event
397
+ this.emit('element-resize', {
398
+ element: this.activeElement,
399
+ width: newWidth,
400
+ height: newHeight,
401
+ handle: this.currentHandle
402
+ });
403
+ }
404
+
405
+ /**
406
+ * Handle mouse up - end resize
407
+ */
408
+ handleMouseUp(e) {
409
+ if (!this.isResizing) return;
410
+
411
+ this.isResizing = false;
412
+ this.currentHandle = null;
413
+
414
+ // Remove resizing class
415
+ if (this.activeElement) {
416
+ this.activeElement.classList.remove('resizing');
417
+ }
418
+
419
+ // Reset cursor
420
+ document.body.style.cursor = '';
421
+ document.body.style.userSelect = '';
422
+
423
+ // Emit resize complete event
424
+ this.emit('element-resize-complete', {
425
+ element: this.activeElement,
426
+ width: this.activeElement.offsetWidth,
427
+ height: this.activeElement.offsetHeight
428
+ });
429
+ }
430
+
431
+ /**
432
+ * Apply dimensions to element
433
+ */
434
+ applyDimensions(width, height) {
435
+ if (!this.activeElement) return;
436
+
437
+ if (this.activeElement.classList.contains('rich-editor-table')) {
438
+ // For tables, set both width and height
439
+ this.activeElement.style.width = width + 'px';
440
+ this.activeElement.style.minWidth = width + 'px';
441
+ this.activeElement.style.height = height + 'px';
442
+ this.activeElement.style.minHeight = height + 'px';
443
+
444
+ // Calculate cell dimensions
445
+ const rows = this.activeElement.querySelectorAll('tr');
446
+ const cols = rows.length > 0 ? rows[0].querySelectorAll('td, th').length : 0;
447
+
448
+ if (rows.length > 0 && cols > 0) {
449
+ const cellWidth = Math.floor(width / cols);
450
+ const cellHeight = Math.floor(height / rows.length);
451
+
452
+ // Apply dimensions to all cells
453
+ const cells = this.activeElement.querySelectorAll('td, th');
454
+ cells.forEach(cell => {
455
+ cell.style.minWidth = cellWidth + 'px';
456
+ cell.style.minHeight = cellHeight + 'px';
457
+ cell.style.height = cellHeight + 'px';
458
+ });
459
+ }
460
+ } else {
461
+ // For images and videos (including iframes)
462
+ this.activeElement.style.width = width + 'px';
463
+ this.activeElement.style.height = height + 'px';
464
+
465
+ // If it's an iframe, update its attributes too
466
+ if (this.activeElement.tagName === 'IFRAME') {
467
+ this.activeElement.width = width;
468
+ this.activeElement.height = height;
469
+ }
470
+ }
471
+ }
472
+
473
+ /**
474
+ * Get current active element
475
+ */
476
+ getActiveElement() {
477
+ return this.activeElement;
478
+ }
479
+
480
+ /**
481
+ * Set active element programmatically
482
+ */
483
+ setActiveElement(element) {
484
+ if (this.isResizableElement(element)) {
485
+ this.showHandles(element);
486
+ }
487
+ }
488
+
489
+ /**
490
+ * Check and update handles if active element has changed
491
+ */
492
+ checkAndUpdateHandles() {
493
+ if (this.activeElement) {
494
+ // Check if element still exists and is still resizable
495
+ if (!document.body.contains(this.activeElement) || !this.isResizableElement(this.activeElement)) {
496
+ this.hideHandles();
497
+ return;
498
+ }
499
+
500
+ // Update position if element still exists
501
+ this.updateHandlePosition();
502
+
503
+ // For tables, also check if size has changed due to content
504
+ if (this.activeElement.classList.contains('rich-editor-table')) {
505
+ this.checkTableSizeChange();
506
+ }
507
+ }
508
+ }
509
+
510
+ /**
511
+ * Force refresh handles for current active element
512
+ */
513
+ refreshHandles() {
514
+ if (this.activeElement && document.body.contains(this.activeElement)) {
515
+ this.updateHandlePosition();
516
+ }
517
+ }
518
+
519
+ /**
520
+ * Setup periodic monitoring for table size changes
521
+ */
522
+ setupTableSizeMonitoring(tableElement) {
523
+ // Clear any existing interval
524
+ if (this.tableSizeInterval) {
525
+ clearInterval(this.tableSizeInterval);
526
+ }
527
+
528
+ // Check table size every 100ms
529
+ this.tableSizeInterval = setInterval(() => {
530
+ if (this.activeElement && this.activeElement.classList.contains('rich-editor-table')) {
531
+ this.checkTableSizeChange();
532
+ } else {
533
+ // Clear interval if no longer monitoring a table
534
+ clearInterval(this.tableSizeInterval);
535
+ this.tableSizeInterval = null;
536
+ }
537
+ }, 100);
538
+ }
539
+
540
+ /**
541
+ * Check if table size has changed and update handles accordingly
542
+ */
543
+ checkTableSizeChange() {
544
+ if (!this.activeElement || !this.activeElement.classList.contains('rich-editor-table')) {
545
+ return;
546
+ }
547
+
548
+ const currentWidth = this.activeElement.offsetWidth;
549
+ const currentHeight = this.activeElement.offsetHeight;
550
+
551
+ // Check if dimensions have changed significantly (more than 1px to avoid floating point issues)
552
+ if (Math.abs(currentWidth - this.lastTableWidth) > 1 ||
553
+ Math.abs(currentHeight - this.lastTableHeight) > 1) {
554
+
555
+ // Update stored dimensions
556
+ this.lastTableWidth = currentWidth;
557
+ this.lastTableHeight = currentHeight;
558
+
559
+ // Update handle positions
560
+ this.updateHandlePosition();
561
+
562
+ // Emit size change event
563
+ this.emit('table-size-changed', {
564
+ element: this.activeElement,
565
+ width: currentWidth,
566
+ height: currentHeight,
567
+ previousWidth: this.lastTableWidth,
568
+ previousHeight: this.lastTableHeight
569
+ });
570
+ }
571
+ }
572
+
573
+ /**
574
+ * Setup mutation observer to watch for DOM changes
575
+ */
576
+ setupMutationObserver() {
577
+ if (typeof MutationObserver !== 'undefined') {
578
+ this.mutationObserver = new MutationObserver((mutations) => {
579
+ let shouldUpdate = false;
580
+
581
+ mutations.forEach((mutation) => {
582
+ // Check if active element was removed or modified
583
+ if (this.activeElement) {
584
+ if (mutation.type === 'childList') {
585
+ // Check if active element was removed
586
+ if (mutation.removedNodes) {
587
+ for (let node of mutation.removedNodes) {
588
+ if (node === this.activeElement || node.contains(this.activeElement)) {
589
+ shouldUpdate = true;
590
+ break;
591
+ }
592
+ }
593
+ }
594
+
595
+ // Check if active element was modified
596
+ if (mutation.target === this.activeElement ||
597
+ (mutation.target.nodeType === Node.ELEMENT_NODE &&
598
+ this.activeElement.contains(mutation.target))) {
599
+ shouldUpdate = true;
600
+ }
601
+ }
602
+
603
+ // Check for text content changes that might affect table size
604
+ if (mutation.type === 'characterData' && this.activeElement.classList.contains('rich-editor-table')) {
605
+ // Check if the text change is within the active table
606
+ let target = mutation.target;
607
+ while (target && target !== this.activeElement) {
608
+ target = target.parentNode;
609
+ }
610
+ if (target === this.activeElement) {
611
+ shouldUpdate = true;
612
+ }
613
+ }
614
+
615
+ // Check for attribute changes that might affect size
616
+ if (mutation.type === 'attributes' && this.activeElement.classList.contains('rich-editor-table')) {
617
+ const attributeName = mutation.attributeName;
618
+ // Monitor changes to style attributes that affect size
619
+ if (attributeName === 'style' || attributeName === 'class') {
620
+ shouldUpdate = true;
621
+ }
622
+ }
623
+ }
624
+ });
625
+
626
+ if (shouldUpdate) {
627
+ // Use setTimeout to ensure DOM is fully updated
628
+ setTimeout(() => {
629
+ this.checkAndUpdateHandles();
630
+ }, 0);
631
+ }
632
+ });
633
+
634
+ // Start observing the editor content with more comprehensive monitoring
635
+ this.mutationObserver.observe(this.editor.editor, {
636
+ childList: true,
637
+ subtree: true,
638
+ attributes: true,
639
+ attributeFilter: ['style', 'class'],
640
+ characterData: true, // Monitor text content changes
641
+ characterDataOldValue: true
642
+ });
643
+ }
644
+ }
645
+
646
+ /**
647
+ * Enable/disable aspect ratio maintenance
648
+ */
649
+ setMaintainAspectRatio(maintain) {
650
+ this.options.maintainAspectRatio = maintain;
651
+ }
652
+
653
+ /**
654
+ * Set resize constraints
655
+ */
656
+ setConstraints(minWidth, minHeight, maxWidth, maxHeight) {
657
+ this.options.minWidth = minWidth || this.options.minWidth;
658
+ this.options.minHeight = minHeight || this.options.minHeight;
659
+ this.options.maxWidth = maxWidth || this.options.maxWidth;
660
+ this.options.maxHeight = maxHeight || this.options.maxHeight;
661
+ }
662
+
663
+ /**
664
+ * Destroy module
665
+ */
666
+ destroy() {
667
+ this.hideHandles();
668
+
669
+ // Remove global event listeners
670
+ if (this._onDocClick) {
671
+ this.editor.editor.removeEventListener('click', this._onEditorClick);
672
+ document.removeEventListener('click', this._onDocClick);
673
+ document.removeEventListener('mousemove', this._onDocMousemove);
674
+ document.removeEventListener('mouseup', this._onDocMouseup);
675
+ window.removeEventListener('scroll', this._onWindowScroll);
676
+ this.editor.editor.removeEventListener('scroll', this._onEditorScroll);
677
+ this._onEditorClick = this._onDocClick = this._onDocMousemove = null;
678
+ this._onDocMouseup = this._onWindowScroll = this._onEditorScroll = null;
679
+ }
680
+
681
+ if (this.handlesContainer) {
682
+ this.handlesContainer.remove();
683
+ }
684
+
685
+ // Clear table size monitoring
686
+ if (this.tableSizeInterval) {
687
+ clearInterval(this.tableSizeInterval);
688
+ this.tableSizeInterval = null;
689
+ }
690
+
691
+ // Disconnect mutation observer
692
+ if (this.mutationObserver) {
693
+ this.mutationObserver.disconnect();
694
+ this.mutationObserver = null;
695
+ }
696
+
697
+ super.destroy();
698
+ }
699
+ }
700
+
701
+ export default ResizeHandles;