@sequent-org/moodboard 1.0.0
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 +44 -0
- package/src/assets/icons/README.md +105 -0
- package/src/assets/icons/attachments.svg +3 -0
- package/src/assets/icons/clear.svg +5 -0
- package/src/assets/icons/comments.svg +3 -0
- package/src/assets/icons/emoji.svg +6 -0
- package/src/assets/icons/frame.svg +3 -0
- package/src/assets/icons/image.svg +3 -0
- package/src/assets/icons/note.svg +3 -0
- package/src/assets/icons/pan.svg +3 -0
- package/src/assets/icons/pencil.svg +3 -0
- package/src/assets/icons/redo.svg +3 -0
- package/src/assets/icons/select.svg +9 -0
- package/src/assets/icons/shapes.svg +3 -0
- package/src/assets/icons/text-add.svg +3 -0
- package/src/assets/icons/topbar/README.md +39 -0
- package/src/assets/icons/topbar/grid-cross.svg +6 -0
- package/src/assets/icons/topbar/grid-dot.svg +3 -0
- package/src/assets/icons/topbar/grid-line.svg +3 -0
- package/src/assets/icons/topbar/grid-off.svg +3 -0
- package/src/assets/icons/topbar/paint.svg +3 -0
- package/src/assets/icons/undo.svg +3 -0
- package/src/core/ApiClient.js +309 -0
- package/src/core/EventBus.js +42 -0
- package/src/core/HistoryManager.js +261 -0
- package/src/core/KeyboardManager.js +710 -0
- package/src/core/PixiEngine.js +439 -0
- package/src/core/SaveManager.js +381 -0
- package/src/core/StateManager.js +64 -0
- package/src/core/commands/BaseCommand.js +68 -0
- package/src/core/commands/CopyObjectCommand.js +44 -0
- package/src/core/commands/CreateObjectCommand.js +46 -0
- package/src/core/commands/DeleteObjectCommand.js +146 -0
- package/src/core/commands/EditFileNameCommand.js +107 -0
- package/src/core/commands/GroupMoveCommand.js +47 -0
- package/src/core/commands/GroupReorderZCommand.js +74 -0
- package/src/core/commands/GroupResizeCommand.js +37 -0
- package/src/core/commands/GroupRotateCommand.js +41 -0
- package/src/core/commands/MoveObjectCommand.js +89 -0
- package/src/core/commands/PasteObjectCommand.js +103 -0
- package/src/core/commands/ReorderZCommand.js +45 -0
- package/src/core/commands/ResizeObjectCommand.js +135 -0
- package/src/core/commands/RotateObjectCommand.js +70 -0
- package/src/core/commands/index.js +14 -0
- package/src/core/events/Events.js +147 -0
- package/src/core/index.js +1632 -0
- package/src/core/rendering/GeometryUtils.js +89 -0
- package/src/core/rendering/HitTestManager.js +186 -0
- package/src/core/rendering/LayerManager.js +137 -0
- package/src/core/rendering/ObjectRenderer.js +363 -0
- package/src/core/rendering/PixiRenderer.js +140 -0
- package/src/core/rendering/index.js +9 -0
- package/src/grid/BaseGrid.js +164 -0
- package/src/grid/CrossGrid.js +75 -0
- package/src/grid/DotGrid.js +148 -0
- package/src/grid/GridFactory.js +173 -0
- package/src/grid/LineGrid.js +115 -0
- package/src/index.js +2 -0
- package/src/moodboard/ActionHandler.js +114 -0
- package/src/moodboard/DataManager.js +114 -0
- package/src/moodboard/MoodBoard.js +359 -0
- package/src/moodboard/WorkspaceManager.js +103 -0
- package/src/objects/BaseObject.js +1 -0
- package/src/objects/CommentObject.js +115 -0
- package/src/objects/DrawingObject.js +114 -0
- package/src/objects/EmojiObject.js +98 -0
- package/src/objects/FileObject.js +318 -0
- package/src/objects/FrameObject.js +127 -0
- package/src/objects/ImageObject.js +72 -0
- package/src/objects/NoteObject.js +227 -0
- package/src/objects/ObjectFactory.js +61 -0
- package/src/objects/ShapeObject.js +134 -0
- package/src/objects/StampObject.js +0 -0
- package/src/objects/StickerObject.js +0 -0
- package/src/objects/TextObject.js +123 -0
- package/src/services/BoardService.js +85 -0
- package/src/services/FileUploadService.js +398 -0
- package/src/services/FrameService.js +138 -0
- package/src/services/ImageUploadService.js +246 -0
- package/src/services/ZOrderManager.js +50 -0
- package/src/services/ZoomPanController.js +78 -0
- package/src/src.7z +0 -0
- package/src/src.zip +0 -0
- package/src/src2.zip +0 -0
- package/src/tools/AlignmentGuides.js +326 -0
- package/src/tools/BaseTool.js +257 -0
- package/src/tools/ResizeHandles.js +381 -0
- package/src/tools/ToolManager.js +580 -0
- package/src/tools/board-tools/PanTool.js +43 -0
- package/src/tools/board-tools/ZoomTool.js +393 -0
- package/src/tools/object-tools/DrawingTool.js +404 -0
- package/src/tools/object-tools/PlacementTool.js +1005 -0
- package/src/tools/object-tools/SelectTool.js +2183 -0
- package/src/tools/object-tools/TextTool.js +416 -0
- package/src/tools/object-tools/selection/BoxSelectController.js +105 -0
- package/src/tools/object-tools/selection/GeometryUtils.js +101 -0
- package/src/tools/object-tools/selection/GroupDragController.js +61 -0
- package/src/tools/object-tools/selection/GroupResizeController.js +90 -0
- package/src/tools/object-tools/selection/GroupRotateController.js +61 -0
- package/src/tools/object-tools/selection/HandlesSync.js +96 -0
- package/src/tools/object-tools/selection/ResizeController.js +68 -0
- package/src/tools/object-tools/selection/RotateController.js +58 -0
- package/src/tools/object-tools/selection/SelectionModel.js +42 -0
- package/src/tools/object-tools/selection/SimpleDragController.js +45 -0
- package/src/ui/CommentPopover.js +187 -0
- package/src/ui/ContextMenu.js +340 -0
- package/src/ui/FilePropertiesPanel.js +298 -0
- package/src/ui/FramePropertiesPanel.js +462 -0
- package/src/ui/HtmlHandlesLayer.js +778 -0
- package/src/ui/HtmlTextLayer.js +279 -0
- package/src/ui/MapPanel.js +290 -0
- package/src/ui/NotePropertiesPanel.js +502 -0
- package/src/ui/SaveStatus.js +250 -0
- package/src/ui/TextPropertiesPanel.js +911 -0
- package/src/ui/Toolbar.js +1118 -0
- package/src/ui/Topbar.js +220 -0
- package/src/ui/ZoomPanel.js +116 -0
- package/src/ui/styles/workspace.css +854 -0
- package/src/utils/colors.js +0 -0
- package/src/utils/geometry.js +0 -0
- package/src/utils/iconLoader.js +270 -0
- package/src/utils/objectIdGenerator.js +17 -0
- package/src/utils/topbarIconLoader.js +114 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
2
|
+
import { Events } from '../events/Events.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ΠΠΎΠΌΠ°Π½Π΄Π° ΡΠ΄Π°Π»Π΅Π½ΠΈΡ ΠΎΠ±ΡΠ΅ΠΊΡΠ°
|
|
6
|
+
*/
|
|
7
|
+
export class DeleteObjectCommand extends BaseCommand {
|
|
8
|
+
constructor(coreMoodboard, objectId) {
|
|
9
|
+
super('delete_object', `Π£Π΄Π°Π»ΠΈΡΡ ΠΎΠ±ΡΠ΅ΠΊΡ`);
|
|
10
|
+
this.coreMoodboard = coreMoodboard;
|
|
11
|
+
this.objectId = objectId;
|
|
12
|
+
|
|
13
|
+
// Π‘ΠΎΡ
ΡΠ°Π½ΡΠ΅ΠΌ Π΄Π°Π½Π½ΡΠ΅ ΠΎΠ±ΡΠ΅ΠΊΡΠ° Π΄Π»Ρ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ Π²ΠΎΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ
|
|
14
|
+
const objects = this.coreMoodboard.state.getObjects();
|
|
15
|
+
const originalData = objects.find(obj => obj.id === objectId);
|
|
16
|
+
|
|
17
|
+
if (!originalData) {
|
|
18
|
+
throw new Error(`Object with id ${objectId} not found`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ΠΠ΅Π»Π°Π΅ΠΌ Π³Π»ΡΠ±ΠΎΠΊΡΡ ΠΊΠΎΠΏΠΈΡ Π΄Π°Π½Π½ΡΡ
ΠΎΠ±ΡΠ΅ΠΊΡΠ°
|
|
22
|
+
this.objectData = JSON.parse(JSON.stringify(originalData));
|
|
23
|
+
|
|
24
|
+
// ΠΠ»Ρ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠΉ ΡΠ±Π΅Π΄ΠΈΠΌΡΡ, ΡΡΠΎ Π΅ΡΡΡ src URL Π΄Π»Ρ Π²ΠΎΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ
|
|
25
|
+
if (this.objectData.type === 'image') {
|
|
26
|
+
console.log('π§ DEBUG DeleteObjectCommand: ΠΈΡΡ
ΠΎΠ΄Π½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ:', {
|
|
27
|
+
id: this.objectData.id,
|
|
28
|
+
imageId: this.objectData.imageId,
|
|
29
|
+
src: this.objectData.src,
|
|
30
|
+
propertiesSrc: this.objectData.properties?.src,
|
|
31
|
+
hasBase64Src: !!(this.objectData.src && this.objectData.src.startsWith('data:')),
|
|
32
|
+
hasBase64Props: !!(this.objectData.properties?.src && this.objectData.properties.src.startsWith('data:'))
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (this.objectData.imageId) {
|
|
36
|
+
const imageUrl = `/api/images/${this.objectData.imageId}/file`;
|
|
37
|
+
|
|
38
|
+
// ΠΡΠ΅Π³Π΄Π° Π²ΠΎΡΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ URL ΠΈΠ· imageId Π΄Π»Ρ Π³Π°ΡΠ°Π½ΡΠΈΠΈ
|
|
39
|
+
// (ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ ΡΠ΄Π°Π»Π΅Π½ ΠΏΡΠΈ ΠΏΡΠ΅Π΄ΡΠ΄ΡΡΠΈΡ
ΡΠΎΡ
ΡΠ°Π½Π΅Π½ΠΈΡΡ
)
|
|
40
|
+
this.objectData.src = imageUrl;
|
|
41
|
+
|
|
42
|
+
if (!this.objectData.properties) {
|
|
43
|
+
this.objectData.properties = {};
|
|
44
|
+
}
|
|
45
|
+
this.objectData.properties.src = imageUrl;
|
|
46
|
+
|
|
47
|
+
console.log('π§ DEBUG DeleteObjectCommand: ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½Π½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ:', {
|
|
48
|
+
id: this.objectData.id,
|
|
49
|
+
imageId: this.objectData.imageId,
|
|
50
|
+
src: this.objectData.src,
|
|
51
|
+
propertiesSrc: this.objectData.properties?.src
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
console.warn('π§ DEBUG DeleteObjectCommand: Ρ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ Π½Π΅Ρ imageId, ΠΎΡΡΠ°Π²Π»ΡΠ΅ΠΌ ΠΊΠ°ΠΊ Π΅ΡΡΡ');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ΠΠ»Ρ ΡΠ°ΠΉΠ»ΠΎΠ² ΡΠΎΡ
ΡΠ°Π½ΡΠ΅ΠΌ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ Π΄Π»Ρ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΠΉ ΠΎΡΠΈΡΡΠΊΠΈ Ρ ΡΠ΅ΡΠ²Π΅ΡΠ°
|
|
59
|
+
if (this.objectData.type === 'file') {
|
|
60
|
+
console.log('π§ DEBUG DeleteObjectCommand: ΠΈΡΡ
ΠΎΠ΄Π½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ ΡΠ°ΠΉΠ»Π°:', {
|
|
61
|
+
id: this.objectData.id,
|
|
62
|
+
fileId: this.objectData.fileId,
|
|
63
|
+
fileName: this.objectData.properties?.fileName
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (this.objectData.fileId) {
|
|
67
|
+
// Π‘ΠΎΡ
ΡΠ°Π½ΡΠ΅ΠΌ fileId Π΄Π»Ρ ΡΠ΄Π°Π»Π΅Π½ΠΈΡ Ρ ΡΠ΅ΡΠ²Π΅ΡΠ°
|
|
68
|
+
this.fileIdToDelete = this.objectData.fileId;
|
|
69
|
+
console.log('π§ DEBUG DeleteObjectCommand: ΡΠ°ΠΉΠ» Π±ΡΠ΄Π΅Ρ ΡΠ΄Π°Π»Π΅Π½ Ρ ΡΠ΅ΡΠ²Π΅ΡΠ°:', this.fileIdToDelete);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ΠΠ±Π½ΠΎΠ²Π»ΡΠ΅ΠΌ ΠΎΠΏΠΈΡΠ°Π½ΠΈΠ΅ Ρ ΡΠΈΠΏΠΎΠΌ ΠΎΠ±ΡΠ΅ΠΊΡΠ°
|
|
74
|
+
this.description = `Π£Π΄Π°Π»ΠΈΡΡ ${this.objectData.type}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async execute() {
|
|
78
|
+
// Π£Π΄Π°Π»ΡΠ΅ΠΌ ΠΎΠ±ΡΠ΅ΠΊΡ ΠΈΠ· ΡΠΎΡΡΠΎΡΠ½ΠΈΡ ΠΈ PIXI
|
|
79
|
+
this.coreMoodboard.state.removeObject(this.objectId);
|
|
80
|
+
this.coreMoodboard.pixi.removeObject(this.objectId);
|
|
81
|
+
|
|
82
|
+
// ΠΡΠ»ΠΈ ΡΡΠΎ ΡΠ°ΠΉΠ»ΠΎΠ²ΡΠΉ ΠΎΠ±ΡΠ΅ΠΊΡ Ρ fileId, ΡΠ΄Π°Π»ΡΠ΅ΠΌ ΡΠ°ΠΉΠ» Ρ ΡΠ΅ΡΠ²Π΅ΡΠ°
|
|
83
|
+
if (this.fileIdToDelete && this.coreMoodboard.fileUploadService) {
|
|
84
|
+
try {
|
|
85
|
+
console.log('ποΈ Π£Π΄Π°Π»ΡΠ΅ΠΌ ΡΠ°ΠΉΠ» Ρ ΡΠ΅ΡΠ²Π΅ΡΠ°:', this.fileIdToDelete);
|
|
86
|
+
await this.coreMoodboard.fileUploadService.deleteFile(this.fileIdToDelete);
|
|
87
|
+
console.log('β
Π€Π°ΠΉΠ» ΡΡΠΏΠ΅ΡΠ½ΠΎ ΡΠ΄Π°Π»Π΅Π½ Ρ ΡΠ΅ΡΠ²Π΅ΡΠ°:', this.fileIdToDelete);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.warn('β οΈ ΠΡΠΈΠ±ΠΊΠ° ΡΠ΄Π°Π»Π΅Π½ΠΈΡ ΡΠ°ΠΉΠ»Π° Ρ ΡΠ΅ΡΠ²Π΅ΡΠ°:', error);
|
|
90
|
+
// ΠΠ΅ ΠΎΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ ΠΊΠΎΠΌΠ°Π½Π΄Ρ, ΡΠ°ΠΊ ΠΊΠ°ΠΊ ΠΎΠ±ΡΠ΅ΠΊΡ ΡΠΆΠ΅ ΡΠ΄Π°Π»Π΅Π½ ΠΈΠ· UI
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.coreMoodboard.eventBus.emit(Events.Object.Deleted, {
|
|
95
|
+
objectId: this.objectId
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
undo() {
|
|
100
|
+
// DEBUG: ΠΠΎΠ³ΠΈΡΡΠ΅ΠΌ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ ΠΎΠ±ΡΠ΅ΠΊΡΠ° ΠΏΡΠΈ Undo
|
|
101
|
+
if (this.objectData.type === 'image') {
|
|
102
|
+
console.log('π DEBUG Undo ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ:', {
|
|
103
|
+
id: this.objectData.id,
|
|
104
|
+
imageId: this.objectData.imageId,
|
|
105
|
+
src: this.objectData.src,
|
|
106
|
+
propertiesSrc: this.objectData.properties?.src,
|
|
107
|
+
hasBase64Src: !!(this.objectData.src && this.objectData.src.startsWith('data:')),
|
|
108
|
+
hasBase64Props: !!(this.objectData.properties?.src && this.objectData.properties.src.startsWith('data:'))
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Π‘ΠΏΠ΅ΡΠΈΠ°Π»ΡΠ½Π°Ρ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π΄Π»Ρ ΡΠ°ΠΉΠ»ΠΎΠ²ΡΡ
ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ²
|
|
113
|
+
if (this.objectData.type === 'file' && this.fileIdToDelete) {
|
|
114
|
+
console.log('π DEBUG Undo ΡΠ°ΠΉΠ»Π°:', {
|
|
115
|
+
id: this.objectData.id,
|
|
116
|
+
fileId: this.objectData.fileId,
|
|
117
|
+
fileName: this.objectData.properties?.fileName
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Π€Π°ΠΉΠ» Π±ΡΠ» ΡΠ΄Π°Π»Π΅Π½ Ρ ΡΠ΅ΡΠ²Π΅ΡΠ°, ΡΠΎΠ·Π΄Π°Π΅ΠΌ ΠΎΠ±ΡΠ΅ΠΊΡ Ρ ΠΏΡΠ΅Π΄ΡΠΏΡΠ΅ΠΆΠ΄Π΅Π½ΠΈΠ΅ΠΌ
|
|
121
|
+
const restoredObjectData = { ...this.objectData };
|
|
122
|
+
if (restoredObjectData.properties) {
|
|
123
|
+
restoredObjectData.properties = {
|
|
124
|
+
...restoredObjectData.properties,
|
|
125
|
+
fileName: `[Π£ΠΠΠΠΠ] ${restoredObjectData.properties.fileName || 'ΡΠ°ΠΉΠ»'}`,
|
|
126
|
+
isDeleted: true // Π€Π»Π°Π³ Π΄Π»Ρ FileObject ΡΡΠΎΠ±Ρ ΠΏΠΎΠΊΠ°Π·Π°ΡΡ Π΄ΡΡΠ³ΡΡ ΠΈΠΊΠΎΠ½ΠΊΡ
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ΠΠΎΡΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ ΠΎΠ±ΡΠ΅ΠΊΡ Ρ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½Π½ΡΠΌΠΈ Π΄Π°Π½Π½ΡΠΌΠΈ
|
|
131
|
+
this.coreMoodboard.state.addObject(restoredObjectData);
|
|
132
|
+
this.coreMoodboard.pixi.createObject(restoredObjectData);
|
|
133
|
+
|
|
134
|
+
console.warn('β οΈ Π€Π°ΠΉΠ» Π²ΠΎΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½ Π½Π° Ρ
ΠΎΠ»ΡΡΠ΅, Π½ΠΎ Π±ΡΠ» ΡΠ΄Π°Π»Π΅Π½ Ρ ΡΠ΅ΡΠ²Π΅ΡΠ°');
|
|
135
|
+
} else {
|
|
136
|
+
// ΠΠΎΡΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ ΠΎΠ±ΡΠ΅ΠΊΡ Ρ ΡΠΎΡ
ΡΠ°Π½Π΅Π½Π½ΡΠΌΠΈ Π΄Π°Π½Π½ΡΠΌΠΈ (Π΄Π»Ρ Π²ΡΠ΅Ρ
ΠΎΡΡΠ°Π»ΡΠ½ΡΡ
ΡΠΈΠΏΠΎΠ²)
|
|
137
|
+
this.coreMoodboard.state.addObject(this.objectData);
|
|
138
|
+
this.coreMoodboard.pixi.createObject(this.objectData);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
this.coreMoodboard.eventBus.emit(Events.Object.Created, {
|
|
142
|
+
objectId: this.objectId,
|
|
143
|
+
objectData: this.objectData
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
2
|
+
import { Events } from '../events/Events.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ΠΠΎΠΌΠ°Π½Π΄Π° ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ Π½Π°Π·Π²Π°Π½ΠΈΡ ΡΠ°ΠΉΠ»Π°
|
|
6
|
+
*/
|
|
7
|
+
export class EditFileNameCommand extends BaseCommand {
|
|
8
|
+
constructor(coreMoodboard, objectId, oldName, newName) {
|
|
9
|
+
super('edit_file_name', `ΠΠ΅ΡΠ΅ΠΈΠΌΠ΅Π½ΠΎΠ²Π°ΡΡ ΡΠ°ΠΉΠ»`);
|
|
10
|
+
this.coreMoodboard = coreMoodboard;
|
|
11
|
+
this.objectId = objectId;
|
|
12
|
+
this.oldName = oldName;
|
|
13
|
+
this.newName = newName;
|
|
14
|
+
|
|
15
|
+
// ΠΠ±Π½ΠΎΠ²Π»ΡΠ΅ΠΌ ΠΎΠΏΠΈΡΠ°Π½ΠΈΠ΅ Ρ Π½Π°Π·Π²Π°Π½ΠΈΡΠΌΠΈ
|
|
16
|
+
this.description = `ΠΠ΅ΡΠ΅ΠΈΠΌΠ΅Π½ΠΎΠ²Π°ΡΡ ΡΠ°ΠΉΠ» "${this.oldName}" β "${this.newName}"`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async execute() {
|
|
20
|
+
// Π£ΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ Π½ΠΎΠ²ΠΎΠ΅ Π½Π°Π·Π²Π°Π½ΠΈΠ΅
|
|
21
|
+
await this._setFileName(this.newName);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async undo() {
|
|
25
|
+
// ΠΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌ ΡΡΠ°ΡΠΎΠ΅ Π½Π°Π·Π²Π°Π½ΠΈΠ΅
|
|
26
|
+
await this._setFileName(this.oldName);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* ΠΠΎΠΆΠ½ΠΎ ΠΎΠ±ΡΠ΅Π΄ΠΈΠ½ΠΈΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ ΠΏΠ΅ΡΠ΅ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½ΠΈΡ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΈ ΡΠΎΠ³ΠΎ ΠΆΠ΅ ΡΠ°ΠΉΠ»Π°
|
|
31
|
+
*/
|
|
32
|
+
canMergeWith(otherCommand) {
|
|
33
|
+
return otherCommand instanceof EditFileNameCommand &&
|
|
34
|
+
otherCommand.objectId === this.objectId;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* ΠΠ±ΡΠ΅Π΄ΠΈΠ½ΡΠ΅Ρ ΠΊΠΎΠΌΠ°Π½Π΄Ρ ΠΏΠ΅ΡΠ΅ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½ΠΈΡ - Π±Π΅ΡΠ΅Ρ Π½Π°ΡΠ°Π»ΡΠ½ΠΎΠ΅ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΠΈΠ· ΠΏΠ΅ΡΠ²ΠΎΠΉ ΠΊΠΎΠΌΠ°Π½Π΄Ρ
|
|
39
|
+
* ΠΈ ΠΊΠΎΠ½Π΅ΡΠ½ΠΎΠ΅ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΠΈΠ· Π²ΡΠΎΡΠΎΠΉ
|
|
40
|
+
*/
|
|
41
|
+
mergeWith(otherCommand) {
|
|
42
|
+
if (!this.canMergeWith(otherCommand)) {
|
|
43
|
+
throw new Error('Cannot merge commands');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ΠΠ±Π½ΠΎΠ²Π»ΡΠ΅ΠΌ ΠΊΠΎΠ½Π΅ΡΠ½ΠΎΠ΅ Π½Π°Π·Π²Π°Π½ΠΈΠ΅
|
|
47
|
+
this.newName = otherCommand.newName;
|
|
48
|
+
this.description = `ΠΠ΅ΡΠ΅ΠΈΠΌΠ΅Π½ΠΎΠ²Π°ΡΡ ΡΠ°ΠΉΠ» "${this.oldName}" β "${this.newName}"`;
|
|
49
|
+
this.timestamp = otherCommand.timestamp;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Π£ΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅Ρ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΡΠ°ΠΉΠ»Π°
|
|
54
|
+
* @private
|
|
55
|
+
*/
|
|
56
|
+
async _setFileName(fileName) {
|
|
57
|
+
// ΠΠ±Π½ΠΎΠ²Π»ΡΠ΅ΠΌ Π² ΡΠΎΡΡΠΎΡΠ½ΠΈΠΈ
|
|
58
|
+
const objects = this.coreMoodboard.state.getObjects();
|
|
59
|
+
const objectData = objects.find(obj => obj.id === this.objectId);
|
|
60
|
+
|
|
61
|
+
if (objectData) {
|
|
62
|
+
// ΠΠ±Π½ΠΎΠ²Π»ΡΠ΅ΠΌ properties ΠΎΠ±ΡΠ΅ΠΊΡΠ°
|
|
63
|
+
if (!objectData.properties) {
|
|
64
|
+
objectData.properties = {};
|
|
65
|
+
}
|
|
66
|
+
objectData.properties.fileName = fileName;
|
|
67
|
+
|
|
68
|
+
// Π‘ΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·ΠΈΡΡΠ΅ΠΌ Ρ ΡΠ΅ΡΠ²Π΅ΡΠΎΠΌ, Π΅ΡΠ»ΠΈ Π΅ΡΡΡ fileId
|
|
69
|
+
if (objectData.fileId && this.coreMoodboard.fileUploadService) {
|
|
70
|
+
try {
|
|
71
|
+
console.log('π Π‘ΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·ΠΈΡΡΠ΅ΠΌ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΡΠ°ΠΉΠ»Π° Ρ ΡΠ΅ΡΠ²Π΅ΡΠΎΠΌ:', { fileId: objectData.fileId, fileName });
|
|
72
|
+
await this.coreMoodboard.fileUploadService.updateFileMetadata(objectData.fileId, {
|
|
73
|
+
fileName: fileName
|
|
74
|
+
});
|
|
75
|
+
console.log('β
ΠΠ°Π·Π²Π°Π½ΠΈΠ΅ ΡΠ°ΠΉΠ»Π° ΡΡΠΏΠ΅ΡΠ½ΠΎ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΎ Π½Π° ΡΠ΅ΡΠ²Π΅ΡΠ΅');
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.warn('β οΈ ΠΡΠΈΠ±ΠΊΠ° ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΠΈ Π½Π°Π·Π²Π°Π½ΠΈΡ ΡΠ°ΠΉΠ»Π° Ρ ΡΠ΅ΡΠ²Π΅ΡΠΎΠΌ:', error);
|
|
78
|
+
// ΠΠ΅ ΠΎΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅, ΠΏΡΠΎΠ΄ΠΎΠ»ΠΆΠ°Π΅ΠΌ Ρ Π»ΠΎΠΊΠ°Π»ΡΠ½ΡΠΌ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ΠΌ
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ΠΠ±Π½ΠΎΠ²Π»ΡΠ΅ΠΌ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅
|
|
83
|
+
this.coreMoodboard.state.markDirty();
|
|
84
|
+
|
|
85
|
+
// ΠΠ±Π½ΠΎΠ²Π»ΡΠ΅ΠΌ Π²ΠΈΠ·ΡΠ°Π»ΡΠ½ΠΎΠ΅ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ΠΈΠ΅
|
|
86
|
+
const pixiReq = { objectId: this.objectId, pixiObject: null };
|
|
87
|
+
this.coreMoodboard.eventBus.emit(Events.Tool.GetObjectPixi, pixiReq);
|
|
88
|
+
|
|
89
|
+
if (pixiReq.pixiObject && pixiReq.pixiObject._mb && pixiReq.pixiObject._mb.instance) {
|
|
90
|
+
const fileInstance = pixiReq.pixiObject._mb.instance;
|
|
91
|
+
if (typeof fileInstance.setFileName === 'function') {
|
|
92
|
+
fileInstance.setFileName(fileName);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ΠΠΌΠΈΡΠΈΠΌ ΡΠΎΠ±ΡΡΠΈΠ΅ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ ΠΎΠ±ΡΠ΅ΠΊΡΠ°
|
|
97
|
+
this.coreMoodboard.eventBus.emit(Events.Object.Updated, {
|
|
98
|
+
objectId: this.objectId,
|
|
99
|
+
type: 'fileName',
|
|
100
|
+
oldValue: this.oldName,
|
|
101
|
+
newValue: fileName
|
|
102
|
+
});
|
|
103
|
+
} else {
|
|
104
|
+
console.warn(`EditFileNameCommand: ΠΎΠ±ΡΠ΅ΠΊΡ Ρ ID ${this.objectId} Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
2
|
+
import { Events } from '../events/Events.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ΠΠΎΠΌΠ°Π½Π΄Π° ΠΏΠ΅ΡΠ΅ΠΌΠ΅ΡΠ΅Π½ΠΈΡ Π³ΡΡΠΏΠΏΡ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ² ΠΎΠ΄Π½ΠΎΠΉ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠ΅ΠΉ (Π΄Π»Ρ Undo/Redo)
|
|
6
|
+
*/
|
|
7
|
+
export class GroupMoveCommand extends BaseCommand {
|
|
8
|
+
/**
|
|
9
|
+
* @param {CoreMoodBoard} core
|
|
10
|
+
* @param {Array<{id:string, from:{x:number,y:number}, to:{x:number,y:number}}>} moves
|
|
11
|
+
*/
|
|
12
|
+
constructor(core, moves) {
|
|
13
|
+
super('group-move', 'ΠΠ΅ΡΠ΅ΠΌΠ΅ΡΡΠΈΡΡ Π³ΡΡΠΏΠΏΡ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ²');
|
|
14
|
+
this.core = core;
|
|
15
|
+
this.moves = moves || [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
execute() {
|
|
19
|
+
// ΠΡΠΈΠΌΠ΅Π½ΡΠ΅ΠΌ ΠΊΠΎΠ½Π΅ΡΠ½ΡΠ΅ ΠΏΠΎΠ·ΠΈΡΠΈΠΈ ΠΊΠΎ Π²ΡΠ΅ΠΌ ΠΎΠ±ΡΠ΅ΠΊΡΠ°ΠΌ
|
|
20
|
+
for (const item of this.moves) {
|
|
21
|
+
this.core.updateObjectPositionDirect(item.id, item.to);
|
|
22
|
+
this.emit(Events.Object.TransformUpdated, {
|
|
23
|
+
objectId: item.id,
|
|
24
|
+
type: 'position',
|
|
25
|
+
position: item.to
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
undo() {
|
|
31
|
+
// ΠΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌ ΠΈΡΡ
ΠΎΠ΄Π½ΡΠ΅ ΠΏΠΎΠ·ΠΈΡΠΈΠΈ
|
|
32
|
+
for (const item of this.moves) {
|
|
33
|
+
this.core.updateObjectPositionDirect(item.id, item.from);
|
|
34
|
+
this.emit(Events.Object.TransformUpdated, {
|
|
35
|
+
objectId: item.id,
|
|
36
|
+
type: 'position',
|
|
37
|
+
position: item.from
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getDescription() {
|
|
43
|
+
return `ΠΠ΅ΡΠ΅ΠΌΠ΅ΡΡΠΈΡΡ Π³ΡΡΠΏΠΏΡ (${this.moves.length})`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
2
|
+
import { Events } from '../events/Events.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ΠΠΎΠΌΠ°Π½Π΄Π° ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ ΠΏΠΎΡΡΠ΄ΠΊΠ° (z-order) Π³ΡΡΠΏΠΏΡ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ² ΠΊΠ°ΠΊ Π΅Π΄ΠΈΠ½ΠΎΠ³ΠΎ Π±Π»ΠΎΠΊΠ°
|
|
6
|
+
* Π‘ΠΎΡ
ΡΠ°Π½ΡΠ΅Ρ Π²Π½ΡΡΡΠ΅Π½Π½ΠΈΠΉ ΠΏΠΎΡΡΠ΄ΠΎΠΊ Π³ΡΡΠΏΠΏΡ
|
|
7
|
+
*/
|
|
8
|
+
export class GroupReorderZCommand extends BaseCommand {
|
|
9
|
+
constructor(core, objectIds, mode) {
|
|
10
|
+
super('group-reorder-z', 'ΠΠ·ΠΌΠ΅Π½ΠΈΡΡ ΠΏΠΎΡΡΠ΄ΠΎΠΊ ΡΠ»ΠΎΡ Π³ΡΡΠΏΠΏΡ');
|
|
11
|
+
this.core = core;
|
|
12
|
+
this.objectIds = Array.from(objectIds || []);
|
|
13
|
+
this.mode = mode; // 'front' | 'back' | 'forward' | 'backward'
|
|
14
|
+
this.beforeOrder = null; // ΡΠ½ΠΈΠΌΠΎΠΊ ΠΏΠΎΡΡΠ΄ΠΊΠ° ids ΠΌΠ°ΡΡΠΈΠ²Π° state
|
|
15
|
+
this.afterOrder = null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
execute() {
|
|
19
|
+
this.beforeOrder = (this.core.state.state.objects || []).map(o => o.id);
|
|
20
|
+
this.reorder(this.mode);
|
|
21
|
+
this.afterOrder = (this.core.state.state.objects || []).map(o => o.id);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
undo() {
|
|
25
|
+
if (!this.beforeOrder) return;
|
|
26
|
+
this.applyOrder(this.beforeOrder);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
applyOrder(idOrder) {
|
|
30
|
+
const map = new Map((this.core.state.state.objects || []).map(o => [o.id, o]));
|
|
31
|
+
this.core.state.state.objects = idOrder.map(id => map.get(id)).filter(Boolean);
|
|
32
|
+
// ΠΠ΅ΡΠ΅ΡΡΠ΅Ρ zIndex
|
|
33
|
+
const app = this.core.pixi?.app;
|
|
34
|
+
if (app) {
|
|
35
|
+
app.stage.sortableChildren = true;
|
|
36
|
+
(this.core.state.state.objects || []).forEach((o, i) => {
|
|
37
|
+
const pixi = this.core.pixi.objects.get(o.id);
|
|
38
|
+
if (pixi) pixi.zIndex = i;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
this.core.state.markDirty();
|
|
42
|
+
this.emit(Events.Object.Reordered, { ids: this.objectIds });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
reorder(mode) {
|
|
46
|
+
const arr = this.core.state.state.objects || [];
|
|
47
|
+
const ids = new Set(this.objectIds);
|
|
48
|
+
const selectedItems = arr.filter(o => ids.has(o.id));
|
|
49
|
+
const others = arr.filter(o => !ids.has(o.id));
|
|
50
|
+
const indices = arr.map((o, i) => ({ id: o.id, i })).filter(p => ids.has(p.id)).map(p => p.i).sort((a,b)=>a-b);
|
|
51
|
+
const minIdx = indices[0] ?? 0;
|
|
52
|
+
const othersBefore = arr.slice(0, minIdx).filter(o => !ids.has(o.id)).length;
|
|
53
|
+
let insertPos = othersBefore;
|
|
54
|
+
switch (mode) {
|
|
55
|
+
case 'front': insertPos = others.length; break;
|
|
56
|
+
case 'back': insertPos = 0; break;
|
|
57
|
+
case 'forward': insertPos = Math.min(othersBefore + 1, others.length); break;
|
|
58
|
+
case 'backward': insertPos = Math.max(othersBefore - 1, 0); break;
|
|
59
|
+
}
|
|
60
|
+
this.core.state.state.objects = [...others.slice(0, insertPos), ...selectedItems, ...others.slice(insertPos)];
|
|
61
|
+
// ΠΠ΅ΡΠ΅ΡΡΠ΅Ρ zIndex
|
|
62
|
+
const app = this.core.pixi?.app;
|
|
63
|
+
if (app) {
|
|
64
|
+
app.stage.sortableChildren = true;
|
|
65
|
+
(this.core.state.state.objects || []).forEach((o, i) => {
|
|
66
|
+
const pixi = this.core.pixi.objects.get(o.id);
|
|
67
|
+
if (pixi) pixi.zIndex = i;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
this.core.state.markDirty();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
2
|
+
import { Events } from '../events/Events.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ΠΠΎΠΌΠ°Π½Π΄Π° ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ ΡΠ°Π·ΠΌΠ΅ΡΠ° Π³ΡΡΠΏΠΏΡ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ² ΠΎΠ΄Π½ΠΎΠΉ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠ΅ΠΉ (Undo/Redo)
|
|
6
|
+
*/
|
|
7
|
+
export class GroupResizeCommand extends BaseCommand {
|
|
8
|
+
/**
|
|
9
|
+
* @param {CoreMoodBoard} core
|
|
10
|
+
* @param {Array<{id:string, fromSize:{width:number,height:number}, toSize:{width:number,height:number}, fromPos:{x:number,y:number}, toPos:{x:number,y:number}, type?:string}>} changes
|
|
11
|
+
*/
|
|
12
|
+
constructor(core, changes) {
|
|
13
|
+
super('group-resize', 'ΠΠ·ΠΌΠ΅Π½ΠΈΡΡ ΡΠ°Π·ΠΌΠ΅Ρ Π³ΡΡΠΏΠΏΡ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ²');
|
|
14
|
+
this.core = core;
|
|
15
|
+
this.changes = changes || [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
execute() {
|
|
19
|
+
for (const c of this.changes) {
|
|
20
|
+
this.core.updateObjectSizeAndPositionDirect(c.id, c.toSize, c.toPos, c.type || null);
|
|
21
|
+
this.emit(Events.Object.TransformUpdated, { objectId: c.id, type: 'resize', size: c.toSize, position: c.toPos });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
undo() {
|
|
26
|
+
for (const c of this.changes) {
|
|
27
|
+
this.core.updateObjectSizeAndPositionDirect(c.id, c.fromSize, c.fromPos, c.type || null);
|
|
28
|
+
this.emit(Events.Object.TransformUpdated, { objectId: c.id, type: 'resize', size: c.fromSize, position: c.fromPos });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getDescription() {
|
|
33
|
+
return `ΠΠ·ΠΌΠ΅Π½ΠΈΡΡ ΡΠ°Π·ΠΌΠ΅Ρ Π³ΡΡΠΏΠΏΡ (${this.changes.length})`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
2
|
+
import { Events } from '../events/Events.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ΠΠΎΠΌΠ°Π½Π΄Π° ΠΏΠΎΠ²ΠΎΡΠΎΡΠ° Π³ΡΡΠΏΠΏΡ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ² ΠΎΠ΄Π½ΠΎΠΉ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠ΅ΠΉ (Π΄Π»Ρ Undo/Redo)
|
|
6
|
+
*/
|
|
7
|
+
export class GroupRotateCommand extends BaseCommand {
|
|
8
|
+
/**
|
|
9
|
+
* @param {CoreMoodBoard} core
|
|
10
|
+
* @param {Array<{id:string, fromAngle:number, toAngle:number, fromPos:{x:number,y:number}, toPos:{x:number,y:number}}>} changes
|
|
11
|
+
*/
|
|
12
|
+
constructor(core, changes) {
|
|
13
|
+
super('group-rotate', 'ΠΠΎΠ²Π΅ΡΠ½ΡΡΡ Π³ΡΡΠΏΠΏΡ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ²');
|
|
14
|
+
this.core = core;
|
|
15
|
+
this.changes = changes || [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
execute() {
|
|
19
|
+
for (const c of this.changes) {
|
|
20
|
+
this.core.updateObjectRotationDirect(c.id, c.toAngle);
|
|
21
|
+
this.core.updateObjectPositionDirect(c.id, c.toPos);
|
|
22
|
+
this.emit(Events.Object.TransformUpdated, { objectId: c.id, type: 'rotation', angle: c.toAngle });
|
|
23
|
+
this.emit(Events.Object.TransformUpdated, { objectId: c.id, type: 'position', position: c.toPos });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
undo() {
|
|
28
|
+
for (const c of this.changes) {
|
|
29
|
+
this.core.updateObjectRotationDirect(c.id, c.fromAngle);
|
|
30
|
+
this.core.updateObjectPositionDirect(c.id, c.fromPos);
|
|
31
|
+
this.emit(Events.Object.TransformUpdated, { objectId: c.id, type: 'rotation', angle: c.fromAngle });
|
|
32
|
+
this.emit(Events.Object.TransformUpdated, { objectId: c.id, type: 'position', position: c.fromPos });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getDescription() {
|
|
37
|
+
return `ΠΠΎΠ²Π΅ΡΠ½ΡΡΡ Π³ΡΡΠΏΠΏΡ (${this.changes.length})`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
2
|
+
import { Events } from '../events/Events.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ΠΠΎΠΌΠ°Π½Π΄Π° ΠΏΠ΅ΡΠ΅ΠΌΠ΅ΡΠ΅Π½ΠΈΡ ΠΎΠ±ΡΠ΅ΠΊΡΠ°
|
|
6
|
+
*/
|
|
7
|
+
export class MoveObjectCommand extends BaseCommand {
|
|
8
|
+
constructor(coreMoodboard, objectId, oldPosition, newPosition) {
|
|
9
|
+
super('move_object', `ΠΠ΅ΡΠ΅ΠΌΠ΅ΡΡΠΈΡΡ ΠΎΠ±ΡΠ΅ΠΊΡ`);
|
|
10
|
+
this.coreMoodboard = coreMoodboard;
|
|
11
|
+
this.objectId = objectId;
|
|
12
|
+
this.oldPosition = { ...oldPosition };
|
|
13
|
+
this.newPosition = { ...newPosition };
|
|
14
|
+
|
|
15
|
+
// ΠΠ±Π½ΠΎΠ²Π»ΡΠ΅ΠΌ ΠΎΠΏΠΈΡΠ°Π½ΠΈΠ΅ Ρ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΠ°ΠΌΠΈ
|
|
16
|
+
this.description = `ΠΠ΅ΡΠ΅ΠΌΠ΅ΡΡΠΈΡΡ ΠΎΠ±ΡΠ΅ΠΊΡ (${Math.round(this.oldPosition.x)}, ${Math.round(this.oldPosition.y)}) β (${Math.round(this.newPosition.x)}, ${Math.round(this.newPosition.y)})`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
execute() {
|
|
20
|
+
// Π£ΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ Π½ΠΎΠ²ΡΡ ΠΏΠΎΠ·ΠΈΡΠΈΡ
|
|
21
|
+
this._setPosition(this.newPosition);
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
undo() {
|
|
26
|
+
// ΠΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌ ΡΡΠ°ΡΡΡ ΠΏΠΎΠ·ΠΈΡΠΈΡ
|
|
27
|
+
this._setPosition(this.oldPosition);
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
_setPosition(position) {
|
|
32
|
+
// ΠΠ±Π½ΠΎΠ²Π»ΡΠ΅ΠΌ ΠΏΠΎΠ·ΠΈΡΠΈΡ Π² PIXI (x/y β ΡΠ΅Π½ΡΡ), position β Π»Π΅Π²ΡΠΉ-Π²Π΅ΡΡ
|
|
33
|
+
const pixiObject = this.coreMoodboard.pixi.objects.get(this.objectId);
|
|
34
|
+
if (pixiObject) {
|
|
35
|
+
const halfW = (pixiObject.width || 0) / 2;
|
|
36
|
+
const halfH = (pixiObject.height || 0) / 2;
|
|
37
|
+
pixiObject.x = position.x + halfW;
|
|
38
|
+
pixiObject.y = position.y + halfH;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ΠΠ±Π½ΠΎΠ²Π»ΡΠ΅ΠΌ ΠΏΠΎΠ·ΠΈΡΠΈΡ Π² ΡΠΎΡΡΠΎΡΠ½ΠΈΠΈ (Π»Π΅Π²ΡΠΉ-Π²Π΅ΡΡ
; Π±Π΅Π· ΡΠΌΠΈΡΠ° ΡΠΎΠ±ΡΡΠΈΡ)
|
|
42
|
+
const objects = this.coreMoodboard.state.state.objects;
|
|
43
|
+
const object = objects.find(obj => obj.id === this.objectId);
|
|
44
|
+
if (object) {
|
|
45
|
+
object.position = { ...position };
|
|
46
|
+
|
|
47
|
+
// ΠΠΎΠΌΠ΅ΡΠ°Π΅ΠΌ, ΡΡΠΎ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ ΡΠΆΠ΅ ΡΠΊΠΎΠΌΠΏΠ΅Π½ΡΠΈΡΠΎΠ²Π°Π½Ρ Π΄Π»Ρ pivot
|
|
48
|
+
if (!object.transform) {
|
|
49
|
+
object.transform = {};
|
|
50
|
+
}
|
|
51
|
+
object.transform.pivotCompensated = true;
|
|
52
|
+
|
|
53
|
+
this.coreMoodboard.state.markDirty();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Π£Π²Π΅Π΄ΠΎΠΌΠ»ΡΠ΅ΠΌ ΠΎ ΡΠΎΠΌ, ΡΡΠΎ ΠΎΠ±ΡΠ΅ΠΊΡ Π±ΡΠ» ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ (Π΄Π»Ρ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ ΡΡΡΠ΅ΠΊ)
|
|
57
|
+
if (this.eventBus) {
|
|
58
|
+
this.eventBus.emit(Events.Object.TransformUpdated, {
|
|
59
|
+
objectId: this.objectId,
|
|
60
|
+
type: 'position',
|
|
61
|
+
position: position
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* ΠΠΎΠΆΠ½ΠΎ Π»ΠΈ ΠΎΠ±ΡΠ΅Π΄ΠΈΠ½ΠΈΡΡ Ρ Π΄ΡΡΠ³ΠΎΠΉ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ ΠΏΠ΅ΡΠ΅ΠΌΠ΅ΡΠ΅Π½ΠΈΡ ΡΠΎΠ³ΠΎ ΠΆΠ΅ ΠΎΠ±ΡΠ΅ΠΊΡΠ°
|
|
68
|
+
*/
|
|
69
|
+
canMergeWith(otherCommand) {
|
|
70
|
+
return otherCommand instanceof MoveObjectCommand &&
|
|
71
|
+
otherCommand.objectId === this.objectId;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* ΠΠ±ΡΠ΅Π΄ΠΈΠ½ΠΈΡΡ Ρ Π΄ΡΡΠ³ΠΎΠΉ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ ΠΏΠ΅ΡΠ΅ΠΌΠ΅ΡΠ΅Π½ΠΈΡ
|
|
76
|
+
*/
|
|
77
|
+
mergeWith(otherCommand) {
|
|
78
|
+
if (!this.canMergeWith(otherCommand)) {
|
|
79
|
+
throw new Error('Cannot merge with this command');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ΠΠ±Π½ΠΎΠ²Π»ΡΠ΅ΠΌ ΠΊΠΎΠ½Π΅ΡΠ½ΡΡ ΠΏΠΎΠ·ΠΈΡΠΈΡ
|
|
83
|
+
this.newPosition = { ...otherCommand.newPosition };
|
|
84
|
+
this.description = `ΠΠ΅ΡΠ΅ΠΌΠ΅ΡΡΠΈΡΡ ΠΎΠ±ΡΠ΅ΠΊΡ (${Math.round(this.oldPosition.x)}, ${Math.round(this.oldPosition.y)}) β (${Math.round(this.newPosition.x)}, ${Math.round(this.newPosition.y)})`;
|
|
85
|
+
this.timestamp = otherCommand.timestamp; // ΠΠ±Π½ΠΎΠ²Π»ΡΠ΅ΠΌ Π²ΡΠ΅ΠΌΡ ΠΏΠΎΡΠ»Π΅Π΄Π½Π΅Π³ΠΎ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
2
|
+
import { Events } from '../events/Events.js';
|
|
3
|
+
import { generateObjectId } from '../../utils/objectIdGenerator.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ΠΠΎΠΌΠ°Π½Π΄Π° Π²ΡΡΠ°Π²ΠΊΠΈ ΠΎΠ±ΡΠ΅ΠΊΡΠ°
|
|
7
|
+
*/
|
|
8
|
+
export class PasteObjectCommand extends BaseCommand {
|
|
9
|
+
constructor(coreMoodboard, pastePosition = null) {
|
|
10
|
+
super();
|
|
11
|
+
this.coreMoodboard = coreMoodboard;
|
|
12
|
+
this.pastePosition = pastePosition;
|
|
13
|
+
this.newObjectId = null;
|
|
14
|
+
this.newObjectData = null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
execute() {
|
|
18
|
+
// ΠΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ, Π΅ΡΡΡ Π»ΠΈ ΡΡΠΎ-ΡΠΎ Π² Π±ΡΡΠ΅ΡΠ΅ ΠΎΠ±ΠΌΠ΅Π½Π°
|
|
19
|
+
if (!this.coreMoodboard.clipboard || this.coreMoodboard.clipboard.type !== 'object') {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ΠΠΎΠ»ΡΡΠ°Π΅ΠΌ Π΄Π°Π½Π½ΡΠ΅ ΠΈΠ· Π±ΡΡΠ΅ΡΠ° ΠΎΠ±ΠΌΠ΅Π½Π°
|
|
24
|
+
const originalData = this.coreMoodboard.clipboard.data;
|
|
25
|
+
|
|
26
|
+
// Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ Π½ΠΎΠ²ΡΠΉ ΠΎΠ±ΡΠ΅ΠΊΡ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ ΡΠΊΠΎΠΏΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠ³ΠΎ
|
|
27
|
+
this.newObjectData = JSON.parse(JSON.stringify(originalData));
|
|
28
|
+
|
|
29
|
+
// ΠΠ΅Π½Π΅ΡΠΈΡΡΠ΅ΠΌ ΡΠ½ΠΈΠΊΠ°Π»ΡΠ½ΡΠΉ ID Ρ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΎΠΉ ΠΊΠΎΠ»Π»ΠΈΠ·ΠΈΠΉ
|
|
30
|
+
const exists = (id) => {
|
|
31
|
+
const inState = (this.coreMoodboard.state.state.objects || []).some(o => o.id === id);
|
|
32
|
+
const inPixi = this.coreMoodboard.pixi?.objects?.has ? this.coreMoodboard.pixi.objects.has(id) : false;
|
|
33
|
+
return inState || inPixi;
|
|
34
|
+
};
|
|
35
|
+
this.newObjectId = generateObjectId(exists);
|
|
36
|
+
this.newObjectData.id = this.newObjectId;
|
|
37
|
+
|
|
38
|
+
// Π‘ΠΎΡ
ΡΠ°Π½ΡΠ΅ΠΌ ID ΠΎΡΠΈΠ³ΠΈΠ½Π°Π»ΡΠ½ΠΎΠ³ΠΎ ΠΎΠ±ΡΠ΅ΠΊΡΠ° Π΄Π»Ρ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΡ ΠΊΠΎΠΏΠΈΠΉ
|
|
39
|
+
this.newObjectData.originalId = originalData.id;
|
|
40
|
+
|
|
41
|
+
// Π£ΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ ΠΏΠΎΠ·ΠΈΡΠΈΡ Π²ΡΡΠ°Π²ΠΊΠΈ
|
|
42
|
+
if (this.pastePosition) {
|
|
43
|
+
this.newObjectData.position = { ...this.pastePosition };
|
|
44
|
+
} else {
|
|
45
|
+
// ΠΡΠ»ΠΈ ΠΏΠΎΠ·ΠΈΡΠΈΡ Π½Π΅ ΡΠΊΠ°Π·Π°Π½Π°, ΡΠΌΠ΅ΡΠ°Π΅ΠΌ ΠΎΡΠ½ΠΎΡΠΈΡΠ΅Π»ΡΠ½ΠΎ ΠΎΡΠΈΠ³ΠΈΠ½Π°Π»Π°
|
|
46
|
+
// ΠΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ, ΡΠΊΠΎΠ»ΡΠΊΠΎ ΠΊΠΎΠΏΠΈΠΉ ΡΡΠΎΠ³ΠΎ ΠΎΠ±ΡΠ΅ΠΊΡΠ° ΡΠΆΠ΅ ΡΠΎΠ·Π΄Π°Π½ΠΎ
|
|
47
|
+
const existingObjects = this.coreMoodboard.state.state.objects;
|
|
48
|
+
const originalId = originalData.id;
|
|
49
|
+
|
|
50
|
+
// ΠΡΠ΅ΠΌ Π²ΡΠ΅ ΠΊΠΎΠΏΠΈΠΈ ΡΡΠΎΠ³ΠΎ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΠΎΠ³ΠΎ ΠΎΠ±ΡΠ΅ΠΊΡΠ°
|
|
51
|
+
const copies = existingObjects.filter(obj =>
|
|
52
|
+
obj.originalId === originalId || // ΠΠΎΠΏΠΈΠΈ ΠΎΡΠΈΠ³ΠΈΠ½Π°Π»ΡΠ½ΠΎΠ³ΠΎ ΠΎΠ±ΡΠ΅ΠΊΡΠ°
|
|
53
|
+
(obj.id === originalId && obj.originalId) // ΠΡΠ»ΠΈ ΠΎΡΠΈΠ³ΠΈΠ½Π°Π» ΡΠ°ΠΌ ΡΠ²Π»ΡΠ΅ΡΡΡ ΠΊΠΎΠΏΠΈΠ΅ΠΉ
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Π Π°ΡΡΡΠΈΡΡΠ²Π°Π΅ΠΌ ΡΠΌΠ΅ΡΠ΅Π½ΠΈΠ΅ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²Π° ΠΊΠΎΠΏΠΈΠΉ
|
|
57
|
+
const offsetMultiplier = copies.length + 1;
|
|
58
|
+
const offsetStep = 25; // Π¨Π°Π³ ΡΠΌΠ΅ΡΠ΅Π½ΠΈΡ Π² ΠΏΠΈΠΊΡΠ΅Π»ΡΡ
|
|
59
|
+
|
|
60
|
+
console.log(`π ΠΡΡΠ°Π²ΠΊΠ° ΠΊΠΎΠΏΠΈΠΈ ΠΎΠ±ΡΠ΅ΠΊΡΠ° ${originalId}: Π½Π°ΠΉΠ΄Π΅Π½ΠΎ ${copies.length} ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΡ
ΠΊΠΎΠΏΠΈΠΉ, ΡΠΌΠ΅ΡΠ΅Π½ΠΈΠ΅ ${offsetStep * offsetMultiplier}px`);
|
|
61
|
+
|
|
62
|
+
this.newObjectData.position = {
|
|
63
|
+
x: originalData.position.x + (offsetStep * offsetMultiplier),
|
|
64
|
+
y: originalData.position.y + (offsetStep * offsetMultiplier)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Π‘Π±ΡΠ°ΡΡΠ²Π°Π΅ΠΌ ΡΠ»Π°Π³ ΠΊΠΎΠΌΠΏΠ΅Π½ΡΠ°ΡΠΈΠΈ pivot Π΄Π»Ρ Π½ΠΎΠ²ΠΎΠ³ΠΎ ΠΎΠ±ΡΠ΅ΠΊΡΠ°
|
|
69
|
+
if (!this.newObjectData.transform) {
|
|
70
|
+
this.newObjectData.transform = {};
|
|
71
|
+
}
|
|
72
|
+
this.newObjectData.transform.pivotCompensated = false;
|
|
73
|
+
|
|
74
|
+
// ΠΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ Π² ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅
|
|
75
|
+
this.coreMoodboard.state.addObject(this.newObjectData);
|
|
76
|
+
|
|
77
|
+
// Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ PIXI ΠΎΠ±ΡΠ΅ΠΊΡ
|
|
78
|
+
this.coreMoodboard.pixi.createObject(this.newObjectData);
|
|
79
|
+
|
|
80
|
+
this.emit(Events.Object.Pasted, {
|
|
81
|
+
originalId: originalData.id,
|
|
82
|
+
newId: this.newObjectId,
|
|
83
|
+
objectData: this.newObjectData
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
undo() {
|
|
88
|
+
if (this.newObjectId) {
|
|
89
|
+
// Π£Π΄Π°Π»ΡΠ΅ΠΌ ΡΠΎΠ·Π΄Π°Π½Π½ΡΠΉ ΠΎΠ±ΡΠ΅ΠΊΡ
|
|
90
|
+
this.coreMoodboard.state.removeObject(this.newObjectId);
|
|
91
|
+
this.coreMoodboard.pixi.removeObject(this.newObjectId);
|
|
92
|
+
|
|
93
|
+
// Π‘ΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΡΡΠ΅Π³ΠΎ ΠΊΠΎΠ½ΡΡΠ°Π½ΡΠ½ΠΎΠ³ΠΎ ΡΠΎΠ±ΡΡΠΈΡ Π½Π΅Ρ β ΠΎΡΡΠ°ΡΠΌΡΡ Π±Π΅Π· ΡΠΌΠΈΡΠ° ΠΈΠ»ΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ Object.Deleted, Π΅ΡΠ»ΠΈ Π½Π°Π΄ΠΎ Π³Π»ΠΎΠ±Π°Π»ΡΠ½ΠΎ
|
|
94
|
+
this.emit(Events.Object.Deleted, {
|
|
95
|
+
objectId: this.newObjectId
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
getDescription() {
|
|
101
|
+
return `ΠΡΡΠ°Π²ΠΈΡΡ ΠΎΠ±ΡΠ΅ΠΊΡ`;
|
|
102
|
+
}
|
|
103
|
+
}
|