@sequent-org/moodboard 1.4.22 → 1.4.24
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/tools/object-tools/selection/SelectInputRouter.js +3 -0
- package/src/tools/object-tools/selection/TextEditorInteractionController.js +22 -7
- package/src/tools/object-tools/selection/TextEditorSyncService.js +9 -13
- package/src/tools/object-tools/selection/TextInlineEditorController.js +37 -5
package/package.json
CHANGED
|
@@ -3,6 +3,9 @@ import { Events } from '../../../core/events/Events.js';
|
|
|
3
3
|
export function onMouseDown(event) {
|
|
4
4
|
// Если активен текстовый редактор, закрываем его при клике вне
|
|
5
5
|
if (this.textEditor.active) {
|
|
6
|
+
// Помечаем, что закрытие инициировано кликом вне редактора.
|
|
7
|
+
// Это используется в blur-handler, чтобы избежать двойного finalize в одном цикле.
|
|
8
|
+
this.textEditor._closingByOutside = true;
|
|
6
9
|
const activeEditorType = this.textEditor.objectType;
|
|
7
10
|
if (this.textEditor.objectType === 'file') {
|
|
8
11
|
this._closeFileNameEditor(true);
|
|
@@ -172,12 +172,21 @@ export function bindTextEditorInteractions(controller, {
|
|
|
172
172
|
finalize,
|
|
173
173
|
}) {
|
|
174
174
|
const blurHandler = () => {
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
175
|
+
const editorObjectId = controller?.textEditor?.objectId || null;
|
|
176
|
+
setTimeout(() => {
|
|
177
|
+
// Если редактор уже закрыт/переключен другим обработчиком (outside click),
|
|
178
|
+
// blur не должен дублировать finalize.
|
|
179
|
+
if (!controller?.textEditor?.active) return;
|
|
180
|
+
if ((controller?.textEditor?.objectId || null) !== editorObjectId) return;
|
|
181
|
+
if (controller?.textEditor?._closingByOutside) return;
|
|
182
|
+
|
|
183
|
+
const value = (textarea.value || '').trim();
|
|
184
|
+
if (isNewCreation && value.length === 0) {
|
|
185
|
+
finalize(false);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
finalize(true);
|
|
189
|
+
}, 0);
|
|
181
190
|
};
|
|
182
191
|
|
|
183
192
|
const keydownHandler = (e) => {
|
|
@@ -220,6 +229,7 @@ export function closeTextEditorFromState(controller, commit) {
|
|
|
220
229
|
const properties = controller.textEditor.properties;
|
|
221
230
|
const isNewCreation = controller.textEditor.isNewCreation;
|
|
222
231
|
const initialContent = controller.textEditor.initialContent ?? '';
|
|
232
|
+
const shouldDeleteEmptyNewCreation = !commitValue && !!isNewCreation && !!objectId;
|
|
223
233
|
|
|
224
234
|
if (objectId) {
|
|
225
235
|
if (typeof window !== 'undefined' && window.moodboardHtmlTextLayer) {
|
|
@@ -244,7 +254,12 @@ export function closeTextEditorFromState(controller, commit) {
|
|
|
244
254
|
|
|
245
255
|
textarea.remove();
|
|
246
256
|
controller.textEditor = { active: false, objectId: null, textarea: null, world: null, objectType: 'text' };
|
|
247
|
-
if (!commitValue)
|
|
257
|
+
if (!commitValue) {
|
|
258
|
+
if (shouldDeleteEmptyNewCreation) {
|
|
259
|
+
controller.eventBus.emit(Events.Tool.ObjectsDelete, { objects: [objectId] });
|
|
260
|
+
}
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
248
263
|
if (objectId == null) {
|
|
249
264
|
controller.eventBus.emit(Events.UI.ToolbarAction, {
|
|
250
265
|
type: objectType,
|
|
@@ -6,32 +6,28 @@ export function createRegularTextAutoSize({
|
|
|
6
6
|
wrapper,
|
|
7
7
|
minWBound,
|
|
8
8
|
minHBound,
|
|
9
|
-
|
|
10
|
-
computeLineHeightPx,
|
|
9
|
+
onSizeChange,
|
|
11
10
|
}) {
|
|
12
11
|
const MAX_AUTO_WIDTH = 360;
|
|
13
|
-
const BASELINE_FIX = 2;
|
|
14
12
|
|
|
15
13
|
return () => {
|
|
16
14
|
textarea.style.width = 'auto';
|
|
17
15
|
textarea.style.height = 'auto';
|
|
18
16
|
|
|
19
|
-
const naturalW = textarea.scrollWidth + 1;
|
|
20
|
-
const targetW = Math.min(MAX_AUTO_WIDTH, Math.max(minWBound, naturalW));
|
|
17
|
+
const naturalW = Math.max(1, Math.ceil(textarea.scrollWidth + 1));
|
|
18
|
+
const targetW = Math.round(Math.min(MAX_AUTO_WIDTH, Math.max(minWBound, naturalW)));
|
|
21
19
|
textarea.style.width = `${targetW}px`;
|
|
22
20
|
wrapper.style.width = `${targetW}px`;
|
|
23
21
|
|
|
24
22
|
textarea.style.height = 'auto';
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const lineH = (computed ? parseFloat(computed.lineHeight) : computeLineHeightPx(effectiveFontPx)) + 10;
|
|
28
|
-
const rawH = textarea.scrollHeight;
|
|
29
|
-
const lines = lineH > 0 ? Math.max(1, Math.round(rawH / lineH)) : 1;
|
|
30
|
-
const targetH = lines <= 1
|
|
31
|
-
? Math.max(minHBound, Math.max(1, lineH - BASELINE_FIX))
|
|
32
|
-
: Math.max(minHBound, Math.max(1, rawH - adjust));
|
|
23
|
+
const naturalH = Math.max(1, Math.ceil(textarea.scrollHeight));
|
|
24
|
+
const targetH = Math.round(Math.max(minHBound, naturalH));
|
|
33
25
|
textarea.style.height = `${targetH}px`;
|
|
34
26
|
wrapper.style.height = `${targetH}px`;
|
|
27
|
+
|
|
28
|
+
if (typeof onSizeChange === 'function') {
|
|
29
|
+
onSizeChange({ widthPx: targetW, heightPx: targetH });
|
|
30
|
+
}
|
|
35
31
|
};
|
|
36
32
|
}
|
|
37
33
|
|
|
@@ -58,8 +58,22 @@ export function openTextEditor(object, create = false) {
|
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
// Закрываем предыдущий редактор, если он
|
|
62
|
-
|
|
61
|
+
// Закрываем предыдущий редактор, если он открыт.
|
|
62
|
+
// Защита от повторного открытия того же объекта в один цикл событий:
|
|
63
|
+
// не пересоздаём textarea/обёртку, если уже редактируем этот объект.
|
|
64
|
+
if (this.textEditor.active) {
|
|
65
|
+
const sameEditorObject = !!(
|
|
66
|
+
objectId &&
|
|
67
|
+
this.textEditor.objectId &&
|
|
68
|
+
this.textEditor.objectId === objectId &&
|
|
69
|
+
this.textEditor.objectType === objectType
|
|
70
|
+
);
|
|
71
|
+
if (sameEditorObject && this.textEditor.textarea) {
|
|
72
|
+
try { this.textEditor.textarea.focus(); } catch (_) {}
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
this._closeTextEditor(true);
|
|
76
|
+
}
|
|
63
77
|
|
|
64
78
|
// Если это редактирование существующего объекта, получаем его данные
|
|
65
79
|
if (!create && objectId) {
|
|
@@ -129,6 +143,8 @@ export function openTextEditor(object, create = false) {
|
|
|
129
143
|
// Базовые стили вынесены в CSS (.moodboard-text-editor)
|
|
130
144
|
|
|
131
145
|
const textarea = createTextEditorTextarea(content || '');
|
|
146
|
+
// Без доступного статичного HTML-элемента (часто при create) не оставляем Caveat как fallback.
|
|
147
|
+
textarea.style.fontFamily = isNote ? 'Caveat, Arial, cursive' : 'Roboto, Arial, sans-serif';
|
|
132
148
|
|
|
133
149
|
// Вычисляем межстрочный интервал; подгоняем к реальным значениям HTML-отображения
|
|
134
150
|
let lhInitial = computeTextEditorLineHeightPx(effectiveFontPx);
|
|
@@ -366,7 +382,7 @@ export function openTextEditor(object, create = false) {
|
|
|
366
382
|
}
|
|
367
383
|
|
|
368
384
|
// Для записок размеры уже установлены выше, пропускаем эту логику
|
|
369
|
-
if (!isNote) {
|
|
385
|
+
if (!isNote && !create) {
|
|
370
386
|
if (initialWpx) {
|
|
371
387
|
textarea.style.width = `${initialWpx}px`;
|
|
372
388
|
wrapper.style.width = `${initialWpx}px`;
|
|
@@ -377,13 +393,29 @@ export function openTextEditor(object, create = false) {
|
|
|
377
393
|
}
|
|
378
394
|
}
|
|
379
395
|
// Автоподгон
|
|
396
|
+
const syncRegularTextSizeToObject = !isNote && objectId
|
|
397
|
+
? ({ widthPx, heightPx }) => {
|
|
398
|
+
try {
|
|
399
|
+
const scaleX = (worldLayerRef?.scale?.x) || 1;
|
|
400
|
+
const widthWorld = Math.max(1, Math.round(widthPx * viewRes / scaleX));
|
|
401
|
+
const heightWorld = Math.max(1, Math.round(heightPx * viewRes / scaleX));
|
|
402
|
+
const posReq = { objectId, position: null };
|
|
403
|
+
this.eventBus.emit(Events.Tool.GetObjectPosition, posReq);
|
|
404
|
+
this.eventBus.emit(Events.Tool.ResizeUpdate, {
|
|
405
|
+
object: objectId,
|
|
406
|
+
size: { width: widthWorld, height: heightWorld },
|
|
407
|
+
position: posReq.position || { x: position.x, y: position.y },
|
|
408
|
+
});
|
|
409
|
+
} catch (_) {}
|
|
410
|
+
}
|
|
411
|
+
: null;
|
|
412
|
+
|
|
380
413
|
const autoSize = createRegularTextAutoSize({
|
|
381
414
|
textarea,
|
|
382
415
|
wrapper,
|
|
383
416
|
minWBound,
|
|
384
417
|
minHBound,
|
|
385
|
-
|
|
386
|
-
computeLineHeightPx: computeTextEditorLineHeightPx,
|
|
418
|
+
onSizeChange: syncRegularTextSizeToObject,
|
|
387
419
|
});
|
|
388
420
|
|
|
389
421
|
// Вызываем autoSize только для обычного текста
|