@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
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Утилита для загрузки SVG иконок
|
|
3
|
+
*/
|
|
4
|
+
export class IconLoader {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.cache = new Map();
|
|
7
|
+
this.icons = {};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Инициализирует иконки при создании экземпляра
|
|
12
|
+
*/
|
|
13
|
+
async init() {
|
|
14
|
+
// Импортируем все SVG файлы статически
|
|
15
|
+
try {
|
|
16
|
+
// Используем динамический импорт для всех иконок
|
|
17
|
+
const iconModules = await Promise.all([
|
|
18
|
+
import('../assets/icons/select.svg?raw'),
|
|
19
|
+
import('../assets/icons/pan.svg?raw'),
|
|
20
|
+
import('../assets/icons/text-add.svg?raw'),
|
|
21
|
+
import('../assets/icons/note.svg?raw'),
|
|
22
|
+
import('../assets/icons/image.svg?raw'),
|
|
23
|
+
import('../assets/icons/shapes.svg?raw'),
|
|
24
|
+
import('../assets/icons/pencil.svg?raw'),
|
|
25
|
+
import('../assets/icons/comments.svg?raw'),
|
|
26
|
+
import('../assets/icons/attachments.svg?raw'),
|
|
27
|
+
import('../assets/icons/emoji.svg?raw'),
|
|
28
|
+
import('../assets/icons/frame.svg?raw'),
|
|
29
|
+
import('../assets/icons/clear.svg?raw'),
|
|
30
|
+
import('../assets/icons/undo.svg?raw'),
|
|
31
|
+
import('../assets/icons/redo.svg?raw')
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
// Сохраняем иконки в кэш
|
|
35
|
+
const iconNames = [
|
|
36
|
+
'select', 'pan', 'text-add', 'note', 'image', 'shapes',
|
|
37
|
+
'pencil', 'comments', 'attachments', 'emoji', 'frame',
|
|
38
|
+
'clear', 'undo', 'redo'
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
iconNames.forEach((name, index) => {
|
|
42
|
+
if (iconModules[index] && iconModules[index].default) {
|
|
43
|
+
this.icons[name] = iconModules[index].default;
|
|
44
|
+
this.cache.set(name, iconModules[index].default);
|
|
45
|
+
} else {
|
|
46
|
+
console.warn(`⚠️ Иконка ${name} не загружена, используем fallback`);
|
|
47
|
+
this.icons[name] = this.getFallbackIcon(name);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error('❌ Ошибка статической загрузки иконок:', error);
|
|
53
|
+
console.log('🔄 Пробуем загрузить встроенные SVG иконки...');
|
|
54
|
+
// В случае ошибки загружаем встроенные SVG иконки
|
|
55
|
+
this.loadBuiltInIcons();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Загружает встроенные SVG иконки (резервный метод)
|
|
61
|
+
*/
|
|
62
|
+
loadBuiltInIcons() {
|
|
63
|
+
const builtInIcons = {
|
|
64
|
+
'select': `<?xml version="1.0" encoding="UTF-8"?>
|
|
65
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
66
|
+
<!-- Symmetrical outline cursor arrow -->
|
|
67
|
+
<path d="M4 2 L4 18 L8 14 L12 22 L14 21 L10 13 L18 13 Z"
|
|
68
|
+
fill="none" stroke="currentColor" stroke-width="2"
|
|
69
|
+
stroke-linejoin="round" stroke-linecap="round"/>
|
|
70
|
+
</svg>`,
|
|
71
|
+
'pan': `<?xml version="1.0" encoding="UTF-8"?>
|
|
72
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
73
|
+
<path d="M8 5L8 19M16 5L16 19M5 8L19 8M5 16L19 16"
|
|
74
|
+
stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
75
|
+
</svg>`,
|
|
76
|
+
'text-add': `<?xml version="1.0" encoding="UTF-8"?>
|
|
77
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
78
|
+
<path d="M4 6H20M4 12H20M4 18H12M16 18V22M16 18H20"
|
|
79
|
+
stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
80
|
+
</svg>`,
|
|
81
|
+
'note': `<?xml version="1.0" encoding="UTF-8"?>
|
|
82
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
83
|
+
<rect x="3" y="3" width="18" height="18" rx="2" fill="#fbbf24" stroke="currentColor" stroke-width="2"/>
|
|
84
|
+
<path d="M8 8H16M8 12H16M8 16H12" stroke="currentColor" stroke-width="1.5"/>
|
|
85
|
+
</svg>`,
|
|
86
|
+
'image': `<?xml version="1.0" encoding="UTF-8"?>
|
|
87
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
88
|
+
<rect x="3" y="3" width="18" height="18" rx="2" fill="none" stroke="currentColor" stroke-width="2"/>
|
|
89
|
+
<circle cx="8.5" cy="8.5" r="1.5" fill="currentColor"/>
|
|
90
|
+
<path d="M21 15L16 10L5 21" stroke="currentColor" stroke-width="2"/>
|
|
91
|
+
</svg>`,
|
|
92
|
+
'shapes': `<?xml version="1.0" encoding="UTF-8"?>
|
|
93
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
94
|
+
<rect x="3" y="3" width="7" height="7" fill="currentColor"/>
|
|
95
|
+
<circle cx="17" cy="7" r="4" fill="currentColor"/>
|
|
96
|
+
<polygon points="12,17 15,21 9,21" fill="currentColor"/>
|
|
97
|
+
</svg>`,
|
|
98
|
+
'pencil': `<?xml version="1.0" encoding="UTF-8"?>
|
|
99
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
100
|
+
<path d="M17 3L21 7L7 21H3V17L17 3Z" fill="none" stroke="currentColor" stroke-width="2"/>
|
|
101
|
+
</svg>`,
|
|
102
|
+
'comments': `<?xml version="1.0" encoding="UTF-8"?>
|
|
103
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
104
|
+
<path d="M21 15C21 15.5304 20.7893 16.0391 20.4142 16.4142C20.0391 16.7893 19.5304 17 19 17H7L3 21V5C3 4.46957 3.21071 3.96086 3.58579 3.58579C3.96086 3.21071 4.46957 3 5 3H19C19.5304 3 20.0391 3.21071 20.4142 3.58579C20.7893 3.96086 21 4.46957 21 5V15Z"
|
|
105
|
+
fill="none" stroke="currentColor" stroke-width="2"/>
|
|
106
|
+
</svg>`,
|
|
107
|
+
'attachments': `<?xml version="1.0" encoding="UTF-8"?>
|
|
108
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
109
|
+
<path d="M21.44 11.05L12.25 20.24C11.1242 21.3658 9.59723 21.9983 8.00547 21.9983C6.41371 21.9983 4.88675 21.3658 3.76094 20.24C2.63513 19.1142 2.00269 17.5872 2.00269 15.9955C2.00269 14.4037 2.63513 12.8768 3.76094 11.751L12.951 2.56004C13.7006 1.81035 14.7169 1.38733 15.7781 1.38733C16.8394 1.38733 17.8557 1.81035 18.6053 2.56004C19.355 3.30973 19.778 4.32607 19.778 5.38733C19.778 6.44859 19.355 7.46493 18.6053 8.21462L9.41494 17.4056C9.03464 17.7859 8.52629 17.9999 7.99994 17.9999C7.47359 17.9999 6.96524 17.7859 6.58494 17.4056C6.20464 17.0253 5.99064 16.5169 5.99064 15.9906C5.99064 15.4642 6.20464 14.9559 6.58494 14.5756L15.366 5.79462"
|
|
110
|
+
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
111
|
+
</svg>`,
|
|
112
|
+
'emoji': `<?xml version="1.0" encoding="UTF-8"?>
|
|
113
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
114
|
+
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2"/>
|
|
115
|
+
<circle cx="8" cy="10" r="1" fill="currentColor"/>
|
|
116
|
+
<circle cx="16" cy="10" r="1" fill="currentColor"/>
|
|
117
|
+
<path d="M8 16C8 16 10 18 12 18C14 18 16 16 16 16" stroke="currentColor" stroke-width="2" fill="none"/>
|
|
118
|
+
</svg>`,
|
|
119
|
+
'frame': `<?xml version="1.0" encoding="UTF-8"?>
|
|
120
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
121
|
+
<rect x="3" y="3" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2"/>
|
|
122
|
+
</svg>`,
|
|
123
|
+
'clear': `<?xml version="1.0" encoding="UTF-8"?>
|
|
124
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
125
|
+
<path d="M3 6H5H21M8 6V4C8 3.46957 8.21071 2.96086 8.58579 2.58579C8.96086 2.21071 9.46957 2 10 2H14C14.5304 2 15.0391 2.21071 15.4142 2.58579C15.7893 2.96086 16 3.46957 16 4V6M19 6V20C19 20.5304 18.7893 21.0391 18.4142 21.4142C18.0391 21.7893 17.5304 22 17 22H7C6.46957 22 5.96086 21.7893 5.58579 21.4142C5.21071 21.0391 5 20.5304 5 20V6H19Z"
|
|
126
|
+
fill="none" stroke="currentColor" stroke-width="2"/>
|
|
127
|
+
</svg>`,
|
|
128
|
+
'undo': `<?xml version="1.0" encoding="UTF-8"?>
|
|
129
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
130
|
+
<path d="M9 14L4 9L9 4M20 20V13C20 11.9391 19.5786 10.9217 18.8284 10.1716C18.0783 9.42143 17.0609 9 16 9H4"
|
|
131
|
+
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
132
|
+
</svg>`,
|
|
133
|
+
'redo': `<?xml version="1.0" encoding="UTF-8"?>
|
|
134
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
135
|
+
<path d="M15 14L20 9L15 4M4 20V13C4 11.9391 4.42143 10.9217 5.17157 10.1716C5.92172 9.42143 6.93913 9 8 9H20"
|
|
136
|
+
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
137
|
+
</svg>`
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
Object.keys(builtInIcons).forEach(name => {
|
|
141
|
+
this.icons[name] = builtInIcons[name];
|
|
142
|
+
this.cache.set(name, builtInIcons[name]);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
console.log('📦 Загружены встроенные SVG иконки');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Загружает SVG иконку по имени
|
|
150
|
+
* @param {string} iconName - имя иконки без расширения
|
|
151
|
+
* @returns {Promise<string>} SVG содержимое
|
|
152
|
+
*/
|
|
153
|
+
async loadIcon(iconName) {
|
|
154
|
+
if (this.cache.has(iconName)) {
|
|
155
|
+
console.log(`📦 Загружаем иконку ${iconName} из кэша`);
|
|
156
|
+
return this.cache.get(iconName);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Если иконка уже загружена статически
|
|
160
|
+
if (this.icons[iconName]) {
|
|
161
|
+
console.log(`📦 Загружаем иконку ${iconName} из статического кэша`);
|
|
162
|
+
return this.icons[iconName];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Возвращаем fallback
|
|
166
|
+
console.warn(`⚠️ Иконка ${iconName} не найдена, используем fallback`);
|
|
167
|
+
return this.getFallbackIcon(iconName);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Загружает все иконки для тулбара
|
|
172
|
+
* @returns {Promise<Object>} объект с иконками
|
|
173
|
+
*/
|
|
174
|
+
async loadAllIcons() {
|
|
175
|
+
// Если иконки еще не инициализированы
|
|
176
|
+
if (Object.keys(this.icons).length === 0) {
|
|
177
|
+
await this.init();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return this.icons;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Загружает fallback иконки
|
|
185
|
+
*/
|
|
186
|
+
loadFallbackIcons() {
|
|
187
|
+
const iconNames = [
|
|
188
|
+
'select', 'pan', 'text-add', 'note', 'image', 'shapes',
|
|
189
|
+
'pencil', 'comments', 'attachments', 'emoji', 'frame',
|
|
190
|
+
'clear', 'undo', 'redo'
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
iconNames.forEach(name => {
|
|
194
|
+
this.icons[name] = this.getFallbackIcon(name);
|
|
195
|
+
this.cache.set(name, this.getFallbackIcon(name));
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
console.log('📦 Загружены fallback иконки');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Возвращает fallback иконку если загрузка не удалась
|
|
203
|
+
* @param {string} iconName - имя иконки
|
|
204
|
+
* @returns {string} fallback SVG
|
|
205
|
+
*/
|
|
206
|
+
getFallbackIcon(iconName) {
|
|
207
|
+
// Простые fallback иконки в виде геометрических фигур
|
|
208
|
+
const fallbacks = {
|
|
209
|
+
'select': '<svg width="20" height="20" viewBox="0 0 20 20"><circle cx="10" cy="10" r="8" fill="none" stroke="currentColor" stroke-width="2"/></svg>',
|
|
210
|
+
'pan': '<svg width="20" height="20" viewBox="0 0 20 20"><rect x="4" y="4" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2"/></svg>',
|
|
211
|
+
'text-add': '<svg width="20" height="20" viewBox="0 0 20 20"><text x="10" y="15" text-anchor="middle" font-size="16" fill="currentColor">T+</text></svg>',
|
|
212
|
+
'note': '<svg width="20" height="20" viewBox="0 0 20 20"><rect x="2" y="2" width="16" height="16" rx="2" fill="#fbbf24"/></svg>',
|
|
213
|
+
'image': '<svg width="20" height="20" viewBox="0 0 20 20"><rect x="2" y="2" width="16" height="16" rx="2" fill="none" stroke="currentColor" stroke-width="2"/></svg>',
|
|
214
|
+
'shapes': '<svg width="20" height="20" viewBox="0 0 20 20"><rect x="2" y="2" width="6" height="6"/><circle cx="14" cy="5" r="3"/><polygon points="10,14 13,18 7,18"/></svg>',
|
|
215
|
+
'pencil': '<svg width="20" height="20" viewBox="0 0 20 20"><path d="M2 14L14 2L18 6L6 18L2 18V14Z" fill="currentColor"/></svg>',
|
|
216
|
+
'comments': '<svg width="20" height="20" viewBox="0 0 20 20"><path d="M2 4C2 2.89543 2.89543 2 4 2H16C17.1046 2 18 2.89543 18 4V12C18 13.1046 17.1046 14 16 14H8L4 18V4Z" fill="currentColor"/></svg>',
|
|
217
|
+
'attachments': '<svg width="20" height="20" viewBox="0 0 20 20"><path d="M8 2C6.89543 2 6 2.89543 6 4V12C6 14.2091 7.79086 16 10 16C12.2091 16 14 14.2091 14 12V6C14 5.44772 13.5523 5 13 5C12.4477 5 12 5.44772 12 6V12C12 13.1046 11.1046 14 10 14C8.89543 14 8 13.1046 8 12V4C8 3.44772 8.44772 3 9 3C9.55228 3 10 3.44772 10 4V12C10 12.5523 9.55228 13 9 13C8.44772 13 8 12.5523 8 12V4Z" fill="currentColor"/></svg>',
|
|
218
|
+
'emoji': '<svg width="20" height="20" viewBox="0 0 20 20"><circle cx="10" cy="10" r="8" fill="none" stroke="currentColor" stroke-width="2"/><circle cx="7" cy="8" r="1"/><circle cx="13" cy="8" r="1"/><path d="M7 13C7 13 8.5 15 10 15C11.5 15 13 13 13 13" stroke="currentColor" stroke-width="1" fill="none"/></svg>',
|
|
219
|
+
'frame': '<svg width="20" height="20" viewBox="0 0 20 20"><rect x="2" y="2" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"/></svg>',
|
|
220
|
+
'clear': '<svg width="20" height="20" viewBox="0 0 20 20"><path d="M3 6H17L16 18H4L3 6Z" fill="currentColor"/></svg>',
|
|
221
|
+
'undo': '<svg width="20" height="20" viewBox="0 0 20 20"><path d="M8 4L3 9L8 14" stroke="currentColor" stroke-width="2" fill="none"/></svg>',
|
|
222
|
+
'redo': '<svg width="20" height="20" viewBox="0 0 20 20"><path d="M12 4L17 9L12 14" stroke="currentColor" stroke-width="2" fill="none"/></svg>'
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
return fallbacks[iconName] || fallbacks['select'];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Очищает кэш иконок
|
|
230
|
+
*/
|
|
231
|
+
clearCache() {
|
|
232
|
+
this.cache.clear();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Принудительно перезагружает иконку (игнорируя кэш)
|
|
237
|
+
* @param {string} iconName - имя иконки без расширения
|
|
238
|
+
* @returns {Promise<string>} SVG содержимое
|
|
239
|
+
*/
|
|
240
|
+
async reloadIcon(iconName) {
|
|
241
|
+
// Удаляем из кэша
|
|
242
|
+
console.log(`🗑️ Очищаем кэш для иконки ${iconName}`);
|
|
243
|
+
this.cache.delete(iconName);
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
// Пробуем переимпортировать иконку
|
|
247
|
+
const iconModule = await import(`../assets/icons/${iconName}.svg?raw`);
|
|
248
|
+
if (iconModule && iconModule.default) {
|
|
249
|
+
const svgContent = iconModule.default;
|
|
250
|
+
console.log(`✅ Иконка ${iconName} перезагружена успешно`);
|
|
251
|
+
this.icons[iconName] = svgContent;
|
|
252
|
+
this.cache.set(iconName, svgContent);
|
|
253
|
+
return svgContent;
|
|
254
|
+
} else {
|
|
255
|
+
throw new Error(`Failed to reload icon: ${iconName}`);
|
|
256
|
+
}
|
|
257
|
+
} catch (error) {
|
|
258
|
+
console.error(`❌ Ошибка перезагрузки иконки ${iconName}:`, error);
|
|
259
|
+
return this.getFallbackIcon(iconName);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Создаем глобальный экземпляр
|
|
265
|
+
export const iconLoader = new IconLoader();
|
|
266
|
+
|
|
267
|
+
// Добавляем в глобальную область для отладки
|
|
268
|
+
if (typeof window !== 'undefined') {
|
|
269
|
+
window.iconLoader = iconLoader;
|
|
270
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Генератор уникальных ID для объектов доски
|
|
3
|
+
* Принимает опциональную функцию existsFn(id) для проверки коллизий
|
|
4
|
+
*/
|
|
5
|
+
export function generateObjectId(existsFn = null) {
|
|
6
|
+
const makeId = () => 'obj_' + Date.now() + '_' + Math.random().toString(36).slice(2, 9);
|
|
7
|
+
let candidate = makeId();
|
|
8
|
+
if (typeof existsFn === 'function') {
|
|
9
|
+
// Проверяем коллизии (важно при массовой вставке в один тик)
|
|
10
|
+
while (existsFn(candidate)) {
|
|
11
|
+
candidate = makeId();
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return candidate;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Загрузчик SVG иконок для верхней панели
|
|
3
|
+
*/
|
|
4
|
+
export class TopbarIconLoader {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.icons = new Map();
|
|
7
|
+
this.init();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async init() {
|
|
11
|
+
try {
|
|
12
|
+
// Загружаем иконки через import.meta.glob
|
|
13
|
+
const iconModules = import.meta.glob('../assets/icons/topbar/*.svg', {
|
|
14
|
+
eager: true,
|
|
15
|
+
as: 'raw'
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
for (const [path, content] of Object.entries(iconModules)) {
|
|
19
|
+
const iconName = path.split('/').pop().replace('.svg', '');
|
|
20
|
+
this.icons.set(iconName, content);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.warn('Не удалось загрузить SVG иконки через import.meta.glob, используем встроенные:', error);
|
|
25
|
+
this.loadBuiltInIcons();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
loadBuiltInIcons() {
|
|
32
|
+
// Встроенные иконки как fallback
|
|
33
|
+
const builtInIcons = {
|
|
34
|
+
'grid-line': `<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
35
|
+
<path d="M2 2H16V4H2V2Z" fill="currentColor"/>
|
|
36
|
+
<path d="M2 7H16V9H2V7Z" fill="currentColor"/>
|
|
37
|
+
<path d="M2 12H16V14H2V12Z" fill="currentColor"/>
|
|
38
|
+
<path d="M2 2V16H4V2H2Z" fill="currentColor"/>
|
|
39
|
+
<path d="M7 2V16H9V2H7Z" fill="currentColor"/>
|
|
40
|
+
<path d="M12 2V16H14V2H12Z" fill="currentColor"/>
|
|
41
|
+
</svg>`,
|
|
42
|
+
'grid-dot': `<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
43
|
+
<circle cx="4" cy="4" r="1.5" fill="currentColor"/>
|
|
44
|
+
<circle cx="9" cy="4" r="1.5" fill="currentColor"/>
|
|
45
|
+
<circle cx="14" cy="4" r="1.5" fill="currentColor"/>
|
|
46
|
+
<circle cx="4" cy="9" r="1.5" fill="currentColor"/>
|
|
47
|
+
<circle cx="9" cy="9" r="1.5" fill="currentColor"/>
|
|
48
|
+
<circle cx="14" cy="9" r="1.5" fill="currentColor"/>
|
|
49
|
+
<circle cx="4" cy="14" r="1.5" fill="currentColor"/>
|
|
50
|
+
<circle cx="9" cy="14" r="1.5" fill="currentColor"/>
|
|
51
|
+
<circle cx="14" cy="14" r="1.5" fill="currentColor"/>
|
|
52
|
+
</svg>`,
|
|
53
|
+
'grid-cross': `<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
54
|
+
<path d="M3 3L6 6M6 3L3 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
55
|
+
<path d="M9 3L12 6M12 3L9 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
56
|
+
<path d="M3 9L6 12M6 9L3 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
57
|
+
<path d="M9 9L12 12M12 9L9 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
58
|
+
<path d="M15 3L18 6M18 3L15 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
59
|
+
<path d="M15 9L18 12M18 9L15 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
60
|
+
<path d="M3 15L6 18M6 15L3 18" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
61
|
+
<path d="M9 15L12 18M12 15L9 18" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
62
|
+
<path d="M15 15L18 18M18 15L15 18" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
63
|
+
</svg>`,
|
|
64
|
+
'grid-off': `<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
65
|
+
<path d="M2 2H16V4H2V2Z" fill="currentColor" opacity="0.3"/>
|
|
66
|
+
<path d="M2 7H16V9H2V7Z" fill="currentColor" opacity="0.3"/>
|
|
67
|
+
<path d="M2 12H16V14H2V12Z" fill="currentColor" opacity="0.3"/>
|
|
68
|
+
<path d="M2 2V16H4V2H2Z" fill="currentColor" opacity="0.3"/>
|
|
69
|
+
<path d="M7 2V16H9V2H7Z" fill="currentColor" opacity="0.3"/>
|
|
70
|
+
<path d="M12 2V16H14V2H12Z" fill="currentColor" opacity="0.3"/>
|
|
71
|
+
<path d="M1 17L17 1" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
72
|
+
</svg>`,
|
|
73
|
+
'paint': `<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
74
|
+
<path d="M4 3H10L13 6V13A2 2 0 0 1 11 15H6A2 2 0 0 1 4 13V3Z" stroke="currentColor" stroke-width="1.5" fill="none"/>
|
|
75
|
+
<path d="M10 3V6H13" stroke="currentColor" stroke-width="1.5"/>
|
|
76
|
+
<path d="M14 10S15.5 11.5 15.5 13A1.5 1.5 0 0 1 13 13C13 11.5 14 10 14 10Z" fill="currentColor" stroke="currentColor" stroke-width="0.5"/>
|
|
77
|
+
</svg>`
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Загружаем иконки статически
|
|
81
|
+
for (const [iconName, svgContent] of Object.entries(builtInIcons)) {
|
|
82
|
+
this.icons.set(iconName, svgContent);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Добавляем встроенные иконки в Map
|
|
86
|
+
Object.entries(builtInIcons).forEach(([name, content]) => {
|
|
87
|
+
this.icons.set(name, content);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
getIcon(name) {
|
|
92
|
+
return this.icons.get(name);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Загружает все иконки и возвращает их как объект
|
|
97
|
+
*/
|
|
98
|
+
async loadAllIcons() {
|
|
99
|
+
const result = {};
|
|
100
|
+
this.icons.forEach((content, name) => {
|
|
101
|
+
result[name] = content;
|
|
102
|
+
});
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
reloadIcon(name) {
|
|
107
|
+
// Перезагружаем конкретную иконку
|
|
108
|
+
this.init();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
clearCache() {
|
|
112
|
+
this.icons.clear();
|
|
113
|
+
}
|
|
114
|
+
}
|