@sequent-org/moodboard 1.2.42 → 1.2.44

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sequent-org/moodboard",
3
- "version": "1.2.42",
3
+ "version": "1.2.44",
4
4
  "type": "module",
5
5
  "description": "Interactive moodboard",
6
6
  "main": "./src/index.js",
@@ -473,6 +473,10 @@ export class SelectTool extends BaseTool {
473
473
  type: 'text',
474
474
  position: posData.position,
475
475
  properties: { content: textContent },
476
+ caretClick: {
477
+ clientX: event?.originalEvent?.clientX,
478
+ clientY: event?.originalEvent?.clientY
479
+ },
476
480
  create: false
477
481
  });
478
482
  return;
@@ -1990,7 +1994,7 @@ export class SelectTool extends BaseTool {
1990
1994
  ];
1991
1995
  } else {
1992
1996
  // Для обычного текста используем стандартное позиционирование
1993
- // Динамически компенсируем внутренние отступы textarea, чтобы каретка оказалась ровно в точке клика
1997
+ // Динамически компенсируем внутренние отступы textarea для точного совпадения со статичным текстом
1994
1998
  let padTop = 0;
1995
1999
  let padLeft = 0;
1996
2000
  let lineHeightPx = 0;
@@ -2011,8 +2015,27 @@ export class SelectTool extends BaseTool {
2011
2015
  if (r && isFinite(r.height)) lineHeightPx = r.height;
2012
2016
  } catch (_) {}
2013
2017
  }
2014
- const leftPx = create ? Math.round(screenPos.x - padLeft) : Math.round(screenPos.x);
2015
- const topPx = create ? Math.round(screenPos.y - padTop - (lineHeightPx / 2)) : Math.round(screenPos.y);
2018
+
2019
+ // Базовая точка позиционирования: для редактирования берём точные координаты статичного HTML-текста,
2020
+ // для создания — используем рассчитанные screenPos
2021
+ let baseLeftPx = screenPos.x;
2022
+ let baseTopPx = screenPos.y;
2023
+ try {
2024
+ if (!create && objectId && typeof window !== 'undefined' && window.moodboardHtmlTextLayer) {
2025
+ const el = window.moodboardHtmlTextLayer.idToEl.get(objectId);
2026
+ if (el) {
2027
+ const cssLeft = parseFloat(el.style.left || 'NaN');
2028
+ const cssTop = parseFloat(el.style.top || 'NaN');
2029
+ if (isFinite(cssLeft)) baseLeftPx = cssLeft;
2030
+ if (isFinite(cssTop)) baseTopPx = cssTop;
2031
+ }
2032
+ }
2033
+ } catch (_) {}
2034
+
2035
+ const leftPx = Math.round(baseLeftPx - padLeft);
2036
+ const topPx = create
2037
+ ? Math.round(baseTopPx - padTop - (lineHeightPx / 2)) // по клику совмещаем центр строки с точкой клика
2038
+ : Math.round(baseTopPx - padTop); // при редактировании совмещаем верх контента
2016
2039
  wrapper.style.left = `${leftPx}px`;
2017
2040
  wrapper.style.top = `${topPx}px`;
2018
2041
  // Сохраняем CSS-позицию редактора для точной синхронизации при закрытии
@@ -2023,6 +2046,7 @@ export class SelectTool extends BaseTool {
2023
2046
  console.log('🧭 Text input', {
2024
2047
  input: { left: leftPx, top: topPx },
2025
2048
  screenPos,
2049
+ baseFromStatic: (!create && objectId) ? { left: baseLeftPx, top: baseTopPx } : null,
2026
2050
  padding: { top: padTop, left: padLeft },
2027
2051
  lineHeightPx,
2028
2052
  caretCenterY: create ? (topPx + padTop + (lineHeightPx / 2)) : topPx,
@@ -2160,6 +2184,59 @@ export class SelectTool extends BaseTool {
2160
2184
  document.head.appendChild(styleEl);
2161
2185
  this.textEditor = { active: true, objectId, textarea, wrapper, world: this.textEditor.world, position, properties: { fontSize }, objectType, _phStyle: styleEl };
2162
2186
 
2187
+ // Если переходим в редактирование существующего текста по двойному клику,
2188
+ // устанавливаем каретку по координате клика между буквами
2189
+ try {
2190
+ const click = (object && object.caretClick) ? object.caretClick : null;
2191
+ if (!create && objectId && click && typeof window !== 'undefined') {
2192
+ setTimeout(() => {
2193
+ try {
2194
+ const el = window.moodboardHtmlTextLayer ? window.moodboardHtmlTextLayer.idToEl.get(objectId) : null;
2195
+ const fullText = (typeof textarea.value === 'string') ? textarea.value : '';
2196
+ if (!el || !fullText || !el.firstChild) return;
2197
+ const textNode = el.firstChild;
2198
+ const len = textNode.textContent.length;
2199
+ if (len === 0) {
2200
+ textarea.selectionStart = textarea.selectionEnd = 0;
2201
+ return;
2202
+ }
2203
+ const doc = el.ownerDocument || document;
2204
+ let bestIdx = 0;
2205
+ let bestDist = Infinity;
2206
+ for (let i = 0; i <= len; i++) {
2207
+ const range = doc.createRange();
2208
+ range.setStart(textNode, i);
2209
+ range.setEnd(textNode, i);
2210
+ const rects = range.getClientRects();
2211
+ const rect = rects && rects.length > 0 ? rects[0] : range.getBoundingClientRect();
2212
+ if (rect && isFinite(rect.left) && isFinite(rect.top)) {
2213
+ if (click.clientX >= rect.left && click.clientX <= rect.right &&
2214
+ click.clientY >= rect.top && click.clientY <= rect.bottom) {
2215
+ bestIdx = i;
2216
+ bestDist = 0;
2217
+ break;
2218
+ }
2219
+ const cx = Math.max(rect.left, Math.min(click.clientX, rect.right));
2220
+ const cy = Math.max(rect.top, Math.min(click.clientY, rect.bottom));
2221
+ const dx = click.clientX - cx;
2222
+ const dy = click.clientY - cy;
2223
+ const d2 = dx * dx + dy * dy;
2224
+ if (d2 < bestDist) {
2225
+ bestDist = d2;
2226
+ bestIdx = i;
2227
+ }
2228
+ }
2229
+ }
2230
+ const clamp = (v, a, b) => Math.max(a, Math.min(b, v));
2231
+ const caret = clamp(bestIdx, 0, fullText.length);
2232
+ textarea.selectionStart = textarea.selectionEnd = caret;
2233
+ if (typeof textarea.scrollTop === 'number') textarea.scrollTop = 0;
2234
+ console.log('🧭 Text caret set', { objectId, caret, len: fullText.length });
2235
+ } catch (_) {}
2236
+ }, 0);
2237
+ }
2238
+ } catch (_) {}
2239
+
2163
2240
  // Если редактируем записку — скрываем PIXI-текст записки (чтобы не было дублирования)
2164
2241
  if (objectType === 'note' && objectId) {
2165
2242
  try {