@sequent-org/moodboard 1.2.119 → 1.3.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 (122) hide show
  1. package/package.json +11 -1
  2. package/src/assets/icons/rotate-icon.svg +1 -1
  3. package/src/core/HistoryManager.js +16 -16
  4. package/src/core/KeyboardManager.js +48 -539
  5. package/src/core/PixiEngine.js +9 -9
  6. package/src/core/SaveManager.js +56 -31
  7. package/src/core/bootstrap/CoreInitializer.js +65 -0
  8. package/src/core/commands/DeleteObjectCommand.js +8 -0
  9. package/src/core/commands/GroupDeleteCommand.js +75 -0
  10. package/src/core/commands/GroupRotateCommand.js +6 -0
  11. package/src/core/commands/UpdateContentCommand.js +52 -0
  12. package/src/core/commands/UpdateFramePropertiesCommand.js +98 -0
  13. package/src/core/commands/UpdateFrameTypeCommand.js +85 -0
  14. package/src/core/commands/UpdateNoteStyleCommand.js +88 -0
  15. package/src/core/commands/UpdateTextStyleCommand.js +90 -0
  16. package/src/core/commands/index.js +6 -0
  17. package/src/core/events/Events.js +6 -0
  18. package/src/core/flows/ClipboardFlow.js +553 -0
  19. package/src/core/flows/LayerAndViewportFlow.js +283 -0
  20. package/src/core/flows/ObjectLifecycleFlow.js +336 -0
  21. package/src/core/flows/SaveFlow.js +34 -0
  22. package/src/core/flows/TransformFlow.js +277 -0
  23. package/src/core/flows/TransformFlowResizeHelpers.js +83 -0
  24. package/src/core/index.js +41 -1773
  25. package/src/core/keyboard/KeyboardClipboardImagePaste.js +190 -0
  26. package/src/core/keyboard/KeyboardContextGuards.js +35 -0
  27. package/src/core/keyboard/KeyboardEventRouter.js +92 -0
  28. package/src/core/keyboard/KeyboardSelectionActions.js +103 -0
  29. package/src/core/keyboard/KeyboardShortcutMap.js +31 -0
  30. package/src/core/keyboard/KeyboardToolSwitching.js +26 -0
  31. package/src/core/rendering/ObjectRenderer.js +3 -7
  32. package/src/grid/BaseGrid.js +26 -0
  33. package/src/grid/CrossGrid.js +7 -6
  34. package/src/grid/DotGrid.js +89 -33
  35. package/src/grid/DotGridZoomPhases.js +42 -0
  36. package/src/grid/LineGrid.js +22 -21
  37. package/src/moodboard/MoodBoard.js +31 -532
  38. package/src/moodboard/bootstrap/MoodBoardInitializer.js +47 -0
  39. package/src/moodboard/bootstrap/MoodBoardManagersFactory.js +38 -0
  40. package/src/moodboard/bootstrap/MoodBoardUiFactory.js +109 -0
  41. package/src/moodboard/integration/MoodBoardEventBindings.js +65 -0
  42. package/src/moodboard/integration/MoodBoardLoadApi.js +82 -0
  43. package/src/moodboard/integration/MoodBoardScreenshotApi.js +33 -0
  44. package/src/moodboard/integration/MoodBoardScreenshotCanvas.js +98 -0
  45. package/src/moodboard/lifecycle/MoodBoardDestroyer.js +97 -0
  46. package/src/objects/FileObject.js +17 -6
  47. package/src/objects/FrameObject.js +50 -10
  48. package/src/objects/NoteObject.js +5 -4
  49. package/src/services/BoardService.js +42 -2
  50. package/src/services/FrameService.js +83 -42
  51. package/src/services/ResizePolicyService.js +152 -0
  52. package/src/services/SettingsApplier.js +7 -2
  53. package/src/services/ZoomPanController.js +35 -9
  54. package/src/tools/ToolManager.js +30 -537
  55. package/src/tools/board-tools/PanTool.js +5 -11
  56. package/src/tools/manager/ToolActivationController.js +49 -0
  57. package/src/tools/manager/ToolEventRouter.js +396 -0
  58. package/src/tools/manager/ToolManagerGuards.js +33 -0
  59. package/src/tools/manager/ToolManagerLifecycle.js +110 -0
  60. package/src/tools/manager/ToolRegistry.js +33 -0
  61. package/src/tools/object-tools/DrawingTool.js +48 -14
  62. package/src/tools/object-tools/PlacementTool.js +50 -1049
  63. package/src/tools/object-tools/PlacementToolV2.js +88 -0
  64. package/src/tools/object-tools/SelectTool.js +174 -2681
  65. package/src/tools/object-tools/placement/GhostController.js +504 -0
  66. package/src/tools/object-tools/placement/PlacementCoordinateResolver.js +20 -0
  67. package/src/tools/object-tools/placement/PlacementEventsBridge.js +91 -0
  68. package/src/tools/object-tools/placement/PlacementInputRouter.js +267 -0
  69. package/src/tools/object-tools/placement/PlacementPayloadFactory.js +111 -0
  70. package/src/tools/object-tools/placement/PlacementSessionStore.js +18 -0
  71. package/src/tools/object-tools/selection/BoxSelectController.js +0 -5
  72. package/src/tools/object-tools/selection/CloneFlowController.js +71 -0
  73. package/src/tools/object-tools/selection/CoordinateMapper.js +10 -0
  74. package/src/tools/object-tools/selection/CursorController.js +78 -0
  75. package/src/tools/object-tools/selection/FileNameInlineEditorController.js +184 -0
  76. package/src/tools/object-tools/selection/HitTestService.js +102 -0
  77. package/src/tools/object-tools/selection/InlineEditorController.js +24 -0
  78. package/src/tools/object-tools/selection/InlineEditorDomFactory.js +50 -0
  79. package/src/tools/object-tools/selection/InlineEditorListenersRegistry.js +14 -0
  80. package/src/tools/object-tools/selection/InlineEditorPositioningService.js +25 -0
  81. package/src/tools/object-tools/selection/NoteInlineEditorController.js +113 -0
  82. package/src/tools/object-tools/selection/SelectInputRouter.js +267 -0
  83. package/src/tools/object-tools/selection/SelectToolLifecycleController.js +128 -0
  84. package/src/tools/object-tools/selection/SelectToolSetup.js +134 -0
  85. package/src/tools/object-tools/selection/SelectionOverlayService.js +81 -0
  86. package/src/tools/object-tools/selection/SelectionStateController.js +91 -0
  87. package/src/tools/object-tools/selection/TextEditorDomFactory.js +65 -0
  88. package/src/tools/object-tools/selection/TextEditorInteractionController.js +266 -0
  89. package/src/tools/object-tools/selection/TextEditorLifecycleRegistry.js +90 -0
  90. package/src/tools/object-tools/selection/TextEditorPositioningService.js +158 -0
  91. package/src/tools/object-tools/selection/TextEditorSyncService.js +110 -0
  92. package/src/tools/object-tools/selection/TextInlineEditorController.js +457 -0
  93. package/src/tools/object-tools/selection/TransformInteractionController.js +466 -0
  94. package/src/ui/FilePropertiesPanel.js +61 -32
  95. package/src/ui/FramePropertiesPanel.js +176 -101
  96. package/src/ui/HtmlHandlesLayer.js +121 -999
  97. package/src/ui/MapPanel.js +12 -7
  98. package/src/ui/NotePropertiesPanel.js +17 -2
  99. package/src/ui/TextPropertiesPanel.js +124 -738
  100. package/src/ui/Toolbar.js +71 -1180
  101. package/src/ui/Topbar.js +23 -25
  102. package/src/ui/ZoomPanel.js +16 -5
  103. package/src/ui/handles/GroupSelectionHandlesController.js +29 -0
  104. package/src/ui/handles/HandlesDomRenderer.js +278 -0
  105. package/src/ui/handles/HandlesEventBridge.js +102 -0
  106. package/src/ui/handles/HandlesInteractionController.js +772 -0
  107. package/src/ui/handles/HandlesPositioningService.js +206 -0
  108. package/src/ui/handles/SingleSelectionHandlesController.js +22 -0
  109. package/src/ui/styles/toolbar.css +2 -0
  110. package/src/ui/styles/workspace.css +13 -6
  111. package/src/ui/text-properties/TextPropertiesPanelBindings.js +92 -0
  112. package/src/ui/text-properties/TextPropertiesPanelEventBridge.js +77 -0
  113. package/src/ui/text-properties/TextPropertiesPanelMapper.js +173 -0
  114. package/src/ui/text-properties/TextPropertiesPanelRenderer.js +434 -0
  115. package/src/ui/text-properties/TextPropertiesPanelState.js +39 -0
  116. package/src/ui/toolbar/ToolbarActionRouter.js +193 -0
  117. package/src/ui/toolbar/ToolbarDialogsController.js +186 -0
  118. package/src/ui/toolbar/ToolbarPopupsController.js +662 -0
  119. package/src/ui/toolbar/ToolbarRenderer.js +97 -0
  120. package/src/ui/toolbar/ToolbarStateController.js +79 -0
  121. package/src/ui/toolbar/ToolbarTooltipController.js +52 -0
  122. package/src/utils/emojiLoaderNoBundler.js +1 -1
@@ -1,4 +1,5 @@
1
1
  import { Events } from '../core/events/Events.js';
2
+ import { UpdateFrameTypeCommand } from '../core/commands/UpdateFrameTypeCommand.js';
2
3
 
3
4
  /**
4
5
  * Панель свойств фрейма
@@ -17,41 +18,67 @@ export class FramePropertiesPanel {
17
18
  }
18
19
 
19
20
  _attachEvents() {
20
- // Показываем панель при изменении выделения
21
- this.eventBus.on(Events.Tool.SelectionAdd, () => this.updateFromSelection());
22
- this.eventBus.on(Events.Tool.SelectionRemove, () => this.updateFromSelection());
23
- this.eventBus.on(Events.Tool.SelectionClear, () => this.hide());
24
-
25
- // Скрываем панель при удалении объекта
26
- this.eventBus.on(Events.Object.Deleted, (objectId) => {
21
+ this._handlers = {};
22
+ this._handlers.onSelectionAdd = () => this.updateFromSelection();
23
+ this._handlers.onSelectionRemove = () => this.updateFromSelection();
24
+ this._handlers.onSelectionClear = () => this.hide();
25
+ this._handlers.onDeleted = (objectId) => {
27
26
  if (this.currentId && objectId === this.currentId) this.hide();
28
- });
29
-
30
- // Обновляем позицию при любых изменениях (как в TextPropertiesPanel)
31
- this.eventBus.on(Events.Tool.DragStart, () => this.hide());
32
- this.eventBus.on(Events.Tool.DragUpdate, () => this.reposition());
33
- this.eventBus.on(Events.Tool.DragEnd, () => this.updateFromSelection());
34
- this.eventBus.on(Events.Tool.GroupDragUpdate, () => this.reposition());
35
- this.eventBus.on(Events.Tool.GroupDragStart, () => this.hide());
36
- this.eventBus.on(Events.Tool.GroupDragEnd, () => this.updateFromSelection());
37
- this.eventBus.on(Events.Tool.ResizeUpdate, () => this.reposition());
38
- this.eventBus.on(Events.Tool.RotateUpdate, () => this.reposition());
39
-
40
- // Обновляем позицию при зуме/пане
41
- this.eventBus.on(Events.UI.ZoomPercent, () => {
42
- if (this.currentId) this.reposition();
43
- });
44
-
45
- this.eventBus.on(Events.Tool.PanUpdate, () => {
46
- if (this.currentId) this.reposition();
47
- });
48
-
49
- // Скрываем панель при активации других инструментов
50
- this.eventBus.on(Events.Tool.Activated, ({ tool }) => {
51
- if (tool !== 'select') {
52
- this.hide();
27
+ };
28
+ this._handlers.onDragStart = () => this.hide();
29
+ this._handlers.onDragUpdate = () => this._repositionThrottled();
30
+ this._handlers.onDragEnd = () => this.updateFromSelection();
31
+ this._handlers.onGroupDragUpdate = () => this._repositionThrottled();
32
+ this._handlers.onGroupDragStart = () => this.hide();
33
+ this._handlers.onGroupDragEnd = () => this.updateFromSelection();
34
+ this._handlers.onResizeUpdate = () => this._repositionThrottled();
35
+ this._handlers.onRotateUpdate = () => this._repositionThrottled();
36
+ this._handlers.onZoomPercent = () => {
37
+ if (this.currentId) this._repositionThrottled();
38
+ };
39
+ this._handlers.onPanUpdate = () => {
40
+ if (this.currentId) this._repositionThrottled();
41
+ };
42
+ this._handlers.onActivated = ({ tool }) => {
43
+ if (tool !== 'select') this.hide();
44
+ };
45
+ this._handlers.onStateChanged = (data) => {
46
+ const { objectId } = data;
47
+ if (this.currentId && objectId === this.currentId && this.panel && this.panel.style.display !== 'none') {
48
+ this._updateControlsFromObject();
49
+ this._syncTypeFromObject();
53
50
  }
54
- });
51
+ };
52
+ this._handlers.onHistoryChanged = () => {
53
+ if (this.currentId && this.panel && this.panel.style.display !== 'none') {
54
+ this._updateControlsFromObject();
55
+ this._syncTypeFromObject();
56
+ }
57
+ };
58
+ this._handlers.onTransformUpdated = (data) => {
59
+ if (this.currentId && data.objectId === this.currentId && this.panel && this.panel.style.display !== 'none') {
60
+ this._repositionThrottled();
61
+ }
62
+ };
63
+
64
+ this.eventBus.on(Events.Tool.SelectionAdd, this._handlers.onSelectionAdd);
65
+ this.eventBus.on(Events.Tool.SelectionRemove, this._handlers.onSelectionRemove);
66
+ this.eventBus.on(Events.Tool.SelectionClear, this._handlers.onSelectionClear);
67
+ this.eventBus.on(Events.Object.Deleted, this._handlers.onDeleted);
68
+ this.eventBus.on(Events.Tool.DragStart, this._handlers.onDragStart);
69
+ this.eventBus.on(Events.Tool.DragUpdate, this._handlers.onDragUpdate);
70
+ this.eventBus.on(Events.Tool.DragEnd, this._handlers.onDragEnd);
71
+ this.eventBus.on(Events.Tool.GroupDragUpdate, this._handlers.onGroupDragUpdate);
72
+ this.eventBus.on(Events.Tool.GroupDragStart, this._handlers.onGroupDragStart);
73
+ this.eventBus.on(Events.Tool.GroupDragEnd, this._handlers.onGroupDragEnd);
74
+ this.eventBus.on(Events.Tool.ResizeUpdate, this._handlers.onResizeUpdate);
75
+ this.eventBus.on(Events.Tool.RotateUpdate, this._handlers.onRotateUpdate);
76
+ this.eventBus.on(Events.UI.ZoomPercent, this._handlers.onZoomPercent);
77
+ this.eventBus.on(Events.Tool.PanUpdate, this._handlers.onPanUpdate);
78
+ this.eventBus.on(Events.Tool.Activated, this._handlers.onActivated);
79
+ this.eventBus.on(Events.Object.StateChanged, this._handlers.onStateChanged);
80
+ this.eventBus.on(Events.History.Changed, this._handlers.onHistoryChanged);
81
+ this.eventBus.on(Events.Object.TransformUpdated, this._handlers.onTransformUpdated);
55
82
  }
56
83
 
57
84
  updateFromSelection() {
@@ -117,6 +144,7 @@ export class FramePropertiesPanel {
117
144
 
118
145
  this.panel = panel;
119
146
  this.container.appendChild(panel);
147
+ this._boundDocumentClickHandler = this._documentClickHandler.bind(this);
120
148
  }
121
149
 
122
150
  _updateControlsFromObject() {
@@ -124,6 +152,18 @@ export class FramePropertiesPanel {
124
152
  // Здесь будет логика синхронизации контролов с объектом
125
153
  }
126
154
 
155
+ _repositionThrottled() {
156
+ if (this._repositionScheduled) return;
157
+ this._repositionScheduled = true;
158
+ const rafId = requestAnimationFrame(() => {
159
+ this._repositionScheduled = false;
160
+ this._repositionRafId = null;
161
+ if (!this.panel) return;
162
+ this.reposition();
163
+ });
164
+ this._repositionRafId = rafId;
165
+ }
166
+
127
167
  reposition() {
128
168
  if (!this.panel || !this.currentId || this.panel.style.display === 'none') {
129
169
  return;
@@ -200,11 +240,16 @@ export class FramePropertiesPanel {
200
240
  outline: 'none'
201
241
  });
202
242
 
203
- // Обработчик изменения названия
243
+ // Обработчик изменения названия (debounce 200ms)
244
+ this._titleDebounceTimer = null;
204
245
  titleInput.addEventListener('input', () => {
205
- if (this.currentId) {
206
- this._changeFrameTitle(titleInput.value);
207
- }
246
+ if (!this.currentId) return;
247
+ if (this._titleDebounceTimer) clearTimeout(this._titleDebounceTimer);
248
+ const value = titleInput.value;
249
+ this._titleDebounceTimer = setTimeout(() => {
250
+ this._titleDebounceTimer = null;
251
+ this._changeFrameTitle(value);
252
+ }, 200);
208
253
  });
209
254
 
210
255
  // Блокируем Delete/Backspace от всплытия, чтобы не удалялся весь фрейм при фокусе в поле
@@ -434,7 +479,7 @@ export class FramePropertiesPanel {
434
479
 
435
480
  // Добавляем обработчик клика по документу для закрытия палитры
436
481
  setTimeout(() => {
437
- document.addEventListener('click', this._documentClickHandler.bind(this));
482
+ document.addEventListener('click', this._boundDocumentClickHandler);
438
483
  }, 0);
439
484
  }
440
485
 
@@ -442,7 +487,9 @@ export class FramePropertiesPanel {
442
487
  if (this.colorPalette) {
443
488
  this.colorPalette.style.display = 'none';
444
489
  }
445
- document.removeEventListener('click', this._documentClickHandler.bind(this));
490
+ if (this._boundDocumentClickHandler) {
491
+ document.removeEventListener('click', this._boundDocumentClickHandler);
492
+ }
446
493
  }
447
494
 
448
495
  _documentClickHandler(e) {
@@ -527,77 +574,105 @@ export class FramePropertiesPanel {
527
574
  }
528
575
 
529
576
  _applyFrameType(typeValue) {
530
- if (!this.currentId) return;
577
+ if (!this.currentId || !this.core?.history) return;
531
578
 
532
- // 1) Обновляем тип и временно отключаем фиксацию пропорций на время программного ресайза
533
- const willLockAfter = typeValue !== 'custom';
534
- this.eventBus.emit(Events.Object.StateChanged, {
535
- objectId: this.currentId,
536
- updates: { properties: { type: typeValue, lockedAspect: false } }
537
- });
579
+ const objectData = this.core.getObjectData(this.currentId);
580
+ const oldType = (objectData?.properties?.type) || 'custom';
538
581
 
539
- // 2) Для пресетов меняем размеры под аспект, сохраняя центр
540
- if (!willLockAfter) return; // Произвольный: без изменения размеров
582
+ if (oldType === typeValue) return;
541
583
 
542
- // Аспект по типу
543
- const aspectMap = {
544
- 'a4': 210 / 297,
545
- '1x1': 1,
546
- '4x3': 4 / 3,
547
- '16x9': 16 / 9
548
- };
549
- const aspect = aspectMap[typeValue] || 1;
584
+ const willLockAfter = typeValue !== 'custom';
550
585
 
551
- // Текущие позиция и размер
552
- const posData = { objectId: this.currentId, position: null };
553
- const sizeData = { objectId: this.currentId, size: null };
554
- this.eventBus.emit(Events.Tool.GetObjectPosition, posData);
555
- this.eventBus.emit(Events.Tool.GetObjectSize, sizeData);
556
- if (!posData.position || !sizeData.size) return;
557
-
558
- const oldX = posData.position.x;
559
- const oldY = posData.position.y;
560
- const oldW = Math.max(1, sizeData.size.width);
561
- const oldH = Math.max(1, sizeData.size.height);
562
- const cx = oldX + oldW / 2;
563
- const cy = oldY + oldH / 2;
564
-
565
- // Сохраняем визуальный масштаб: подбираем размеры с тем же приблизительным "площадью"
566
- const area = oldW * oldH;
567
- let newW = Math.max(1, Math.round(Math.sqrt(area * aspect)));
568
- let newH = Math.max(1, Math.round(newW / aspect));
569
-
570
- const newX = Math.round(cx - newW / 2);
571
- const newY = Math.round(cy - newH / 2);
572
-
573
- // Применяем через события resize для согласованности с историей/ядром
574
- this.eventBus.emit(Events.Tool.ResizeUpdate, {
575
- object: this.currentId,
576
- size: { width: newW, height: newH },
577
- position: { x: newX, y: newY }
578
- });
579
- this.eventBus.emit(Events.Tool.ResizeEnd, {
580
- object: this.currentId,
581
- oldSize: { width: oldW, height: oldH },
582
- newSize: { width: newW, height: newH },
583
- oldPosition: { x: oldX, y: oldY },
584
- newPosition: { x: newX, y: newY }
585
- });
586
+ if (!willLockAfter) {
587
+ // Произвольный только тип, без изменения размера
588
+ const command = new UpdateFrameTypeCommand(
589
+ this.core,
590
+ this.currentId,
591
+ oldType,
592
+ typeValue,
593
+ null,
594
+ null,
595
+ null,
596
+ null
597
+ );
598
+ command.setEventBus(this.core.eventBus);
599
+ this.core.history.executeCommand(command);
600
+ } else {
601
+ // Пресет тип + resize
602
+ const aspectMap = { 'a4': 210 / 297, '1x1': 1, '4x3': 4 / 3, '16x9': 16 / 9 };
603
+ const aspect = aspectMap[typeValue] || 1;
604
+
605
+ const posData = { objectId: this.currentId, position: null };
606
+ const sizeData = { objectId: this.currentId, size: null };
607
+ this.eventBus.emit(Events.Tool.GetObjectPosition, posData);
608
+ this.eventBus.emit(Events.Tool.GetObjectSize, sizeData);
609
+ if (!posData.position || !sizeData.size) return;
610
+
611
+ const oldX = posData.position.x;
612
+ const oldY = posData.position.y;
613
+ const oldW = Math.max(1, sizeData.size.width);
614
+ const oldH = Math.max(1, sizeData.size.height);
615
+ const cx = oldX + oldW / 2;
616
+ const cy = oldY + oldH / 2;
617
+
618
+ const area = oldW * oldH;
619
+ let newW = Math.max(1, Math.round(Math.sqrt(area * aspect)));
620
+ let newH = Math.max(1, Math.round(newW / aspect));
621
+ const newX = Math.round(cx - newW / 2);
622
+ const newY = Math.round(cy - newH / 2);
623
+
624
+ const command = new UpdateFrameTypeCommand(
625
+ this.core,
626
+ this.currentId,
627
+ oldType,
628
+ typeValue,
629
+ { width: oldW, height: oldH },
630
+ { width: newW, height: newH },
631
+ { x: oldX, y: oldY },
632
+ { x: newX, y: newY }
633
+ );
634
+ command.setEventBus(this.core.eventBus);
635
+ this.core.history.executeCommand(command);
636
+ }
586
637
 
587
- // Обновим UI сразу
588
638
  this._syncTypeFromObject();
589
-
590
- // 3) Включаем обратно фиксацию пропорций (для пресетов)
591
- this.eventBus.emit(Events.Object.StateChanged, {
592
- objectId: this.currentId,
593
- updates: { properties: { lockedAspect: true } }
594
- });
595
639
  }
596
640
 
597
641
  destroy() {
598
- // Удаляем обработчик клика по документу
599
- document.removeEventListener('click', this._documentClickHandler.bind(this));
600
-
642
+ this._hideColorPalette();
643
+ if (this._repositionRafId != null) {
644
+ cancelAnimationFrame(this._repositionRafId);
645
+ this._repositionRafId = null;
646
+ }
647
+ if (this._handlers && this.eventBus?.off) {
648
+ this.eventBus.off(Events.Tool.SelectionAdd, this._handlers.onSelectionAdd);
649
+ this.eventBus.off(Events.Tool.SelectionRemove, this._handlers.onSelectionRemove);
650
+ this.eventBus.off(Events.Tool.SelectionClear, this._handlers.onSelectionClear);
651
+ this.eventBus.off(Events.Object.Deleted, this._handlers.onDeleted);
652
+ this.eventBus.off(Events.Tool.DragStart, this._handlers.onDragStart);
653
+ this.eventBus.off(Events.Tool.DragUpdate, this._handlers.onDragUpdate);
654
+ this.eventBus.off(Events.Tool.DragEnd, this._handlers.onDragEnd);
655
+ this.eventBus.off(Events.Tool.GroupDragUpdate, this._handlers.onGroupDragUpdate);
656
+ this.eventBus.off(Events.Tool.GroupDragStart, this._handlers.onGroupDragStart);
657
+ this.eventBus.off(Events.Tool.GroupDragEnd, this._handlers.onGroupDragEnd);
658
+ this.eventBus.off(Events.Tool.ResizeUpdate, this._handlers.onResizeUpdate);
659
+ this.eventBus.off(Events.Tool.RotateUpdate, this._handlers.onRotateUpdate);
660
+ this.eventBus.off(Events.UI.ZoomPercent, this._handlers.onZoomPercent);
661
+ this.eventBus.off(Events.Tool.PanUpdate, this._handlers.onPanUpdate);
662
+ this.eventBus.off(Events.Tool.Activated, this._handlers.onActivated);
663
+ this.eventBus.off(Events.Object.StateChanged, this._handlers.onStateChanged);
664
+ this.eventBus.off(Events.History.Changed, this._handlers.onHistoryChanged);
665
+ this.eventBus.off(Events.Object.TransformUpdated, this._handlers.onTransformUpdated);
666
+ this._handlers = null;
667
+ }
668
+ if (this._boundDocumentClickHandler) {
669
+ document.removeEventListener('click', this._boundDocumentClickHandler);
670
+ this._boundDocumentClickHandler = null;
671
+ }
672
+ if (this._titleDebounceTimer) {
673
+ clearTimeout(this._titleDebounceTimer);
674
+ this._titleDebounceTimer = null;
675
+ }
601
676
  if (this.panel && this.panel.parentNode) {
602
677
  this.panel.parentNode.removeChild(this.panel);
603
678
  }