@sequent-org/moodboard 1.4.32 → 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.
- package/package.json +5 -1
- package/src/assets/fonts/inter/inter-cyrillic-400-normal.woff2 +0 -0
- package/src/assets/fonts/inter/inter-cyrillic-500-normal.woff2 +0 -0
- package/src/assets/fonts/inter/inter-latin-400-normal.woff2 +0 -0
- package/src/assets/fonts/inter/inter-latin-500-normal.woff2 +0 -0
- package/src/assets/icons/attachments.svg +3 -1
- package/src/assets/icons/comments.svg +2 -2
- package/src/assets/icons/connector.svg +6 -0
- package/src/assets/icons/emoji.svg +6 -1
- package/src/assets/icons/frame.svg +4 -1
- package/src/assets/icons/image.svg +5 -1
- package/src/assets/icons/laser.svg +1 -0
- package/src/assets/icons/lasso.svg +5 -0
- package/src/assets/icons/mindmap.svg +10 -2
- package/src/assets/icons/note.svg +4 -1
- package/src/assets/icons/pan.svg +5 -2
- package/src/assets/icons/pencil.svg +4 -1
- package/src/assets/icons/reactions.svg +5 -0
- package/src/assets/icons/redo.svg +3 -2
- package/src/assets/icons/select.svg +2 -8
- package/src/assets/icons/shapes.svg +5 -1
- package/src/assets/icons/text-add.svg +15 -1
- package/src/assets/icons/undo.svg +3 -2
- package/src/assets/reactions/1f44d.svg +20 -0
- package/src/assets/reactions/1f44e.svg +20 -0
- package/src/assets/reactions/2705.svg +20 -0
- package/src/assets/reactions/274c.svg +19 -0
- package/src/assets/reactions/2753.svg +20 -0
- package/src/assets/reactions/2764.svg +22 -0
- package/src/assets/reactions/2b50.svg +19 -0
- package/src/assets/reactions/plus-one.svg +25 -0
- package/src/core/PixiEngine.js +23 -0
- package/src/core/bootstrap/CoreInitializer.js +43 -0
- package/src/core/commands/GroupDeleteCommand.js +13 -1
- package/src/core/commands/UpdateShapeStyleCommand.js +121 -0
- package/src/core/commands/UpdateTextStyleCommand.js +17 -6
- package/src/core/commands/index.js +3 -0
- package/src/core/events/Events.js +22 -0
- package/src/core/flows/LayerAndViewportFlow.js +1 -0
- package/src/core/flows/ObjectLifecycleFlow.js +155 -7
- package/src/core/index.js +28 -1
- package/src/grid/CrossGridZoomPhases.js +3 -3
- package/src/initNoBundler.js +1 -1
- package/src/moodboard/DataManager.js +28 -0
- package/src/moodboard/MoodBoard.js +27 -0
- package/src/moodboard/bootstrap/MoodBoardInitializer.js +69 -1
- package/src/moodboard/bootstrap/MoodBoardUiFactory.js +22 -4
- package/src/moodboard/integration/MoodBoardEventBindings.js +5 -1
- package/src/moodboard/integration/MoodBoardLoadApi.js +10 -1
- package/src/moodboard/lifecycle/MoodBoardDestroyer.js +9 -0
- package/src/objects/ConnectorObject.js +2 -2
- package/src/objects/FrameObject.js +119 -59
- package/src/objects/ShapeObject.js +49 -74
- package/src/objects/shape/ShapeDrawer.js +210 -0
- package/src/services/ConnectorBindingResolver.js +112 -0
- package/src/services/ConnectorRouter.js +210 -0
- package/src/services/comments/CommentService.js +344 -0
- package/src/tools/object-tools/CommentTool.js +85 -0
- package/src/tools/object-tools/DrawingTool.js +110 -10
- package/src/tools/object-tools/LaserPointerTool.js +121 -0
- package/src/tools/object-tools/SelectTool.js +25 -1
- package/src/tools/object-tools/TextTool.js +6 -1
- package/src/tools/object-tools/connector/ConnectorDragController.js +50 -3
- package/src/tools/object-tools/connector/connectorGesture.js +33 -19
- package/src/tools/object-tools/placement/PlacementInputRouter.js +22 -1
- package/src/tools/object-tools/selection/BoxSelectController.js +24 -2
- package/src/tools/object-tools/selection/FrameTitleInlineEditorController.js +139 -0
- package/src/tools/object-tools/selection/InlineEditorController.js +12 -0
- package/src/tools/object-tools/selection/InlineEditorDomFactory.js +36 -0
- package/src/tools/object-tools/selection/LassoSelectController.js +125 -0
- package/src/tools/object-tools/selection/MindmapInlineEditorController.js +1 -0
- package/src/tools/object-tools/selection/SelectInputRouter.js +64 -5
- package/src/tools/object-tools/selection/SelectToolLifecycleController.js +11 -1
- package/src/tools/object-tools/selection/SelectToolSetup.js +13 -1
- package/src/tools/object-tools/selection/TextEditorInteractionController.js +46 -12
- package/src/tools/object-tools/selection/TextEditorSyncService.js +1 -0
- package/src/tools/object-tools/selection/TextInlineEditorController.js +65 -6
- package/src/ui/CommentPopover.js +6 -0
- package/src/ui/CommentsBar.js +91 -0
- package/src/ui/ConnectorPropertiesPanel.js +150 -0
- package/src/ui/ContextMenu.js +25 -0
- package/src/ui/DrawingPropertiesPanel.js +362 -0
- package/src/ui/FilePropertiesPanel.js +5 -0
- package/src/ui/FramePropertiesPanel.js +5 -0
- package/src/ui/HtmlTextLayer.js +246 -66
- package/src/ui/NotePropertiesPanel.js +6 -0
- package/src/ui/ShapePropertiesPanel.js +307 -0
- package/src/ui/TextPropertiesPanel.js +100 -1
- package/src/ui/Toolbar.js +25 -2
- package/src/ui/Topbar.js +2 -2
- package/src/ui/animation/HoverLiftController.js +6 -7
- package/src/ui/chat/ChatComposer.js +58 -7
- package/src/ui/chat/ChatWindow.js +60 -143
- package/src/ui/comments/CommentListPanel.js +213 -0
- package/src/ui/comments/CommentPinLayer.js +448 -0
- package/src/ui/comments/CommentThreadPopover.js +539 -0
- package/src/ui/comments/commentFormat.js +32 -0
- package/src/ui/connector-properties/ConnectorPropertiesPanelBindings.js +223 -0
- package/src/ui/connector-properties/ConnectorPropertiesPanelEventBridge.js +114 -0
- package/src/ui/connector-properties/ConnectorPropertiesPanelMapper.js +144 -0
- package/src/ui/connector-properties/ConnectorPropertiesPanelRenderer.js +447 -0
- package/src/ui/connector-properties/ConnectorPropertiesPanelState.js +61 -0
- package/src/ui/connectors/ConnectionAnchorsLayer.js +1 -0
- package/src/ui/connectors/ConnectorHandlesLayer.js +321 -0
- package/src/ui/connectors/ConnectorLabelLayer.js +334 -0
- package/src/ui/connectors/ConnectorLayer.js +264 -57
- package/src/ui/handles/HandlesDomRenderer.js +5 -13
- package/src/ui/handles/HandlesEventBridge.js +1 -0
- package/src/ui/handles/SingleSelectionHandlesController.js +4 -0
- package/src/ui/mindmap/MindmapCollapseLayer.js +1 -0
- package/src/ui/mindmap/MindmapConnectionLayer.js +1 -0
- package/src/ui/mindmap/MindmapHtmlTextLayer.js +6 -0
- package/src/ui/shape-properties/ShapePropertiesPanelDom.js +533 -0
- package/src/ui/shape-properties/ShapePropertiesPanelSync.js +132 -0
- package/src/ui/styles/chat.css +709 -19
- package/src/ui/styles/index.css +1 -0
- package/src/ui/styles/panels.css +112 -2
- package/src/ui/styles/shape-properties-panel.css +250 -0
- package/src/ui/styles/toolbar.css +7 -2
- package/src/ui/styles/topbar.css +1 -1
- package/src/ui/styles/workspace.css +257 -6
- package/src/ui/text-properties/TextFormatControls.js +88 -0
- package/src/ui/text-properties/TextListRenderer.js +137 -0
- package/src/ui/text-properties/TextPropertiesPanelBindings.js +27 -0
- package/src/ui/text-properties/TextPropertiesPanelEventBridge.js +3 -1
- package/src/ui/text-properties/TextPropertiesPanelMapper.js +56 -0
- package/src/ui/text-properties/TextPropertiesPanelRenderer.js +24 -0
- package/src/ui/text-properties/TextPropertiesPanelState.js +8 -0
- package/src/ui/toolbar/ReactionsPopupController.js +88 -0
- package/src/ui/toolbar/ToolbarActionRouter.js +71 -5
- package/src/ui/toolbar/ToolbarPopupsController.js +120 -118
- package/src/ui/toolbar/ToolbarRenderer.js +9 -1
- package/src/ui/toolbar/ToolbarStateController.js +4 -1
- package/src/utils/iconLoader.js +17 -16
- package/src/utils/markdown.js +14 -0
- package/src/utils/richText.js +125 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import {
|
|
2
|
+
hexToPixiColor,
|
|
3
|
+
buildStyleUpdate,
|
|
4
|
+
} from './ConnectorPropertiesPanelMapper.js';
|
|
5
|
+
import {
|
|
6
|
+
showStrokeDropdown,
|
|
7
|
+
hideStrokeDropdown,
|
|
8
|
+
showLabelColorDropdown,
|
|
9
|
+
hideLabelColorDropdown,
|
|
10
|
+
} from './ConnectorPropertiesPanelRenderer.js';
|
|
11
|
+
import { Events } from '../../core/events/Events.js';
|
|
12
|
+
|
|
13
|
+
export function bindConnectorPropertiesPanelControls(inst) {
|
|
14
|
+
if (inst._bindingsAttached) return;
|
|
15
|
+
|
|
16
|
+
// ── Цвет линии ──────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
inst.strokeColorButton.addEventListener('click', (e) => {
|
|
19
|
+
e.stopPropagation();
|
|
20
|
+
if (inst.strokeColorDropdown.style.display === 'none') {
|
|
21
|
+
showStrokeDropdown(inst);
|
|
22
|
+
} else {
|
|
23
|
+
hideStrokeDropdown(inst);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
inst._strokePresetButtons.forEach(btn => {
|
|
28
|
+
btn.addEventListener('click', () => {
|
|
29
|
+
const pixi = parseInt(btn.dataset.pixiValue, 10);
|
|
30
|
+
inst._emitStyle({ stroke: pixi });
|
|
31
|
+
hideStrokeDropdown(inst);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
inst.strokeColorInput.addEventListener('change', (e) => {
|
|
36
|
+
const pixi = hexToPixiColor(e.target.value);
|
|
37
|
+
inst._emitStyle({ stroke: pixi });
|
|
38
|
+
hideStrokeDropdown(inst);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
inst._onStrokeDocumentClick = (e) => {
|
|
42
|
+
if (inst._strokeSelectorContainer && !inst._strokeSelectorContainer.contains(e.target)) {
|
|
43
|
+
hideStrokeDropdown(inst);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
document.addEventListener('click', inst._onStrokeDocumentClick);
|
|
47
|
+
|
|
48
|
+
// ── Ширина ───────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
inst.widthButtons.forEach(btn => {
|
|
51
|
+
btn.addEventListener('click', () => {
|
|
52
|
+
const w = parseInt(btn.dataset.width, 10);
|
|
53
|
+
inst._emitStyle({ width: w });
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// ── Маршрут ──────────────────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
inst.routeSelect.addEventListener('change', (e) => {
|
|
60
|
+
inst._emitStyle({ route: e.target.value });
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// ── Пунктир ──────────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
inst.dashButton.addEventListener('click', () => {
|
|
66
|
+
const connector = inst._getConnector();
|
|
67
|
+
const currentDash = connector?.properties?.style?.dash ?? false;
|
|
68
|
+
inst._emitStyle({ dash: !currentDash });
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// ── Наконечник конец ─────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
inst.headEndSelect.addEventListener('change', (e) => {
|
|
74
|
+
const connector = inst._getConnector();
|
|
75
|
+
const currentHead = connector?.properties?.style?.head ?? { start: 'none', end: 'arrow' };
|
|
76
|
+
inst._emitStyle({ head: { ...currentHead, end: e.target.value } });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// ── Наконечник начало ────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
inst.headStartSelect.addEventListener('change', (e) => {
|
|
82
|
+
const connector = inst._getConnector();
|
|
83
|
+
const currentHead = connector?.properties?.style?.head ?? { start: 'none', end: 'arrow' };
|
|
84
|
+
inst._emitStyle({ head: { ...currentHead, start: e.target.value } });
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// ── Разворот ─────────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
inst._swapBtn.addEventListener('click', () => {
|
|
90
|
+
if (!inst.currentId) return;
|
|
91
|
+
const connector = inst._getConnector();
|
|
92
|
+
if (!connector?.properties) return;
|
|
93
|
+
|
|
94
|
+
const oldStart = connector.properties.start;
|
|
95
|
+
const oldEnd = connector.properties.end;
|
|
96
|
+
const oldHead = connector.properties.style?.head ?? { start: 'none', end: 'arrow' };
|
|
97
|
+
|
|
98
|
+
inst.eventBus.emit(Events.Object.StateChanged, {
|
|
99
|
+
objectId: inst.currentId,
|
|
100
|
+
updates: {
|
|
101
|
+
properties: {
|
|
102
|
+
start: oldEnd,
|
|
103
|
+
end: oldStart,
|
|
104
|
+
style: {
|
|
105
|
+
...connector.properties.style,
|
|
106
|
+
head: { start: oldHead.end, end: oldHead.start },
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// ── Кнопка T+: создать / открыть редактор метки ──────────────────────────
|
|
114
|
+
|
|
115
|
+
inst._textBtn.addEventListener('click', () => {
|
|
116
|
+
if (!inst.currentId) return;
|
|
117
|
+
const connector = inst._getConnector();
|
|
118
|
+
if (!connector) return;
|
|
119
|
+
|
|
120
|
+
const existingLabel = connector.properties?.style?.label;
|
|
121
|
+
if (!existingLabel) {
|
|
122
|
+
// Инициализируем метку с пустым текстом
|
|
123
|
+
const strokeColor = connector.properties?.style?.stroke ?? 0x212121;
|
|
124
|
+
inst._emitLabel({ text: '', color: strokeColor, fontSize: 14 });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Открываем редактор после небольшой задержки (StateChanged успевает обработаться)
|
|
128
|
+
requestAnimationFrame(() => {
|
|
129
|
+
inst.core?.connectorLabelLayer?.openEditorForConnector(inst.currentId);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// ── Замок ────────────────────────────────────────────────────────────────
|
|
134
|
+
|
|
135
|
+
inst._lockBtn.addEventListener('click', () => {
|
|
136
|
+
if (!inst.currentId) return;
|
|
137
|
+
const connector = inst._getConnector();
|
|
138
|
+
const locked = !(connector?.properties?.locked ?? false);
|
|
139
|
+
inst.eventBus.emit(Events.Object.StateChanged, {
|
|
140
|
+
objectId: inst.currentId,
|
|
141
|
+
updates: { properties: { locked } },
|
|
142
|
+
});
|
|
143
|
+
inst._lockBtn.textContent = locked ? '🔒' : '🔓';
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// ── Удалить ──────────────────────────────────────────────────────────────
|
|
147
|
+
|
|
148
|
+
inst._delBtn.addEventListener('click', () => {
|
|
149
|
+
if (!inst.currentId) return;
|
|
150
|
+
inst.eventBus.emit(Events.Tool.ObjectsDelete, { objects: [inst.currentId] });
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// ── Label: цвет (dropdown пресетов) ─────────────────────────────────────
|
|
154
|
+
|
|
155
|
+
if (inst._labelColorBtn) {
|
|
156
|
+
inst._labelColorBtn.addEventListener('click', (e) => {
|
|
157
|
+
e.stopPropagation();
|
|
158
|
+
if (inst._labelColorDropdown?.style.display === 'none') {
|
|
159
|
+
showLabelColorDropdown(inst);
|
|
160
|
+
} else {
|
|
161
|
+
hideLabelColorDropdown(inst);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
inst._labelPresetButtons?.forEach(btn => {
|
|
167
|
+
btn.addEventListener('click', () => {
|
|
168
|
+
const pixi = parseInt(btn.dataset.pixiValue, 10);
|
|
169
|
+
const connector = inst._getConnector();
|
|
170
|
+
const existing = connector?.properties?.style?.label;
|
|
171
|
+
if (!existing) return;
|
|
172
|
+
inst._emitLabel({ ...existing, color: pixi });
|
|
173
|
+
hideLabelColorDropdown(inst);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
inst._onLabelDocumentClick = (e) => {
|
|
178
|
+
if (inst._labelColorContainer && !inst._labelColorContainer.contains(e.target)) {
|
|
179
|
+
hideLabelColorDropdown(inst);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
document.addEventListener('click', inst._onLabelDocumentClick);
|
|
183
|
+
|
|
184
|
+
// ── Label: размер шрифта (степпер) ───────────────────────────────────────
|
|
185
|
+
|
|
186
|
+
const FONT_SIZES = [8, 10, 12, 14, 16, 18, 20, 24, 28, 32, 40, 48];
|
|
187
|
+
|
|
188
|
+
if (inst._labelSizeDown) {
|
|
189
|
+
inst._labelSizeDown.addEventListener('click', () => {
|
|
190
|
+
const connector = inst._getConnector();
|
|
191
|
+
const existing = connector?.properties?.style?.label;
|
|
192
|
+
if (!existing) return;
|
|
193
|
+
const cur = existing.fontSize ?? 14;
|
|
194
|
+
const next = FONT_SIZES.slice().reverse().find(s => s < cur) ?? FONT_SIZES[0];
|
|
195
|
+
inst._emitLabel({ ...existing, fontSize: next });
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (inst._labelSizeUp) {
|
|
200
|
+
inst._labelSizeUp.addEventListener('click', () => {
|
|
201
|
+
const connector = inst._getConnector();
|
|
202
|
+
const existing = connector?.properties?.style?.label;
|
|
203
|
+
if (!existing) return;
|
|
204
|
+
const cur = existing.fontSize ?? 14;
|
|
205
|
+
const next = FONT_SIZES.find(s => s > cur) ?? FONT_SIZES[FONT_SIZES.length - 1];
|
|
206
|
+
inst._emitLabel({ ...existing, fontSize: next });
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
inst._bindingsAttached = true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function unbindConnectorPropertiesPanelControls(inst) {
|
|
214
|
+
if (inst._onStrokeDocumentClick) {
|
|
215
|
+
document.removeEventListener('click', inst._onStrokeDocumentClick);
|
|
216
|
+
inst._onStrokeDocumentClick = null;
|
|
217
|
+
}
|
|
218
|
+
if (inst._onLabelDocumentClick) {
|
|
219
|
+
document.removeEventListener('click', inst._onLabelDocumentClick);
|
|
220
|
+
inst._onLabelDocumentClick = null;
|
|
221
|
+
}
|
|
222
|
+
inst._bindingsAttached = false;
|
|
223
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { Events } from '../../core/events/Events.js';
|
|
2
|
+
|
|
3
|
+
export function attachConnectorPropertiesPanelEventBridge(inst) {
|
|
4
|
+
if (inst._eventBridgeAttached) return;
|
|
5
|
+
|
|
6
|
+
inst._eventBridgeHandlers = {
|
|
7
|
+
onSelectionAdd: () => inst.updateFromSelection(),
|
|
8
|
+
onSelectionRemove: () => inst.updateFromSelection(),
|
|
9
|
+
onSelectionClear: () => inst.hide(),
|
|
10
|
+
|
|
11
|
+
onDragStart: () => inst.hide(),
|
|
12
|
+
onDragUpdate: () => inst.reposition(),
|
|
13
|
+
onDragEnd: () => inst.updateFromSelection(),
|
|
14
|
+
|
|
15
|
+
onGroupDragStart: () => inst.hide(),
|
|
16
|
+
onGroupDragUpdate: () => inst.reposition(),
|
|
17
|
+
onGroupDragEnd: () => inst.updateFromSelection(),
|
|
18
|
+
|
|
19
|
+
onResizeUpdate: () => inst.reposition(),
|
|
20
|
+
onRotateUpdate: () => inst.reposition(),
|
|
21
|
+
|
|
22
|
+
onZoomPercent: () => { if (inst.currentId) inst.reposition(); },
|
|
23
|
+
onPanUpdate: () => { if (inst.currentId) inst.reposition(); },
|
|
24
|
+
onViewportChanged: () => { if (inst.currentId) inst.reposition(); },
|
|
25
|
+
|
|
26
|
+
onDeleted: (objectId) => {
|
|
27
|
+
if (inst.currentId && objectId === inst.currentId) inst.hide();
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
onToolActivated: ({ tool }) => {
|
|
31
|
+
if (tool !== 'select') inst.hide();
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
onStateChanged: ({ objectId }) => {
|
|
35
|
+
if (inst.currentId && objectId === inst.currentId
|
|
36
|
+
&& inst.panel && inst.panel.style.display !== 'none') {
|
|
37
|
+
inst._updateControlsFromObject();
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
// History.Changed — синхронизация после undo/redo (как у FramePropertiesPanel)
|
|
42
|
+
onHistoryChanged: () => {
|
|
43
|
+
if (inst.currentId && inst.panel && inst.panel.style.display !== 'none') {
|
|
44
|
+
inst._updateControlsFromObject();
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const h = inst._eventBridgeHandlers;
|
|
50
|
+
const eb = inst.eventBus;
|
|
51
|
+
|
|
52
|
+
eb.on(Events.Tool.SelectionAdd, h.onSelectionAdd);
|
|
53
|
+
eb.on(Events.Tool.SelectionRemove, h.onSelectionRemove);
|
|
54
|
+
eb.on(Events.Tool.SelectionClear, h.onSelectionClear);
|
|
55
|
+
|
|
56
|
+
eb.on(Events.Tool.DragStart, h.onDragStart);
|
|
57
|
+
eb.on(Events.Tool.DragUpdate, h.onDragUpdate);
|
|
58
|
+
eb.on(Events.Tool.DragEnd, h.onDragEnd);
|
|
59
|
+
|
|
60
|
+
eb.on(Events.Tool.GroupDragStart, h.onGroupDragStart);
|
|
61
|
+
eb.on(Events.Tool.GroupDragUpdate, h.onGroupDragUpdate);
|
|
62
|
+
eb.on(Events.Tool.GroupDragEnd, h.onGroupDragEnd);
|
|
63
|
+
|
|
64
|
+
eb.on(Events.Tool.ResizeUpdate, h.onResizeUpdate);
|
|
65
|
+
eb.on(Events.Tool.RotateUpdate, h.onRotateUpdate);
|
|
66
|
+
|
|
67
|
+
eb.on(Events.UI.ZoomPercent, h.onZoomPercent);
|
|
68
|
+
eb.on(Events.Tool.PanUpdate, h.onPanUpdate);
|
|
69
|
+
eb.on(Events.Viewport.Changed, h.onViewportChanged);
|
|
70
|
+
|
|
71
|
+
eb.on(Events.Object.Deleted, h.onDeleted);
|
|
72
|
+
eb.on(Events.Tool.Activated, h.onToolActivated);
|
|
73
|
+
eb.on(Events.Object.StateChanged, h.onStateChanged);
|
|
74
|
+
eb.on(Events.History.Changed, h.onHistoryChanged);
|
|
75
|
+
|
|
76
|
+
inst._eventBridgeAttached = true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function detachConnectorPropertiesPanelEventBridge(inst) {
|
|
80
|
+
if (!inst._eventBridgeAttached || !inst._eventBridgeHandlers || !inst.eventBus?.off) {
|
|
81
|
+
inst._eventBridgeAttached = false;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const h = inst._eventBridgeHandlers;
|
|
86
|
+
const eb = inst.eventBus;
|
|
87
|
+
|
|
88
|
+
eb.off(Events.Tool.SelectionAdd, h.onSelectionAdd);
|
|
89
|
+
eb.off(Events.Tool.SelectionRemove, h.onSelectionRemove);
|
|
90
|
+
eb.off(Events.Tool.SelectionClear, h.onSelectionClear);
|
|
91
|
+
|
|
92
|
+
eb.off(Events.Tool.DragStart, h.onDragStart);
|
|
93
|
+
eb.off(Events.Tool.DragUpdate, h.onDragUpdate);
|
|
94
|
+
eb.off(Events.Tool.DragEnd, h.onDragEnd);
|
|
95
|
+
|
|
96
|
+
eb.off(Events.Tool.GroupDragStart, h.onGroupDragStart);
|
|
97
|
+
eb.off(Events.Tool.GroupDragUpdate, h.onGroupDragUpdate);
|
|
98
|
+
eb.off(Events.Tool.GroupDragEnd, h.onGroupDragEnd);
|
|
99
|
+
|
|
100
|
+
eb.off(Events.Tool.ResizeUpdate, h.onResizeUpdate);
|
|
101
|
+
eb.off(Events.Tool.RotateUpdate, h.onRotateUpdate);
|
|
102
|
+
|
|
103
|
+
eb.off(Events.UI.ZoomPercent, h.onZoomPercent);
|
|
104
|
+
eb.off(Events.Tool.PanUpdate, h.onPanUpdate);
|
|
105
|
+
eb.off(Events.Viewport.Changed, h.onViewportChanged);
|
|
106
|
+
|
|
107
|
+
eb.off(Events.Object.Deleted, h.onDeleted);
|
|
108
|
+
eb.off(Events.Tool.Activated, h.onToolActivated);
|
|
109
|
+
eb.off(Events.Object.StateChanged, h.onStateChanged);
|
|
110
|
+
eb.off(Events.History.Changed, h.onHistoryChanged);
|
|
111
|
+
|
|
112
|
+
inst._eventBridgeHandlers = null;
|
|
113
|
+
inst._eventBridgeAttached = false;
|
|
114
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { Events } from '../../core/events/Events.js';
|
|
2
|
+
|
|
3
|
+
// ── Цветовые пресеты линии ──────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
export const STROKE_COLOR_PRESETS = [
|
|
6
|
+
{ color: '#2563EB', value: 0x2563EB, name: 'Синий' },
|
|
7
|
+
{ color: '#111827', value: 0x111827, name: 'Чёрный' },
|
|
8
|
+
{ color: '#6B7280', value: 0x6B7280, name: 'Серый' },
|
|
9
|
+
{ color: '#EF4444', value: 0xEF4444, name: 'Красный' },
|
|
10
|
+
{ color: '#F59E0B', value: 0xF59E0B, name: 'Жёлтый' },
|
|
11
|
+
{ color: '#10B981', value: 0x10B981, name: 'Зелёный' },
|
|
12
|
+
{ color: '#8B5CF6', value: 0x8B5CF6, name: 'Фиолетовый' },
|
|
13
|
+
{ color: '#EC4899', value: 0xEC4899, name: 'Розовый' },
|
|
14
|
+
{ color: '#06B6D4', value: 0x06B6D4, name: 'Голубой' },
|
|
15
|
+
{ color: '#FFFFFF', value: 0xFFFFFF, name: 'Белый' },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
// ── Пресеты ширины ───────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
export const WIDTH_PRESETS = [1, 2, 4, 8];
|
|
21
|
+
|
|
22
|
+
// ── Варианты маршрута ────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
export const ROUTE_OPTIONS = [
|
|
25
|
+
{ value: 'straight', label: 'Прямая' },
|
|
26
|
+
{ value: 'elbow', label: 'Угловой' },
|
|
27
|
+
{ value: 'bezier', label: 'Кривая' },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
// ── Варианты наконечника ─────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
export const HEAD_OPTIONS = [
|
|
33
|
+
{ value: 'none', label: '— нет' },
|
|
34
|
+
{ value: 'arrow', label: '→ стрелка' },
|
|
35
|
+
{ value: 'triangle', label: '▷ треугольник' },
|
|
36
|
+
{ value: 'circle', label: '○ круг' },
|
|
37
|
+
{ value: 'diamond', label: '◇ ромб' },
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
// ── Дефолтный style коннектора ───────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
export const CONNECTOR_STYLE_DEFAULTS = {
|
|
43
|
+
stroke: 0x2563EB,
|
|
44
|
+
width: 2,
|
|
45
|
+
dash: false,
|
|
46
|
+
route: 'elbow',
|
|
47
|
+
head: { start: 'none', end: 'arrow' },
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// ── Вспомогательные функции ──────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Конвертирует PIXI-число (0xRRGGBB) в CSS #RRGGBB.
|
|
54
|
+
*/
|
|
55
|
+
export function pixiColorToHex(pixi) {
|
|
56
|
+
return '#' + (pixi >>> 0).toString(16).padStart(6, '0').toUpperCase();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Конвертирует CSS #RRGGBB в PIXI-число.
|
|
61
|
+
*/
|
|
62
|
+
export function hexToPixiColor(hex) {
|
|
63
|
+
return parseInt((hex || '#000000').replace('#', ''), 16);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Возвращает id выделенного коннектора или null.
|
|
68
|
+
*/
|
|
69
|
+
export function getSelectedConnectorId(core) {
|
|
70
|
+
const ids = core?.selectTool ? Array.from(core.selectTool.selectedObjects || []) : [];
|
|
71
|
+
if (ids.length !== 1) return null;
|
|
72
|
+
const id = ids[0];
|
|
73
|
+
const obj = (core?.state?.state?.objects ?? []).find(o => o.id === id);
|
|
74
|
+
return (obj?.type === 'connector') ? id : null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Возвращает объект-коннектор из state по id.
|
|
79
|
+
*/
|
|
80
|
+
export function getConnectorData(core, id) {
|
|
81
|
+
return (core?.state?.state?.objects ?? []).find(o => o.id === id) ?? null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Возвращает экранную середину коннектора, опираясь на _lastSegments.
|
|
86
|
+
* Координаты — целые числа (screen-space integer contract).
|
|
87
|
+
* @returns {{ x: number, y: number } | null}
|
|
88
|
+
*/
|
|
89
|
+
export function getConnectorMidpointScreen(core, connectorId) {
|
|
90
|
+
const segments = core?.connectorLayer?._lastSegments;
|
|
91
|
+
if (!segments) return null;
|
|
92
|
+
const seg = segments.find(s => s.id === connectorId);
|
|
93
|
+
if (!seg) return null;
|
|
94
|
+
|
|
95
|
+
const worldLayer = core?.pixi?.worldLayer;
|
|
96
|
+
const scale = worldLayer?.scale?.x ?? 1;
|
|
97
|
+
const worldX = worldLayer?.x ?? 0;
|
|
98
|
+
const worldY = worldLayer?.y ?? 0;
|
|
99
|
+
|
|
100
|
+
// Поддерживаем оба формата: { points } и устаревший { start, end }
|
|
101
|
+
let midWorldX, midWorldY;
|
|
102
|
+
if (seg.points && seg.points.length >= 2) {
|
|
103
|
+
// Берём середину ломаной (средняя точка по индексу)
|
|
104
|
+
const pts = seg.points;
|
|
105
|
+
const mid = pts[Math.floor((pts.length - 1) / 2)];
|
|
106
|
+
const mid2 = pts[Math.ceil((pts.length - 1) / 2)];
|
|
107
|
+
midWorldX = (mid.x + mid2.x) / 2;
|
|
108
|
+
midWorldY = (mid.y + mid2.y) / 2;
|
|
109
|
+
} else if (seg.start && seg.end) {
|
|
110
|
+
midWorldX = (seg.start.x + seg.end.x) / 2;
|
|
111
|
+
midWorldY = (seg.start.y + seg.end.y) / 2;
|
|
112
|
+
} else {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
x: Math.round(midWorldX * scale + worldX),
|
|
118
|
+
y: Math.round(midWorldY * scale + worldY),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Формирует updates.properties.style для StateChanged-эмита.
|
|
124
|
+
*/
|
|
125
|
+
export function buildStyleUpdate(partialStyle) {
|
|
126
|
+
return { properties: { style: partialStyle } };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Читает текущий style коннектора с fallback на дефолты.
|
|
131
|
+
*/
|
|
132
|
+
export function getConnectorStyle(connector) {
|
|
133
|
+
const s = connector?.properties?.style ?? {};
|
|
134
|
+
return {
|
|
135
|
+
stroke: s.stroke ?? CONNECTOR_STYLE_DEFAULTS.stroke,
|
|
136
|
+
width: s.width ?? CONNECTOR_STYLE_DEFAULTS.width,
|
|
137
|
+
dash: s.dash ?? CONNECTOR_STYLE_DEFAULTS.dash,
|
|
138
|
+
route: s.route ?? CONNECTOR_STYLE_DEFAULTS.route,
|
|
139
|
+
head: {
|
|
140
|
+
start: s.head?.start ?? CONNECTOR_STYLE_DEFAULTS.head.start,
|
|
141
|
+
end: s.head?.end ?? CONNECTOR_STYLE_DEFAULTS.head.end,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|