@sequent-org/moodboard 1.2.14 → 1.2.16

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.2.14",
3
+ "version": "1.2.16",
4
4
  "type": "module",
5
5
  "description": "Interactive moodboard",
6
6
  "main": "./src/index.js",
@@ -170,12 +170,51 @@ export class PixiEngine {
170
170
  removeObject(objectId) {
171
171
  const pixiObject = this.objects.get(objectId);
172
172
  if (pixiObject) {
173
+ console.log('🗑️ PixiEngine: удаляем объект из сцены:', objectId);
174
+
175
+ // Удаляем из родительского контейнера
173
176
  if (this.worldLayer) {
174
177
  this.worldLayer.removeChild(pixiObject);
175
178
  } else {
176
179
  this.app.stage.removeChild(pixiObject);
177
180
  }
181
+
182
+ // ИСПРАВЛЕНИЕ: Полная очистка для изображений/эмоджи
183
+ if (pixiObject instanceof PIXI.Sprite) {
184
+ console.log('🗑️ PixiEngine: очищаем ресурсы изображения/эмоджи');
185
+
186
+ // Очищаем текстуру (особенно важно для data URL)
187
+ if (pixiObject.texture && pixiObject.texture !== PIXI.Texture.WHITE) {
188
+ // Не уничтожаем базовые текстуры PIXI
189
+ const textureSource = pixiObject.texture.baseTexture?.resource?.src;
190
+ if (textureSource && (textureSource.startsWith('data:') || textureSource.includes('emodji'))) {
191
+ pixiObject.texture.destroy(false); // Уничтожаем только созданную текстуру
192
+ }
193
+ }
194
+
195
+ // Очищаем все события
196
+ pixiObject.removeAllListeners();
197
+
198
+ // Принудительно уничтожаем спрайт
199
+ pixiObject.destroy({ children: true, texture: false, baseTexture: false });
200
+ } else {
201
+ // Для других типов объектов - стандартная очистка
202
+ if (pixiObject.destroy) {
203
+ pixiObject.destroy({ children: true });
204
+ }
205
+ }
206
+
207
+ // Удаляем из карты объектов
178
208
  this.objects.delete(objectId);
209
+
210
+ // ПРИНУДИТЕЛЬНЫЙ РЕНДЕР после удаления
211
+ if (this.app && this.app.renderer) {
212
+ this.app.renderer.render(this.app.stage);
213
+ }
214
+
215
+ console.log(`✅ PixiEngine: объект ${objectId} полностью удален и рендер обновлен`);
216
+ } else {
217
+ console.warn(`⚠️ PixiEngine: объект ${objectId} не найден для удаления`);
179
218
  }
180
219
  }
181
220
 
@@ -54,10 +54,14 @@ export class DeleteObjectCommand extends BaseCommand {
54
54
  }
55
55
 
56
56
  async execute() {
57
+ console.log('🗑️ DeleteObjectCommand: начинаем удаление объекта:', this.objectId);
58
+
57
59
  // Удаляем объект из состояния и PIXI
58
60
  this.coreMoodboard.state.removeObject(this.objectId);
59
61
  this.coreMoodboard.pixi.removeObject(this.objectId);
60
62
 
63
+ console.log('🗑️ DeleteObjectCommand: объект удален из state и PIXI');
64
+
61
65
  // Если это файловый объект с fileId, удаляем файл с сервера
62
66
  if (this.fileIdToDelete && this.coreMoodboard.fileUploadService) {
63
67
  try {
@@ -70,9 +74,12 @@ export class DeleteObjectCommand extends BaseCommand {
70
74
  }
71
75
  }
72
76
 
77
+ // Эмитим событие удаления для обновления всех UI компонентов
73
78
  this.coreMoodboard.eventBus.emit(Events.Object.Deleted, {
74
79
  objectId: this.objectId
75
80
  });
81
+
82
+ console.log('✅ DeleteObjectCommand: событие Events.Object.Deleted отправлено');
76
83
  }
77
84
 
78
85
  undo() {
@@ -135,13 +135,28 @@ export class SelectTool extends BaseTool {
135
135
  // Обработка удаления объектов (undo создания, delete команды и т.д.)
136
136
  this.eventBus.on(Events.Object.Deleted, (data) => {
137
137
  const objectId = data?.objectId || data;
138
- if (objectId && this.selection.has(objectId)) {
138
+ console.log('🗑️ SelectTool: получено событие удаления объекта:', objectId, 'данные:', data);
139
+
140
+ // ЗАЩИТА: Проверяем что данные валидны
141
+ if (!objectId) {
142
+ console.warn('⚠️ SelectTool: получено событие удаления с невалидным objectId');
143
+ return;
144
+ }
145
+
146
+ if (this.selection.has(objectId)) {
147
+ console.log('🗑️ SelectTool: удаляем объект из selection:', objectId);
139
148
  this.removeFromSelection(objectId);
140
149
 
141
- // Если выделение стало пустым, скрываем ручки
150
+ // ИСПРАВЛЕНИЕ: Принудительно очищаем selection если он стал пустым
142
151
  if (this.selection.size() === 0) {
152
+ console.log('🗑️ SelectTool: selection пустой, скрываем ручки');
153
+ this.emit(Events.Tool.SelectionClear);
143
154
  this.updateResizeHandles();
144
155
  }
156
+ } else {
157
+ console.log('🗑️ SelectTool: объект не был в selection, обновляем ручки на всякий случай');
158
+ // Принудительно обновляем ручки без излишних действий
159
+ this.updateResizeHandles();
145
160
  }
146
161
  });
147
162
  }
@@ -132,7 +132,8 @@ export class CommentPopover {
132
132
 
133
133
  _onDocMouseDown(e) {
134
134
  if (!this.popover || this.popover.style.display === 'none') return;
135
- if (this.popover.contains(e.target)) return; // клик внутри окна не закрываем
135
+ // ИСПРАВЛЕНИЕ: Защита от null элементов
136
+ if (this.popover && e.target && this.popover.contains(e.target)) return; // клик внутри окна — не закрываем
136
137
  this.hide();
137
138
  }
138
139
 
@@ -51,7 +51,8 @@ export class ContextMenu {
51
51
  // Скрывать при клике вне меню или по Esc
52
52
  document.addEventListener('mousedown', (e) => {
53
53
  if (!this.isVisible) return;
54
- if (!this.element.contains(e.target)) {
54
+ // ИСПРАВЛЕНИЕ: Защита от null элементов
55
+ if (this.element && e.target && !this.element.contains(e.target)) {
55
56
  this.hide();
56
57
  }
57
58
  });
@@ -435,7 +435,8 @@ export class FramePropertiesPanel {
435
435
  }
436
436
 
437
437
  _documentClickHandler(e) {
438
- if (this.colorPalette && !this.colorPalette.contains(e.target) &&
438
+ // ИСПРАВЛЕНИЕ: Защита от null элементов
439
+ if (this.colorPalette && e.target && !this.colorPalette.contains(e.target) &&
439
440
  this.colorButton && !this.colorButton.contains(e.target)) {
440
441
  this._hideColorPalette();
441
442
  }
@@ -35,6 +35,23 @@ export class HtmlHandlesLayer {
35
35
  this.eventBus.on(Events.Tool.SelectionRemove, () => this.update());
36
36
  this.eventBus.on(Events.Tool.SelectionClear, () => this.hide());
37
37
  this.eventBus.on(Events.Tool.DragUpdate, () => this.update());
38
+
39
+ // ИСПРАВЛЕНИЕ: Обработка удаления объектов
40
+ this.eventBus.on(Events.Object.Deleted, (data) => {
41
+ const objectId = data?.objectId || data;
42
+ console.log('🗑️ HtmlHandlesLayer: получено событие удаления:', data, 'objectId:', objectId);
43
+
44
+ // Принудительно скрываем и очищаем все ручки
45
+ this.hide();
46
+
47
+ // Очищаем DOM от старых ручек
48
+ this.layer.innerHTML = '';
49
+
50
+ // Обновляем для актуального состояния
51
+ setTimeout(() => {
52
+ this.update();
53
+ }, 10); // Небольшая задержка для полной очистки
54
+ });
38
55
  this.eventBus.on(Events.Tool.DragStart, () => { this._handlesSuppressed = true; this._setHandlesVisibility(false); });
39
56
  this.eventBus.on(Events.Tool.DragEnd, () => { this._handlesSuppressed = false; this._setHandlesVisibility(true); });
40
57
  this.eventBus.on(Events.Tool.ResizeUpdate, () => this.update());
@@ -38,6 +38,8 @@ export class MapPanel {
38
38
  // Закрытие по клику вне панели
39
39
  document.addEventListener('mousedown', (e) => {
40
40
  if (!this.popupEl) return;
41
+ // ИСПРАВЛЕНИЕ: Защита от null элементов
42
+ if (!this.element || !e.target) return;
41
43
  if (this.element.contains(e.target)) return;
42
44
  this.hidePopup();
43
45
  });
@@ -45,7 +47,8 @@ export class MapPanel {
45
47
  // Колесо для зума внутри миникарты
46
48
  this.element.addEventListener('wheel', (e) => {
47
49
  if (!this.popupEl) return;
48
- if (!this.popupEl.contains(e.target)) return;
50
+ // ИСПРАВЛЕНИЕ: Защита от null элементов
51
+ if (!this.popupEl || !e.target || !this.popupEl.contains(e.target)) return;
49
52
  e.preventDefault();
50
53
  // Масштабируем вокруг точки под курсором в миникарте
51
54
  const rect = this.canvas.getBoundingClientRect();
@@ -448,14 +448,16 @@ export class NotePropertiesPanel {
448
448
  let shouldClose = true;
449
449
 
450
450
  for (let palette of palettes) {
451
- if (palette && palette.contains(e.target)) {
451
+ // ИСПРАВЛЕНИЕ: Защита от null элементов
452
+ if (palette && e.target && palette.contains(e.target)) {
452
453
  shouldClose = false;
453
454
  break;
454
455
  }
455
456
  }
456
457
 
457
458
  for (let button of buttons) {
458
- if (button && button.contains(e.target)) {
459
+ // ИСПРАВЛЕНИЕ: Защита от null элементов
460
+ if (button && e.target && button.contains(e.target)) {
459
461
  shouldClose = false;
460
462
  break;
461
463
  }
@@ -259,7 +259,8 @@ export class TextPropertiesPanel {
259
259
 
260
260
  // Закрываем панель при клике вне её
261
261
  document.addEventListener('click', (e) => {
262
- if (!colorSelectorContainer.contains(e.target)) {
262
+ // ИСПРАВЛЕНИЕ: Защита от null элементов
263
+ if (!colorSelectorContainer || !e.target || !colorSelectorContainer.contains(e.target)) {
263
264
  this._hideColorDropdown();
264
265
  }
265
266
  });
@@ -460,7 +461,8 @@ export class TextPropertiesPanel {
460
461
 
461
462
  // Закрываем панель при клике вне её
462
463
  document.addEventListener('click', (e) => {
463
- if (!bgSelectorContainer.contains(e.target)) {
464
+ // ИСПРАВЛЕНИЕ: Защита от null элементов
465
+ if (!bgSelectorContainer || !e.target || !bgSelectorContainer.contains(e.target)) {
464
466
  this._hideBgColorDropdown();
465
467
  }
466
468
  });
@@ -871,7 +873,7 @@ export class TextPropertiesPanel {
871
873
  }
872
874
 
873
875
  _onDocMouseDown(e) {
874
- // Скрываем панель при клике вне неё и вне текстового объекта
876
+ // ИСПРАВЛЕНИЕ: Защита от null элементов + скрываем панель при клике вне неё
875
877
  if (!this.panel || !e.target) return;
876
878
 
877
879
  // Если клик внутри панели - не скрываем
package/src/ui/Toolbar.js CHANGED
@@ -555,7 +555,10 @@ export class Toolbar {
555
555
 
556
556
  // Клик вне попапов — закрыть
557
557
  document.addEventListener('click', (e) => {
558
- const isInsideToolbar = this.element.contains(e.target);
558
+ // ИСПРАВЛЕНИЕ: Защита от null элементов
559
+ if (!e.target) return;
560
+
561
+ const isInsideToolbar = this.element && this.element.contains(e.target);
559
562
  const isInsideShapesPopup = this.shapesPopupEl && this.shapesPopupEl.contains(e.target);
560
563
  const isInsideDrawPopup = this.drawPopupEl && this.drawPopupEl.contains(e.target);
561
564
  const isInsideEmojiPopup = this.emojiPopupEl && this.emojiPopupEl.contains(e.target);
@@ -74,6 +74,8 @@ export class ZoomPanel {
74
74
 
75
75
  document.addEventListener('mousedown', (e) => {
76
76
  if (!this.menuEl) return;
77
+ // ИСПРАВЛЕНИЕ: Защита от null элементов
78
+ if (!this.element || !e.target) return;
77
79
  if (this.element.contains(e.target)) return;
78
80
  this.hideMenu();
79
81
  });