@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.
- 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 +59 -12
- package/src/ui/chat/ChatExtendedPromptModal.js +1 -12
- package/src/ui/chat/ChatWindow.js +60 -144
- package/src/ui/chat/ChatWindowRenderer.js +1 -8
- package/src/ui/chat/icons.js +0 -4
- 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 +682 -28
- 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,362 @@
|
|
|
1
|
+
import { Events } from '../core/events/Events.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* DrawingPropertiesPanel — плавающая панель свойств выделённой линии (рисунка).
|
|
5
|
+
* Появляется при одиночном выделении объекта типа 'drawing'.
|
|
6
|
+
* Позволяет менять толщину и цвет. Изменения эмитятся через Events.Object.StateChanged
|
|
7
|
+
* (применяются в ObjectLifecycleFlow: instance.setStyle + обновление state.properties).
|
|
8
|
+
*
|
|
9
|
+
* Дублирует часть палитры рисования (см. ToolbarPopupsController.createDrawPopup),
|
|
10
|
+
* поэтому переиспользует те же CSS-классы moodboard-draw__*.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const PALETTE = [
|
|
14
|
+
'#111827', '#374151', '#9ca3af', '#d1d5db', '#ffffff',
|
|
15
|
+
'#ef4444', '#f97316', '#facc15', '#22c55e', '#3b82f6',
|
|
16
|
+
'#fca5a5', '#fdba74', '#fde68a', '#86efac', '#93c5fd',
|
|
17
|
+
'#f9a8d4', '#e9d5ff', '#c4b5fd', '#a5f3fc', '#bfdbfe',
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const MIN_WIDTH = 1;
|
|
21
|
+
const MAX_WIDTH = 24;
|
|
22
|
+
|
|
23
|
+
export class DrawingPropertiesPanel {
|
|
24
|
+
constructor(eventBus, container, core = null) {
|
|
25
|
+
this.eventBus = eventBus;
|
|
26
|
+
this.container = container;
|
|
27
|
+
this.core = core;
|
|
28
|
+
this.panel = null;
|
|
29
|
+
this.currentId = null;
|
|
30
|
+
|
|
31
|
+
this._slider = null;
|
|
32
|
+
this._widthValue = null;
|
|
33
|
+
this._colorGrid = null;
|
|
34
|
+
this._customBtn = null;
|
|
35
|
+
this._colorInput = null;
|
|
36
|
+
|
|
37
|
+
this._attachEvents();
|
|
38
|
+
this._createPanel();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── Публичное API ──────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
updateFromSelection() {
|
|
44
|
+
const ids = this.core?.selectTool
|
|
45
|
+
? Array.from(this.core.selectTool.selectedObjects || [])
|
|
46
|
+
: [];
|
|
47
|
+
|
|
48
|
+
if (!ids || ids.length !== 1) { this.hide(); return; }
|
|
49
|
+
|
|
50
|
+
const id = ids[0];
|
|
51
|
+
const pixi = this.core?.pixi?.objects?.get ? this.core.pixi.objects.get(id) : null;
|
|
52
|
+
const isDrawing = !!(pixi && pixi._mb && pixi._mb.type === 'drawing');
|
|
53
|
+
|
|
54
|
+
if (!isDrawing) { this.hide(); return; }
|
|
55
|
+
|
|
56
|
+
this.currentId = id;
|
|
57
|
+
if (this.panel) {
|
|
58
|
+
this.panel.style.display = 'flex';
|
|
59
|
+
this._updateControlsFromObject();
|
|
60
|
+
this.reposition();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
hide() {
|
|
65
|
+
this.currentId = null;
|
|
66
|
+
if (this.panel) this.panel.style.display = 'none';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
reposition() {
|
|
70
|
+
if (!this.panel || !this.currentId || this.panel.style.display === 'none') return;
|
|
71
|
+
|
|
72
|
+
const ids = this.core?.selectTool
|
|
73
|
+
? Array.from(this.core.selectTool.selectedObjects || [])
|
|
74
|
+
: [];
|
|
75
|
+
if (!ids.includes(this.currentId)) { this.hide(); return; }
|
|
76
|
+
|
|
77
|
+
const posData = { objectId: this.currentId, position: null };
|
|
78
|
+
const sizeData = { objectId: this.currentId, size: null };
|
|
79
|
+
this.eventBus.emit(Events.Tool.GetObjectPosition, posData);
|
|
80
|
+
this.eventBus.emit(Events.Tool.GetObjectSize, sizeData);
|
|
81
|
+
if (!posData.position || !sizeData.size) return;
|
|
82
|
+
|
|
83
|
+
const worldLayer = this.core?.pixi?.worldLayer;
|
|
84
|
+
const scale = worldLayer?.scale?.x || 1;
|
|
85
|
+
const worldX = worldLayer?.x || 0;
|
|
86
|
+
const worldY = worldLayer?.y || 0;
|
|
87
|
+
|
|
88
|
+
const screenX = posData.position.x * scale + worldX;
|
|
89
|
+
const screenY = posData.position.y * scale + worldY;
|
|
90
|
+
const objectWidth = sizeData.size.width * scale;
|
|
91
|
+
const objectHeight = sizeData.size.height * scale;
|
|
92
|
+
|
|
93
|
+
const panelW = this.panel.offsetWidth || 200;
|
|
94
|
+
const panelH = this.panel.offsetHeight || 120;
|
|
95
|
+
let panelX = screenX + (objectWidth / 2) - (panelW / 2);
|
|
96
|
+
let panelY = screenY - panelH - 16;
|
|
97
|
+
|
|
98
|
+
if (panelY < 0) panelY = screenY + objectHeight + 16;
|
|
99
|
+
|
|
100
|
+
const cw = this.container.offsetWidth || window.innerWidth;
|
|
101
|
+
panelX = Math.max(8, Math.min(panelX, cw - panelW - 8));
|
|
102
|
+
panelY = Math.max(8, panelY);
|
|
103
|
+
|
|
104
|
+
this.panel.style.left = `${Math.round(panelX)}px`;
|
|
105
|
+
this.panel.style.top = `${Math.round(panelY)}px`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
destroy() {
|
|
109
|
+
this._cancelRaf();
|
|
110
|
+
|
|
111
|
+
if (this._handlers && this.eventBus?.off) {
|
|
112
|
+
const H = this._handlers;
|
|
113
|
+
this.eventBus.off(Events.Tool.SelectionAdd, H.onSelectionAdd);
|
|
114
|
+
this.eventBus.off(Events.Tool.SelectionRemove, H.onSelectionRemove);
|
|
115
|
+
this.eventBus.off(Events.Tool.SelectionClear, H.onSelectionClear);
|
|
116
|
+
this.eventBus.off(Events.Object.Deleted, H.onDeleted);
|
|
117
|
+
this.eventBus.off(Events.Tool.DragStart, H.onDragStart);
|
|
118
|
+
this.eventBus.off(Events.Tool.DragUpdate, H.onDragUpdate);
|
|
119
|
+
this.eventBus.off(Events.Tool.DragEnd, H.onDragEnd);
|
|
120
|
+
this.eventBus.off(Events.Tool.GroupDragStart, H.onGroupDragStart);
|
|
121
|
+
this.eventBus.off(Events.Tool.GroupDragUpdate, H.onGroupDragUpdate);
|
|
122
|
+
this.eventBus.off(Events.Tool.GroupDragEnd, H.onGroupDragEnd);
|
|
123
|
+
this.eventBus.off(Events.Tool.ResizeUpdate, H.onResizeUpdate);
|
|
124
|
+
this.eventBus.off(Events.Tool.RotateUpdate, H.onRotateUpdate);
|
|
125
|
+
this.eventBus.off(Events.UI.ZoomPercent, H.onZoom);
|
|
126
|
+
this.eventBus.off(Events.Tool.PanUpdate, H.onPan);
|
|
127
|
+
this.eventBus.off(Events.Viewport.Changed, H.onViewportChanged);
|
|
128
|
+
this.eventBus.off(Events.Tool.Activated, H.onActivated);
|
|
129
|
+
this.eventBus.off(Events.Object.TransformUpdated, H.onTransformUpdated);
|
|
130
|
+
this._handlers = null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (this.panel?.parentNode) this.panel.parentNode.removeChild(this.panel);
|
|
134
|
+
this.panel = null;
|
|
135
|
+
this.currentId = null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ── Подписки ───────────────────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
_attachEvents() {
|
|
141
|
+
const H = this._handlers = {};
|
|
142
|
+
|
|
143
|
+
H.onSelectionAdd = () => this.updateFromSelection();
|
|
144
|
+
H.onSelectionRemove = () => this.updateFromSelection();
|
|
145
|
+
H.onSelectionClear = () => this.hide();
|
|
146
|
+
H.onDeleted = (objectId) => {
|
|
147
|
+
if (this.currentId && objectId === this.currentId) this.hide();
|
|
148
|
+
};
|
|
149
|
+
H.onDragStart = () => this.hide();
|
|
150
|
+
H.onDragUpdate = () => this._repositionThrottled();
|
|
151
|
+
H.onDragEnd = () => this.updateFromSelection();
|
|
152
|
+
H.onGroupDragStart = () => this.hide();
|
|
153
|
+
H.onGroupDragUpdate = () => this._repositionThrottled();
|
|
154
|
+
H.onGroupDragEnd = () => this.updateFromSelection();
|
|
155
|
+
H.onResizeUpdate = () => this._repositionThrottled();
|
|
156
|
+
H.onRotateUpdate = () => this._repositionThrottled();
|
|
157
|
+
H.onZoom = () => { if (this.currentId) this._repositionThrottled(); };
|
|
158
|
+
H.onPan = () => { if (this.currentId) this._repositionThrottled(); };
|
|
159
|
+
H.onViewportChanged = () => { if (this.currentId) this._repositionThrottled(); };
|
|
160
|
+
H.onActivated = ({ tool }) => { if (tool !== 'select') this.hide(); };
|
|
161
|
+
H.onTransformUpdated = ({ objectId }) => {
|
|
162
|
+
if (this.currentId && objectId === this.currentId &&
|
|
163
|
+
this.panel?.style.display !== 'none') {
|
|
164
|
+
this._repositionThrottled();
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
this.eventBus.on(Events.Tool.SelectionAdd, H.onSelectionAdd);
|
|
169
|
+
this.eventBus.on(Events.Tool.SelectionRemove, H.onSelectionRemove);
|
|
170
|
+
this.eventBus.on(Events.Tool.SelectionClear, H.onSelectionClear);
|
|
171
|
+
this.eventBus.on(Events.Object.Deleted, H.onDeleted);
|
|
172
|
+
this.eventBus.on(Events.Tool.DragStart, H.onDragStart);
|
|
173
|
+
this.eventBus.on(Events.Tool.DragUpdate, H.onDragUpdate);
|
|
174
|
+
this.eventBus.on(Events.Tool.DragEnd, H.onDragEnd);
|
|
175
|
+
this.eventBus.on(Events.Tool.GroupDragStart, H.onGroupDragStart);
|
|
176
|
+
this.eventBus.on(Events.Tool.GroupDragUpdate, H.onGroupDragUpdate);
|
|
177
|
+
this.eventBus.on(Events.Tool.GroupDragEnd, H.onGroupDragEnd);
|
|
178
|
+
this.eventBus.on(Events.Tool.ResizeUpdate, H.onResizeUpdate);
|
|
179
|
+
this.eventBus.on(Events.Tool.RotateUpdate, H.onRotateUpdate);
|
|
180
|
+
this.eventBus.on(Events.UI.ZoomPercent, H.onZoom);
|
|
181
|
+
this.eventBus.on(Events.Tool.PanUpdate, H.onPan);
|
|
182
|
+
this.eventBus.on(Events.Viewport.Changed, H.onViewportChanged);
|
|
183
|
+
this.eventBus.on(Events.Tool.Activated, H.onActivated);
|
|
184
|
+
this.eventBus.on(Events.Object.TransformUpdated, H.onTransformUpdated);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ── Построение DOM ─────────────────────────────────────────────────────────
|
|
188
|
+
|
|
189
|
+
_createPanel() {
|
|
190
|
+
const panel = document.createElement('div');
|
|
191
|
+
panel.className = 'moodboard-toolbar__popup moodboard-toolbar__popup--draw drawing-properties-panel';
|
|
192
|
+
panel.id = 'drawing-properties-panel';
|
|
193
|
+
panel.style.display = 'none';
|
|
194
|
+
// Панель позиционируется абсолютно внутри canvasContainer
|
|
195
|
+
panel.style.left = '0px';
|
|
196
|
+
panel.style.top = '0px';
|
|
197
|
+
panel.addEventListener('pointerdown', (e) => e.stopPropagation());
|
|
198
|
+
|
|
199
|
+
const inner = document.createElement('div');
|
|
200
|
+
inner.className = 'moodboard-draw__panel';
|
|
201
|
+
|
|
202
|
+
inner.appendChild(this._buildThicknessSection());
|
|
203
|
+
inner.appendChild(this._buildColorSection());
|
|
204
|
+
|
|
205
|
+
panel.appendChild(inner);
|
|
206
|
+
this.panel = panel;
|
|
207
|
+
this.container.appendChild(panel);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
_buildThicknessSection() {
|
|
211
|
+
const section = document.createElement('div');
|
|
212
|
+
section.className = 'moodboard-draw__section moodboard-draw__section--thickness';
|
|
213
|
+
|
|
214
|
+
const header = document.createElement('div');
|
|
215
|
+
header.className = 'moodboard-draw__section-header';
|
|
216
|
+
const label = document.createElement('span');
|
|
217
|
+
label.textContent = 'Толщина';
|
|
218
|
+
label.className = 'moodboard-draw__section-label';
|
|
219
|
+
const value = document.createElement('span');
|
|
220
|
+
value.textContent = '2px';
|
|
221
|
+
value.className = 'moodboard-draw__thickness-value';
|
|
222
|
+
header.appendChild(label);
|
|
223
|
+
header.appendChild(value);
|
|
224
|
+
|
|
225
|
+
const slider = document.createElement('input');
|
|
226
|
+
slider.type = 'range';
|
|
227
|
+
slider.min = String(MIN_WIDTH);
|
|
228
|
+
slider.max = String(MAX_WIDTH);
|
|
229
|
+
slider.value = '2';
|
|
230
|
+
slider.className = 'moodboard-draw__slider';
|
|
231
|
+
slider.addEventListener('input', () => {
|
|
232
|
+
const w = parseInt(slider.value, 10);
|
|
233
|
+
value.textContent = `${w}px`;
|
|
234
|
+
this._emit({ strokeWidth: w });
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
section.appendChild(header);
|
|
238
|
+
section.appendChild(slider);
|
|
239
|
+
|
|
240
|
+
this._slider = slider;
|
|
241
|
+
this._widthValue = value;
|
|
242
|
+
return section;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
_buildColorSection() {
|
|
246
|
+
const section = document.createElement('div');
|
|
247
|
+
section.className = 'moodboard-draw__section';
|
|
248
|
+
|
|
249
|
+
const grid = document.createElement('div');
|
|
250
|
+
grid.className = 'moodboard-draw__color-grid';
|
|
251
|
+
|
|
252
|
+
PALETTE.forEach((hex) => {
|
|
253
|
+
const btn = document.createElement('button');
|
|
254
|
+
btn.className = 'moodboard-draw__color-btn';
|
|
255
|
+
btn.title = hex;
|
|
256
|
+
btn.dataset.hex = hex.toLowerCase();
|
|
257
|
+
btn.style.background = hex;
|
|
258
|
+
if (hex === '#ffffff') btn.style.border = '1.5px solid #d1d5db';
|
|
259
|
+
btn.addEventListener('click', () => {
|
|
260
|
+
this._setActiveSwatch(btn);
|
|
261
|
+
this._emit({ strokeColor: parseInt(hex.replace('#', ''), 16) });
|
|
262
|
+
});
|
|
263
|
+
grid.appendChild(btn);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Кастомный пикер (радужный кружок)
|
|
267
|
+
const customBtn = document.createElement('button');
|
|
268
|
+
customBtn.className = 'moodboard-draw__color-btn moodboard-draw__color-btn--custom';
|
|
269
|
+
customBtn.title = 'Выбрать цвет';
|
|
270
|
+
const colorInput = document.createElement('input');
|
|
271
|
+
colorInput.type = 'color';
|
|
272
|
+
colorInput.value = '#000000';
|
|
273
|
+
colorInput.className = 'moodboard-draw__color-input';
|
|
274
|
+
colorInput.addEventListener('input', () => {
|
|
275
|
+
this._setActiveSwatch(customBtn);
|
|
276
|
+
this._emit({ strokeColor: parseInt(colorInput.value.replace('#', ''), 16) });
|
|
277
|
+
});
|
|
278
|
+
customBtn.appendChild(colorInput);
|
|
279
|
+
customBtn.addEventListener('click', (e) => {
|
|
280
|
+
e.stopPropagation();
|
|
281
|
+
colorInput.click();
|
|
282
|
+
});
|
|
283
|
+
grid.appendChild(customBtn);
|
|
284
|
+
|
|
285
|
+
section.appendChild(grid);
|
|
286
|
+
|
|
287
|
+
this._colorGrid = grid;
|
|
288
|
+
this._customBtn = customBtn;
|
|
289
|
+
this._colorInput = colorInput;
|
|
290
|
+
return section;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ── Синхронизация контролов с объектом ──────────────────────────────────────
|
|
294
|
+
|
|
295
|
+
_updateControlsFromObject() {
|
|
296
|
+
const props = this._getDrawingProps();
|
|
297
|
+
if (!props) return;
|
|
298
|
+
|
|
299
|
+
const width = Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, Math.round(props.strokeWidth ?? 2)));
|
|
300
|
+
if (this._slider) this._slider.value = String(width);
|
|
301
|
+
if (this._widthValue) this._widthValue.textContent = `${width}px`;
|
|
302
|
+
|
|
303
|
+
const hex = this._pixiToHex(props.strokeColor ?? 0x111827);
|
|
304
|
+
this._syncActiveSwatchByHex(hex);
|
|
305
|
+
if (this._colorInput) this._colorInput.value = hex;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
_getDrawingProps() {
|
|
309
|
+
const id = this.currentId;
|
|
310
|
+
if (!id) return null;
|
|
311
|
+
const obj = (this.core?.state?.state?.objects ?? this.core?.state?.getObjects?.() ?? [])
|
|
312
|
+
.find(o => o.id === id);
|
|
313
|
+
return obj?.properties ?? null;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
_setActiveSwatch(activeBtn) {
|
|
317
|
+
if (!this._colorGrid) return;
|
|
318
|
+
this._colorGrid.querySelectorAll('.moodboard-draw__color-btn--active')
|
|
319
|
+
.forEach(el => el.classList.remove('moodboard-draw__color-btn--active'));
|
|
320
|
+
if (activeBtn) activeBtn.classList.add('moodboard-draw__color-btn--active');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
_syncActiveSwatchByHex(hex) {
|
|
324
|
+
if (!this._colorGrid) return;
|
|
325
|
+
const match = this._colorGrid.querySelector(`.moodboard-draw__color-btn[data-hex="${hex}"]`);
|
|
326
|
+
this._setActiveSwatch(match || this._customBtn);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
_pixiToHex(pixi) {
|
|
330
|
+
return '#' + (pixi >>> 0).toString(16).padStart(6, '0').slice(-6).toLowerCase();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// ── Emit ────────────────────────────────────────────────────────────────────
|
|
334
|
+
|
|
335
|
+
_emit(propsPatch) {
|
|
336
|
+
if (!this.currentId) return;
|
|
337
|
+
this.eventBus.emit(Events.Object.StateChanged, {
|
|
338
|
+
objectId: this.currentId,
|
|
339
|
+
updates: { properties: propsPatch },
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ── RAF-позиционирование ───────────────────────────────────────────────────
|
|
344
|
+
|
|
345
|
+
_repositionThrottled() {
|
|
346
|
+
if (this._repositionScheduled) return;
|
|
347
|
+
this._repositionScheduled = true;
|
|
348
|
+
this._repositionRafId = requestAnimationFrame(() => {
|
|
349
|
+
this._repositionScheduled = false;
|
|
350
|
+
this._repositionRafId = null;
|
|
351
|
+
if (this.panel) this.reposition();
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
_cancelRaf() {
|
|
356
|
+
if (this._repositionRafId != null) {
|
|
357
|
+
cancelAnimationFrame(this._repositionRafId);
|
|
358
|
+
this._repositionRafId = null;
|
|
359
|
+
}
|
|
360
|
+
this._repositionScheduled = false;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
@@ -39,6 +39,9 @@ export class FilePropertiesPanel {
|
|
|
39
39
|
this._handlers.onPanUpdate = () => {
|
|
40
40
|
if (this.currentId) this.reposition();
|
|
41
41
|
};
|
|
42
|
+
this._handlers.onViewportChanged = () => {
|
|
43
|
+
if (this.currentId) this.reposition();
|
|
44
|
+
};
|
|
42
45
|
this._handlers.onActivated = ({ tool }) => {
|
|
43
46
|
if (tool !== 'select') this.hide();
|
|
44
47
|
};
|
|
@@ -60,6 +63,7 @@ export class FilePropertiesPanel {
|
|
|
60
63
|
this.eventBus.on(Events.Tool.RotateUpdate, this._handlers.onRotateUpdate);
|
|
61
64
|
this.eventBus.on(Events.UI.ZoomPercent, this._handlers.onZoomPercent);
|
|
62
65
|
this.eventBus.on(Events.Tool.PanUpdate, this._handlers.onPanUpdate);
|
|
66
|
+
this.eventBus.on(Events.Viewport.Changed, this._handlers.onViewportChanged);
|
|
63
67
|
this.eventBus.on(Events.Tool.Activated, this._handlers.onActivated);
|
|
64
68
|
this.eventBus.on(Events.Object.TransformUpdated, this._handlers.onTransformUpdated);
|
|
65
69
|
}
|
|
@@ -348,6 +352,7 @@ export class FilePropertiesPanel {
|
|
|
348
352
|
this.eventBus.off(Events.Tool.RotateUpdate, this._handlers.onRotateUpdate);
|
|
349
353
|
this.eventBus.off(Events.UI.ZoomPercent, this._handlers.onZoomPercent);
|
|
350
354
|
this.eventBus.off(Events.Tool.PanUpdate, this._handlers.onPanUpdate);
|
|
355
|
+
this.eventBus.off(Events.Viewport.Changed, this._handlers.onViewportChanged);
|
|
351
356
|
this.eventBus.off(Events.Tool.Activated, this._handlers.onActivated);
|
|
352
357
|
this.eventBus.off(Events.Object.TransformUpdated, this._handlers.onTransformUpdated);
|
|
353
358
|
this._handlers = null;
|
|
@@ -39,6 +39,9 @@ export class FramePropertiesPanel {
|
|
|
39
39
|
this._handlers.onPanUpdate = () => {
|
|
40
40
|
if (this.currentId) this._repositionThrottled();
|
|
41
41
|
};
|
|
42
|
+
this._handlers.onViewportChanged = () => {
|
|
43
|
+
if (this.currentId) this._repositionThrottled();
|
|
44
|
+
};
|
|
42
45
|
this._handlers.onActivated = ({ tool }) => {
|
|
43
46
|
if (tool !== 'select') this.hide();
|
|
44
47
|
};
|
|
@@ -75,6 +78,7 @@ export class FramePropertiesPanel {
|
|
|
75
78
|
this.eventBus.on(Events.Tool.RotateUpdate, this._handlers.onRotateUpdate);
|
|
76
79
|
this.eventBus.on(Events.UI.ZoomPercent, this._handlers.onZoomPercent);
|
|
77
80
|
this.eventBus.on(Events.Tool.PanUpdate, this._handlers.onPanUpdate);
|
|
81
|
+
this.eventBus.on(Events.Viewport.Changed, this._handlers.onViewportChanged);
|
|
78
82
|
this.eventBus.on(Events.Tool.Activated, this._handlers.onActivated);
|
|
79
83
|
this.eventBus.on(Events.Object.StateChanged, this._handlers.onStateChanged);
|
|
80
84
|
this.eventBus.on(Events.History.Changed, this._handlers.onHistoryChanged);
|
|
@@ -659,6 +663,7 @@ export class FramePropertiesPanel {
|
|
|
659
663
|
this.eventBus.off(Events.Tool.RotateUpdate, this._handlers.onRotateUpdate);
|
|
660
664
|
this.eventBus.off(Events.UI.ZoomPercent, this._handlers.onZoomPercent);
|
|
661
665
|
this.eventBus.off(Events.Tool.PanUpdate, this._handlers.onPanUpdate);
|
|
666
|
+
this.eventBus.off(Events.Viewport.Changed, this._handlers.onViewportChanged);
|
|
662
667
|
this.eventBus.off(Events.Tool.Activated, this._handlers.onActivated);
|
|
663
668
|
this.eventBus.off(Events.Object.StateChanged, this._handlers.onStateChanged);
|
|
664
669
|
this.eventBus.off(Events.History.Changed, this._handlers.onHistoryChanged);
|