@sequent-org/moodboard 1.0.10 → 1.0.12

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.10",
3
+ "version": "1.0.12",
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;
@@ -196,9 +199,9 @@ export class MoodBoard {
196
199
  }
197
200
 
198
201
  initZoombar() {
199
- // Рисуем панель зума поверх холста (в том же контейнере, что и topbar)
202
+ // Рисуем панель зума в правом нижнем углу (внутри workspace контейнера)
200
203
  this.zoombar = new ZoomPanel(
201
- this.topbarContainer,
204
+ this.workspaceElement,
202
205
  this.coreMoodboard.eventBus
203
206
  );
204
207
  }
@@ -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
 
@@ -326,40 +350,75 @@ export class MoodBoard {
326
350
  * Очистка ресурсов
327
351
  */
328
352
  destroy() {
353
+ // Предотвращаем повторное уничтожение
354
+ if (this.destroyed) {
355
+ console.warn('MoodBoard уже был уничтожен');
356
+ return;
357
+ }
358
+
359
+ // Устанавливаем флаг уничтожения
360
+ this.destroyed = true;
361
+
362
+ // Уничтожаем UI компоненты
329
363
  if (this.toolbar) {
330
364
  this.toolbar.destroy();
365
+ this.toolbar = null;
331
366
  }
332
367
 
333
368
  if (this.saveStatus) {
334
369
  this.saveStatus.destroy();
370
+ this.saveStatus = null;
335
371
  }
336
372
 
337
373
  if (this.textPropertiesPanel) {
338
374
  this.textPropertiesPanel.destroy();
375
+ this.textPropertiesPanel = null;
339
376
  }
340
377
 
341
378
  if (this.framePropertiesPanel) {
342
379
  this.framePropertiesPanel.destroy();
380
+ this.framePropertiesPanel = null;
343
381
  }
344
382
 
345
383
  if (this.notePropertiesPanel) {
346
384
  this.notePropertiesPanel.destroy();
385
+ this.notePropertiesPanel = null;
347
386
  }
348
387
 
349
388
  if (this.alignmentGuides) {
350
389
  this.alignmentGuides.destroy();
390
+ this.alignmentGuides = null;
351
391
  }
352
392
 
353
393
  if (this.commentPopover) {
354
394
  this.commentPopover.destroy();
395
+ this.commentPopover = null;
355
396
  }
356
397
 
398
+ if (this.contextMenu) {
399
+ this.contextMenu.destroy();
400
+ this.contextMenu = null;
401
+ }
402
+
403
+ // Уничтожаем ядро
357
404
  if (this.coreMoodboard) {
358
405
  this.coreMoodboard.destroy();
406
+ this.coreMoodboard = null;
359
407
  }
360
408
 
409
+ // Уничтожаем workspace
361
410
  if (this.workspaceManager) {
362
411
  this.workspaceManager.destroy();
412
+ this.workspaceManager = null;
363
413
  }
414
+
415
+ // Очищаем ссылки на менеджеры
416
+ this.dataManager = null;
417
+ this.actionHandler = null;
418
+
419
+ // Очищаем ссылку на контейнер
420
+ this.container = null;
421
+
422
+ console.log('MoodBoard успешно уничтожен');
364
423
  }
365
424
  }
@@ -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
  }
@@ -50,11 +50,11 @@
50
50
  pointer-events: none;
51
51
  }
52
52
 
53
- /* Right top zoom panel container (reuses topbar container area) */
53
+ /* Bottom-right zoom panel container (positioned left of map panel) */
54
54
  .moodboard-zoombar {
55
55
  position: absolute;
56
- top: 16px;
57
- right: 16px;
56
+ bottom: 16px;
57
+ right: 80px; /* Слева от кнопки карты (36px + 8px gap + 36px button = 80px) */
58
58
  display: inline-flex;
59
59
  align-items: center;
60
60
  gap: 8px;
@@ -103,7 +103,7 @@
103
103
  .moodboard-zoombar__menu {
104
104
  position: absolute;
105
105
  right: 0;
106
- top: 49px;
106
+ bottom: 49px; /* Открывается вверх от панели зума */
107
107
  background: #fff;
108
108
  border: 1px solid #e0e0e0;
109
109
  border-radius: 8px;