@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 +1 -1
- package/src/core/PixiEngine.js +39 -0
- package/src/core/commands/DeleteObjectCommand.js +7 -0
- package/src/tools/object-tools/SelectTool.js +17 -2
- package/src/ui/CommentPopover.js +2 -1
- package/src/ui/ContextMenu.js +2 -1
- package/src/ui/FramePropertiesPanel.js +2 -1
- package/src/ui/HtmlHandlesLayer.js +17 -0
- package/src/ui/MapPanel.js +4 -1
- package/src/ui/NotePropertiesPanel.js +4 -2
- package/src/ui/TextPropertiesPanel.js +5 -3
- package/src/ui/Toolbar.js +4 -1
- package/src/ui/ZoomPanel.js +2 -0
package/package.json
CHANGED
package/src/core/PixiEngine.js
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|
package/src/ui/CommentPopover.js
CHANGED
|
@@ -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
|
-
|
|
135
|
+
// ИСПРАВЛЕНИЕ: Защита от null элементов
|
|
136
|
+
if (this.popover && e.target && this.popover.contains(e.target)) return; // клик внутри окна — не закрываем
|
|
136
137
|
this.hide();
|
|
137
138
|
}
|
|
138
139
|
|
package/src/ui/ContextMenu.js
CHANGED
|
@@ -51,7 +51,8 @@ export class ContextMenu {
|
|
|
51
51
|
// Скрывать при клике вне меню или по Esc
|
|
52
52
|
document.addEventListener('mousedown', (e) => {
|
|
53
53
|
if (!this.isVisible) return;
|
|
54
|
-
|
|
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
|
-
|
|
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());
|
package/src/ui/MapPanel.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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);
|
package/src/ui/ZoomPanel.js
CHANGED
|
@@ -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
|
});
|