@sequent-org/moodboard 1.0.11 → 1.0.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sequent-org/moodboard",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "type": "module",
5
5
  "description": "Interactive moodboard",
6
6
  "main": "./src/index.js",
package/src/core/index.js CHANGED
@@ -37,6 +37,9 @@ export class CoreMoodBoard {
37
37
  ...options
38
38
  };
39
39
 
40
+ // Флаг состояния объекта
41
+ this.destroyed = false;
42
+
40
43
  this.eventBus = new EventBus();
41
44
  this.state = new StateManager(this.eventBus);
42
45
 
@@ -1623,10 +1626,63 @@ export class CoreMoodBoard {
1623
1626
  }
1624
1627
 
1625
1628
  destroy() {
1626
- this.saveManager.destroy();
1627
- this.keyboard.destroy();
1628
- this.history.destroy();
1629
- this.pixi.destroy();
1630
- this.eventBus.removeAllListeners();
1629
+ // Предотвращаем повторное уничтожение
1630
+ if (this.destroyed) {
1631
+ console.warn('CoreMoodBoard уже был уничтожен');
1632
+ return;
1633
+ }
1634
+
1635
+ // Устанавливаем флаг уничтожения
1636
+ this.destroyed = true;
1637
+
1638
+ // Останавливаем ResizeObserver
1639
+ if (this.resizeObserver) {
1640
+ this.resizeObserver.disconnect();
1641
+ this.resizeObserver = null;
1642
+ }
1643
+
1644
+ // Уничтожаем менеджеры
1645
+ if (this.saveManager) {
1646
+ this.saveManager.destroy();
1647
+ this.saveManager = null;
1648
+ }
1649
+
1650
+ if (this.keyboard) {
1651
+ this.keyboard.destroy();
1652
+ this.keyboard = null;
1653
+ }
1654
+
1655
+ if (this.history) {
1656
+ this.history.destroy();
1657
+ this.history = null;
1658
+ }
1659
+
1660
+ if (this.pixi) {
1661
+ this.pixi.destroy();
1662
+ this.pixi = null;
1663
+ }
1664
+
1665
+ // Очищаем EventBus
1666
+ if (this.eventBus) {
1667
+ this.eventBus.removeAllListeners();
1668
+ this.eventBus = null;
1669
+ }
1670
+
1671
+ // Очищаем глобальную ссылку
1672
+ if (typeof window !== 'undefined' && window.moodboardEventBus === this.eventBus) {
1673
+ window.moodboardEventBus = null;
1674
+ }
1675
+
1676
+ // Очищаем ссылки на менеджеры
1677
+ this.state = null;
1678
+ this.toolManager = null;
1679
+ this.apiClient = null;
1680
+ this.imageUploadService = null;
1681
+ this.fileUploadService = null;
1682
+
1683
+ // Очищаем контейнер
1684
+ this.container = null;
1685
+
1686
+ console.log('CoreMoodBoard успешно уничтожен');
1631
1687
  }
1632
1688
  }
@@ -41,6 +41,9 @@ export class MoodBoard {
41
41
 
42
42
  this.data = data;
43
43
 
44
+ // Флаг состояния объекта
45
+ this.destroyed = false;
46
+
44
47
  // Основные компоненты
45
48
  this.coreMoodboard = null;
46
49
  this.toolbar = null;
@@ -222,6 +225,11 @@ export class MoodBoard {
222
225
  * Изменение темы
223
226
  */
224
227
  setTheme(theme) {
228
+ if (this.destroyed) {
229
+ console.warn('MoodBoard уже уничтожен');
230
+ return;
231
+ }
232
+
225
233
  this.options.theme = theme;
226
234
 
227
235
  // Обновляем тему в менеджерах
@@ -256,6 +264,10 @@ export class MoodBoard {
256
264
  * Создание объекта программно
257
265
  */
258
266
  createObject(type, position, properties = {}) {
267
+ if (this.destroyed) {
268
+ console.warn('MoodBoard уже уничтожен');
269
+ return null;
270
+ }
259
271
  return this.actionHandler ? this.actionHandler.createObject(type, position, properties) : null;
260
272
  }
261
273
 
@@ -263,6 +275,10 @@ export class MoodBoard {
263
275
  * Удаление объекта программно
264
276
  */
265
277
  deleteObject(objectId) {
278
+ if (this.destroyed) {
279
+ console.warn('MoodBoard уже уничтожен');
280
+ return;
281
+ }
266
282
  if (this.actionHandler) {
267
283
  this.actionHandler.deleteObject(objectId);
268
284
  }
@@ -272,6 +288,10 @@ export class MoodBoard {
272
288
  * Очистка доски программно
273
289
  */
274
290
  clearBoard() {
291
+ if (this.destroyed) {
292
+ console.warn('MoodBoard уже уничтожен');
293
+ return 0;
294
+ }
275
295
  return this.actionHandler ? this.actionHandler.clearBoard() : 0;
276
296
  }
277
297
 
@@ -279,6 +299,10 @@ export class MoodBoard {
279
299
  * Экспорт данных программно
280
300
  */
281
301
  exportBoard() {
302
+ if (this.destroyed) {
303
+ console.warn('MoodBoard уже уничтожен');
304
+ return null;
305
+ }
282
306
  return this.actionHandler ? this.actionHandler.exportBoard() : null;
283
307
  }
284
308
 
@@ -322,44 +346,84 @@ export class MoodBoard {
322
346
  }
323
347
  }
324
348
 
349
+ /**
350
+ * Безопасное уничтожение объекта с проверкой наличия метода destroy
351
+ * @param {Object} obj - объект для уничтожения
352
+ * @param {string} name - имя объекта для логирования
353
+ */
354
+ _safeDestroy(obj, name) {
355
+ if (obj) {
356
+ try {
357
+ if (typeof obj.destroy === 'function') {
358
+ obj.destroy();
359
+ } else {
360
+ console.warn(`Объект ${name} не имеет метода destroy()`);
361
+ }
362
+ } catch (error) {
363
+ console.error(`Ошибка при уничтожении ${name}:`, error);
364
+ }
365
+ }
366
+ }
367
+
325
368
  /**
326
369
  * Очистка ресурсов
327
370
  */
328
371
  destroy() {
329
- if (this.toolbar) {
330
- this.toolbar.destroy();
372
+ // Предотвращаем повторное уничтожение
373
+ if (this.destroyed) {
374
+ console.warn('MoodBoard уже был уничтожен');
375
+ return;
331
376
  }
332
377
 
333
- if (this.saveStatus) {
334
- this.saveStatus.destroy();
335
- }
378
+ // Устанавливаем флаг уничтожения
379
+ this.destroyed = true;
336
380
 
337
- if (this.textPropertiesPanel) {
338
- this.textPropertiesPanel.destroy();
339
- }
381
+ // Уничтожаем UI компоненты с безопасными проверками
382
+ this._safeDestroy(this.toolbar, 'toolbar');
383
+ this.toolbar = null;
384
+
385
+ this._safeDestroy(this.saveStatus, 'saveStatus');
386
+ this.saveStatus = null;
387
+
388
+ this._safeDestroy(this.textPropertiesPanel, 'textPropertiesPanel');
389
+ this.textPropertiesPanel = null;
340
390
 
341
- if (this.framePropertiesPanel) {
342
- this.framePropertiesPanel.destroy();
343
- }
391
+ this._safeDestroy(this.framePropertiesPanel, 'framePropertiesPanel');
392
+ this.framePropertiesPanel = null;
344
393
 
345
- if (this.notePropertiesPanel) {
346
- this.notePropertiesPanel.destroy();
347
- }
394
+ this._safeDestroy(this.notePropertiesPanel, 'notePropertiesPanel');
395
+ this.notePropertiesPanel = null;
348
396
 
349
- if (this.alignmentGuides) {
350
- this.alignmentGuides.destroy();
351
- }
397
+ this._safeDestroy(this.alignmentGuides, 'alignmentGuides');
398
+ this.alignmentGuides = null;
352
399
 
353
- if (this.commentPopover) {
354
- this.commentPopover.destroy();
355
- }
400
+ this._safeDestroy(this.commentPopover, 'commentPopover');
401
+ this.commentPopover = null;
356
402
 
357
- if (this.coreMoodboard) {
358
- this.coreMoodboard.destroy();
359
- }
403
+ this._safeDestroy(this.contextMenu, 'contextMenu');
404
+ this.contextMenu = null;
360
405
 
361
- if (this.workspaceManager) {
362
- this.workspaceManager.destroy();
363
- }
406
+ this._safeDestroy(this.zoombar, 'zoombar');
407
+ this.zoombar = null;
408
+
409
+ this._safeDestroy(this.mapbar, 'mapbar');
410
+ this.mapbar = null;
411
+
412
+ // Уничтожаем ядро
413
+ this._safeDestroy(this.coreMoodboard, 'coreMoodboard');
414
+ this.coreMoodboard = null;
415
+
416
+ // Уничтожаем workspace
417
+ this._safeDestroy(this.workspaceManager, 'workspaceManager');
418
+ this.workspaceManager = null;
419
+
420
+ // Очищаем ссылки на менеджеры
421
+ this.dataManager = null;
422
+ this.actionHandler = null;
423
+
424
+ // Очищаем ссылку на контейнер
425
+ this.container = null;
426
+
427
+ console.log('MoodBoard успешно уничтожен');
364
428
  }
365
429
  }
@@ -11,6 +11,9 @@ export class BaseTool {
11
11
  this.cursor = 'default';
12
12
  this.hotkey = null;
13
13
 
14
+ // Флаг состояния объекта
15
+ this.destroyed = false;
16
+
14
17
  // Состояние инструмента
15
18
  this.isPressed = false;
16
19
  this.startPoint = null;
@@ -251,6 +254,11 @@ export class BaseTool {
251
254
  * Очистка ресурсов инструмента
252
255
  */
253
256
  destroy() {
257
+ if (this.destroyed) {
258
+ return;
259
+ }
260
+
261
+ this.destroyed = true;
254
262
  this.deactivate();
255
263
  this.eventBus = null;
256
264
  }
@@ -23,6 +23,9 @@ export class SelectTool extends BaseTool {
23
23
  this.cursor = 'default';
24
24
  this.hotkey = 'v';
25
25
 
26
+ // Флаг состояния объекта
27
+ this.destroyed = false;
28
+
26
29
  // Состояние выделения перенесено в модель
27
30
  this.selection = new SelectionModel();
28
31
  this.isMultiSelect = false;
@@ -501,6 +504,11 @@ export class SelectTool extends BaseTool {
501
504
  * Получить PIXI объект по координатам (для внутреннего использования)
502
505
  */
503
506
  getPixiObjectAt(x, y) {
507
+ // Проверяем, что инструмент не уничтожен
508
+ if (this.destroyed) {
509
+ return null;
510
+ }
511
+
504
512
  if (!this.resizeHandles || !this.resizeHandles.app) return null;
505
513
 
506
514
  const point = new PIXI.Point(x, y);
@@ -2179,5 +2187,39 @@ export class SelectTool extends BaseTool {
2179
2187
  }
2180
2188
  }
2181
2189
 
2190
+ /**
2191
+ * Уничтожение инструмента
2192
+ */
2193
+ destroy() {
2194
+ if (this.destroyed) {
2195
+ return;
2196
+ }
2197
+
2198
+ this.destroyed = true;
2199
+
2200
+ // Очищаем выделение
2201
+ this.clearSelection();
2202
+
2203
+ // Уничтожаем ручки изменения размера
2204
+ if (this.resizeHandles) {
2205
+ this.resizeHandles.destroy();
2206
+ this.resizeHandles = null;
2207
+ }
2208
+
2209
+ // Очищаем контроллеры
2210
+ this.dragController = null;
2211
+ this.resizeController = null;
2212
+ this.rotateController = null;
2213
+ this.groupDragController = null;
2214
+ this.groupResizeController = null;
2215
+ this.groupRotateController = null;
2216
+ this.boxSelectController = null;
2217
+
2218
+ // Очищаем модель выделения
2219
+ this.selection = null;
2220
+
2221
+ // Вызываем destroy родительского класса
2222
+ super.destroy();
2223
+ }
2182
2224
 
2183
2225
  }
@@ -9,6 +9,9 @@ export class ContextMenu {
9
9
  this.lastX = 0;
10
10
  this.lastY = 0;
11
11
  this.currentGridType = 'line';
12
+
13
+ // Флаг состояния объекта
14
+ this.destroyed = false;
12
15
 
13
16
  this.createElement();
14
17
  this.attachEvents();
@@ -335,6 +338,30 @@ export class ContextMenu {
335
338
  // По умолчанию — пусто
336
339
  this.element.innerHTML = '<div style="padding:8px 12px; color:#888;">(пусто)</div>';
337
340
  }
341
+
342
+ /**
343
+ * Уничтожение контекстного меню
344
+ */
345
+ destroy() {
346
+ if (this.destroyed) {
347
+ return;
348
+ }
349
+
350
+ this.destroyed = true;
351
+
352
+ // Скрываем меню
353
+ this.hide();
354
+
355
+ // Удаляем DOM элемент
356
+ if (this.element && this.element.parentNode) {
357
+ this.element.parentNode.removeChild(this.element);
358
+ this.element = null;
359
+ }
360
+
361
+ // Очищаем ссылки
362
+ this.container = null;
363
+ this.eventBus = null;
364
+ }
338
365
  }
339
366
 
340
367