@sequent-org/moodboard 1.4.31 → 1.4.33

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 (139) hide show
  1. package/package.json +5 -1
  2. package/src/assets/fonts/inter/inter-cyrillic-400-normal.woff2 +0 -0
  3. package/src/assets/fonts/inter/inter-cyrillic-500-normal.woff2 +0 -0
  4. package/src/assets/fonts/inter/inter-latin-400-normal.woff2 +0 -0
  5. package/src/assets/fonts/inter/inter-latin-500-normal.woff2 +0 -0
  6. package/src/assets/icons/attachments.svg +3 -1
  7. package/src/assets/icons/comments.svg +2 -2
  8. package/src/assets/icons/connector.svg +6 -0
  9. package/src/assets/icons/emoji.svg +6 -1
  10. package/src/assets/icons/frame.svg +4 -1
  11. package/src/assets/icons/image.svg +5 -1
  12. package/src/assets/icons/laser.svg +1 -0
  13. package/src/assets/icons/lasso.svg +5 -0
  14. package/src/assets/icons/mindmap.svg +10 -2
  15. package/src/assets/icons/note.svg +4 -1
  16. package/src/assets/icons/pan.svg +5 -2
  17. package/src/assets/icons/pencil.svg +4 -1
  18. package/src/assets/icons/reactions.svg +5 -0
  19. package/src/assets/icons/redo.svg +3 -2
  20. package/src/assets/icons/select.svg +2 -8
  21. package/src/assets/icons/shapes.svg +5 -1
  22. package/src/assets/icons/text-add.svg +15 -1
  23. package/src/assets/icons/undo.svg +3 -2
  24. package/src/assets/reactions/1f44d.svg +20 -0
  25. package/src/assets/reactions/1f44e.svg +20 -0
  26. package/src/assets/reactions/2705.svg +20 -0
  27. package/src/assets/reactions/274c.svg +19 -0
  28. package/src/assets/reactions/2753.svg +20 -0
  29. package/src/assets/reactions/2764.svg +22 -0
  30. package/src/assets/reactions/2b50.svg +19 -0
  31. package/src/assets/reactions/plus-one.svg +25 -0
  32. package/src/core/PixiEngine.js +23 -0
  33. package/src/core/bootstrap/CoreInitializer.js +43 -0
  34. package/src/core/commands/GroupDeleteCommand.js +13 -1
  35. package/src/core/commands/UpdateShapeStyleCommand.js +121 -0
  36. package/src/core/commands/UpdateTextStyleCommand.js +17 -6
  37. package/src/core/commands/index.js +3 -0
  38. package/src/core/events/Events.js +22 -0
  39. package/src/core/flows/LayerAndViewportFlow.js +1 -0
  40. package/src/core/flows/ObjectLifecycleFlow.js +155 -7
  41. package/src/core/index.js +28 -1
  42. package/src/grid/CrossGridZoomPhases.js +3 -3
  43. package/src/initNoBundler.js +1 -1
  44. package/src/moodboard/DataManager.js +28 -0
  45. package/src/moodboard/MoodBoard.js +27 -0
  46. package/src/moodboard/bootstrap/MoodBoardInitializer.js +69 -1
  47. package/src/moodboard/bootstrap/MoodBoardUiFactory.js +22 -4
  48. package/src/moodboard/integration/MoodBoardEventBindings.js +5 -1
  49. package/src/moodboard/integration/MoodBoardLoadApi.js +10 -1
  50. package/src/moodboard/lifecycle/MoodBoardDestroyer.js +9 -0
  51. package/src/objects/ConnectorObject.js +2 -2
  52. package/src/objects/FrameObject.js +119 -59
  53. package/src/objects/ShapeObject.js +49 -74
  54. package/src/objects/shape/ShapeDrawer.js +210 -0
  55. package/src/services/ConnectorBindingResolver.js +112 -0
  56. package/src/services/ConnectorRouter.js +210 -0
  57. package/src/services/comments/CommentService.js +344 -0
  58. package/src/tools/object-tools/CommentTool.js +85 -0
  59. package/src/tools/object-tools/DrawingTool.js +110 -10
  60. package/src/tools/object-tools/LaserPointerTool.js +121 -0
  61. package/src/tools/object-tools/SelectTool.js +25 -1
  62. package/src/tools/object-tools/TextTool.js +6 -1
  63. package/src/tools/object-tools/connector/ConnectorDragController.js +50 -3
  64. package/src/tools/object-tools/connector/connectorGesture.js +33 -19
  65. package/src/tools/object-tools/placement/PlacementInputRouter.js +22 -1
  66. package/src/tools/object-tools/selection/BoxSelectController.js +24 -2
  67. package/src/tools/object-tools/selection/FrameTitleInlineEditorController.js +139 -0
  68. package/src/tools/object-tools/selection/InlineEditorController.js +12 -0
  69. package/src/tools/object-tools/selection/InlineEditorDomFactory.js +36 -0
  70. package/src/tools/object-tools/selection/LassoSelectController.js +125 -0
  71. package/src/tools/object-tools/selection/MindmapInlineEditorController.js +1 -0
  72. package/src/tools/object-tools/selection/SelectInputRouter.js +64 -5
  73. package/src/tools/object-tools/selection/SelectToolLifecycleController.js +11 -1
  74. package/src/tools/object-tools/selection/SelectToolSetup.js +13 -1
  75. package/src/tools/object-tools/selection/TextEditorInteractionController.js +46 -12
  76. package/src/tools/object-tools/selection/TextEditorSyncService.js +1 -0
  77. package/src/tools/object-tools/selection/TextInlineEditorController.js +65 -6
  78. package/src/ui/CommentPopover.js +6 -0
  79. package/src/ui/CommentsBar.js +91 -0
  80. package/src/ui/ConnectorPropertiesPanel.js +150 -0
  81. package/src/ui/ContextMenu.js +25 -0
  82. package/src/ui/DrawingPropertiesPanel.js +362 -0
  83. package/src/ui/FilePropertiesPanel.js +5 -0
  84. package/src/ui/FramePropertiesPanel.js +5 -0
  85. package/src/ui/HtmlTextLayer.js +246 -66
  86. package/src/ui/NotePropertiesPanel.js +6 -0
  87. package/src/ui/ShapePropertiesPanel.js +307 -0
  88. package/src/ui/TextPropertiesPanel.js +100 -1
  89. package/src/ui/Toolbar.js +25 -2
  90. package/src/ui/Topbar.js +2 -2
  91. package/src/ui/animation/HoverLiftController.js +6 -7
  92. package/src/ui/chat/ChatComposer.js +59 -12
  93. package/src/ui/chat/ChatExtendedPromptModal.js +1 -12
  94. package/src/ui/chat/ChatWindow.js +60 -144
  95. package/src/ui/chat/ChatWindowRenderer.js +1 -8
  96. package/src/ui/chat/icons.js +0 -4
  97. package/src/ui/comments/CommentListPanel.js +213 -0
  98. package/src/ui/comments/CommentPinLayer.js +448 -0
  99. package/src/ui/comments/CommentThreadPopover.js +539 -0
  100. package/src/ui/comments/commentFormat.js +32 -0
  101. package/src/ui/connector-properties/ConnectorPropertiesPanelBindings.js +223 -0
  102. package/src/ui/connector-properties/ConnectorPropertiesPanelEventBridge.js +114 -0
  103. package/src/ui/connector-properties/ConnectorPropertiesPanelMapper.js +144 -0
  104. package/src/ui/connector-properties/ConnectorPropertiesPanelRenderer.js +447 -0
  105. package/src/ui/connector-properties/ConnectorPropertiesPanelState.js +61 -0
  106. package/src/ui/connectors/ConnectionAnchorsLayer.js +1 -0
  107. package/src/ui/connectors/ConnectorHandlesLayer.js +321 -0
  108. package/src/ui/connectors/ConnectorLabelLayer.js +334 -0
  109. package/src/ui/connectors/ConnectorLayer.js +264 -57
  110. package/src/ui/handles/HandlesDomRenderer.js +5 -13
  111. package/src/ui/handles/HandlesEventBridge.js +1 -0
  112. package/src/ui/handles/SingleSelectionHandlesController.js +4 -0
  113. package/src/ui/mindmap/MindmapCollapseLayer.js +1 -0
  114. package/src/ui/mindmap/MindmapConnectionLayer.js +1 -0
  115. package/src/ui/mindmap/MindmapHtmlTextLayer.js +6 -0
  116. package/src/ui/shape-properties/ShapePropertiesPanelDom.js +533 -0
  117. package/src/ui/shape-properties/ShapePropertiesPanelSync.js +132 -0
  118. package/src/ui/styles/chat.css +682 -28
  119. package/src/ui/styles/index.css +1 -0
  120. package/src/ui/styles/panels.css +112 -2
  121. package/src/ui/styles/shape-properties-panel.css +250 -0
  122. package/src/ui/styles/toolbar.css +7 -2
  123. package/src/ui/styles/topbar.css +1 -1
  124. package/src/ui/styles/workspace.css +257 -6
  125. package/src/ui/text-properties/TextFormatControls.js +88 -0
  126. package/src/ui/text-properties/TextListRenderer.js +137 -0
  127. package/src/ui/text-properties/TextPropertiesPanelBindings.js +27 -0
  128. package/src/ui/text-properties/TextPropertiesPanelEventBridge.js +3 -1
  129. package/src/ui/text-properties/TextPropertiesPanelMapper.js +56 -0
  130. package/src/ui/text-properties/TextPropertiesPanelRenderer.js +24 -0
  131. package/src/ui/text-properties/TextPropertiesPanelState.js +8 -0
  132. package/src/ui/toolbar/ReactionsPopupController.js +88 -0
  133. package/src/ui/toolbar/ToolbarActionRouter.js +71 -5
  134. package/src/ui/toolbar/ToolbarPopupsController.js +120 -118
  135. package/src/ui/toolbar/ToolbarRenderer.js +9 -1
  136. package/src/ui/toolbar/ToolbarStateController.js +4 -1
  137. package/src/utils/iconLoader.js +17 -16
  138. package/src/utils/markdown.js +14 -0
  139. package/src/utils/richText.js +125 -0
@@ -0,0 +1,447 @@
1
+ import {
2
+ STROKE_COLOR_PRESETS,
3
+ WIDTH_PRESETS,
4
+ ROUTE_OPTIONS,
5
+ HEAD_OPTIONS,
6
+ pixiColorToHex,
7
+ } from './ConnectorPropertiesPanelMapper.js';
8
+
9
+ // ── Создание панели ──────────────────────────────────────────────────────────
10
+
11
+ export function createConnectorPropertiesPanelDom(inst) {
12
+ const panel = document.createElement('div');
13
+ panel.className = 'connector-properties-panel';
14
+ panel.id = 'connector-properties-panel';
15
+ Object.assign(panel.style, {
16
+ position: 'absolute',
17
+ display: 'none',
18
+ flexDirection: 'column',
19
+ alignItems: 'stretch',
20
+ backgroundColor: 'white',
21
+ border: '1px solid #e0e0e0',
22
+ borderRadius: '12px',
23
+ boxShadow: '0 6px 24px rgba(0,0,0,0.16)',
24
+ fontSize: '12px',
25
+ fontFamily: 'Arial, sans-serif',
26
+ zIndex: '10000',
27
+ whiteSpace: 'nowrap',
28
+ userSelect: 'none',
29
+ minWidth: '0',
30
+ });
31
+
32
+ // Основной ряд (все существующие контролы)
33
+ const mainRow = document.createElement('div');
34
+ Object.assign(mainRow.style, {
35
+ display: 'flex',
36
+ flexDirection: 'row',
37
+ alignItems: 'center',
38
+ gap: '4px',
39
+ padding: '6px 16px',
40
+ height: '40px',
41
+ });
42
+
43
+ _appendSep(mainRow);
44
+ _createStrokeControl(inst, mainRow);
45
+ _appendSep(mainRow);
46
+ _createWidthControl(inst, mainRow);
47
+ _appendSep(mainRow);
48
+ _createRouteControl(inst, mainRow);
49
+ _appendSep(mainRow);
50
+ _createDashControl(inst, mainRow);
51
+ _appendSep(mainRow);
52
+ _createHeadControl(inst, mainRow);
53
+ _appendSep(mainRow);
54
+ _createActionButtons(inst, mainRow);
55
+ panel.appendChild(mainRow);
56
+ inst._mainRow = mainRow;
57
+
58
+ // Второй ряд — контролы текстовой метки (скрыт по умолчанию)
59
+ const labelRow = _createLabelRow(inst);
60
+ labelRow.style.display = 'none';
61
+ inst._labelRow = labelRow;
62
+ panel.appendChild(labelRow);
63
+
64
+ inst.panel = panel;
65
+ return panel;
66
+ }
67
+
68
+ // ── Обновление контролов из данных объекта ───────────────────────────────────
69
+
70
+ export function updateConnectorPanelControls(inst, style) {
71
+ // Цвет линии
72
+ if (inst.strokeColorButton) {
73
+ const hex = pixiColorToHex(style.stroke);
74
+ inst.strokeColorButton.style.backgroundColor = hex;
75
+ if (inst.strokeColorInput) inst.strokeColorInput.value = hex;
76
+ }
77
+
78
+ // Ширина
79
+ inst.widthButtons.forEach(btn => {
80
+ const active = btn.dataset.width === String(style.width);
81
+ btn.style.fontWeight = active ? 'bold' : 'normal';
82
+ btn.style.backgroundColor = active ? '#EFF6FF' : '';
83
+ btn.style.borderColor = active ? '#2563EB' : '#ddd';
84
+ });
85
+
86
+ // Маршрут
87
+ if (inst.routeSelect) inst.routeSelect.value = style.route;
88
+
89
+ // Пунктир
90
+ if (inst.dashButton) {
91
+ inst.dashButton.style.fontWeight = style.dash ? 'bold' : 'normal';
92
+ inst.dashButton.style.backgroundColor = style.dash ? '#EFF6FF' : '';
93
+ inst.dashButton.style.borderColor = style.dash ? '#2563EB' : '#ddd';
94
+ }
95
+
96
+ // Наконечники
97
+ if (inst.headEndSelect) inst.headEndSelect.value = style.head?.end ?? 'arrow';
98
+ if (inst.headStartSelect) inst.headStartSelect.value = style.head?.start ?? 'none';
99
+ }
100
+
101
+ /**
102
+ * Обновляет видимость и значения второго ряда (label controls).
103
+ * Вызывается из _updateControlsFromObject.
104
+ */
105
+ export function updateLabelRow(inst, connector) {
106
+ const label = connector?.properties?.style?.label;
107
+ if (!inst._labelRow) return;
108
+ if (!label) {
109
+ inst._labelRow.style.display = 'none';
110
+ return;
111
+ }
112
+ inst._labelRow.style.display = 'flex';
113
+ if (inst._labelColorBtn) {
114
+ inst._labelColorBtn.style.backgroundColor = pixiColorToHex(label.color ?? 0x212121);
115
+ }
116
+ if (inst._labelSizeDisplay) {
117
+ inst._labelSizeDisplay.textContent = String(label.fontSize ?? 14);
118
+ }
119
+ }
120
+
121
+ // ── Dropdown цвета: показать / скрыть ────────────────────────────────────────
122
+
123
+ export function showStrokeDropdown(inst) {
124
+ if (inst.strokeColorDropdown) inst.strokeColorDropdown.style.display = 'block';
125
+ }
126
+
127
+ export function hideStrokeDropdown(inst) {
128
+ if (inst.strokeColorDropdown) inst.strokeColorDropdown.style.display = 'none';
129
+ }
130
+
131
+ export function showLabelColorDropdown(inst) {
132
+ if (inst._labelColorDropdown) inst._labelColorDropdown.style.display = 'block';
133
+ }
134
+
135
+ export function hideLabelColorDropdown(inst) {
136
+ if (inst._labelColorDropdown) inst._labelColorDropdown.style.display = 'none';
137
+ }
138
+
139
+ // ── Внутренние строители ─────────────────────────────────────────────────────
140
+
141
+ function _appendSep(parent) {
142
+ const sep = document.createElement('div');
143
+ sep.style.cssText = 'width:1px;height:18px;background:#e8e8e8;margin:0 2px;flex-shrink:0;';
144
+ parent.appendChild(sep);
145
+ }
146
+
147
+ function _btn(label, title, extra = {}) {
148
+ const b = document.createElement('button');
149
+ b.type = 'button';
150
+ b.textContent = label;
151
+ b.title = title;
152
+ Object.assign(b.style, {
153
+ border: '1px solid #ddd',
154
+ borderRadius: '4px',
155
+ padding: '2px 6px',
156
+ cursor: 'pointer',
157
+ fontSize: '11px',
158
+ background: '',
159
+ lineHeight: '16px',
160
+ flexShrink: '0',
161
+ ...extra,
162
+ });
163
+ return b;
164
+ }
165
+
166
+ function _createStrokeControl(inst, panel) {
167
+ const wrap = document.createElement('div');
168
+ wrap.style.cssText = 'position:relative;display:inline-flex;align-items:center;gap:4px;';
169
+ inst._strokeSelectorContainer = wrap;
170
+
171
+ const label = document.createElement('span');
172
+ label.textContent = 'Цвет';
173
+ label.style.cssText = 'font-size:10px;color:#999;';
174
+ wrap.appendChild(label);
175
+
176
+ const btn = document.createElement('button');
177
+ btn.type = 'button';
178
+ btn.title = 'Цвет линии';
179
+ Object.assign(btn.style, {
180
+ width: '22px',
181
+ height: '22px',
182
+ borderRadius: '50%',
183
+ border: '1px solid #ddd',
184
+ cursor: 'pointer',
185
+ backgroundColor: '#2563EB',
186
+ flexShrink: '0',
187
+ padding: '0',
188
+ });
189
+ inst.strokeColorButton = btn;
190
+ wrap.appendChild(btn);
191
+
192
+ // Dropdown
193
+ const dropdown = document.createElement('div');
194
+ dropdown.style.cssText = [
195
+ 'position:absolute;top:calc(100% + 6px);left:0;',
196
+ 'background:white;border:1px solid #ddd;border-radius:6px;',
197
+ 'box-shadow:0 2px 8px rgba(0,0,0,0.15);padding:8px;display:none;',
198
+ 'z-index:10001;min-width:200px;',
199
+ ].join('');
200
+ inst.strokeColorDropdown = dropdown;
201
+
202
+ // Сетка пресетов
203
+ const grid = document.createElement('div');
204
+ grid.style.cssText = 'display:grid;grid-template-columns:repeat(5,28px);gap:6px;margin-bottom:8px;';
205
+ inst._strokePresetButtons = [];
206
+
207
+ STROKE_COLOR_PRESETS.forEach(preset => {
208
+ const cb = document.createElement('button');
209
+ cb.type = 'button';
210
+ cb.title = preset.name;
211
+ cb.dataset.colorValue = preset.color;
212
+ cb.dataset.pixiValue = String(preset.value);
213
+ cb.style.cssText = [
214
+ `width:28px;height:28px;border-radius:50%;`,
215
+ `background-color:${preset.color};`,
216
+ `border:1px solid ${preset.color === '#FFFFFF' ? '#ccc' : 'transparent'};`,
217
+ 'cursor:pointer;padding:0;box-sizing:border-box;',
218
+ ].join('');
219
+ grid.appendChild(cb);
220
+ inst._strokePresetButtons.push(cb);
221
+ });
222
+ dropdown.appendChild(grid);
223
+
224
+ const sep = document.createElement('div');
225
+ sep.style.cssText = 'height:1px;background:#eee;margin:4px 0 8px;';
226
+ dropdown.appendChild(sep);
227
+
228
+ const customRow = document.createElement('div');
229
+ customRow.style.cssText = 'display:flex;align-items:center;gap:8px;';
230
+ const cl = document.createElement('span');
231
+ cl.textContent = 'Свой:';
232
+ cl.style.cssText = 'font-size:11px;color:#666;';
233
+
234
+ const ci = document.createElement('input');
235
+ ci.type = 'color';
236
+ ci.style.cssText = 'width:32px;height:24px;border:1px solid #ddd;border-radius:3px;cursor:pointer;padding:0;';
237
+ inst.strokeColorInput = ci;
238
+
239
+ customRow.appendChild(cl);
240
+ customRow.appendChild(ci);
241
+ dropdown.appendChild(customRow);
242
+
243
+ wrap.appendChild(dropdown);
244
+ panel.appendChild(wrap);
245
+ }
246
+
247
+ function _createWidthControl(inst, panel) {
248
+ const label = document.createElement('span');
249
+ label.textContent = 'Вес';
250
+ label.style.cssText = 'font-size:10px;color:#999;';
251
+ panel.appendChild(label);
252
+
253
+ inst.widthButtons = WIDTH_PRESETS.map(w => {
254
+ const b = _btn(String(w), `Толщина ${w}px`);
255
+ b.dataset.width = String(w);
256
+ panel.appendChild(b);
257
+ return b;
258
+ });
259
+ }
260
+
261
+ function _createRouteControl(inst, panel) {
262
+ const label = document.createElement('span');
263
+ label.textContent = 'Тип';
264
+ label.style.cssText = 'font-size:10px;color:#999;';
265
+ panel.appendChild(label);
266
+
267
+ const sel = document.createElement('select');
268
+ sel.style.cssText = [
269
+ 'border:1px solid #ddd;border-radius:4px;padding:2px 4px;',
270
+ 'font-size:11px;cursor:pointer;background:white;height:24px;',
271
+ ].join('');
272
+ ROUTE_OPTIONS.forEach(opt => {
273
+ const o = document.createElement('option');
274
+ o.value = opt.value;
275
+ o.textContent = opt.label;
276
+ sel.appendChild(o);
277
+ });
278
+ inst.routeSelect = sel;
279
+ panel.appendChild(sel);
280
+ }
281
+
282
+ function _createDashControl(inst, panel) {
283
+ const b = _btn('- - -', 'Пунктир / сплошная');
284
+ inst.dashButton = b;
285
+ panel.appendChild(b);
286
+ }
287
+
288
+ function _createHeadControl(inst, panel) {
289
+ const label = document.createElement('span');
290
+ label.textContent = 'Конец';
291
+ label.style.cssText = 'font-size:10px;color:#999;';
292
+ panel.appendChild(label);
293
+
294
+ const selEnd = _headSelect();
295
+ inst.headEndSelect = selEnd;
296
+ panel.appendChild(selEnd);
297
+
298
+ const labelS = document.createElement('span');
299
+ labelS.textContent = 'Начало';
300
+ labelS.style.cssText = 'font-size:10px;color:#999;margin-left:4px;';
301
+ panel.appendChild(labelS);
302
+
303
+ const selStart = _headSelect();
304
+ inst.headStartSelect = selStart;
305
+ panel.appendChild(selStart);
306
+ }
307
+
308
+ function _headSelect() {
309
+ const sel = document.createElement('select');
310
+ sel.style.cssText = [
311
+ 'border:1px solid #ddd;border-radius:4px;padding:2px 4px;',
312
+ 'font-size:11px;cursor:pointer;background:white;height:24px;',
313
+ ].join('');
314
+ HEAD_OPTIONS.forEach(opt => {
315
+ const o = document.createElement('option');
316
+ o.value = opt.value;
317
+ o.textContent = opt.label;
318
+ sel.appendChild(o);
319
+ });
320
+ return sel;
321
+ }
322
+
323
+ function _createActionButtons(inst, panel) {
324
+ // Разворот
325
+ const swapBtn = _btn('⇄', 'Разворот — поменять местами начало и конец');
326
+ inst._swapBtn = swapBtn;
327
+ panel.appendChild(swapBtn);
328
+
329
+ // Кнопка добавить текст-метку (активна)
330
+ const textBtn = _btn('T+', 'Добавить / редактировать текст-метку');
331
+ inst._textBtn = textBtn;
332
+ panel.appendChild(textBtn);
333
+
334
+ // Замок
335
+ const lockBtn = _btn('🔓', 'Заблокировать объект');
336
+ inst._lockBtn = lockBtn;
337
+ panel.appendChild(lockBtn);
338
+
339
+ // Удалить
340
+ const delBtn = _btn('🗑', 'Удалить коннектор');
341
+ delBtn.style.color = '#EF4444';
342
+ inst._delBtn = delBtn;
343
+ panel.appendChild(delBtn);
344
+ }
345
+
346
+ // ── Второй ряд: контролы текстовой метки ─────────────────────────────────────
347
+
348
+ function _createLabelRow(inst) {
349
+ const row = document.createElement('div');
350
+ Object.assign(row.style, {
351
+ flexDirection: 'row',
352
+ alignItems: 'center',
353
+ gap: '4px',
354
+ padding: '4px 16px',
355
+ borderTop: '1px solid #f0f0f0',
356
+ minHeight: '34px',
357
+ });
358
+
359
+ const titleLbl = document.createElement('span');
360
+ titleLbl.textContent = 'Текст';
361
+ titleLbl.style.cssText = 'font-size:10px;color:#999;flex-shrink:0;';
362
+ row.appendChild(titleLbl);
363
+
364
+ _appendSep(row);
365
+
366
+ // Кнопка цвета с dropdown пресетов
367
+ const colorWrap = document.createElement('div');
368
+ colorWrap.style.cssText = 'position:relative;display:inline-flex;align-items:center;gap:4px;';
369
+
370
+ const colorLbl = document.createElement('span');
371
+ colorLbl.textContent = 'Цвет';
372
+ colorLbl.style.cssText = 'font-size:10px;color:#999;';
373
+ colorWrap.appendChild(colorLbl);
374
+
375
+ const colorBtn = document.createElement('button');
376
+ colorBtn.type = 'button';
377
+ colorBtn.title = 'Цвет текста метки';
378
+ Object.assign(colorBtn.style, {
379
+ width: '22px',
380
+ height: '22px',
381
+ borderRadius: '50%',
382
+ border: '1px solid #ddd',
383
+ cursor: 'pointer',
384
+ backgroundColor: '#212121',
385
+ flexShrink: '0',
386
+ padding: '0',
387
+ });
388
+ inst._labelColorBtn = colorBtn;
389
+ colorWrap.appendChild(colorBtn);
390
+
391
+ const colorDropdown = document.createElement('div');
392
+ colorDropdown.style.cssText = [
393
+ 'position:absolute;bottom:calc(100% + 6px);left:0;',
394
+ 'background:white;border:1px solid #ddd;border-radius:6px;',
395
+ 'box-shadow:0 2px 8px rgba(0,0,0,0.15);padding:8px;display:none;',
396
+ 'z-index:10002;',
397
+ ].join('');
398
+ inst._labelColorDropdown = colorDropdown;
399
+ inst._labelColorContainer = colorWrap;
400
+
401
+ const colorGrid = document.createElement('div');
402
+ colorGrid.style.cssText = 'display:grid;grid-template-columns:repeat(5,28px);gap:6px;';
403
+ inst._labelPresetButtons = [];
404
+
405
+ STROKE_COLOR_PRESETS.forEach(preset => {
406
+ const cb = document.createElement('button');
407
+ cb.type = 'button';
408
+ cb.title = preset.name;
409
+ cb.dataset.colorValue = preset.color;
410
+ cb.dataset.pixiValue = String(preset.value);
411
+ cb.style.cssText = [
412
+ `width:28px;height:28px;border-radius:50%;`,
413
+ `background-color:${preset.color};`,
414
+ `border:1px solid ${preset.color === '#FFFFFF' ? '#ccc' : 'transparent'};`,
415
+ 'cursor:pointer;padding:0;box-sizing:border-box;',
416
+ ].join('');
417
+ colorGrid.appendChild(cb);
418
+ inst._labelPresetButtons.push(cb);
419
+ });
420
+ colorDropdown.appendChild(colorGrid);
421
+ colorWrap.appendChild(colorDropdown);
422
+ row.appendChild(colorWrap);
423
+
424
+ _appendSep(row);
425
+
426
+ // Степпер размера шрифта
427
+ const sizeLbl = document.createElement('span');
428
+ sizeLbl.textContent = 'Размер';
429
+ sizeLbl.style.cssText = 'font-size:10px;color:#999;flex-shrink:0;';
430
+ row.appendChild(sizeLbl);
431
+
432
+ const sizeDown = _btn('−', 'Уменьшить шрифт метки', { padding: '1px 6px', fontSize: '13px' });
433
+ const sizeDisplay = document.createElement('span');
434
+ sizeDisplay.style.cssText = 'font-size:11px;min-width:22px;text-align:center;flex-shrink:0;';
435
+ sizeDisplay.textContent = '14';
436
+ const sizeUp = _btn('+', 'Увеличить шрифт метки', { padding: '1px 6px', fontSize: '13px' });
437
+
438
+ inst._labelSizeDown = sizeDown;
439
+ inst._labelSizeDisplay = sizeDisplay;
440
+ inst._labelSizeUp = sizeUp;
441
+
442
+ row.appendChild(sizeDown);
443
+ row.appendChild(sizeDisplay);
444
+ row.appendChild(sizeUp);
445
+
446
+ return row;
447
+ }
@@ -0,0 +1,61 @@
1
+ export function createConnectorPropertiesPanelState() {
2
+ return {
3
+ panel: null,
4
+ currentId: null,
5
+
6
+ // Main row
7
+ _mainRow: null,
8
+
9
+ // Stroke color
10
+ strokeColorButton: null,
11
+ strokeColorDropdown: null,
12
+ strokeColorInput: null,
13
+ _strokePresetButtons: [],
14
+ _strokeSelectorContainer: null,
15
+ _onStrokeDocumentClick: null,
16
+
17
+ // Width buttons
18
+ widthButtons: [],
19
+
20
+ // Route dropdown
21
+ routeSelect: null,
22
+
23
+ // Dash toggle
24
+ dashButton: null,
25
+
26
+ // Head selects
27
+ headEndSelect: null,
28
+ headStartSelect: null,
29
+
30
+ // Action buttons
31
+ _swapBtn: null,
32
+ _textBtn: null,
33
+ _lockBtn: null,
34
+ _delBtn: null,
35
+
36
+ // Label row (second row)
37
+ _labelRow: null,
38
+ _labelColorBtn: null,
39
+ _labelColorDropdown: null,
40
+ _labelColorContainer:null,
41
+ _labelPresetButtons: [],
42
+ _labelSizeDown: null,
43
+ _labelSizeDisplay: null,
44
+ _labelSizeUp: null,
45
+ _onLabelDocumentClick: null,
46
+
47
+ // Internal flags
48
+ _bindingsAttached: false,
49
+ _eventBridgeAttached: false,
50
+ _eventBridgeHandlers: null,
51
+ };
52
+ }
53
+
54
+ export function clearConnectorPropertiesPanelState(state) {
55
+ state.currentId = null;
56
+ state._bindingsAttached = false;
57
+ state._eventBridgeAttached = false;
58
+ state._eventBridgeHandlers = null;
59
+ state._onStrokeDocumentClick = null;
60
+ state._onLabelDocumentClick = null;
61
+ }
@@ -91,6 +91,7 @@ export class ConnectionAnchorsLayer {
91
91
  [Events.Tool.GroupResizeUpdate, () => this.update()],
92
92
  [Events.Tool.RotateUpdate, () => this.update()],
93
93
  [Events.Tool.PanUpdate, () => this.update()],
94
+ [Events.Viewport.Changed, () => this.update()],
94
95
  [Events.UI.ZoomPercent, () => this.update()],
95
96
  [Events.History.Changed, () => this.update()],
96
97
  [Events.Board.Loaded, () => this.update()]