@inditextech/weave-sdk 5.5.0 → 5.6.0

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/dist/types.js CHANGED
@@ -5937,6 +5937,7 @@ const MOVE_TOOL_STATE = {
5937
5937
  };
5938
5938
  //#endregion
5939
5939
  //#region src/nodes/node.ts
5940
+ const nodeInstanceListeners = /* @__PURE__ */ new WeakMap();
5940
5941
  const augmentKonvaNodeClass = (config) => {
5941
5942
  const { transform } = config ?? {};
5942
5943
  Konva.Node.prototype.getTransformerProperties = function() {
@@ -5952,6 +5953,8 @@ const augmentKonvaNodeClass = (config) => {
5952
5953
  Konva.Node.prototype.triggerCrop = function() {};
5953
5954
  Konva.Node.prototype.closeCrop = function() {};
5954
5955
  Konva.Node.prototype.resetCrop = function() {};
5956
+ Konva.Node.prototype.flipHorizontal = function() {};
5957
+ Konva.Node.prototype.flipVertical = function() {};
5955
5958
  Konva.Node.prototype.dblClick = function() {};
5956
5959
  Konva.Node.prototype.click = function() {};
5957
5960
  Konva.Node.prototype.allowedAnchors = function() {
@@ -6037,6 +6040,8 @@ var WeaveNode = class {
6037
6040
  };
6038
6041
  node.updatePosition = function() {};
6039
6042
  node.resetCrop = function() {};
6043
+ node.flipHorizontal = function() {};
6044
+ node.flipVertical = function() {};
6040
6045
  node.handleMouseover = function() {};
6041
6046
  node.handleMouseout = function() {};
6042
6047
  node.handleSelectNode = function() {};
@@ -6207,7 +6212,7 @@ var WeaveNode = class {
6207
6212
  node.draggable(false);
6208
6213
  };
6209
6214
  if (node.getAttrs().locked ?? false) {
6210
- this.instance.removeEventListener("onNodesChange", handleNodesChange);
6215
+ this.removeNodeInstanceListeners(node, false);
6211
6216
  node.off("transformstart");
6212
6217
  node.off("transform");
6213
6218
  node.off("transformend");
@@ -6217,7 +6222,7 @@ var WeaveNode = class {
6217
6222
  node.off("pointerover");
6218
6223
  node.off("pointerleave");
6219
6224
  } else {
6220
- this.instance.addEventListener("onNodesChange", handleNodesChange);
6225
+ this.setNodeInstanceListener(node, "onNodesChange", handleNodesChange);
6221
6226
  node.off("transformstart");
6222
6227
  node.on("transformstart", (e) => {
6223
6228
  if (e.target.getAttrs().strokeScaleEnabled !== false) {
@@ -6276,7 +6281,7 @@ var WeaveNode = class {
6276
6281
  });
6277
6282
  const stage = this.instance.getStage();
6278
6283
  let originalPosition = null;
6279
- this.instance.addEventListener("onSelectionState", (state) => {
6284
+ this.setNodeInstanceListener(node, "onSelectionState", (state) => {
6280
6285
  const selectedNodes = this.getSelectionPlugin()?.getSelectedNodes() ?? [];
6281
6286
  if (!state && selectedNodes?.some((n) => n.getAttrs().id === node.getAttrs().id)) originalPosition = node.getAbsolutePosition();
6282
6287
  });
@@ -6592,8 +6597,39 @@ var WeaveNode = class {
6592
6597
  async onRegister() {}
6593
6598
  onAdd(nodeInstance) {}
6594
6599
  onDestroy(nodeInstance) {
6600
+ this.removeNodeInstanceListeners(nodeInstance);
6595
6601
  nodeInstance.destroy();
6596
6602
  }
6603
+ /**
6604
+ * Registers a Weave instance listener on behalf of a node. Only one listener
6605
+ * is kept per node and event: registering the same event again for the same
6606
+ * node replaces the previous listener, including the ones registered by
6607
+ * setupDefaultNodeEvents (onNodesChange, onSelectionState).
6608
+ */
6609
+ setNodeInstanceListener(node, event, callback) {
6610
+ let listeners = nodeInstanceListeners.get(node);
6611
+ if (!listeners) {
6612
+ listeners = /* @__PURE__ */ new Map();
6613
+ nodeInstanceListeners.set(node, listeners);
6614
+ }
6615
+ const previous = listeners.get(event);
6616
+ if (previous) this.instance.removeEventListener(event, previous);
6617
+ listeners.set(event, callback);
6618
+ this.instance.addEventListener(event, callback);
6619
+ }
6620
+ /**
6621
+ * Removes the listeners registered with setNodeInstanceListener for the node
6622
+ * and, unless disabled, for its descendants.
6623
+ */
6624
+ removeNodeInstanceListeners(node, includeDescendants = true) {
6625
+ const nodes = includeDescendants && node instanceof Konva.Container ? [node, ...node.find(() => true)] : [node];
6626
+ for (const actNode of nodes) {
6627
+ const listeners = nodeInstanceListeners.get(actNode);
6628
+ if (!listeners) continue;
6629
+ for (const [event, callback] of listeners) this.instance.removeEventListener(event, callback);
6630
+ nodeInstanceListeners.delete(actNode);
6631
+ }
6632
+ }
6597
6633
  onDestroyInstance() {}
6598
6634
  serialize(instance) {
6599
6635
  const attrs = instance.getAttrs();
@@ -9466,7 +9502,7 @@ var WeaveRegisterManager = class {
9466
9502
  };
9467
9503
  //#endregion
9468
9504
  //#region package.json
9469
- var version = "5.5.0";
9505
+ var version = "5.6.0";
9470
9506
  //#endregion
9471
9507
  //#region src/managers/setup.ts
9472
9508
  var WeaveSetupManager = class {
@@ -11187,6 +11223,9 @@ var Weave = class {
11187
11223
  getBoundingBox(nodes, config) {
11188
11224
  return getBoundingBox(nodes, config);
11189
11225
  }
11226
+ flipImage(node, options) {
11227
+ this.getNodeHandler("image")?.flipImage(node, options);
11228
+ }
11190
11229
  stateTransactional(callback, origin) {
11191
11230
  this.stateManager.stateTransactional(callback, origin);
11192
11231
  }
@@ -14487,24 +14526,64 @@ var WeaveImageCrop = class WeaveImageCrop {
14487
14526
  this.onClose = () => {};
14488
14527
  this.handleHide = this.hide.bind(this);
14489
14528
  }
14490
- setupCropImage(imageAttrs, visible) {
14491
- this.cropGroup.destroyChildren();
14529
+ /**
14530
+ * Mirrors the start of a segment inside a container along one axis:
14531
+ * the segment [start, start + length] inside [0, size] becomes
14532
+ * [size - start - length, size - start]. Used to convert between visual
14533
+ * space (what the user sees on a flipped image) and source space (where
14534
+ * the crop information is stored). It is its own inverse.
14535
+ */
14536
+ static mirrorOffset(start, length, size) {
14537
+ return size - start - length;
14538
+ }
14539
+ /**
14540
+ * Returns the offset (in uncropped display units) of the visible crop
14541
+ * window inside the visually rendered uncropped image, along with the real
14542
+ * scale. When the image is flipped the stored crop (source space) is
14543
+ * mirrored to match what the user sees.
14544
+ */
14545
+ getVisualCropOffset(imageAttrs) {
14492
14546
  const actualScale = imageAttrs.uncroppedImage.width / imageAttrs.imageInfo.width;
14493
14547
  const realScale = actualScale / (imageAttrs.cropInfo ? imageAttrs.cropInfo.scaleX : actualScale);
14548
+ let x = 0;
14549
+ let y = 0;
14550
+ if (imageAttrs.cropInfo) {
14551
+ x = imageAttrs.cropInfo.x * realScale;
14552
+ y = imageAttrs.cropInfo.y * realScale;
14553
+ if (imageAttrs.flipX === true) x = WeaveImageCrop.mirrorOffset(x, imageAttrs.cropInfo.width * realScale, imageAttrs.uncroppedImage.width);
14554
+ if (imageAttrs.flipY === true) y = WeaveImageCrop.mirrorOffset(y, imageAttrs.cropInfo.height * realScale, imageAttrs.uncroppedImage.height);
14555
+ }
14556
+ return {
14557
+ x,
14558
+ y,
14559
+ realScale
14560
+ };
14561
+ }
14562
+ setupCropImage(imageAttrs, visible) {
14563
+ this.cropGroup.destroyChildren();
14564
+ const { x, y, realScale } = this.getVisualCropOffset(imageAttrs);
14565
+ const flipX = imageAttrs.flipX === true;
14566
+ const flipY = imageAttrs.flipY === true;
14494
14567
  this.cropImage = new Konva.Image({
14495
- x: imageAttrs.cropInfo ? -imageAttrs.cropInfo.x * realScale : 0,
14496
- y: imageAttrs.cropInfo ? -imageAttrs.cropInfo.y * realScale : 0,
14568
+ x: -x,
14569
+ y: -y,
14497
14570
  width: imageAttrs.uncroppedImage.width,
14498
14571
  height: imageAttrs.uncroppedImage.height,
14499
- scaleX: 1,
14500
- scaleY: 1,
14572
+ scaleX: flipX ? -1 : 1,
14573
+ scaleY: flipY ? -1 : 1,
14574
+ offsetX: flipX ? imageAttrs.uncroppedImage.width : 0,
14575
+ offsetY: flipY ? imageAttrs.uncroppedImage.height : 0,
14501
14576
  image: this.internalImage.image(),
14502
14577
  crop: void 0,
14503
14578
  visible,
14504
14579
  listening: false,
14505
14580
  draggable: false
14506
14581
  });
14507
- return { realScale };
14582
+ return {
14583
+ realScale,
14584
+ offsetX: x,
14585
+ offsetY: y
14586
+ };
14508
14587
  }
14509
14588
  show(onClose, options) {
14510
14589
  this.onClose = onClose;
@@ -14515,9 +14594,9 @@ var WeaveImageCrop = class WeaveImageCrop {
14515
14594
  this.image.listening(false);
14516
14595
  const imageAttrs = this.image.getAttrs();
14517
14596
  this.internalImage.hide();
14518
- const { realScale } = this.setupCropImage(imageAttrs, true);
14519
- this.imageOffsetX = imageAttrs.cropInfo ? imageAttrs.cropInfo.x * realScale : 0;
14520
- this.imageOffsetY = imageAttrs.cropInfo ? imageAttrs.cropInfo.y * realScale : 0;
14597
+ const { realScale, offsetX, offsetY } = this.setupCropImage(imageAttrs, true);
14598
+ this.imageOffsetX = offsetX;
14599
+ this.imageOffsetY = offsetY;
14521
14600
  const cropModeConfiguration = this.node.getConfiguration().cropMode;
14522
14601
  this.cropRect = new Konva.Rect({
14523
14602
  x: 0,
@@ -14565,7 +14644,10 @@ var WeaveImageCrop = class WeaveImageCrop {
14565
14644
  const center = this.cropImage.getAbsolutePosition();
14566
14645
  const width = this.cropImage.width() * stage.scaleX();
14567
14646
  const height = this.cropImage.height() * stage.scaleY();
14568
- const offset = this.cropImage.offset();
14647
+ const offset = {
14648
+ x: this.cropImage.scaleX() < 0 ? 0 : this.cropImage.offsetX(),
14649
+ y: this.cropImage.scaleY() < 0 ? 0 : this.cropImage.offsetY()
14650
+ };
14569
14651
  const corners = [
14570
14652
  {
14571
14653
  x: -offset.x,
@@ -14909,11 +14991,15 @@ var WeaveImageCrop = class WeaveImageCrop {
14909
14991
  if (!intersectionRect) return;
14910
14992
  const imageAttrs = this.image.getAttrs();
14911
14993
  const actualScale = imageAttrs.uncroppedImage.width / imageAttrs.imageInfo.width;
14994
+ let sourceX = clipRectGroup.x + this.imageOffsetX;
14995
+ let sourceY = clipRectGroup.y + this.imageOffsetY;
14996
+ if (imageAttrs.flipX === true) sourceX = WeaveImageCrop.mirrorOffset(sourceX, clipRectGroup.width, imageAttrs.uncroppedImage.width);
14997
+ if (imageAttrs.flipY === true) sourceY = WeaveImageCrop.mirrorOffset(sourceY, clipRectGroup.height, imageAttrs.uncroppedImage.height);
14912
14998
  const realClipRect = {
14913
14999
  scaleX: actualScale,
14914
15000
  scaleY: actualScale,
14915
- x: WeaveImageCrop.roundTo6Decimals(clipRectGroup.x + this.imageOffsetX),
14916
- y: WeaveImageCrop.roundTo6Decimals(clipRectGroup.y + this.imageOffsetY),
15001
+ x: WeaveImageCrop.roundTo6Decimals(sourceX),
15002
+ y: WeaveImageCrop.roundTo6Decimals(sourceY),
14917
15003
  width: WeaveImageCrop.roundTo6Decimals(clipRectGroup.width),
14918
15004
  height: WeaveImageCrop.roundTo6Decimals(clipRectGroup.height)
14919
15005
  };
@@ -14955,16 +15041,18 @@ var WeaveImageCrop = class WeaveImageCrop {
14955
15041
  if (!intersectionRect || !intersectionRectAbs) return;
14956
15042
  const imageAttrs = this.image.getAttrs();
14957
15043
  const actualScale = imageAttrs.uncroppedImage.width / imageAttrs.imageInfo.width;
14958
- const realScale = actualScale / (imageAttrs.cropInfo ? imageAttrs.cropInfo.scaleX : actualScale);
14959
- const imageOffsetX = imageAttrs.cropInfo ? imageAttrs.cropInfo.x * realScale : 0;
14960
- const imageOffsetY = imageAttrs.cropInfo ? imageAttrs.cropInfo.y * realScale : 0;
15044
+ const { x: imageOffsetX, y: imageOffsetY } = this.getVisualCropOffset(imageAttrs);
14961
15045
  const diffX = intersectionRect.x - imageRect.x;
14962
15046
  const diffY = intersectionRect.y - imageRect.y;
15047
+ let sourceX = diffX + imageOffsetX;
15048
+ let sourceY = diffY + imageOffsetY;
15049
+ if (imageAttrs.flipX === true) sourceX = WeaveImageCrop.mirrorOffset(sourceX, intersectionRect.width, imageAttrs.uncroppedImage.width);
15050
+ if (imageAttrs.flipY === true) sourceY = WeaveImageCrop.mirrorOffset(sourceY, intersectionRect.height, imageAttrs.uncroppedImage.height);
14963
15051
  const realClipRect = {
14964
15052
  scaleX: actualScale,
14965
15053
  scaleY: actualScale,
14966
- x: WeaveImageCrop.roundTo6Decimals(diffX + imageOffsetX),
14967
- y: WeaveImageCrop.roundTo6Decimals(diffY + imageOffsetY),
15054
+ x: WeaveImageCrop.roundTo6Decimals(sourceX),
15055
+ y: WeaveImageCrop.roundTo6Decimals(sourceY),
14968
15056
  width: WeaveImageCrop.roundTo6Decimals(intersectionRect.width),
14969
15057
  height: WeaveImageCrop.roundTo6Decimals(intersectionRect.height)
14970
15058
  };
@@ -15270,6 +15358,27 @@ var WeaveImageNode = class extends WeaveNode {
15270
15358
  if (!internalImage || !cropGroup) return;
15271
15359
  new WeaveImageCrop(this.instance, this, imageNode, internalImage, cropGroup).unCrop();
15272
15360
  };
15361
+ flipImage = (imageNode, options) => {
15362
+ if (imageNode.getAttrs().cropping) return;
15363
+ const prop = options.axis === "horizontal" ? "flipX" : "flipY";
15364
+ this.instance.stateTransactional(() => {
15365
+ imageNode.setAttrs({ [prop]: !(imageNode.getAttrs()[prop] ?? false) });
15366
+ this.applyFlip(imageNode);
15367
+ this.cacheNode(imageNode);
15368
+ this.instance.updateNode(this.serialize(imageNode));
15369
+ });
15370
+ };
15371
+ applyFlip(image) {
15372
+ const attrs = image.getAttrs();
15373
+ const internalImage = image.findOne(`#${attrs.id}-image`);
15374
+ if (!internalImage) return;
15375
+ const flipX = attrs.flipX === true;
15376
+ const flipY = attrs.flipY === true;
15377
+ internalImage.scaleX(flipX ? -1 : 1);
15378
+ internalImage.scaleY(flipY ? -1 : 1);
15379
+ internalImage.offsetX(flipX ? internalImage.width() : 0);
15380
+ internalImage.offsetY(flipY ? internalImage.height() : 0);
15381
+ }
15273
15382
  loadAsyncElement(nodeId) {
15274
15383
  this.instance.loadAsyncElement(nodeId, WEAVE_IMAGE_NODE_TYPE);
15275
15384
  }
@@ -15317,6 +15426,12 @@ var WeaveImageNode = class extends WeaveNode {
15317
15426
  new WeaveImageCrop(this.instance, this, image, internalImage, cropGroup).unCrop();
15318
15427
  };
15319
15428
  }
15429
+ image.flipHorizontal = () => {
15430
+ this.flipImage(image, { axis: "horizontal" });
15431
+ };
15432
+ image.flipVertical = () => {
15433
+ this.flipImage(image, { axis: "vertical" });
15434
+ };
15320
15435
  const defaultTransformerProperties = this.defaultGetTransformerProperties(this.config.transform);
15321
15436
  image.getTransformerProperties = function() {
15322
15437
  return defaultTransformerProperties;
@@ -15932,6 +16047,10 @@ var WeaveImageNode = class extends WeaveNode {
15932
16047
  if (!this.imageState[imageAttrs.id ?? ""]?.loaded) return;
15933
16048
  }
15934
16049
  updateImageCrop(image) {
16050
+ this.updateImageCropGeometry(image);
16051
+ this.applyFlip(image);
16052
+ }
16053
+ updateImageCropGeometry(image) {
15935
16054
  const imageAttrs = image.getAttrs();
15936
16055
  const internalImage = image?.findOne(`#${imageAttrs.id}-image`);
15937
16056
  if (!this.imageState[imageAttrs.id ?? ""]?.loaded) return;
@@ -16009,8 +16128,12 @@ var WeaveImageNode = class extends WeaveNode {
16009
16128
  });
16010
16129
  }
16011
16130
  if (internalImage) {
16012
- internalImage.width(Math.max(5, internalImage.width() * internalImage.scaleX()));
16013
- internalImage.height(Math.max(5, internalImage.height() * internalImage.scaleY()));
16131
+ internalImage.offset({
16132
+ x: 0,
16133
+ y: 0
16134
+ });
16135
+ internalImage.width(Math.max(5, internalImage.width() * Math.abs(internalImage.scaleX())));
16136
+ internalImage.height(Math.max(5, internalImage.height() * Math.abs(internalImage.scaleY())));
16014
16137
  internalImage.scale({
16015
16138
  x: 1,
16016
16139
  y: 1
@@ -16065,7 +16188,7 @@ var WeaveImageNode = class extends WeaveNode {
16065
16188
  clearTimeout(this.imageTryoutIds[nodeId]);
16066
16189
  delete this.imageTryoutIds[nodeId];
16067
16190
  }
16068
- nodeInstance.destroy();
16191
+ super.onDestroy(nodeInstance);
16069
16192
  }
16070
16193
  loadImageTryout(imageId) {
16071
16194
  this.imageTryoutIds[imageId] = setTimeout(() => {
@@ -16152,7 +16275,9 @@ var WeaveImageNode = class extends WeaveNode {
16152
16275
  width: props.uncroppedImage.width,
16153
16276
  height: props.uncroppedImage.height
16154
16277
  },
16155
- cropping: props.cropping
16278
+ cropping: props.cropping,
16279
+ ...props.flipX !== void 0 && { flipX: props.flipX },
16280
+ ...props.flipY !== void 0 && { flipY: props.flipY }
16156
16281
  } });
16157
16282
  }
16158
16283
  static updateNodeState(prevNodeState, nextProps) {
@@ -16175,7 +16300,9 @@ var WeaveImageNode = class extends WeaveNode {
16175
16300
  width: nextProps.uncroppedImage?.width,
16176
16301
  height: nextProps.uncroppedImage?.height
16177
16302
  } },
16178
- cropping: nextProps.cropping
16303
+ cropping: nextProps.cropping,
16304
+ ...nextProps.flipX !== void 0 && { flipX: nextProps.flipX },
16305
+ ...nextProps.flipY !== void 0 && { flipY: nextProps.flipY }
16179
16306
  } });
16180
16307
  }
16181
16308
  static getSchema() {
@@ -16199,7 +16326,9 @@ var WeaveImageNode = class extends WeaveNode {
16199
16326
  width: z.number().describe("The width of the image before cropping, used for cropping calculations"),
16200
16327
  height: z.number().describe("The height of the image before cropping, used for cropping calculations")
16201
16328
  }),
16202
- cropping: z.boolean().default(false).describe("Whether the image is currently being cropped")
16329
+ cropping: z.boolean().default(false).describe("Whether the image is currently being cropped"),
16330
+ flipX: z.boolean().optional().describe("Whether the image is flipped horizontally"),
16331
+ flipY: z.boolean().optional().describe("Whether the image is flipped vertically")
16203
16332
  })
16204
16333
  });
16205
16334
  }
@@ -16716,14 +16845,26 @@ var WeaveRegularPolygonNode = class extends WeaveNode {
16716
16845
  //#region src/nodes/polygon/constants.ts
16717
16846
  const WEAVE_POLYGON_NODE_TYPE = "polygon";
16718
16847
  //#endregion
16848
+ //#region src/utils/native-backend.ts
16849
+ const MODULE_NOT_FOUND_CODES = ["ERR_MODULE_NOT_FOUND", "MODULE_NOT_FOUND"];
16850
+ const isMissingPackageError = (error, packageName) => error instanceof Error && MODULE_NOT_FOUND_CODES.includes(error.code ?? "") && error.message.includes(`'${packageName}'`);
16851
+ const loadNativeBackend = async (packageName, setupFunctionName, load) => {
16852
+ try {
16853
+ await load();
16854
+ } catch (error) {
16855
+ if (isMissingPackageError(error, packageName)) throw new Error(`${setupFunctionName}() requires the "${packageName}" package. Install it with \`npm install ${packageName}\`, using the version required by @inditextech/weave-sdk.`, { cause: error });
16856
+ throw error;
16857
+ }
16858
+ };
16859
+ //#endregion
16719
16860
  //#region src/backend.ts
16720
16861
  const setupSkiaBackend = async () => {
16721
16862
  globalThis._weave_serverSideBackend = WEAVE_KONVA_BACKEND.SKIA;
16722
- await import("konva/skia-backend");
16863
+ await loadNativeBackend("skia-canvas", "setupSkiaBackend", () => import("konva/skia-backend"));
16723
16864
  };
16724
16865
  const setupCanvasBackend = async () => {
16725
16866
  globalThis._weave_serverSideBackend = WEAVE_KONVA_BACKEND.CANVAS;
16726
- await import("konva/canvas-backend");
16867
+ await loadNativeBackend("canvas", "setupCanvasBackend", () => import("konva/canvas-backend"));
16727
16868
  };
16728
16869
  //#endregion
16729
16870
  //#region src/nodes/polygon/presets.ts
@@ -19638,7 +19779,7 @@ var WeaveCommentNode = class extends WeaveNode {
19638
19779
  this.normalizeNodeSize(commentNode);
19639
19780
  }
19640
19781
  onDestroy(nodeInstance) {
19641
- nodeInstance.destroy();
19782
+ super.onDestroy(nodeInstance);
19642
19783
  }
19643
19784
  expandNode(commentNode) {
19644
19785
  const widthExpanded = this.config.style.expanded.width;
@@ -23292,6 +23433,70 @@ const RECTANGLE_TOOL_STATE = {
23292
23433
  ["ADDED"]: "added"
23293
23434
  };
23294
23435
  //#endregion
23436
+ //#region src/actions/shared/shape-geometry.ts
23437
+ /** Returns the normalized bounds described by a drag, including its modifiers. */
23438
+ function getShapeDragBounds(anchor, pointer, options = {}) {
23439
+ const { centered = false, constrained = false, aspectRatio = 1 } = options;
23440
+ const deltaX = Math.abs(pointer.x - anchor.x);
23441
+ const deltaY = Math.abs(pointer.y - anchor.y);
23442
+ let width = deltaX;
23443
+ let height = deltaY;
23444
+ if (constrained && aspectRatio > 0) {
23445
+ if (width / (height || Number.EPSILON) > aspectRatio) height = width / aspectRatio;
23446
+ else width = height * aspectRatio;
23447
+ }
23448
+ if (constrained && aspectRatio > 0 && width > 0 && height > 0) {
23449
+ const minimumSizeScale = Math.max(1, 1 / width, 1 / height);
23450
+ width *= minimumSizeScale;
23451
+ height *= minimumSizeScale;
23452
+ } else {
23453
+ width = Math.max(1, width);
23454
+ height = Math.max(1, height);
23455
+ }
23456
+ if (centered) {
23457
+ width *= 2;
23458
+ height *= 2;
23459
+ return {
23460
+ x: anchor.x - width / 2,
23461
+ y: anchor.y - height / 2,
23462
+ width,
23463
+ height
23464
+ };
23465
+ }
23466
+ const signedWidth = Math.sign(pointer.x - anchor.x || 1) * width;
23467
+ const signedHeight = Math.sign(pointer.y - anchor.y || 1) * height;
23468
+ return {
23469
+ x: Math.min(anchor.x, anchor.x + signedWidth),
23470
+ y: Math.min(anchor.y, anchor.y + signedHeight),
23471
+ width,
23472
+ height
23473
+ };
23474
+ }
23475
+ //#endregion
23476
+ //#region src/actions/shared/shape-keyboard.ts
23477
+ /** Binds the common keyboard behavior for tools that create shapes by dragging. */
23478
+ function bindShapeDragKeyboardHandlers(signal, isActionActive, isDragInProgress, canUpdateModifiers, onCancel, onModifiersChange) {
23479
+ const handleModifierKey = (event) => {
23480
+ if (event.key !== "Shift" && event.key !== "Alt") return;
23481
+ const actionActive = isActionActive();
23482
+ const dragInProgress = isDragInProgress();
23483
+ if (event.type === "keydown" && event.key === "Alt" && actionActive && dragInProgress) event.preventDefault();
23484
+ if (!actionActive || !canUpdateModifiers()) return;
23485
+ onModifiersChange({
23486
+ shiftKey: event.shiftKey,
23487
+ altKey: event.altKey
23488
+ });
23489
+ };
23490
+ window.addEventListener("keydown", (event) => {
23491
+ if (isActionActive() && (event.code === "Enter" || event.code === "Escape")) {
23492
+ onCancel();
23493
+ return;
23494
+ }
23495
+ handleModifierKey(event);
23496
+ }, { signal });
23497
+ window.addEventListener("keyup", handleModifierKey, { signal });
23498
+ }
23499
+ //#endregion
23295
23500
  //#region src/actions/rectangle-tool/rectangle-tool.ts
23296
23501
  var WeaveRectangleToolAction = class extends WeaveAction {
23297
23502
  initialized = false;
@@ -23337,16 +23542,7 @@ var WeaveRectangleToolAction = class extends WeaveAction {
23337
23542
  }
23338
23543
  setupEvents() {
23339
23544
  const stage = this.instance.getStage();
23340
- window.addEventListener("keydown", (e) => {
23341
- if (e.code === "Enter" && this.instance.getActiveAction() === "rectangleTool") {
23342
- this.cancelAction();
23343
- return;
23344
- }
23345
- if (e.code === "Escape" && this.instance.getActiveAction() === "rectangleTool") {
23346
- this.cancelAction();
23347
- return;
23348
- }
23349
- }, { signal: this.instance.getEventsController().signal });
23545
+ bindShapeDragKeyboardHandlers(this.instance.getEventsController().signal, () => this.instance.getActiveAction() === RECTANGLE_TOOL_ACTION_NAME, () => this.state === RECTANGLE_TOOL_STATE.DEFINING_SIZE, () => this.state === RECTANGLE_TOOL_STATE.DEFINING_SIZE && this.moved, () => this.cancelAction(), (modifiers) => this.handleMovement(modifiers));
23350
23546
  stage.on("pointermove", () => {
23351
23547
  if (this.state === RECTANGLE_TOOL_STATE.IDLE) return;
23352
23548
  this.setCursor();
@@ -23372,12 +23568,12 @@ var WeaveRectangleToolAction = class extends WeaveAction {
23372
23568
  this.state = RECTANGLE_TOOL_STATE.ADDING;
23373
23569
  return;
23374
23570
  }
23375
- if (this.state === RECTANGLE_TOOL_STATE.DEFINING_SIZE) this.handleMovement();
23571
+ if (this.state === RECTANGLE_TOOL_STATE.DEFINING_SIZE) this.handleMovement(e.evt);
23376
23572
  });
23377
23573
  stage.on("pointerup", (e) => {
23378
23574
  this.pointers.delete(e.evt.pointerId);
23379
23575
  if (this.isTap(e)) this.moved = false;
23380
- if (this.state === RECTANGLE_TOOL_STATE.DEFINING_SIZE) this.handleSettingSize();
23576
+ if (this.state === RECTANGLE_TOOL_STATE.DEFINING_SIZE) this.handleSettingSize(e.evt);
23381
23577
  });
23382
23578
  this.initialized = true;
23383
23579
  }
@@ -23411,7 +23607,7 @@ var WeaveRectangleToolAction = class extends WeaveAction {
23411
23607
  }
23412
23608
  this.setState(RECTANGLE_TOOL_STATE.DEFINING_SIZE);
23413
23609
  }
23414
- handleSettingSize() {
23610
+ handleSettingSize(modifiers = {}) {
23415
23611
  if (this.rectId && this.tempRectNode && this.clickPoint && this.container) {
23416
23612
  const { mousePoint } = this.instance.getMousePointerRelativeToContainer(this.container);
23417
23613
  const rectPos = {
@@ -23421,10 +23617,14 @@ var WeaveRectangleToolAction = class extends WeaveAction {
23421
23617
  let rectWidth = this.props.width;
23422
23618
  let rectHeight = this.props.height;
23423
23619
  if (this.moved) {
23424
- rectPos.x = Math.min(this.clickPoint.x, mousePoint.x);
23425
- rectPos.y = Math.min(this.clickPoint.y, mousePoint.y);
23426
- rectWidth = Math.abs(this.clickPoint.x - mousePoint.x);
23427
- rectHeight = Math.abs(this.clickPoint.y - mousePoint.y);
23620
+ const bounds = getShapeDragBounds(this.clickPoint, mousePoint, {
23621
+ centered: modifiers.altKey,
23622
+ constrained: modifiers.shiftKey
23623
+ });
23624
+ rectPos.x = bounds.x;
23625
+ rectPos.y = bounds.y;
23626
+ rectWidth = bounds.width;
23627
+ rectHeight = bounds.height;
23428
23628
  }
23429
23629
  this.tempRectNode.setAttrs({
23430
23630
  ...this.props,
@@ -23456,16 +23656,20 @@ var WeaveRectangleToolAction = class extends WeaveAction {
23456
23656
  }
23457
23657
  this.cancelAction();
23458
23658
  }
23459
- handleMovement() {
23659
+ handleMovement(modifiers = {}) {
23460
23660
  if (this.state !== RECTANGLE_TOOL_STATE.DEFINING_SIZE) return;
23461
23661
  if (this.rectId && this.tempRectNode && this.measureContainer && this.clickPoint) {
23462
23662
  this.moved = true;
23463
23663
  const { mousePoint } = this.instance.getMousePointerRelativeToContainer(this.measureContainer);
23464
- const deltaX = mousePoint.x - this.clickPoint?.x;
23465
- const deltaY = mousePoint.y - this.clickPoint?.y;
23664
+ const bounds = getShapeDragBounds(this.clickPoint, mousePoint, {
23665
+ centered: modifiers.altKey,
23666
+ constrained: modifiers.shiftKey
23667
+ });
23466
23668
  this.tempRectNode.setAttrs({
23467
- width: deltaX,
23468
- height: deltaY
23669
+ x: bounds.x,
23670
+ y: bounds.y,
23671
+ width: bounds.width,
23672
+ height: bounds.height
23469
23673
  });
23470
23674
  }
23471
23675
  }
@@ -23564,16 +23768,7 @@ var WeaveEllipseToolAction = class extends WeaveAction {
23564
23768
  }
23565
23769
  setupEvents() {
23566
23770
  const stage = this.instance.getStage();
23567
- window.addEventListener("keydown", (e) => {
23568
- if (e.code === "Enter" && this.instance.getActiveAction() === "ellipseTool") {
23569
- this.cancelAction();
23570
- return;
23571
- }
23572
- if (e.code === "Escape" && this.instance.getActiveAction() === "ellipseTool") {
23573
- this.cancelAction();
23574
- return;
23575
- }
23576
- }, { signal: this.instance.getEventsController().signal });
23771
+ bindShapeDragKeyboardHandlers(this.instance.getEventsController().signal, () => this.instance.getActiveAction() === ELLIPSE_TOOL_ACTION_NAME, () => this.state === ELLIPSE_TOOL_STATE.DEFINING_SIZE, () => this.state === ELLIPSE_TOOL_STATE.DEFINING_SIZE && this.moved, () => this.cancelAction(), (modifiers) => this.handleMovement(modifiers));
23577
23772
  stage.on("pointerdown", (e) => {
23578
23773
  this.setTapStart(e);
23579
23774
  this.pointers.set(e.evt.pointerId, {
@@ -23600,7 +23795,7 @@ var WeaveEllipseToolAction = class extends WeaveAction {
23600
23795
  }
23601
23796
  if (this.state === ELLIPSE_TOOL_STATE.DEFINING_SIZE) {
23602
23797
  this.moved = true;
23603
- this.handleMovement();
23798
+ this.handleMovement(e.evt);
23604
23799
  }
23605
23800
  });
23606
23801
  stage.on("pointerup", (e) => {
@@ -23608,7 +23803,7 @@ var WeaveEllipseToolAction = class extends WeaveAction {
23608
23803
  if (this.isTap(e)) this.moved = false;
23609
23804
  if (this.state === ELLIPSE_TOOL_STATE.DEFINING_SIZE) {
23610
23805
  this.creating = false;
23611
- this.handleSettingSize();
23806
+ this.handleSettingSize(e.evt);
23612
23807
  }
23613
23808
  });
23614
23809
  this.initialized = true;
@@ -23632,8 +23827,8 @@ var WeaveEllipseToolAction = class extends WeaveAction {
23632
23827
  if (nodeHandler) {
23633
23828
  const node = nodeHandler.create(this.ellipseId, {
23634
23829
  ...this.props,
23635
- x: this.clickPoint?.x ?? 0 + this.props.radiusX,
23636
- y: this.clickPoint?.y ?? 0 + this.props.radiusY,
23830
+ x: this.clickPoint?.x ?? 0,
23831
+ y: this.clickPoint?.y ?? 0,
23637
23832
  radiusX: 1,
23638
23833
  radiusY: 1
23639
23834
  });
@@ -23641,22 +23836,28 @@ var WeaveEllipseToolAction = class extends WeaveAction {
23641
23836
  }
23642
23837
  this.setState(ELLIPSE_TOOL_STATE.DEFINING_SIZE);
23643
23838
  }
23644
- handleSettingSize() {
23839
+ handleSettingSize(modifiers = {}) {
23645
23840
  const ellipse = this.instance.getStage().findOne(`#${this.ellipseId}`);
23646
23841
  if (this.ellipseId && this.clickPoint && this.container && ellipse) {
23647
23842
  const { mousePoint } = this.instance.getMousePointerRelativeToContainer(this.container);
23648
23843
  const nodeHandler = this.instance.getNodeHandler("ellipse");
23649
- const ellipsePos = {
23844
+ let ellipsePos = {
23650
23845
  x: this.clickPoint.x,
23651
23846
  y: this.clickPoint.y
23652
23847
  };
23653
- let ellipseRadiusX = this.props.radiusY;
23848
+ let ellipseRadiusX = this.props.radiusX;
23654
23849
  let ellipseRadiusY = this.props.radiusY;
23655
23850
  if (this.moved) {
23656
- ellipsePos.x = Math.min(this.clickPoint.x, mousePoint.x);
23657
- ellipsePos.y = Math.min(this.clickPoint.y, mousePoint.y);
23658
- ellipseRadiusX = Math.abs(this.clickPoint.x - mousePoint.x);
23659
- ellipseRadiusY = Math.abs(this.clickPoint.y - mousePoint.y);
23851
+ const bounds = getShapeDragBounds(this.clickPoint, mousePoint, {
23852
+ centered: modifiers.altKey,
23853
+ constrained: modifiers.shiftKey
23854
+ });
23855
+ ellipsePos = {
23856
+ x: bounds.x,
23857
+ y: bounds.y
23858
+ };
23859
+ ellipseRadiusX = bounds.width;
23860
+ ellipseRadiusY = bounds.height;
23660
23861
  }
23661
23862
  ellipse.setAttrs({
23662
23863
  ...this.props,
@@ -23670,27 +23871,23 @@ var WeaveEllipseToolAction = class extends WeaveAction {
23670
23871
  }
23671
23872
  this.cancelAction();
23672
23873
  }
23673
- handleMovement() {
23874
+ handleMovement(modifiers = {}) {
23674
23875
  if (this.state !== ELLIPSE_TOOL_STATE.DEFINING_SIZE) return;
23675
23876
  const ellipse = this.instance.getStage().findOne(`#${this.ellipseId}`);
23676
23877
  if (this.ellipseId && this.container && this.clickPoint && ellipse) {
23677
23878
  const { mousePoint } = this.instance.getMousePointerRelativeToContainer(this.container);
23678
- const deltaX = Math.abs(mousePoint.x - this.clickPoint?.x);
23679
- const deltaY = Math.abs(mousePoint.y - this.clickPoint?.y);
23680
- const ellipsePos = {
23681
- x: this.clickPoint.x,
23682
- y: this.clickPoint.y
23683
- };
23684
- if (this.moved) {
23685
- ellipsePos.x = Math.min(this.clickPoint.x, mousePoint.x);
23686
- ellipsePos.y = Math.min(this.clickPoint.y, mousePoint.y);
23687
- }
23879
+ const bounds = getShapeDragBounds(this.clickPoint, mousePoint, {
23880
+ centered: modifiers.altKey,
23881
+ constrained: modifiers.shiftKey
23882
+ });
23688
23883
  const nodeHandler = this.instance.getNodeHandler("ellipse");
23689
23884
  if (nodeHandler) nodeHandler.onUpdate(ellipse, {
23690
23885
  ...this.props,
23691
23886
  id: this.ellipseId,
23692
- radiusX: deltaX / 2,
23693
- radiusY: deltaY / 2
23887
+ x: bounds.x,
23888
+ y: bounds.y,
23889
+ radiusX: bounds.width / 2,
23890
+ radiusY: bounds.height / 2
23694
23891
  });
23695
23892
  }
23696
23893
  }
@@ -26661,16 +26858,7 @@ var WeaveRegularPolygonToolAction = class extends WeaveAction {
26661
26858
  }
26662
26859
  setupEvents() {
26663
26860
  const stage = this.instance.getStage();
26664
- window.addEventListener("keydown", (e) => {
26665
- if (e.code === "Enter" && this.instance.getActiveAction() === "regularPolygonTool") {
26666
- this.cancelAction();
26667
- return;
26668
- }
26669
- if (e.code === "Escape" && this.instance.getActiveAction() === "regularPolygonTool") {
26670
- this.cancelAction();
26671
- return;
26672
- }
26673
- }, { signal: this.instance.getEventsController().signal });
26861
+ bindShapeDragKeyboardHandlers(this.instance.getEventsController().signal, () => this.instance.getActiveAction() === REGULAR_POLYGON_TOOL_ACTION_NAME, () => this.state === REGULAR_POLYGON_TOOL_STATE.DEFINING_SIZE, () => this.state === REGULAR_POLYGON_TOOL_STATE.DEFINING_SIZE && this.moved, () => this.cancelAction(), (modifiers) => this.handleMovement(modifiers));
26674
26862
  stage.on("pointerdown", (e) => {
26675
26863
  this.setTapStart(e);
26676
26864
  this.pointers.set(e.evt.pointerId, {
@@ -26697,7 +26885,7 @@ var WeaveRegularPolygonToolAction = class extends WeaveAction {
26697
26885
  }
26698
26886
  if (this.state === REGULAR_POLYGON_TOOL_STATE.DEFINING_SIZE) {
26699
26887
  this.moved = true;
26700
- this.handleMovement();
26888
+ this.handleMovement(e.evt);
26701
26889
  }
26702
26890
  });
26703
26891
  stage.on("pointerup", (e) => {
@@ -26705,7 +26893,7 @@ var WeaveRegularPolygonToolAction = class extends WeaveAction {
26705
26893
  if (this.isTap(e)) this.moved = false;
26706
26894
  if (this.state === REGULAR_POLYGON_TOOL_STATE.DEFINING_SIZE) {
26707
26895
  this.creating = false;
26708
- this.handleSettingSize();
26896
+ this.handleSettingSize(e.evt);
26709
26897
  }
26710
26898
  });
26711
26899
  this.initialized = true;
@@ -26737,20 +26925,26 @@ var WeaveRegularPolygonToolAction = class extends WeaveAction {
26737
26925
  }
26738
26926
  this.setState(REGULAR_POLYGON_TOOL_STATE.DEFINING_SIZE);
26739
26927
  }
26740
- handleSettingSize() {
26928
+ handleSettingSize(modifiers = {}) {
26741
26929
  const regularPolygon = this.instance.getStage().findOne(`#${this.regularPolygonId}`);
26742
26930
  if (this.regularPolygonId && this.clickPoint && this.container && regularPolygon) {
26743
26931
  const { mousePoint } = this.instance.getMousePointerRelativeToContainer(this.container);
26744
26932
  const nodeHandler = this.instance.getNodeHandler("regular-polygon");
26745
- const starPos = {
26933
+ let starPos = {
26746
26934
  x: this.clickPoint.x,
26747
26935
  y: this.clickPoint.y
26748
26936
  };
26749
26937
  let newRadius = this.props.radius;
26750
26938
  if (this.moved) {
26751
- starPos.x = Math.min(this.clickPoint.x, mousePoint.x);
26752
- starPos.y = Math.min(this.clickPoint.y, mousePoint.y);
26753
- newRadius = Math.abs(this.clickPoint.x - mousePoint.x);
26939
+ const bounds = getShapeDragBounds(this.clickPoint, mousePoint, {
26940
+ centered: modifiers.altKey,
26941
+ constrained: true
26942
+ });
26943
+ starPos = {
26944
+ x: bounds.x,
26945
+ y: bounds.y
26946
+ };
26947
+ newRadius = bounds.width;
26754
26948
  }
26755
26949
  regularPolygon.setAttrs({
26756
26950
  ...this.props,
@@ -26763,25 +26957,22 @@ var WeaveRegularPolygonToolAction = class extends WeaveAction {
26763
26957
  }
26764
26958
  this.cancelAction();
26765
26959
  }
26766
- handleMovement() {
26960
+ handleMovement(modifiers = {}) {
26767
26961
  if (this.state !== REGULAR_POLYGON_TOOL_STATE.DEFINING_SIZE) return;
26768
26962
  const regularPolygon = this.instance.getStage().findOne(`#${this.regularPolygonId}`);
26769
26963
  if (this.regularPolygonId && this.container && this.clickPoint && regularPolygon) {
26770
26964
  const { mousePoint } = this.instance.getMousePointerRelativeToContainer(this.container);
26771
- const deltaX = Math.abs(mousePoint.x - this.clickPoint?.x);
26772
- const starPos = {
26773
- x: this.clickPoint.x,
26774
- y: this.clickPoint.y
26775
- };
26776
- if (this.moved) {
26777
- starPos.x = Math.min(this.clickPoint.x, mousePoint.x);
26778
- starPos.y = Math.min(this.clickPoint.y, mousePoint.y);
26779
- }
26965
+ const bounds = getShapeDragBounds(this.clickPoint, mousePoint, {
26966
+ centered: modifiers.altKey,
26967
+ constrained: true
26968
+ });
26780
26969
  const nodeHandler = this.instance.getNodeHandler("regular-polygon");
26781
26970
  if (nodeHandler) nodeHandler.onUpdate(regularPolygon, {
26782
26971
  ...this.props,
26783
26972
  id: this.regularPolygonId,
26784
- radius: deltaX / 2
26973
+ x: bounds.x,
26974
+ y: bounds.y,
26975
+ radius: bounds.width / 2
26785
26976
  });
26786
26977
  }
26787
26978
  }
@@ -26830,14 +27021,22 @@ const POLYGON_TOOL_ACTION_NAME = "polygonTool";
26830
27021
  const POLYGON_TOOL_STATE = {
26831
27022
  ["IDLE"]: "idle",
26832
27023
  ["ADDING"]: "adding",
27024
+ ["DEFINING_SIZE"]: "defining-size",
26833
27025
  ["ADDED"]: "added"
26834
27026
  };
26835
27027
  //#endregion
26836
27028
  //#region src/actions/polygon-tool/polygon-tool.ts
27029
+ const MIN_POLYGON_DRAG_DISTANCE = 3;
26837
27030
  var WeavePolygonToolAction = class extends WeaveAction {
26838
27031
  initialized = false;
26839
27032
  state;
26840
27033
  polygonId;
27034
+ pointers;
27035
+ clickPoint;
27036
+ container;
27037
+ moved;
27038
+ placementCommitted;
27039
+ polygonNode;
26841
27040
  cancelAction;
26842
27041
  preset;
26843
27042
  onPropsChange = void 0;
@@ -26851,6 +27050,12 @@ var WeavePolygonToolAction = class extends WeaveAction {
26851
27050
  this.initialized = false;
26852
27051
  this.state = POLYGON_TOOL_STATE.IDLE;
26853
27052
  this.polygonId = null;
27053
+ this.pointers = /* @__PURE__ */ new Map();
27054
+ this.clickPoint = null;
27055
+ this.container = void 0;
27056
+ this.moved = false;
27057
+ this.placementCommitted = false;
27058
+ this.polygonNode = null;
26854
27059
  this.props = this.initProps();
26855
27060
  }
26856
27061
  getName() {
@@ -26875,18 +27080,41 @@ var WeavePolygonToolAction = class extends WeaveAction {
26875
27080
  }
26876
27081
  setupEvents() {
26877
27082
  const stage = this.instance.getStage();
26878
- window.addEventListener("keydown", (e) => {
26879
- if ((e.code === "Enter" || e.code === "Escape") && this.instance.getActiveAction() === "polygonTool") this.cancelAction();
26880
- }, { signal: this.instance.getEventsController().signal });
27083
+ bindShapeDragKeyboardHandlers(this.instance.getEventsController().signal, () => this.instance.getActiveAction() === POLYGON_TOOL_ACTION_NAME, () => this.state === POLYGON_TOOL_STATE.DEFINING_SIZE, () => this.state === POLYGON_TOOL_STATE.DEFINING_SIZE && this.moved, () => this.cancelAction(), (modifiers) => this.handleMovement(modifiers));
26881
27084
  stage.on("pointermove", () => {
26882
27085
  if (this.state === POLYGON_TOOL_STATE.IDLE) return;
26883
27086
  this.setCursor();
26884
27087
  });
26885
27088
  stage.on("pointerdown", (e) => {
26886
27089
  this.setTapStart(e);
27090
+ this.pointers.set(e.evt.pointerId, {
27091
+ x: e.evt.clientX,
27092
+ y: e.evt.clientY
27093
+ });
26887
27094
  if (this.state !== POLYGON_TOOL_STATE.ADDING) return;
26888
27095
  this.handleAdding();
26889
27096
  });
27097
+ stage.on("pointermove", (e) => {
27098
+ if (this.state === POLYGON_TOOL_STATE.IDLE) return;
27099
+ this.setCursor();
27100
+ if (!this.isPressed(e) || !this.pointers.has(e.evt.pointerId)) return;
27101
+ if (this.state === POLYGON_TOOL_STATE.DEFINING_SIZE) {
27102
+ const pointerStart = this.pointers.get(e.evt.pointerId);
27103
+ if (!pointerStart || Math.hypot(e.evt.clientX - pointerStart.x, e.evt.clientY - pointerStart.y) < MIN_POLYGON_DRAG_DISTANCE) return;
27104
+ this.moved = true;
27105
+ this.handleMovement(e.evt);
27106
+ }
27107
+ });
27108
+ stage.on("pointerup", (e) => {
27109
+ this.pointers.delete(e.evt.pointerId);
27110
+ if (this.isTap(e)) this.moved = false;
27111
+ if (this.state === POLYGON_TOOL_STATE.DEFINING_SIZE) this.handleSettingSize(e.evt);
27112
+ });
27113
+ stage.on("pointercancel", (e) => {
27114
+ if (!this.pointers.has(e.evt.pointerId)) return;
27115
+ this.pointers.delete(e.evt.pointerId);
27116
+ if (this.state === POLYGON_TOOL_STATE.DEFINING_SIZE) this.cancelAction();
27117
+ });
26890
27118
  this.initialized = true;
26891
27119
  }
26892
27120
  setState(state) {
@@ -26900,7 +27128,10 @@ var WeavePolygonToolAction = class extends WeaveAction {
26900
27128
  }
26901
27129
  handleAdding() {
26902
27130
  const { mousePoint, container } = this.instance.getMousePointer();
27131
+ this.clickPoint = mousePoint;
27132
+ this.container = container;
26903
27133
  this.polygonId = v4();
27134
+ this.polygonNode = null;
26904
27135
  const presetDef = WEAVE_POLYGON_PRESETS[this.preset];
26905
27136
  const scaleFactor = this.props.scaleFactor ?? 1;
26906
27137
  const { points, innerRect, width, height } = instantiatePreset(presetDef, presetDef.defaultWidth * scaleFactor, presetDef.defaultHeight * scaleFactor);
@@ -26916,15 +27147,49 @@ var WeavePolygonToolAction = class extends WeaveAction {
26916
27147
  points,
26917
27148
  innerRect
26918
27149
  });
27150
+ this.polygonNode = node;
26919
27151
  this.instance.addNode(node, container?.getAttrs().id);
26920
27152
  }
26921
- this.instance.emitEvent("onAddedPolygon");
27153
+ this.setState(POLYGON_TOOL_STATE.DEFINING_SIZE);
27154
+ }
27155
+ handleMovement(modifiers = {}) {
27156
+ if (this.state !== POLYGON_TOOL_STATE.DEFINING_SIZE || !this.polygonId || !this.clickPoint || !this.container) return;
27157
+ const polygon = this.instance.getStage().findOne(`#${this.polygonId}`);
27158
+ const nodeHandler = this.instance.getNodeHandler(WEAVE_POLYGON_NODE_TYPE);
27159
+ const presetDef = WEAVE_POLYGON_PRESETS[this.preset];
27160
+ if (!polygon || !nodeHandler) return;
27161
+ const { mousePoint } = this.instance.getMousePointerRelativeToContainer(this.container);
27162
+ const defaultGeometry = instantiatePreset(presetDef, presetDef.defaultWidth, presetDef.defaultHeight);
27163
+ const bounds = getShapeDragBounds(this.clickPoint, mousePoint, {
27164
+ centered: modifiers.altKey,
27165
+ constrained: modifiers.shiftKey,
27166
+ aspectRatio: defaultGeometry.width / defaultGeometry.height
27167
+ });
27168
+ const geometry = instantiatePreset(presetDef, bounds.width * presetDef.defaultWidth / defaultGeometry.width, bounds.height * presetDef.defaultHeight / defaultGeometry.height);
27169
+ nodeHandler.onUpdate(polygon, {
27170
+ ...this.props,
27171
+ id: this.polygonId,
27172
+ x: bounds.x,
27173
+ y: bounds.y,
27174
+ ...geometry
27175
+ });
27176
+ }
27177
+ handleSettingSize(modifiers = {}) {
27178
+ if (this.moved) this.handleMovement(modifiers);
27179
+ const polygon = this.polygonId && this.instance.getStage().findOne(`#${this.polygonId}`);
27180
+ const nodeHandler = this.instance.getNodeHandler(WEAVE_POLYGON_NODE_TYPE);
27181
+ if (polygon && nodeHandler) {
27182
+ this.instance.updateNode(nodeHandler.serialize(polygon));
27183
+ this.placementCommitted = true;
27184
+ this.instance.emitEvent("onAddedPolygon");
27185
+ }
26922
27186
  this.cancelAction();
26923
27187
  }
26924
27188
  trigger(cancelAction, params) {
26925
27189
  if (!this.instance) throw new Error("Instance not defined");
26926
27190
  if (!this.initialized) this.setupEvents();
26927
27191
  this.preset = params?.presetId ?? "pentagon";
27192
+ this.placementCommitted = false;
26928
27193
  const stage = this.instance.getStage();
26929
27194
  stage.container().tabIndex = 1;
26930
27195
  stage.container().focus();
@@ -26937,13 +27202,21 @@ var WeavePolygonToolAction = class extends WeaveAction {
26937
27202
  cleanup() {
26938
27203
  const stage = this.instance.getStage();
26939
27204
  stage.container().style.cursor = "default";
27205
+ const isCancelledPlacement = this.state === POLYGON_TOOL_STATE.DEFINING_SIZE && this.polygonId !== null && !this.placementCommitted;
27206
+ if (isCancelledPlacement && this.polygonNode) this.instance.removeNode(this.polygonNode);
26940
27207
  const selectionPlugin = this.instance.getPlugin("nodesSelection");
26941
27208
  if (selectionPlugin) {
26942
- const node = stage.findOne(`#${this.polygonId}`);
27209
+ const node = isCancelledPlacement ? void 0 : stage.findOne(`#${this.polygonId}`);
26943
27210
  if (node) selectionPlugin.setSelectedNodes([node]);
26944
27211
  this.instance.triggerAction(SELECTION_TOOL_ACTION_NAME);
26945
27212
  }
26946
27213
  this.polygonId = null;
27214
+ this.pointers.clear();
27215
+ this.clickPoint = null;
27216
+ this.container = void 0;
27217
+ this.moved = false;
27218
+ this.placementCommitted = false;
27219
+ this.polygonNode = null;
26947
27220
  this.setState(POLYGON_TOOL_STATE.IDLE);
26948
27221
  }
26949
27222
  setCursor() {