@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 +1 -1
- package/src/core/index.js +61 -5
- package/src/moodboard/MoodBoard.js +90 -26
- package/src/tools/BaseTool.js +8 -0
- package/src/tools/object-tools/SelectTool.js +42 -0
- package/src/ui/ContextMenu.js +27 -0
package/package.json
CHANGED
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
|
-
|
|
1627
|
-
this.
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
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
|
-
|
|
330
|
-
|
|
372
|
+
// Предотвращаем повторное уничтожение
|
|
373
|
+
if (this.destroyed) {
|
|
374
|
+
console.warn('MoodBoard уже был уничтожен');
|
|
375
|
+
return;
|
|
331
376
|
}
|
|
332
377
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
378
|
+
// Устанавливаем флаг уничтожения
|
|
379
|
+
this.destroyed = true;
|
|
336
380
|
|
|
337
|
-
|
|
338
|
-
|
|
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
|
-
|
|
342
|
-
|
|
343
|
-
}
|
|
391
|
+
this._safeDestroy(this.framePropertiesPanel, 'framePropertiesPanel');
|
|
392
|
+
this.framePropertiesPanel = null;
|
|
344
393
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
394
|
+
this._safeDestroy(this.notePropertiesPanel, 'notePropertiesPanel');
|
|
395
|
+
this.notePropertiesPanel = null;
|
|
348
396
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
397
|
+
this._safeDestroy(this.alignmentGuides, 'alignmentGuides');
|
|
398
|
+
this.alignmentGuides = null;
|
|
352
399
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
400
|
+
this._safeDestroy(this.commentPopover, 'commentPopover');
|
|
401
|
+
this.commentPopover = null;
|
|
356
402
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
403
|
+
this._safeDestroy(this.contextMenu, 'contextMenu');
|
|
404
|
+
this.contextMenu = null;
|
|
360
405
|
|
|
361
|
-
|
|
362
|
-
|
|
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
|
}
|
package/src/tools/BaseTool.js
CHANGED
|
@@ -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
|
}
|
package/src/ui/ContextMenu.js
CHANGED
|
@@ -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
|
|