@sequent-org/moodboard 1.2.118 → 1.2.119

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.118",
3
+ "version": "1.2.119",
4
4
  "type": "module",
5
5
  "description": "Interactive moodboard",
6
6
  "main": "./src/index.js",
@@ -28,6 +28,7 @@ export const Events = {
28
28
  GroupResizeStart: 'tool:group:resize:start',
29
29
  GroupResizeUpdate: 'tool:group:resize:update',
30
30
  GroupResizeEnd: 'tool:group:resize:end',
31
+ RotateStart: 'tool:rotate:start',
31
32
  RotateUpdate: 'tool:rotate:update',
32
33
  RotateEnd: 'tool:rotate:end',
33
34
  GroupRotateStart: 'tool:group:rotate:start',
package/src/core/index.js CHANGED
@@ -1042,8 +1042,8 @@ export class CoreMoodBoard {
1042
1042
  if (!obj || !pixiObj) continue;
1043
1043
  this._groupResizeSnapshot.set(id, {
1044
1044
  size: { width: obj.width, height: obj.height },
1045
- // Позицию берем из PIXI (центр с учетом pivot), чтобы избежать смещения при первом ресайзе
1046
- position: { x: pixiObj.x, y: pixiObj.y },
1045
+ // В snapshot фиксируем top-left как в state, чтобы не смешивать системы координат.
1046
+ position: { x: obj.position.x, y: obj.position.y },
1047
1047
  type: obj.type || null
1048
1048
  });
1049
1049
  }
@@ -1343,7 +1343,6 @@ export class CoreMoodBoard {
1343
1343
  if (!newPos) newPos = calcNew;
1344
1344
  }
1345
1345
  const command = new ResizeObjectCommand(
1346
- this,
1347
1346
  this,
1348
1347
  data.object,
1349
1348
  data.oldSize,
@@ -1402,7 +1401,8 @@ export class CoreMoodBoard {
1402
1401
 
1403
1402
  this.eventBus.on(Events.Tool.GroupRotateUpdate, (data) => {
1404
1403
  // Поворачиваем каждый объект вокруг общего центра с сохранением относительного смещения
1405
- const center = this._groupRotateCenter || { x: 0, y: 0 };
1404
+ const center = this._groupRotateCenter;
1405
+ if (!center) return;
1406
1406
  const rad = (data.angle || 0) * Math.PI / 180;
1407
1407
  const cos = Math.cos(rad);
1408
1408
  const sin = Math.sin(rad);
@@ -1431,14 +1431,22 @@ export class CoreMoodBoard {
1431
1431
 
1432
1432
  this.eventBus.on(Events.Tool.GroupRotateEnd, (data) => {
1433
1433
  // Оформляем как батч-команду GroupRotateCommand
1434
- const center = this._groupRotateCenter || { x: 0, y: 0 };
1434
+ const center = this._groupRotateCenter;
1435
+ if (!center) return;
1435
1436
  const changes = [];
1436
1437
  for (const id of data.objects) {
1437
1438
  const start = this._groupRotateStart?.get(id);
1438
1439
  const pixiObject = this.pixi.objects.get(id);
1439
1440
  if (!start || !pixiObject) continue;
1440
1441
  const toAngle = pixiObject.rotation * 180 / Math.PI;
1441
- const toPos = { x: pixiObject.x, y: pixiObject.y };
1442
+ const objState = this.state.state.objects.find(o => o.id === id);
1443
+ const toPos = objState?.position
1444
+ ? { x: objState.position.x, y: objState.position.y }
1445
+ : (() => {
1446
+ const halfW = (pixiObject.width || 0) / 2;
1447
+ const halfH = (pixiObject.height || 0) / 2;
1448
+ return { x: pixiObject.x - halfW, y: pixiObject.y - halfH };
1449
+ })();
1442
1450
  if (Math.abs(start.angle - toAngle) > 0.1 || Math.abs(start.position.x - toPos.x) > 0.1 || Math.abs(start.position.y - toPos.y) > 0.1) {
1443
1451
  changes.push({ id, fromAngle: start.angle, toAngle, fromPos: start.position, toPos });
1444
1452
  }
@@ -691,7 +691,7 @@ export class HtmlHandlesLayer {
691
691
  el.style.width = `${Math.max(1, Math.round(endCSS.width))}px`;
692
692
  el.style.height = 'auto';
693
693
  const measured = Math.max(1, Math.round(el.scrollHeight));
694
- const worldH2 = (measured * res) / s;
694
+ const worldH2 = (measured * rendererRes) / s;
695
695
  const fixData = {
696
696
  object: id,
697
697
  size: { width: worldW, height: worldH2 },
@@ -981,7 +981,30 @@ export class HtmlHandlesLayer {
981
981
  const req = { selection: [] };
982
982
  this.eventBus.emit(Events.Tool.GetSelection, req);
983
983
  const objects = req.selection || [];
984
- this.eventBus.emit(Events.Tool.GroupRotateStart, { objects });
984
+ // Центр поворота должен передаваться в world-координатах.
985
+ // При отсутствии core/pixi (например, в изолированных тестах) используем CSS-центр как fallback.
986
+ let centerWorldX = centerX;
987
+ let centerWorldY = centerY;
988
+ try {
989
+ const world = this.core?.pixi?.worldLayer || this.core?.pixi?.app?.stage;
990
+ const s = world?.scale?.x || 1;
991
+ const tx = world?.x || 0;
992
+ const ty = world?.y || 0;
993
+ const rendererRes = (this.core?.pixi?.app?.renderer?.resolution) || 1;
994
+ const containerRect = this.container?.getBoundingClientRect ? this.container.getBoundingClientRect() : { left: 0, top: 0 };
995
+ const view = this.core?.pixi?.app?.view || null;
996
+ const viewRect = view && view.getBoundingClientRect ? view.getBoundingClientRect() : { left: 0, top: 0 };
997
+ const offsetLeft = viewRect.left - containerRect.left;
998
+ const offsetTop = viewRect.top - containerRect.top;
999
+ const screenX = centerX - offsetLeft;
1000
+ const screenY = centerY - offsetTop;
1001
+ centerWorldX = ((screenX * rendererRes) - tx) / s;
1002
+ centerWorldY = ((screenY * rendererRes) - ty) / s;
1003
+ } catch (_) {}
1004
+ this.eventBus.emit(Events.Tool.GroupRotateStart, {
1005
+ objects,
1006
+ center: { x: centerWorldX, y: centerWorldY },
1007
+ });
985
1008
  }
986
1009
 
987
1010
  const onRotateMove = (ev) => {