@sequent-org/moodboard 1.2.119 → 1.3.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.
Files changed (122) hide show
  1. package/package.json +11 -1
  2. package/src/assets/icons/rotate-icon.svg +1 -1
  3. package/src/core/HistoryManager.js +16 -16
  4. package/src/core/KeyboardManager.js +48 -539
  5. package/src/core/PixiEngine.js +9 -9
  6. package/src/core/SaveManager.js +56 -31
  7. package/src/core/bootstrap/CoreInitializer.js +65 -0
  8. package/src/core/commands/DeleteObjectCommand.js +8 -0
  9. package/src/core/commands/GroupDeleteCommand.js +75 -0
  10. package/src/core/commands/GroupRotateCommand.js +6 -0
  11. package/src/core/commands/UpdateContentCommand.js +52 -0
  12. package/src/core/commands/UpdateFramePropertiesCommand.js +98 -0
  13. package/src/core/commands/UpdateFrameTypeCommand.js +85 -0
  14. package/src/core/commands/UpdateNoteStyleCommand.js +88 -0
  15. package/src/core/commands/UpdateTextStyleCommand.js +90 -0
  16. package/src/core/commands/index.js +6 -0
  17. package/src/core/events/Events.js +6 -0
  18. package/src/core/flows/ClipboardFlow.js +553 -0
  19. package/src/core/flows/LayerAndViewportFlow.js +283 -0
  20. package/src/core/flows/ObjectLifecycleFlow.js +336 -0
  21. package/src/core/flows/SaveFlow.js +34 -0
  22. package/src/core/flows/TransformFlow.js +277 -0
  23. package/src/core/flows/TransformFlowResizeHelpers.js +83 -0
  24. package/src/core/index.js +41 -1773
  25. package/src/core/keyboard/KeyboardClipboardImagePaste.js +190 -0
  26. package/src/core/keyboard/KeyboardContextGuards.js +35 -0
  27. package/src/core/keyboard/KeyboardEventRouter.js +92 -0
  28. package/src/core/keyboard/KeyboardSelectionActions.js +103 -0
  29. package/src/core/keyboard/KeyboardShortcutMap.js +31 -0
  30. package/src/core/keyboard/KeyboardToolSwitching.js +26 -0
  31. package/src/core/rendering/ObjectRenderer.js +3 -7
  32. package/src/grid/BaseGrid.js +26 -0
  33. package/src/grid/CrossGrid.js +7 -6
  34. package/src/grid/DotGrid.js +89 -33
  35. package/src/grid/DotGridZoomPhases.js +42 -0
  36. package/src/grid/LineGrid.js +22 -21
  37. package/src/moodboard/MoodBoard.js +31 -532
  38. package/src/moodboard/bootstrap/MoodBoardInitializer.js +47 -0
  39. package/src/moodboard/bootstrap/MoodBoardManagersFactory.js +38 -0
  40. package/src/moodboard/bootstrap/MoodBoardUiFactory.js +109 -0
  41. package/src/moodboard/integration/MoodBoardEventBindings.js +65 -0
  42. package/src/moodboard/integration/MoodBoardLoadApi.js +82 -0
  43. package/src/moodboard/integration/MoodBoardScreenshotApi.js +33 -0
  44. package/src/moodboard/integration/MoodBoardScreenshotCanvas.js +98 -0
  45. package/src/moodboard/lifecycle/MoodBoardDestroyer.js +97 -0
  46. package/src/objects/FileObject.js +17 -6
  47. package/src/objects/FrameObject.js +50 -10
  48. package/src/objects/NoteObject.js +5 -4
  49. package/src/services/BoardService.js +42 -2
  50. package/src/services/FrameService.js +83 -42
  51. package/src/services/ResizePolicyService.js +152 -0
  52. package/src/services/SettingsApplier.js +7 -2
  53. package/src/services/ZoomPanController.js +35 -9
  54. package/src/tools/ToolManager.js +30 -537
  55. package/src/tools/board-tools/PanTool.js +5 -11
  56. package/src/tools/manager/ToolActivationController.js +49 -0
  57. package/src/tools/manager/ToolEventRouter.js +396 -0
  58. package/src/tools/manager/ToolManagerGuards.js +33 -0
  59. package/src/tools/manager/ToolManagerLifecycle.js +110 -0
  60. package/src/tools/manager/ToolRegistry.js +33 -0
  61. package/src/tools/object-tools/DrawingTool.js +48 -14
  62. package/src/tools/object-tools/PlacementTool.js +50 -1049
  63. package/src/tools/object-tools/PlacementToolV2.js +88 -0
  64. package/src/tools/object-tools/SelectTool.js +174 -2681
  65. package/src/tools/object-tools/placement/GhostController.js +504 -0
  66. package/src/tools/object-tools/placement/PlacementCoordinateResolver.js +20 -0
  67. package/src/tools/object-tools/placement/PlacementEventsBridge.js +91 -0
  68. package/src/tools/object-tools/placement/PlacementInputRouter.js +267 -0
  69. package/src/tools/object-tools/placement/PlacementPayloadFactory.js +111 -0
  70. package/src/tools/object-tools/placement/PlacementSessionStore.js +18 -0
  71. package/src/tools/object-tools/selection/BoxSelectController.js +0 -5
  72. package/src/tools/object-tools/selection/CloneFlowController.js +71 -0
  73. package/src/tools/object-tools/selection/CoordinateMapper.js +10 -0
  74. package/src/tools/object-tools/selection/CursorController.js +78 -0
  75. package/src/tools/object-tools/selection/FileNameInlineEditorController.js +184 -0
  76. package/src/tools/object-tools/selection/HitTestService.js +102 -0
  77. package/src/tools/object-tools/selection/InlineEditorController.js +24 -0
  78. package/src/tools/object-tools/selection/InlineEditorDomFactory.js +50 -0
  79. package/src/tools/object-tools/selection/InlineEditorListenersRegistry.js +14 -0
  80. package/src/tools/object-tools/selection/InlineEditorPositioningService.js +25 -0
  81. package/src/tools/object-tools/selection/NoteInlineEditorController.js +113 -0
  82. package/src/tools/object-tools/selection/SelectInputRouter.js +267 -0
  83. package/src/tools/object-tools/selection/SelectToolLifecycleController.js +128 -0
  84. package/src/tools/object-tools/selection/SelectToolSetup.js +134 -0
  85. package/src/tools/object-tools/selection/SelectionOverlayService.js +81 -0
  86. package/src/tools/object-tools/selection/SelectionStateController.js +91 -0
  87. package/src/tools/object-tools/selection/TextEditorDomFactory.js +65 -0
  88. package/src/tools/object-tools/selection/TextEditorInteractionController.js +266 -0
  89. package/src/tools/object-tools/selection/TextEditorLifecycleRegistry.js +90 -0
  90. package/src/tools/object-tools/selection/TextEditorPositioningService.js +158 -0
  91. package/src/tools/object-tools/selection/TextEditorSyncService.js +110 -0
  92. package/src/tools/object-tools/selection/TextInlineEditorController.js +457 -0
  93. package/src/tools/object-tools/selection/TransformInteractionController.js +466 -0
  94. package/src/ui/FilePropertiesPanel.js +61 -32
  95. package/src/ui/FramePropertiesPanel.js +176 -101
  96. package/src/ui/HtmlHandlesLayer.js +121 -999
  97. package/src/ui/MapPanel.js +12 -7
  98. package/src/ui/NotePropertiesPanel.js +17 -2
  99. package/src/ui/TextPropertiesPanel.js +124 -738
  100. package/src/ui/Toolbar.js +71 -1180
  101. package/src/ui/Topbar.js +23 -25
  102. package/src/ui/ZoomPanel.js +16 -5
  103. package/src/ui/handles/GroupSelectionHandlesController.js +29 -0
  104. package/src/ui/handles/HandlesDomRenderer.js +278 -0
  105. package/src/ui/handles/HandlesEventBridge.js +102 -0
  106. package/src/ui/handles/HandlesInteractionController.js +772 -0
  107. package/src/ui/handles/HandlesPositioningService.js +206 -0
  108. package/src/ui/handles/SingleSelectionHandlesController.js +22 -0
  109. package/src/ui/styles/toolbar.css +2 -0
  110. package/src/ui/styles/workspace.css +13 -6
  111. package/src/ui/text-properties/TextPropertiesPanelBindings.js +92 -0
  112. package/src/ui/text-properties/TextPropertiesPanelEventBridge.js +77 -0
  113. package/src/ui/text-properties/TextPropertiesPanelMapper.js +173 -0
  114. package/src/ui/text-properties/TextPropertiesPanelRenderer.js +434 -0
  115. package/src/ui/text-properties/TextPropertiesPanelState.js +39 -0
  116. package/src/ui/toolbar/ToolbarActionRouter.js +193 -0
  117. package/src/ui/toolbar/ToolbarDialogsController.js +186 -0
  118. package/src/ui/toolbar/ToolbarPopupsController.js +662 -0
  119. package/src/ui/toolbar/ToolbarRenderer.js +97 -0
  120. package/src/ui/toolbar/ToolbarStateController.js +79 -0
  121. package/src/ui/toolbar/ToolbarTooltipController.js +52 -0
  122. package/src/utils/emojiLoaderNoBundler.js +1 -1
@@ -0,0 +1,434 @@
1
+ import {
2
+ BACKGROUND_COLOR_PRESETS,
3
+ FONT_OPTIONS,
4
+ FONT_SIZE_OPTIONS,
5
+ TEXT_COLOR_PRESETS,
6
+ } from './TextPropertiesPanelMapper.js';
7
+
8
+ export function createTextPropertiesPanelRenderer(panelInstance) {
9
+ const panel = document.createElement('div');
10
+ panel.className = 'text-properties-panel';
11
+
12
+ createFontControls(panelInstance, panel);
13
+
14
+ panelInstance.panel = panel;
15
+ return panel;
16
+ }
17
+
18
+ export function toggleColorDropdown(panelInstance) {
19
+ if (!panelInstance.colorDropdown) {
20
+ return;
21
+ }
22
+
23
+ if (panelInstance.colorDropdown.style.display === 'none') {
24
+ panelInstance.colorDropdown.style.display = 'block';
25
+ } else {
26
+ panelInstance.colorDropdown.style.display = 'none';
27
+ }
28
+ }
29
+
30
+ export function hideColorDropdown(panelInstance) {
31
+ if (panelInstance.colorDropdown) {
32
+ panelInstance.colorDropdown.style.display = 'none';
33
+ }
34
+ }
35
+
36
+ export function updateCurrentColorButton(panelInstance, color) {
37
+ if (panelInstance.currentColorButton) {
38
+ panelInstance.currentColorButton.style.backgroundColor = color;
39
+ panelInstance.currentColorButton.title = `Текущий цвет: ${color}`;
40
+ }
41
+ if (panelInstance.colorInput) {
42
+ panelInstance.colorInput.value = color;
43
+ }
44
+ }
45
+
46
+ export function toggleBgColorDropdown(panelInstance) {
47
+ if (!panelInstance.bgColorDropdown) {
48
+ return;
49
+ }
50
+
51
+ if (panelInstance.bgColorDropdown.style.display === 'none') {
52
+ panelInstance.bgColorDropdown.style.display = 'block';
53
+ } else {
54
+ panelInstance.bgColorDropdown.style.display = 'none';
55
+ }
56
+ }
57
+
58
+ export function hideBgColorDropdown(panelInstance) {
59
+ if (panelInstance.bgColorDropdown) {
60
+ panelInstance.bgColorDropdown.style.display = 'none';
61
+ }
62
+ }
63
+
64
+ export function updateCurrentBgColorButton(panelInstance, color) {
65
+ if (panelInstance.currentBgColorButton) {
66
+ if (color === 'transparent') {
67
+ panelInstance.currentBgColorButton.style.backgroundColor = 'white';
68
+ panelInstance.currentBgColorButton.title = 'Без выделения';
69
+
70
+ if (!panelInstance.currentBgColorButton.querySelector('div')) {
71
+ const line = document.createElement('div');
72
+ line.style.cssText = `
73
+ width: 20px;
74
+ height: 1px;
75
+ background: #ff0000;
76
+ transform: rotate(45deg);
77
+ position: absolute;
78
+ top: 50%;
79
+ left: 50%;
80
+ transform-origin: center;
81
+ transform: translate(-50%, -50%) rotate(45deg);
82
+ `;
83
+ panelInstance.currentBgColorButton.appendChild(line);
84
+ }
85
+ } else {
86
+ panelInstance.currentBgColorButton.style.backgroundColor = color;
87
+ panelInstance.currentBgColorButton.title = `Цвет выделения: ${color}`;
88
+ const line = panelInstance.currentBgColorButton.querySelector('div');
89
+ if (line) {
90
+ line.remove();
91
+ }
92
+ }
93
+ }
94
+
95
+ if (panelInstance.bgColorInput) {
96
+ panelInstance.bgColorInput.value = color === 'transparent' ? '#ffff99' : color;
97
+ }
98
+ }
99
+
100
+ function createFontControls(panelInstance, panel) {
101
+ const fontLabel = document.createElement('span');
102
+ fontLabel.textContent = 'Шрифт:';
103
+ fontLabel.className = 'tpp-label';
104
+ panel.appendChild(fontLabel);
105
+
106
+ panelInstance.fontSelect = document.createElement('select');
107
+ panelInstance.fontSelect.className = 'font-select';
108
+ panelInstance.fontSelect.className = 'font-select';
109
+
110
+ FONT_OPTIONS.forEach((font) => {
111
+ const option = document.createElement('option');
112
+ option.value = font.value;
113
+ option.textContent = font.name;
114
+ option.style.fontFamily = font.value;
115
+ panelInstance.fontSelect.appendChild(option);
116
+ });
117
+
118
+ panel.appendChild(panelInstance.fontSelect);
119
+
120
+ const sizeLabel = document.createElement('span');
121
+ sizeLabel.textContent = 'Размер:';
122
+ sizeLabel.className = 'tpp-label tpp-label--spaced';
123
+ panel.appendChild(sizeLabel);
124
+
125
+ panelInstance.fontSizeSelect = document.createElement('select');
126
+ panelInstance.fontSizeSelect.className = 'font-size-select';
127
+ panelInstance.fontSizeSelect.className = 'font-size-select';
128
+
129
+ FONT_SIZE_OPTIONS.forEach((size) => {
130
+ const option = document.createElement('option');
131
+ option.value = size;
132
+ option.textContent = `${size}px`;
133
+ panelInstance.fontSizeSelect.appendChild(option);
134
+ });
135
+
136
+ panel.appendChild(panelInstance.fontSizeSelect);
137
+
138
+ const colorLabel = document.createElement('span');
139
+ colorLabel.textContent = 'Цвет:';
140
+ colorLabel.className = 'tpp-label tpp-label--spaced';
141
+ panel.appendChild(colorLabel);
142
+
143
+ createCompactColorSelector(panelInstance, panel);
144
+
145
+ const bgColorLabel = document.createElement('span');
146
+ bgColorLabel.textContent = 'Фон:';
147
+ bgColorLabel.className = 'tpp-label tpp-label--spaced';
148
+ panel.appendChild(bgColorLabel);
149
+
150
+ createCompactBackgroundSelector(panelInstance, panel);
151
+ }
152
+
153
+ function createCompactColorSelector(panelInstance, panel) {
154
+ const colorSelectorContainer = document.createElement('div');
155
+ colorSelectorContainer.style.cssText = `
156
+ position: relative;
157
+ display: inline-block;
158
+ margin-left: 4px;
159
+ `;
160
+ panelInstance._colorSelectorContainer = colorSelectorContainer;
161
+
162
+ panelInstance.currentColorButton = document.createElement('button');
163
+ panelInstance.currentColorButton.type = 'button';
164
+ panelInstance.currentColorButton.title = 'Выбрать цвет';
165
+ panelInstance.currentColorButton.className = 'current-color-button';
166
+
167
+ panelInstance.colorDropdown = document.createElement('div');
168
+ panelInstance.colorDropdown.style.cssText = `
169
+ position: absolute;
170
+ top: 100%;
171
+ left: 0;
172
+ background: white;
173
+ border: 1px solid #ddd;
174
+ border-radius: 6px;
175
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
176
+ padding: 8px;
177
+ display: none;
178
+ z-index: 10000;
179
+ min-width: 200px;
180
+ `;
181
+
182
+ createColorGrid(panelInstance, panelInstance.colorDropdown);
183
+
184
+ colorSelectorContainer.appendChild(panelInstance.currentColorButton);
185
+ colorSelectorContainer.appendChild(panelInstance.colorDropdown);
186
+ panel.appendChild(colorSelectorContainer);
187
+ }
188
+
189
+ function createColorGrid(panelInstance, container) {
190
+ const presetsGrid = document.createElement('div');
191
+ presetsGrid.style.cssText = `
192
+ display: grid;
193
+ grid-template-columns: repeat(6, 28px);
194
+ gap: 6px;
195
+ margin-bottom: 8px;
196
+ align-items: center;
197
+ justify-items: center;
198
+ `;
199
+
200
+ panelInstance._colorPresetButtons = [];
201
+
202
+ TEXT_COLOR_PRESETS.forEach((preset) => {
203
+ const colorButton = document.createElement('button');
204
+ colorButton.type = 'button';
205
+ colorButton.title = preset.name;
206
+ colorButton.dataset.colorValue = preset.color;
207
+ colorButton.style.cssText = `
208
+ width: 28px;
209
+ height: 28px;
210
+ border: 1px solid #ddd;
211
+ border-radius: 50%;
212
+ background-color: ${preset.color};
213
+ cursor: pointer;
214
+ margin: 0;
215
+ padding: 0;
216
+ display: block;
217
+ box-sizing: border-box;
218
+ ${preset.color === '#ffffff' ? 'border-color: #ccc;' : ''}
219
+ position: relative;
220
+ `;
221
+
222
+ const tick = document.createElement('i');
223
+ tick.style.cssText = `
224
+ position: absolute;
225
+ left: 50%;
226
+ top: 50%;
227
+ width: 8px;
228
+ height: 5px;
229
+ transform: translate(-50%, -50%) rotate(315deg) scaleX(-1);
230
+ border-right: 2px solid #111;
231
+ border-bottom: 2px solid #111;
232
+ display: none;
233
+ pointer-events: none;
234
+ `;
235
+ colorButton.appendChild(tick);
236
+ presetsGrid.appendChild(colorButton);
237
+ panelInstance._colorPresetButtons.push(colorButton);
238
+ });
239
+
240
+ container.appendChild(presetsGrid);
241
+
242
+ const separator = document.createElement('div');
243
+ separator.style.cssText = `
244
+ height: 1px;
245
+ background: #eee;
246
+ margin: 8px 0;
247
+ `;
248
+ container.appendChild(separator);
249
+
250
+ const customContainer = document.createElement('div');
251
+ customContainer.style.cssText = `
252
+ display: flex;
253
+ align-items: center;
254
+ gap: 8px;
255
+ `;
256
+
257
+ const customLabel = document.createElement('span');
258
+ customLabel.textContent = 'Свой цвет:';
259
+ customLabel.style.cssText = `
260
+ font-size: 12px;
261
+ color: #666;
262
+ `;
263
+
264
+ panelInstance.colorInput = document.createElement('input');
265
+ panelInstance.colorInput.type = 'color';
266
+ panelInstance.colorInput.style.cssText = `
267
+ width: 32px;
268
+ height: 24px;
269
+ border: 1px solid #ddd;
270
+ border-radius: 3px;
271
+ cursor: pointer;
272
+ padding: 0;
273
+ `;
274
+
275
+ customContainer.appendChild(customLabel);
276
+ customContainer.appendChild(panelInstance.colorInput);
277
+ container.appendChild(customContainer);
278
+ }
279
+
280
+ function createCompactBackgroundSelector(panelInstance, panel) {
281
+ const bgSelectorContainer = document.createElement('div');
282
+ bgSelectorContainer.style.cssText = `
283
+ position: relative;
284
+ display: inline-block;
285
+ margin-left: 4px;
286
+ `;
287
+ panelInstance._bgSelectorContainer = bgSelectorContainer;
288
+
289
+ panelInstance.currentBgColorButton = document.createElement('button');
290
+ panelInstance.currentBgColorButton.type = 'button';
291
+ panelInstance.currentBgColorButton.title = 'Выбрать цвет выделения';
292
+ panelInstance.currentBgColorButton.className = 'current-bgcolor-button';
293
+
294
+ panelInstance.bgColorDropdown = document.createElement('div');
295
+ panelInstance.bgColorDropdown.style.cssText = `
296
+ position: absolute;
297
+ top: 100%;
298
+ left: 0;
299
+ background: white;
300
+ border: 1px solid #ddd;
301
+ border-radius: 6px;
302
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
303
+ padding: 8px;
304
+ display: none;
305
+ z-index: 10000;
306
+ min-width: 200px;
307
+ `;
308
+
309
+ createBackgroundColorGrid(panelInstance, panelInstance.bgColorDropdown);
310
+
311
+ bgSelectorContainer.appendChild(panelInstance.currentBgColorButton);
312
+ bgSelectorContainer.appendChild(panelInstance.bgColorDropdown);
313
+ panel.appendChild(bgSelectorContainer);
314
+ }
315
+
316
+ function createBackgroundColorGrid(panelInstance, container) {
317
+ const presetsGrid = document.createElement('div');
318
+ presetsGrid.style.cssText = `
319
+ display: grid;
320
+ grid-template-columns: repeat(6, 28px);
321
+ gap: 6px;
322
+ margin-bottom: 8px;
323
+ align-items: center;
324
+ justify-items: center;
325
+ `;
326
+
327
+ panelInstance._bgPresetButtons = [];
328
+
329
+ BACKGROUND_COLOR_PRESETS.forEach((preset) => {
330
+ const colorButton = document.createElement('button');
331
+ colorButton.type = 'button';
332
+ colorButton.title = preset.name;
333
+ colorButton.dataset.colorValue = preset.color;
334
+
335
+ if (preset.color === 'transparent') {
336
+ colorButton.style.cssText = `
337
+ width: 28px;
338
+ height: 28px;
339
+ border: 1px solid #ddd;
340
+ border-radius: 50%;
341
+ background: white;
342
+ cursor: pointer;
343
+ margin: 0;
344
+ padding: 0;
345
+ display: flex;
346
+ align-items: center;
347
+ justify-content: center;
348
+ box-sizing: border-box;
349
+ position: relative;
350
+ `;
351
+
352
+ const line = document.createElement('div');
353
+ line.style.cssText = `
354
+ width: 20px;
355
+ height: 1px;
356
+ background: #ff0000;
357
+ transform: rotate(45deg);
358
+ `;
359
+ colorButton.appendChild(line);
360
+ } else {
361
+ colorButton.style.cssText = `
362
+ width: 28px;
363
+ height: 28px;
364
+ border: 1px solid #ddd;
365
+ border-radius: 50%;
366
+ background-color: ${preset.color};
367
+ cursor: pointer;
368
+ margin: 0;
369
+ padding: 0;
370
+ display: block;
371
+ box-sizing: border-box;
372
+ ${preset.color === '#ffffff' ? 'border-color: #ccc;' : ''}
373
+ position: relative;
374
+ `;
375
+
376
+ const tick = document.createElement('i');
377
+ tick.style.cssText = `
378
+ position: absolute;
379
+ left: 50%;
380
+ top: 50%;
381
+ width: 8px;
382
+ height: 5px;
383
+ transform: translate(-50%, -50%) rotate(315deg) scaleX(-1);
384
+ border-right: 2px solid #111;
385
+ border-bottom: 2px solid #111;
386
+ display: none;
387
+ pointer-events: none;
388
+ `;
389
+ colorButton.appendChild(tick);
390
+ }
391
+
392
+ presetsGrid.appendChild(colorButton);
393
+ panelInstance._bgPresetButtons.push(colorButton);
394
+ });
395
+
396
+ container.appendChild(presetsGrid);
397
+
398
+ const separator = document.createElement('div');
399
+ separator.style.cssText = `
400
+ height: 1px;
401
+ background: #eee;
402
+ margin: 8px 0;
403
+ `;
404
+ container.appendChild(separator);
405
+
406
+ const customContainer = document.createElement('div');
407
+ customContainer.style.cssText = `
408
+ display: flex;
409
+ align-items: center;
410
+ gap: 8px;
411
+ `;
412
+
413
+ const customLabel = document.createElement('span');
414
+ customLabel.textContent = 'Свой цвет:';
415
+ customLabel.style.cssText = `
416
+ font-size: 12px;
417
+ color: #666;
418
+ `;
419
+
420
+ panelInstance.bgColorInput = document.createElement('input');
421
+ panelInstance.bgColorInput.type = 'color';
422
+ panelInstance.bgColorInput.style.cssText = `
423
+ width: 32px;
424
+ height: 24px;
425
+ border: 1px solid #ddd;
426
+ border-radius: 3px;
427
+ cursor: pointer;
428
+ padding: 0;
429
+ `;
430
+
431
+ customContainer.appendChild(customLabel);
432
+ customContainer.appendChild(panelInstance.bgColorInput);
433
+ container.appendChild(customContainer);
434
+ }
@@ -0,0 +1,39 @@
1
+ export function createTextPropertiesPanelState() {
2
+ return {
3
+ layer: null,
4
+ panel: null,
5
+ currentId: null,
6
+ isTextEditing: false,
7
+ fontSelect: null,
8
+ fontSizeSelect: null,
9
+ currentColorButton: null,
10
+ colorDropdown: null,
11
+ colorInput: null,
12
+ currentBgColorButton: null,
13
+ bgColorDropdown: null,
14
+ bgColorInput: null,
15
+ _bindingsAttached: false,
16
+ _eventBridgeAttached: false,
17
+ _eventBridgeHandlers: null,
18
+ _colorSelectorContainer: null,
19
+ _bgSelectorContainer: null,
20
+ _colorPresetButtons: [],
21
+ _bgPresetButtons: [],
22
+ _onColorDocumentClick: null,
23
+ _onBgDocumentClick: null,
24
+ };
25
+ }
26
+
27
+ export function resetCurrentSelection(panel) {
28
+ panel.currentId = null;
29
+ }
30
+
31
+ export function clearTextPropertiesPanelState(panel) {
32
+ panel.layer = null;
33
+ panel.currentId = null;
34
+ panel._bindingsAttached = false;
35
+ panel._eventBridgeAttached = false;
36
+ panel._eventBridgeHandlers = null;
37
+ panel._onColorDocumentClick = null;
38
+ panel._onBgDocumentClick = null;
39
+ }
@@ -0,0 +1,193 @@
1
+ import { Events } from '../../core/events/Events.js';
2
+
3
+ export class ToolbarActionRouter {
4
+ constructor(toolbar) {
5
+ this.toolbar = toolbar;
6
+ }
7
+
8
+ routeToolbarAction(button, toolType, toolId) {
9
+ if (toolType === 'undo') {
10
+ this.toolbar.eventBus.emit(Events.Keyboard.Undo);
11
+ this.toolbar.animateButton(button);
12
+ return true;
13
+ }
14
+
15
+ if (toolType === 'redo') {
16
+ this.toolbar.eventBus.emit(Events.Keyboard.Redo);
17
+ this.toolbar.animateButton(button);
18
+ return true;
19
+ }
20
+
21
+ if (toolType === 'activate-select') {
22
+ this.toolbar.animateButton(button);
23
+ this.toolbar.closeShapesPopup();
24
+ this.toolbar.closeDrawPopup();
25
+ this.toolbar.closeEmojiPopup();
26
+ this.toolbar.eventBus.emit(Events.Place.Set, null);
27
+ this.toolbar.placeSelectedButtonId = null;
28
+ this.toolbar.eventBus.emit(Events.Keyboard.ToolSelect, { tool: 'select' });
29
+ this.toolbar.setActiveToolbarButton('select');
30
+ return true;
31
+ }
32
+
33
+ if (toolType === 'activate-pan') {
34
+ this.toolbar.animateButton(button);
35
+ this.toolbar.closeShapesPopup();
36
+ this.toolbar.closeDrawPopup();
37
+ this.toolbar.closeEmojiPopup();
38
+ this.toolbar.eventBus.emit(Events.Keyboard.ToolSelect, { tool: 'pan' });
39
+ this.toolbar.setActiveToolbarButton('pan');
40
+ return true;
41
+ }
42
+
43
+ if (toolType === 'text-add') {
44
+ this.toolbar.animateButton(button);
45
+ this.toolbar.closeShapesPopup();
46
+ this.toolbar.closeDrawPopup();
47
+ this.toolbar.closeEmojiPopup();
48
+ this.toolbar.eventBus.emit(Events.Keyboard.ToolSelect, { tool: 'place' });
49
+ this.toolbar.placeSelectedButtonId = 'text';
50
+ this.toolbar.setActiveToolbarButton('place');
51
+ this.toolbar.eventBus.emit(Events.Place.Set, {
52
+ type: 'text',
53
+ properties: { editOnCreate: true, fontSize: 18 }
54
+ });
55
+ return true;
56
+ }
57
+
58
+ if (toolType === 'note-add') {
59
+ this.toolbar.animateButton(button);
60
+ this.toolbar.closeShapesPopup();
61
+ this.toolbar.closeDrawPopup();
62
+ this.toolbar.closeEmojiPopup();
63
+ this.toolbar.eventBus.emit(Events.Keyboard.ToolSelect, { tool: 'place' });
64
+ this.toolbar.placeSelectedButtonId = 'note';
65
+ this.toolbar.setActiveToolbarButton('place');
66
+ this.toolbar.eventBus.emit(Events.Place.Set, {
67
+ type: 'note',
68
+ properties: {
69
+ content: 'Новая записка',
70
+ fontFamily: 'Caveat, Arial, cursive',
71
+ fontSize: 32,
72
+ width: 250,
73
+ height: 250
74
+ }
75
+ });
76
+ return true;
77
+ }
78
+
79
+ if (toolType === 'frame') {
80
+ this.toolbar.animateButton(button);
81
+ this.toolbar.toggleFramePopup(button);
82
+ this.toolbar.closeShapesPopup();
83
+ this.toolbar.closeDrawPopup();
84
+ this.toolbar.closeEmojiPopup();
85
+ this.toolbar.eventBus.emit(Events.Keyboard.ToolSelect, { tool: 'place' });
86
+ this.toolbar.placeSelectedButtonId = 'frame';
87
+ this.toolbar.setActiveToolbarButton('place');
88
+ // Сразу включаем произвольный фрейм; в popup можно выбрать A4, 1:1 и т.д.
89
+ this.toolbar.eventBus.emit(Events.Place.Set, { type: 'frame-draw', properties: {} });
90
+ return true;
91
+ }
92
+
93
+ if (toolType === 'image-add') {
94
+ this.toolbar.animateButton(button);
95
+ this.toolbar.closeShapesPopup();
96
+ this.toolbar.closeDrawPopup();
97
+ this.toolbar.closeEmojiPopup();
98
+ this.toolbar.openImageDialog();
99
+ return true;
100
+ }
101
+
102
+ if (toolType === 'image2-add') {
103
+ this.toolbar.animateButton(button);
104
+ this.toolbar.closeShapesPopup();
105
+ this.toolbar.closeDrawPopup();
106
+ this.toolbar.closeEmojiPopup();
107
+ this.toolbar.openImageObject2Dialog();
108
+ return true;
109
+ }
110
+
111
+ if (toolType === 'custom-comments') {
112
+ this.toolbar.animateButton(button);
113
+ this.toolbar.closeShapesPopup();
114
+ this.toolbar.closeDrawPopup();
115
+ this.toolbar.closeEmojiPopup();
116
+ this.toolbar.eventBus.emit(Events.Keyboard.ToolSelect, { tool: 'place' });
117
+ this.toolbar.placeSelectedButtonId = 'comments';
118
+ this.toolbar.setActiveToolbarButton('place');
119
+ this.toolbar.eventBus.emit(Events.Place.Set, { type: 'comment', properties: { width: 72, height: 72 } });
120
+ return true;
121
+ }
122
+
123
+ if (toolType === 'custom-attachments') {
124
+ this.toolbar.animateButton(button);
125
+ this.toolbar.closeShapesPopup();
126
+ this.toolbar.closeDrawPopup();
127
+ this.toolbar.closeEmojiPopup();
128
+ this.toolbar.openFileDialog();
129
+ return true;
130
+ }
131
+
132
+ if (toolType === 'custom-frame') {
133
+ this.toolbar.animateButton(button);
134
+ this.toolbar.closeShapesPopup();
135
+ this.toolbar.closeDrawPopup();
136
+ this.toolbar.closeEmojiPopup();
137
+ this.toolbar.eventBus.emit(Events.Keyboard.ToolSelect, { tool: 'place' });
138
+ this.toolbar.placeSelectedButtonId = 'frame-tool';
139
+ this.toolbar.setActiveToolbarButton('place');
140
+ this.toolbar.eventBus.emit(Events.Place.Set, {
141
+ type: 'frame',
142
+ properties: { width: 200, height: 300 }
143
+ });
144
+ return true;
145
+ }
146
+
147
+ if (toolType === 'custom-shapes') {
148
+ this.toolbar.animateButton(button);
149
+ this.toolbar.toggleShapesPopup(button);
150
+ this.toolbar.closeDrawPopup();
151
+ this.toolbar.closeEmojiPopup();
152
+ this.toolbar.eventBus.emit(Events.Keyboard.ToolSelect, { tool: 'place' });
153
+ this.toolbar.placeSelectedButtonId = 'shapes';
154
+ this.toolbar.setActiveToolbarButton('place');
155
+ return true;
156
+ }
157
+
158
+ if (toolType === 'custom-draw') {
159
+ this.toolbar.animateButton(button);
160
+ this.toolbar.toggleDrawPopup(button);
161
+ this.toolbar.closeShapesPopup();
162
+ this.toolbar.closeEmojiPopup();
163
+ this.toolbar.eventBus.emit(Events.Keyboard.ToolSelect, { tool: 'draw' });
164
+ this.toolbar.setActiveToolbarButton('draw');
165
+ return true;
166
+ }
167
+
168
+ if (toolType === 'custom-emoji') {
169
+ this.toolbar.animateButton(button);
170
+ this.toolbar.toggleEmojiPopup(button);
171
+ this.toolbar.closeShapesPopup();
172
+ this.toolbar.closeDrawPopup();
173
+ this.toolbar.eventBus.emit(Events.Keyboard.ToolSelect, { tool: 'place' });
174
+ this.toolbar.placeSelectedButtonId = 'emoji';
175
+ this.toolbar.setActiveToolbarButton('place');
176
+ return true;
177
+ }
178
+
179
+ if (toolType === 'clear') {
180
+ this.toolbar.animateButton(button);
181
+ this.toolbar.showClearConfirmation();
182
+ return true;
183
+ }
184
+
185
+ this.toolbar.eventBus.emit(Events.UI.ToolbarAction, {
186
+ type: toolType,
187
+ id: toolId,
188
+ position: this.toolbar.getRandomPosition()
189
+ });
190
+ this.toolbar.animateButton(button);
191
+ return true;
192
+ }
193
+ }