@sequent-org/moodboard 1.4.0 → 1.4.1
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 +1 -1
- package/src/moodboard/integration/MoodBoardEventBindings.js +58 -2
- package/src/tools/object-tools/selection/MindmapInlineEditorController.js +42 -1
- package/src/tools/object-tools/selection/SelectInputRouter.js +14 -1
- package/src/tools/object-tools/selection/SelectionStateController.js +7 -0
- package/src/ui/mindmap/MindmapConnectionLayer.js +23 -8
package/package.json
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import { Events } from '../../core/events/Events.js';
|
|
2
2
|
import { logMindmapCompoundDebug } from '../../mindmap/MindmapCompoundContract.js';
|
|
3
3
|
|
|
4
|
+
function getSelectTool(board) {
|
|
5
|
+
const toolManager = board?.coreMoodboard?.toolManager;
|
|
6
|
+
if (!toolManager) return null;
|
|
7
|
+
return toolManager?.tools?.get?.('select')
|
|
8
|
+
|| toolManager?.registry?.get?.('select')
|
|
9
|
+
|| null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function hasOpenTextEditorInDom(board) {
|
|
13
|
+
const doc = board?.workspaceElement?.ownerDocument
|
|
14
|
+
|| (typeof document !== 'undefined' ? document : null);
|
|
15
|
+
if (!doc || typeof doc.querySelector !== 'function') return false;
|
|
16
|
+
return Boolean(doc.querySelector('.moodboard-text-input'));
|
|
17
|
+
}
|
|
18
|
+
|
|
4
19
|
export function bindToolbarEvents(board) {
|
|
5
20
|
board.coreMoodboard.eventBus.on(Events.UI.ToolbarAction, (action) => {
|
|
6
21
|
if (action?.type === 'mindmap') {
|
|
@@ -13,8 +28,49 @@ export function bindToolbarEvents(board) {
|
|
|
13
28
|
const createdObject = board.actionHandler.handleToolbarAction(action);
|
|
14
29
|
if (action?.type === 'mindmap' && createdObject?.id) {
|
|
15
30
|
const content = String(createdObject?.properties?.content || '');
|
|
16
|
-
|
|
31
|
+
const createdMeta = createdObject?.properties?.mindmap || {};
|
|
32
|
+
const isRootMindmap = createdMeta?.role === 'root';
|
|
33
|
+
const mindmapObjects = (board?.coreMoodboard?.state?.state?.objects || [])
|
|
34
|
+
.filter((obj) => obj?.type === 'mindmap');
|
|
35
|
+
const rootCount = mindmapObjects.filter((obj) => (obj?.properties?.mindmap?.role || null) === 'root').length;
|
|
36
|
+
const shouldAutoOpenForRoot = !isRootMindmap || rootCount <= 1;
|
|
37
|
+
const selectTool = getSelectTool(board);
|
|
38
|
+
const hasActiveEditor = Boolean(selectTool?.textEditor?.active);
|
|
39
|
+
const hasEditorDom = hasOpenTextEditorInDom(board);
|
|
40
|
+
const shouldBlockAutoOpen = hasActiveEditor && hasEditorDom;
|
|
41
|
+
if (content.trim().length === 0 && !shouldBlockAutoOpen && shouldAutoOpenForRoot) {
|
|
42
|
+
const doc = board?.workspaceElement?.ownerDocument
|
|
43
|
+
|| (typeof document !== 'undefined' ? document : null);
|
|
44
|
+
const closeSeqAtSchedule = Number(selectTool?._mindmapEditorCloseSeq || 0);
|
|
45
|
+
let cancelledByPointer = false;
|
|
46
|
+
const cancelOnPointerDown = () => {
|
|
47
|
+
cancelledByPointer = true;
|
|
48
|
+
};
|
|
49
|
+
const cancelOnEscape = (event) => {
|
|
50
|
+
if (event?.key === 'Escape') {
|
|
51
|
+
cancelledByPointer = true;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
if (doc && typeof doc.addEventListener === 'function') {
|
|
55
|
+
doc.addEventListener('pointerdown', cancelOnPointerDown, true);
|
|
56
|
+
doc.addEventListener('keydown', cancelOnEscape, true);
|
|
57
|
+
}
|
|
17
58
|
setTimeout(() => {
|
|
59
|
+
if (doc && typeof doc.removeEventListener === 'function') {
|
|
60
|
+
doc.removeEventListener('pointerdown', cancelOnPointerDown, true);
|
|
61
|
+
doc.removeEventListener('keydown', cancelOnEscape, true);
|
|
62
|
+
}
|
|
63
|
+
if (cancelledByPointer) return;
|
|
64
|
+
const latestSelectTool = getSelectTool(board);
|
|
65
|
+
const latestCloseSeq = Number(latestSelectTool?._mindmapEditorCloseSeq || 0);
|
|
66
|
+
if (latestCloseSeq !== closeSeqAtSchedule) return;
|
|
67
|
+
|
|
68
|
+
const nextSelectTool = getSelectTool(board);
|
|
69
|
+
const nextHasActiveEditor = Boolean(nextSelectTool?.textEditor?.active);
|
|
70
|
+
const nextHasEditorDom = hasOpenTextEditorInDom(board);
|
|
71
|
+
if (nextHasActiveEditor || nextHasEditorDom) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
18
74
|
board.coreMoodboard.eventBus.emit(Events.Keyboard.ToolSelect, { tool: 'select' });
|
|
19
75
|
board.coreMoodboard.eventBus.emit(Events.Tool.ObjectEdit, {
|
|
20
76
|
object: {
|
|
@@ -25,7 +81,7 @@ export function bindToolbarEvents(board) {
|
|
|
25
81
|
},
|
|
26
82
|
create: true,
|
|
27
83
|
});
|
|
28
|
-
},
|
|
84
|
+
}, 0);
|
|
29
85
|
}
|
|
30
86
|
}
|
|
31
87
|
});
|
|
@@ -175,7 +175,12 @@ export function openMindmapEditor(object, create = false) {
|
|
|
175
175
|
|
|
176
176
|
this.eventBus.emit(Events.UI.TextEditStart, { objectId: objectId || null });
|
|
177
177
|
if (objectId && typeof this.setSelection === 'function') {
|
|
178
|
-
this.
|
|
178
|
+
this._selectionSyncFromEditor = true;
|
|
179
|
+
try {
|
|
180
|
+
this.setSelection([objectId]);
|
|
181
|
+
} finally {
|
|
182
|
+
this._selectionSyncFromEditor = false;
|
|
183
|
+
}
|
|
179
184
|
}
|
|
180
185
|
updateGlobalTextEditorHandlesLayer();
|
|
181
186
|
|
|
@@ -496,6 +501,36 @@ export function openMindmapEditor(object, create = false) {
|
|
|
496
501
|
|
|
497
502
|
hideStaticTextDuringEditing(this, objectId);
|
|
498
503
|
|
|
504
|
+
const ownerDoc = view?.ownerDocument || (typeof document !== 'undefined' ? document : null);
|
|
505
|
+
const onOutsideDocumentPointerDown = (event) => {
|
|
506
|
+
if (!this.textEditor?.active || this.textEditor.objectType !== 'mindmap') return;
|
|
507
|
+
const target = event?.target;
|
|
508
|
+
if (!target) return;
|
|
509
|
+
if (wrapper.contains(target)) return;
|
|
510
|
+
const sideButton = (typeof target.closest === 'function')
|
|
511
|
+
? target.closest('.mb-mindmap-side-btn')
|
|
512
|
+
: null;
|
|
513
|
+
if (sideButton?.dataset?.side === 'bottom') return;
|
|
514
|
+
finalize(true);
|
|
515
|
+
if (typeof this.clearSelection === 'function') {
|
|
516
|
+
this.clearSelection();
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
const onEscapeDocumentKeyDown = (event) => {
|
|
520
|
+
if (!this.textEditor?.active || this.textEditor.objectType !== 'mindmap') return;
|
|
521
|
+
if (event?.key !== 'Escape') return;
|
|
522
|
+
if (typeof event.preventDefault === 'function') event.preventDefault();
|
|
523
|
+
if (typeof event.stopPropagation === 'function') event.stopPropagation();
|
|
524
|
+
finalize(false);
|
|
525
|
+
if (typeof this.clearSelection === 'function') {
|
|
526
|
+
this.clearSelection();
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
if (ownerDoc && typeof ownerDoc.addEventListener === 'function') {
|
|
530
|
+
ownerDoc.addEventListener('pointerdown', onOutsideDocumentPointerDown, true);
|
|
531
|
+
ownerDoc.addEventListener('keydown', onEscapeDocumentKeyDown, true);
|
|
532
|
+
}
|
|
533
|
+
|
|
499
534
|
const syncEditorBoundsToObject = () => {
|
|
500
535
|
if (!objectId || !wrapper || !view || !view.parentElement || !world) return;
|
|
501
536
|
const staticEl = (typeof window !== 'undefined')
|
|
@@ -561,8 +596,14 @@ export function openMindmapEditor(object, create = false) {
|
|
|
561
596
|
const finalize = (commit) => {
|
|
562
597
|
if (finalized) return;
|
|
563
598
|
finalized = true;
|
|
599
|
+
this._mindmapEditorLastClosedAt = Date.now();
|
|
600
|
+
this._mindmapEditorCloseSeq = Number(this._mindmapEditorCloseSeq || 0) + 1;
|
|
564
601
|
|
|
565
602
|
unregisterEditorListeners(this.eventBus, editorListeners);
|
|
603
|
+
if (ownerDoc && typeof ownerDoc.removeEventListener === 'function') {
|
|
604
|
+
ownerDoc.removeEventListener('pointerdown', onOutsideDocumentPointerDown, true);
|
|
605
|
+
ownerDoc.removeEventListener('keydown', onEscapeDocumentKeyDown, true);
|
|
606
|
+
}
|
|
566
607
|
|
|
567
608
|
textarea.removeEventListener('blur', onBlur);
|
|
568
609
|
textarea.removeEventListener('keydown', onKeyDown);
|
|
@@ -250,7 +250,20 @@ export function onContextMenu(event) {
|
|
|
250
250
|
export function onKeyDown(event) {
|
|
251
251
|
// Проверяем, не активен ли текстовый редактор (редактирование названия файла или текста)
|
|
252
252
|
if (this.textEditor && this.textEditor.active) {
|
|
253
|
-
|
|
253
|
+
if (event.key === 'Escape') {
|
|
254
|
+
if (this.textEditor.objectType === 'file') {
|
|
255
|
+
this._closeFileNameEditor(false);
|
|
256
|
+
} else {
|
|
257
|
+
this._closeTextEditor(false);
|
|
258
|
+
}
|
|
259
|
+
if (this.textEditor.objectType === 'mindmap') {
|
|
260
|
+
this.clearSelection();
|
|
261
|
+
}
|
|
262
|
+
if (event?.originalEvent?.preventDefault) {
|
|
263
|
+
event.originalEvent.preventDefault();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return; // Не обрабатываем остальные клавиши во время редактирования
|
|
254
267
|
}
|
|
255
268
|
|
|
256
269
|
switch (event.key) {
|
|
@@ -48,6 +48,13 @@ export function hasSelection() {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export function setSelection(objectIds) {
|
|
51
|
+
if (this.textEditor?.active && !this._selectionSyncFromEditor) {
|
|
52
|
+
if (this.textEditor.objectType === 'file' && typeof this._closeFileNameEditor === 'function') {
|
|
53
|
+
this._closeFileNameEditor(true);
|
|
54
|
+
} else if (typeof this._closeTextEditor === 'function') {
|
|
55
|
+
this._closeTextEditor(true);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
51
58
|
const prev = this.selection.toArray();
|
|
52
59
|
this.selection.clear();
|
|
53
60
|
this.selection.addMany(objectIds);
|
|
@@ -109,17 +109,14 @@ export class MindmapConnectionLayer {
|
|
|
109
109
|
this.graphics = null;
|
|
110
110
|
this.subscriptions = [];
|
|
111
111
|
this._lastSegments = [];
|
|
112
|
+
this._eventsAttached = false;
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
attach() {
|
|
115
116
|
if (!this.core?.pixi) return;
|
|
116
|
-
if (this.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
this.graphics.zIndex = 2;
|
|
120
|
-
const world = this.core.pixi.worldLayer || this.core.pixi.app?.stage;
|
|
121
|
-
world?.addChild?.(this.graphics);
|
|
122
|
-
this._attachEvents();
|
|
117
|
+
if (!this._eventsAttached) {
|
|
118
|
+
this._attachEvents();
|
|
119
|
+
}
|
|
123
120
|
this.updateAll();
|
|
124
121
|
}
|
|
125
122
|
|
|
@@ -135,6 +132,7 @@ export class MindmapConnectionLayer {
|
|
|
135
132
|
}
|
|
136
133
|
|
|
137
134
|
_attachEvents() {
|
|
135
|
+
if (this._eventsAttached) return;
|
|
138
136
|
const bindings = [
|
|
139
137
|
[Events.Object.Created, () => this.updateAll()],
|
|
140
138
|
[Events.Object.Deleted, () => this.updateAll()],
|
|
@@ -156,6 +154,7 @@ export class MindmapConnectionLayer {
|
|
|
156
154
|
this.eventBus.on(event, handler);
|
|
157
155
|
this.subscriptions.push([event, handler]);
|
|
158
156
|
});
|
|
157
|
+
this._eventsAttached = true;
|
|
159
158
|
}
|
|
160
159
|
|
|
161
160
|
_detachEvents() {
|
|
@@ -165,10 +164,10 @@ export class MindmapConnectionLayer {
|
|
|
165
164
|
}
|
|
166
165
|
this.subscriptions.forEach(([event, handler]) => this.eventBus.off(event, handler));
|
|
167
166
|
this.subscriptions = [];
|
|
167
|
+
this._eventsAttached = false;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
updateAll() {
|
|
171
|
-
if (!this.graphics) return;
|
|
172
171
|
const objects = asArray(this.core?.state?.state?.objects);
|
|
173
172
|
const mindmaps = objects.filter(isMindmap);
|
|
174
173
|
const byId = new Map(mindmaps.map((obj) => [obj.id, obj]));
|
|
@@ -184,6 +183,22 @@ export class MindmapConnectionLayer {
|
|
|
184
183
|
return meta.role === 'child' && typeof meta.parentId === 'string' && meta.parentId.length > 0;
|
|
185
184
|
});
|
|
186
185
|
|
|
186
|
+
if (children.length === 0) {
|
|
187
|
+
if (this.graphics) {
|
|
188
|
+
this.graphics.clear();
|
|
189
|
+
}
|
|
190
|
+
this._lastSegments = [];
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!this.graphics) {
|
|
195
|
+
this.graphics = new PIXI.Graphics();
|
|
196
|
+
this.graphics.name = 'mindmap-connection-layer';
|
|
197
|
+
this.graphics.zIndex = 2;
|
|
198
|
+
const world = this.core?.pixi?.worldLayer || this.core?.pixi?.app?.stage;
|
|
199
|
+
world?.addChild?.(this.graphics);
|
|
200
|
+
}
|
|
201
|
+
|
|
187
202
|
const g = this.graphics;
|
|
188
203
|
g.clear();
|
|
189
204
|
this._lastSegments = [];
|