@tldraw/editor 3.9.0-canary.6beea33123dd → 3.9.0-canary.6f51d9267136

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +1 -1
  3. package/dist-cjs/index.d.ts +39 -7
  4. package/dist-cjs/index.js +1 -1
  5. package/dist-cjs/index.js.map +2 -2
  6. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +1 -1
  7. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +2 -2
  8. package/dist-cjs/lib/editor/Editor.js +430 -248
  9. package/dist-cjs/lib/editor/Editor.js.map +3 -3
  10. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +7 -2
  11. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  12. package/dist-cjs/lib/exports/getSvgAsImage.js +1 -1
  13. package/dist-cjs/lib/exports/getSvgAsImage.js.map +2 -2
  14. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  15. package/dist-cjs/lib/globals/environment.js +3 -1
  16. package/dist-cjs/lib/globals/environment.js.map +2 -2
  17. package/dist-cjs/lib/license/LicenseManager.js +1 -1
  18. package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
  19. package/dist-cjs/lib/utils/browserCanvasMaxSize.js +104 -28
  20. package/dist-cjs/lib/utils/browserCanvasMaxSize.js.map +3 -3
  21. package/dist-cjs/version.js +3 -3
  22. package/dist-cjs/version.js.map +1 -1
  23. package/dist-esm/index.d.mts +39 -7
  24. package/dist-esm/index.mjs +1 -1
  25. package/dist-esm/index.mjs.map +2 -2
  26. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
  27. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +2 -2
  28. package/dist-esm/lib/editor/Editor.mjs +426 -244
  29. package/dist-esm/lib/editor/Editor.mjs.map +3 -3
  30. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +7 -2
  31. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  32. package/dist-esm/lib/exports/getSvgAsImage.mjs +1 -1
  33. package/dist-esm/lib/exports/getSvgAsImage.mjs.map +2 -2
  34. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  35. package/dist-esm/lib/globals/environment.mjs +3 -1
  36. package/dist-esm/lib/globals/environment.mjs.map +2 -2
  37. package/dist-esm/lib/license/LicenseManager.mjs +1 -1
  38. package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
  39. package/dist-esm/lib/utils/browserCanvasMaxSize.mjs +104 -18
  40. package/dist-esm/lib/utils/browserCanvasMaxSize.mjs.map +2 -2
  41. package/dist-esm/version.mjs +3 -3
  42. package/dist-esm/version.mjs.map +1 -1
  43. package/package.json +7 -9
  44. package/src/index.ts +2 -0
  45. package/src/lib/components/default-components/DefaultErrorFallback.tsx +5 -3
  46. package/src/lib/editor/Editor.ts +555 -272
  47. package/src/lib/editor/shapes/ShapeUtil.ts +32 -5
  48. package/src/lib/exports/getSvgAsImage.ts +1 -1
  49. package/src/lib/exports/getSvgJsx.tsx +1 -0
  50. package/src/lib/globals/environment.ts +3 -0
  51. package/src/lib/license/LicenseManager.test.ts +16 -13
  52. package/src/lib/license/LicenseManager.ts +2 -2
  53. package/src/lib/utils/browserCanvasMaxSize.ts +121 -21
  54. package/src/version.ts +3 -3
@@ -80,6 +80,7 @@ var import_state = require("@tldraw/state");
80
80
  var import_store = require("@tldraw/store");
81
81
  var import_tlschema = require("@tldraw/tlschema");
82
82
  var import_utils = require("@tldraw/utils");
83
+ var import_core_js = require("core-js");
83
84
  var import_eventemitter3 = __toESM(require("eventemitter3"));
84
85
  var import_TLEditorSnapshot = require("../config/TLEditorSnapshot");
85
86
  var import_createTLUser = require("../config/createTLUser");
@@ -121,8 +122,8 @@ var import_TextManager = require("./managers/TextManager");
121
122
  var import_TickManager = require("./managers/TickManager");
122
123
  var import_UserPreferencesManager = require("./managers/UserPreferencesManager");
123
124
  var import_RootState = require("./tools/RootState");
124
- var __setMetaKeyTimeout_dec, __setCtrlKeyTimeout_dec, __setAltKeyTimeout_dec, __setShiftKeyTimeout_dec, _getIsReadonly_dec, _getIsFocused_dec, _getSharedOpacity_dec, _getSharedStyles_dec, __getSelectionSharedStyles_dec, __getBindingsIndexCache_dec, _getCurrentPageRenderingShapesSorted_dec, _getCurrentPageShapesSorted_dec, _getCurrentPageShapes_dec, _getCurrentPageBounds_dec, _getCulledShapes_dec, __notVisibleShapes_dec, __getShapeMaskedPageBoundsCache_dec, __getShapeMaskCache_dec, __getShapeClipPathCache_dec, __getShapePageBoundsCache_dec, __getShapePageTransformCache_dec, __getShapeHandlesCache_dec, __getShapeGeometryCache_dec, __getAllAssetsQuery_dec, _getCurrentPageShapeIdsSorted_dec, _getCurrentPageId_dec, _getPages_dec, __getAllPagesQuery_dec, _getRenderingShapes_dec, _getCollaboratorsOnCurrentPage_dec, _getCollaborators_dec, __getCollaboratorsQuery_dec, _getViewportPageBounds_dec, _getViewportScreenCenter_dec, _getViewportScreenBounds_dec, _getZoomLevel_dec, _getCameraForFollowing_dec, _getViewportPageBoundsForFollowing_dec, _getCamera_dec, __unsafe_getCameraId_dec, _getErasingShapes_dec, _getErasingShapeIds_dec, _getHintingShape_dec, _getHintingShapeIds_dec, _getHoveredShape_dec, _getHoveredShapeId_dec, _getEditingShape_dec, _getEditingShapeId_dec, _getFocusedGroup_dec, _getFocusedGroupId_dec, _getSelectionRotatedScreenBounds_dec, _getSelectionRotatedPageBounds_dec, _getSelectionRotation_dec, _getSelectionPageBounds_dec, _getOnlySelectedShape_dec, _getOnlySelectedShapeId_dec, _getSelectedShapes_dec, _getSelectedShapeIds_dec, __getCurrentPageStateId_dec, _getCurrentPageState_dec, __getPageStatesQuery_dec, _getPageStates_dec, _getIsMenuOpen_dec, _getOpenMenus_dec, _getInstanceState_dec, _getDocumentSettings_dec, _getCurrentToolId_dec, _getCurrentTool_dec, _getPath_dec, _getCanRedo_dec, _getCanUndo_dec, _getIsShapeHiddenCache_dec, _a, _init;
125
- class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_dec = [import_state.computed], _getCanUndo_dec = [import_state.computed], _getCanRedo_dec = [import_state.computed], _getPath_dec = [import_state.computed], _getCurrentTool_dec = [import_state.computed], _getCurrentToolId_dec = [import_state.computed], _getDocumentSettings_dec = [import_state.computed], _getInstanceState_dec = [import_state.computed], _getOpenMenus_dec = [import_state.computed], _getIsMenuOpen_dec = [import_state.computed], _getPageStates_dec = [import_state.computed], __getPageStatesQuery_dec = [import_state.computed], _getCurrentPageState_dec = [import_state.computed], __getCurrentPageStateId_dec = [import_state.computed], _getSelectedShapeIds_dec = [import_state.computed], _getSelectedShapes_dec = [import_state.computed], _getOnlySelectedShapeId_dec = [import_state.computed], _getOnlySelectedShape_dec = [import_state.computed], _getSelectionPageBounds_dec = [import_state.computed], _getSelectionRotation_dec = [import_state.computed], _getSelectionRotatedPageBounds_dec = [import_state.computed], _getSelectionRotatedScreenBounds_dec = [import_state.computed], _getFocusedGroupId_dec = [import_state.computed], _getFocusedGroup_dec = [import_state.computed], _getEditingShapeId_dec = [import_state.computed], _getEditingShape_dec = [import_state.computed], _getHoveredShapeId_dec = [import_state.computed], _getHoveredShape_dec = [import_state.computed], _getHintingShapeIds_dec = [import_state.computed], _getHintingShape_dec = [import_state.computed], _getErasingShapeIds_dec = [import_state.computed], _getErasingShapes_dec = [import_state.computed], __unsafe_getCameraId_dec = [import_state.computed], _getCamera_dec = [import_state.computed], _getViewportPageBoundsForFollowing_dec = [import_state.computed], _getCameraForFollowing_dec = [import_state.computed], _getZoomLevel_dec = [import_state.computed], _getViewportScreenBounds_dec = [import_state.computed], _getViewportScreenCenter_dec = [import_state.computed], _getViewportPageBounds_dec = [import_state.computed], __getCollaboratorsQuery_dec = [import_state.computed], _getCollaborators_dec = [import_state.computed], _getCollaboratorsOnCurrentPage_dec = [import_state.computed], _getRenderingShapes_dec = [import_state.computed], __getAllPagesQuery_dec = [import_state.computed], _getPages_dec = [import_state.computed], _getCurrentPageId_dec = [import_state.computed], _getCurrentPageShapeIdsSorted_dec = [import_state.computed], __getAllAssetsQuery_dec = [import_state.computed], __getShapeGeometryCache_dec = [import_state.computed], __getShapeHandlesCache_dec = [import_state.computed], __getShapePageTransformCache_dec = [import_state.computed], __getShapePageBoundsCache_dec = [import_state.computed], __getShapeClipPathCache_dec = [import_state.computed], __getShapeMaskCache_dec = [import_state.computed], __getShapeMaskedPageBoundsCache_dec = [import_state.computed], __notVisibleShapes_dec = [import_state.computed], _getCulledShapes_dec = [import_state.computed], _getCurrentPageBounds_dec = [import_state.computed], _getCurrentPageShapes_dec = [import_state.computed], _getCurrentPageShapesSorted_dec = [import_state.computed], _getCurrentPageRenderingShapesSorted_dec = [import_state.computed], __getBindingsIndexCache_dec = [import_state.computed], __getSelectionSharedStyles_dec = [import_state.computed], _getSharedStyles_dec = [(0, import_state.computed)({ isEqual: (a, b) => a.equals(b) })], _getSharedOpacity_dec = [import_state.computed], _getIsFocused_dec = [import_state.computed], _getIsReadonly_dec = [import_state.computed], __setShiftKeyTimeout_dec = [import_utils.bind], __setAltKeyTimeout_dec = [import_utils.bind], __setCtrlKeyTimeout_dec = [import_utils.bind], __setMetaKeyTimeout_dec = [import_utils.bind], _a) {
125
+ var __setMetaKeyTimeout_dec, __setCtrlKeyTimeout_dec, __setAltKeyTimeout_dec, __setShiftKeyTimeout_dec, _getIsReadonly_dec, _getIsFocused_dec, _getSharedOpacity_dec, _getSharedStyles_dec, __getSelectionSharedStyles_dec, __getBindingsIndexCache_dec, _getCurrentPageRenderingShapesSorted_dec, _getCurrentPageShapesSorted_dec, _getCurrentPageShapes_dec, _getCurrentPageBounds_dec, _getCulledShapes_dec, __notVisibleShapes_dec, __getShapeMaskedPageBoundsCache_dec, __getShapeMaskCache_dec, __getShapeClipPathCache_dec, __getShapePageBoundsCache_dec, __getShapePageTransformCache_dec, __getShapeHandlesCache_dec, __getAllAssetsQuery_dec, _getCurrentPageShapeIdsSorted_dec, _getCurrentPageId_dec, _getPages_dec, __getAllPagesQuery_dec, _getRenderingShapes_dec, _getCollaboratorsOnCurrentPage_dec, _getCollaborators_dec, __getCollaboratorsQuery_dec, _getViewportPageBounds_dec, _getViewportScreenCenter_dec, _getViewportScreenBounds_dec, _getZoomLevel_dec, _getCameraForFollowing_dec, _getViewportPageBoundsForFollowing_dec, _getCamera_dec, __unsafe_getCameraId_dec, _getErasingShapes_dec, _getErasingShapeIds_dec, _getHintingShape_dec, _getHintingShapeIds_dec, _getHoveredShape_dec, _getHoveredShapeId_dec, _getEditingShape_dec, _getEditingShapeId_dec, _getFocusedGroup_dec, _getFocusedGroupId_dec, _getSelectionRotatedScreenBounds_dec, _getSelectionRotatedPageBounds_dec, _getSelectionRotation_dec, _getSelectionPageBounds_dec, _getOnlySelectedShape_dec, _getOnlySelectedShapeId_dec, _getSelectedShapes_dec, _getSelectedShapeIds_dec, __getCurrentPageStateId_dec, _getCurrentPageState_dec, __getPageStatesQuery_dec, _getPageStates_dec, _getIsMenuOpen_dec, _getOpenMenus_dec, _getInstanceState_dec, _getDocumentSettings_dec, _getCurrentToolId_dec, _getCurrentTool_dec, _getPath_dec, _getCanRedo_dec, _getCanUndo_dec, _getIsShapeHiddenCache_dec, _a, _init;
126
+ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_dec = [import_state.computed], _getCanUndo_dec = [import_state.computed], _getCanRedo_dec = [import_state.computed], _getPath_dec = [import_state.computed], _getCurrentTool_dec = [import_state.computed], _getCurrentToolId_dec = [import_state.computed], _getDocumentSettings_dec = [import_state.computed], _getInstanceState_dec = [import_state.computed], _getOpenMenus_dec = [import_state.computed], _getIsMenuOpen_dec = [import_state.computed], _getPageStates_dec = [import_state.computed], __getPageStatesQuery_dec = [import_state.computed], _getCurrentPageState_dec = [import_state.computed], __getCurrentPageStateId_dec = [import_state.computed], _getSelectedShapeIds_dec = [import_state.computed], _getSelectedShapes_dec = [import_state.computed], _getOnlySelectedShapeId_dec = [import_state.computed], _getOnlySelectedShape_dec = [import_state.computed], _getSelectionPageBounds_dec = [import_state.computed], _getSelectionRotation_dec = [import_state.computed], _getSelectionRotatedPageBounds_dec = [import_state.computed], _getSelectionRotatedScreenBounds_dec = [import_state.computed], _getFocusedGroupId_dec = [import_state.computed], _getFocusedGroup_dec = [import_state.computed], _getEditingShapeId_dec = [import_state.computed], _getEditingShape_dec = [import_state.computed], _getHoveredShapeId_dec = [import_state.computed], _getHoveredShape_dec = [import_state.computed], _getHintingShapeIds_dec = [import_state.computed], _getHintingShape_dec = [import_state.computed], _getErasingShapeIds_dec = [import_state.computed], _getErasingShapes_dec = [import_state.computed], __unsafe_getCameraId_dec = [import_state.computed], _getCamera_dec = [import_state.computed], _getViewportPageBoundsForFollowing_dec = [import_state.computed], _getCameraForFollowing_dec = [import_state.computed], _getZoomLevel_dec = [import_state.computed], _getViewportScreenBounds_dec = [import_state.computed], _getViewportScreenCenter_dec = [import_state.computed], _getViewportPageBounds_dec = [import_state.computed], __getCollaboratorsQuery_dec = [import_state.computed], _getCollaborators_dec = [import_state.computed], _getCollaboratorsOnCurrentPage_dec = [import_state.computed], _getRenderingShapes_dec = [import_state.computed], __getAllPagesQuery_dec = [import_state.computed], _getPages_dec = [import_state.computed], _getCurrentPageId_dec = [import_state.computed], _getCurrentPageShapeIdsSorted_dec = [import_state.computed], __getAllAssetsQuery_dec = [import_state.computed], __getShapeHandlesCache_dec = [import_state.computed], __getShapePageTransformCache_dec = [import_state.computed], __getShapePageBoundsCache_dec = [import_state.computed], __getShapeClipPathCache_dec = [import_state.computed], __getShapeMaskCache_dec = [import_state.computed], __getShapeMaskedPageBoundsCache_dec = [import_state.computed], __notVisibleShapes_dec = [import_state.computed], _getCulledShapes_dec = [import_state.computed], _getCurrentPageBounds_dec = [import_state.computed], _getCurrentPageShapes_dec = [import_state.computed], _getCurrentPageShapesSorted_dec = [import_state.computed], _getCurrentPageRenderingShapesSorted_dec = [import_state.computed], __getBindingsIndexCache_dec = [import_state.computed], __getSelectionSharedStyles_dec = [import_state.computed], _getSharedStyles_dec = [(0, import_state.computed)({ isEqual: (a, b) => a.equals(b) })], _getSharedOpacity_dec = [import_state.computed], _getIsFocused_dec = [import_state.computed], _getIsReadonly_dec = [import_state.computed], __setShiftKeyTimeout_dec = [import_utils.bind], __setAltKeyTimeout_dec = [import_utils.bind], __setCtrlKeyTimeout_dec = [import_utils.bind], __setMetaKeyTimeout_dec = [import_utils.bind], _a) {
126
127
  constructor({
127
128
  store,
128
129
  user,
@@ -284,6 +285,8 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
284
285
  __publicField(this, "_cameraStateTimeoutRemaining", 0);
285
286
  /* @internal */
286
287
  __publicField(this, "_currentPageShapeIds");
288
+ /* --------------------- Shapes --------------------- */
289
+ __publicField(this, "_shapeGeometryCaches", {});
287
290
  // Parents and children
288
291
  /**
289
292
  * A cache of parents to children.
@@ -2092,9 +2095,9 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
2092
2095
  this.stopFollowingUser();
2093
2096
  }
2094
2097
  const _point = import_Vec.Vec.Cast(point);
2095
- if (!Number.isFinite(_point.x)) _point.x = 0;
2096
- if (!Number.isFinite(_point.y)) _point.y = 0;
2097
- if (_point.z === void 0 || !Number.isFinite(_point.z)) point.z = this.getZoomLevel();
2098
+ if (!import_core_js.Number.isFinite(_point.x)) _point.x = 0;
2099
+ if (!import_core_js.Number.isFinite(_point.y)) _point.y = 0;
2100
+ if (_point.z === void 0 || !import_core_js.Number.isFinite(_point.z)) point.z = this.getZoomLevel();
2098
2101
  const camera = this.getConstrainedCamera(_point, opts);
2099
2102
  if (opts?.animation) {
2100
2103
  const { width, height } = this.getViewportScreenBounds();
@@ -3195,13 +3198,6 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
3195
3198
  async uploadAsset(asset, file, abortSignal) {
3196
3199
  return await this.store.props.assets.upload(asset, file, abortSignal);
3197
3200
  }
3198
- _getShapeGeometryCache() {
3199
- return this.store.createComputedCache(
3200
- "bounds",
3201
- (shape) => this.getShapeUtil(shape).getGeometry(shape),
3202
- (a, b) => a.props === b.props
3203
- );
3204
- }
3205
3201
  /**
3206
3202
  * Get the geometry of a shape.
3207
3203
  *
@@ -3209,14 +3205,26 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
3209
3205
  * ```ts
3210
3206
  * editor.getShapeGeometry(myShape)
3211
3207
  * editor.getShapeGeometry(myShapeId)
3208
+ * editor.getShapeGeometry(myShapeId, { context: "arrow" })
3212
3209
  * ```
3213
3210
  *
3214
3211
  * @param shape - The shape (or shape id) to get the geometry for.
3212
+ * @param opts - Additional options about the request for geometry. Passed to {@link ShapeUtil.getGeometry}.
3215
3213
  *
3216
3214
  * @public
3217
3215
  */
3218
- getShapeGeometry(shape) {
3219
- return this._getShapeGeometryCache().get(typeof shape === "string" ? shape : shape.id);
3216
+ getShapeGeometry(shape, opts) {
3217
+ const context = opts?.context ?? "none";
3218
+ if (!this._shapeGeometryCaches[context]) {
3219
+ this._shapeGeometryCaches[context] = this.store.createComputedCache(
3220
+ "bounds",
3221
+ (shape2) => this.getShapeUtil(shape2).getGeometry(shape2, opts),
3222
+ { areRecordsEqual: (a, b) => a.props === b.props }
3223
+ );
3224
+ }
3225
+ return this._shapeGeometryCaches[context].get(
3226
+ typeof shape === "string" ? shape : shape.id
3227
+ );
3220
3228
  }
3221
3229
  _getShapeHandlesCache() {
3222
3230
  return this.store.createComputedCache("handles", (shape) => {
@@ -4282,27 +4290,28 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4282
4290
  });
4283
4291
  return this;
4284
4292
  }
4293
+ // Gets a shape partial that includes life cycle changes: on translate start, on translate, on translate end
4285
4294
  getChangesToTranslateShape(initialShape, newShapeCoords) {
4286
4295
  let workingShape = initialShape;
4287
4296
  const util = this.getShapeUtil(initialShape);
4288
- workingShape = applyPartialToRecordWithProps(
4289
- workingShape,
4290
- util.onTranslateStart?.(workingShape) ?? void 0
4291
- );
4297
+ const afterTranslateStart = util.onTranslateStart?.(workingShape);
4298
+ if (afterTranslateStart) {
4299
+ workingShape = applyPartialToRecordWithProps(workingShape, afterTranslateStart);
4300
+ }
4292
4301
  workingShape = applyPartialToRecordWithProps(workingShape, {
4293
4302
  id: initialShape.id,
4294
4303
  type: initialShape.type,
4295
4304
  x: newShapeCoords.x,
4296
4305
  y: newShapeCoords.y
4297
4306
  });
4298
- workingShape = applyPartialToRecordWithProps(
4299
- workingShape,
4300
- util.onTranslate?.(initialShape, workingShape) ?? void 0
4301
- );
4302
- workingShape = applyPartialToRecordWithProps(
4303
- workingShape,
4304
- util.onTranslateEnd?.(initialShape, workingShape) ?? void 0
4305
- );
4307
+ const afterTranslate = util.onTranslate?.(initialShape, workingShape);
4308
+ if (afterTranslate) {
4309
+ workingShape = applyPartialToRecordWithProps(workingShape, afterTranslate);
4310
+ }
4311
+ const afterTranslateEnd = util.onTranslateEnd?.(initialShape, workingShape);
4312
+ if (afterTranslateEnd) {
4313
+ workingShape = applyPartialToRecordWithProps(workingShape, afterTranslateEnd);
4314
+ }
4306
4315
  return workingShape;
4307
4316
  }
4308
4317
  /**
@@ -4609,6 +4618,30 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4609
4618
  if (changes) this.updateShapes(changes);
4610
4619
  return this;
4611
4620
  }
4621
+ /**
4622
+ * @internal
4623
+ */
4624
+ collectShapesViaArrowBindings(info) {
4625
+ const { initialShapes, resultShapes, resultBounds, bindings, visited } = info;
4626
+ for (const binding of bindings) {
4627
+ for (const id of [binding.fromId, binding.toId]) {
4628
+ if (!visited.has(id)) {
4629
+ const aligningShape = initialShapes.find((s) => s.id === id);
4630
+ if (aligningShape && !visited.has(aligningShape.id)) {
4631
+ visited.add(aligningShape.id);
4632
+ const shapePageBounds = this.getShapePageBounds(aligningShape);
4633
+ if (!shapePageBounds) continue;
4634
+ resultShapes.push(aligningShape);
4635
+ resultBounds.push(shapePageBounds);
4636
+ this.collectShapesViaArrowBindings({
4637
+ ...info,
4638
+ bindings: this.getBindingsInvolvingShape(aligningShape, "arrow")
4639
+ });
4640
+ }
4641
+ }
4642
+ }
4643
+ }
4644
+ }
4612
4645
  /**
4613
4646
  * Flip shape positions.
4614
4647
  *
@@ -4624,35 +4657,52 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4624
4657
  * @public
4625
4658
  */
4626
4659
  flipShapes(shapes, operation) {
4627
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4628
4660
  if (this.getIsReadonly()) return this;
4629
- let shapesToFlip = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4661
+ const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4662
+ const shapesToFlipFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4663
+ for (const shape of shapesToFlipFirstPass) {
4664
+ if (this.isShapeOfType(shape, "group")) {
4665
+ const childrenOfGroups = (0, import_utils.compact)(
4666
+ this.getSortedChildIdsForParent(shape.id).map((id) => this.getShape(id))
4667
+ );
4668
+ shapesToFlipFirstPass.push(...childrenOfGroups);
4669
+ }
4670
+ }
4671
+ const shapesToFlip = [];
4672
+ const allBounds = [];
4673
+ for (const shape of shapesToFlipFirstPass) {
4674
+ const util = this.getShapeUtil(shape);
4675
+ if (!util.canBeLaidOut(shape, {
4676
+ type: "flip",
4677
+ shapes: shapesToFlipFirstPass
4678
+ })) {
4679
+ continue;
4680
+ }
4681
+ const pageBounds = this.getShapePageBounds(shape);
4682
+ const localBounds = this.getShapeGeometry(shape).bounds;
4683
+ const pageTransform = this.getShapePageTransform(shape.id);
4684
+ if (!(pageBounds && localBounds && pageTransform)) continue;
4685
+ shapesToFlip.push({
4686
+ shape,
4687
+ localBounds,
4688
+ pageTransform,
4689
+ isAspectRatioLocked: util.isAspectRatioLocked(shape)
4690
+ });
4691
+ allBounds.push(pageBounds);
4692
+ }
4630
4693
  if (!shapesToFlip.length) return this;
4631
- shapesToFlip = (0, import_utils.compact)(
4632
- shapesToFlip.map((shape) => {
4633
- if (this.isShapeOfType(shape, "group")) {
4634
- return this.getSortedChildIdsForParent(shape.id).map((id) => this.getShape(id));
4635
- }
4636
- return shape;
4637
- }).flat()
4638
- );
4639
- const scaleOriginPage = import_Box.Box.Common(
4640
- (0, import_utils.compact)(shapesToFlip.map((id) => this.getShapePageBounds(id)))
4641
- ).center;
4694
+ const scaleOriginPage = import_Box.Box.Common(allBounds).center;
4642
4695
  this.run(() => {
4643
- for (const shape of shapesToFlip) {
4644
- const bounds = this.getShapeGeometry(shape).bounds;
4645
- const initialPageTransform = this.getShapePageTransform(shape.id);
4646
- if (!initialPageTransform) continue;
4696
+ for (const { shape, localBounds, pageTransform, isAspectRatioLocked } of shapesToFlip) {
4647
4697
  this.resizeShape(
4648
4698
  shape.id,
4649
4699
  { x: operation === "horizontal" ? -1 : 1, y: operation === "vertical" ? -1 : 1 },
4650
4700
  {
4651
- initialBounds: bounds,
4652
- initialPageTransform,
4701
+ initialBounds: localBounds,
4702
+ initialPageTransform: pageTransform,
4653
4703
  initialShape: shape,
4704
+ isAspectRatioLocked,
4654
4705
  mode: "scale_shape",
4655
- isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
4656
4706
  scaleOrigin: scaleOriginPage,
4657
4707
  scaleAxisRotation: 0
4658
4708
  }
@@ -4679,15 +4729,40 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4679
4729
  stackShapes(shapes, operation, gap) {
4680
4730
  const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4681
4731
  if (this.getIsReadonly()) return this;
4682
- const shapesToStack = ids.map((id) => this.getShape(id)).filter((shape) => {
4683
- if (!shape) return false;
4684
- return this.getShapeUtil(shape).canBeLaidOut(shape);
4685
- });
4686
- const len = shapesToStack.length;
4732
+ const shapesToStackFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4733
+ const shapeClustersToStack = [];
4734
+ const allBounds = [];
4735
+ const visited = /* @__PURE__ */ new Set();
4736
+ for (const shape of shapesToStackFirstPass) {
4737
+ if (visited.has(shape.id)) continue;
4738
+ visited.add(shape.id);
4739
+ const shapePageBounds = this.getShapePageBounds(shape);
4740
+ if (!shapePageBounds) continue;
4741
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
4742
+ type: "stack",
4743
+ shapes: shapesToStackFirstPass
4744
+ })) {
4745
+ continue;
4746
+ }
4747
+ const shapesMovingTogether = [shape];
4748
+ const boundsOfShapesMovingTogether = [shapePageBounds];
4749
+ this.collectShapesViaArrowBindings({
4750
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
4751
+ initialShapes: shapesToStackFirstPass,
4752
+ resultShapes: shapesMovingTogether,
4753
+ resultBounds: boundsOfShapesMovingTogether,
4754
+ visited
4755
+ });
4756
+ const commonPageBounds = import_Box.Box.Common(boundsOfShapesMovingTogether);
4757
+ if (!commonPageBounds) continue;
4758
+ shapeClustersToStack.push({
4759
+ shapes: shapesMovingTogether,
4760
+ pageBounds: commonPageBounds
4761
+ });
4762
+ allBounds.push(commonPageBounds);
4763
+ }
4764
+ const len = shapeClustersToStack.length;
4687
4765
  if (gap === 0 && len < 3 || len < 2) return this;
4688
- const pageBounds = Object.fromEntries(
4689
- shapesToStack.map((shape) => [shape.id, this.getShapePageBounds(shape)])
4690
- );
4691
4766
  let val;
4692
4767
  let min;
4693
4768
  let max;
@@ -4703,57 +4778,55 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4703
4778
  max = "maxY";
4704
4779
  dim = "height";
4705
4780
  }
4706
- let shapeGap;
4781
+ let shapeGap = 0;
4707
4782
  if (gap === 0) {
4708
- const gaps = [];
4709
- shapesToStack.sort((a, b) => pageBounds[a.id][min] - pageBounds[b.id][min]);
4783
+ const gaps = {};
4784
+ shapeClustersToStack.sort((a, b) => a.pageBounds[min] - b.pageBounds[min]);
4710
4785
  for (let i = 0; i < len - 1; i++) {
4711
- const shape = shapesToStack[i];
4712
- const nextShape = shapesToStack[i + 1];
4713
- const bounds = pageBounds[shape.id];
4714
- const nextBounds = pageBounds[nextShape.id];
4715
- const gap2 = nextBounds[min] - bounds[max];
4716
- const current = gaps.find((g) => g.gap === gap2);
4717
- if (current) {
4718
- current.count++;
4719
- } else {
4720
- gaps.push({ gap: gap2, count: 1 });
4786
+ const currCluster = shapeClustersToStack[i];
4787
+ const nextCluster = shapeClustersToStack[i + 1];
4788
+ const gap2 = nextCluster.pageBounds[min] - currCluster.pageBounds[max];
4789
+ if (!gaps[gap2]) {
4790
+ gaps[gap2] = 0;
4721
4791
  }
4792
+ gaps[gap2]++;
4722
4793
  }
4723
- let maxCount = 0;
4724
- gaps.forEach((g) => {
4725
- if (g.count > maxCount) {
4726
- maxCount = g.count;
4727
- shapeGap = g.gap;
4794
+ let maxCount = 1;
4795
+ for (const [gap2, count] of Object.entries(gaps)) {
4796
+ if (count > maxCount) {
4797
+ maxCount = count;
4798
+ shapeGap = parseFloat(gap2);
4728
4799
  }
4729
- });
4800
+ }
4730
4801
  if (maxCount === 1) {
4731
- shapeGap = Math.max(0, gaps.reduce((a, c) => a + c.gap * c.count, 0) / (len - 1));
4802
+ let totalCount = 0;
4803
+ for (const [gap2, count] of Object.entries(gaps)) {
4804
+ shapeGap += parseFloat(gap2) * count;
4805
+ totalCount += count;
4806
+ }
4807
+ shapeGap /= totalCount;
4732
4808
  }
4733
4809
  } else {
4734
4810
  shapeGap = gap;
4735
4811
  }
4736
4812
  const changes = [];
4737
- let v = pageBounds[shapesToStack[0].id][max];
4738
- shapesToStack.forEach((shape, i) => {
4739
- if (i === 0) return;
4740
- const delta = { x: 0, y: 0 };
4741
- delta[val] = v + shapeGap - pageBounds[shape.id][val];
4742
- const parent = this.getShapeParent(shape);
4743
- const localDelta = parent ? import_Vec.Vec.Rot(delta, -this.getShapePageTransform(parent).decompose().rotation) : delta;
4744
- const translateStartChanges = this.getShapeUtil(shape).onTranslateStart?.(shape);
4745
- changes.push(
4746
- translateStartChanges ? {
4747
- ...translateStartChanges,
4748
- [val]: shape[val] + localDelta[val]
4749
- } : {
4750
- id: shape.id,
4751
- type: shape.type,
4752
- [val]: shape[val] + localDelta[val]
4813
+ let v = shapeClustersToStack[0].pageBounds[max];
4814
+ for (let i = 1; i < shapeClustersToStack.length; i++) {
4815
+ const { shapes: shapes2, pageBounds } = shapeClustersToStack[i];
4816
+ const delta = new import_Vec.Vec();
4817
+ delta[val] = v + shapeGap - pageBounds[val];
4818
+ for (const shape of shapes2) {
4819
+ const shapeDelta = delta.clone();
4820
+ const parent = this.getShapeParent(shape);
4821
+ if (parent) {
4822
+ const parentTransform = this.getShapePageTransform(parent);
4823
+ if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
4753
4824
  }
4754
- );
4755
- v += pageBounds[shape.id][dim] + shapeGap;
4756
- });
4825
+ shapeDelta.add(shape);
4826
+ changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
4827
+ }
4828
+ v += pageBounds[dim] + shapeGap;
4829
+ }
4757
4830
  this.updateShapes(changes);
4758
4831
  return this;
4759
4832
  }
@@ -4771,91 +4844,101 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4771
4844
  * @param gap - The padding to apply to the packed shapes. Defaults to 16.
4772
4845
  */
4773
4846
  packShapes(shapes, gap) {
4774
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4775
4847
  if (this.getIsReadonly()) return this;
4776
- if (ids.length < 2) return this;
4777
- const shapesToPack = ids.map((id) => this.getShape(id)).filter((shape2) => {
4778
- if (!shape2) return false;
4779
- return this.getShapeUtil(shape2).canBeLaidOut(shape2);
4780
- });
4781
- const shapePageBounds = {};
4782
- const nextShapePageBounds = {};
4783
- let shape, bounds, area = 0;
4784
- for (let i = 0; i < shapesToPack.length; i++) {
4785
- shape = shapesToPack[i];
4786
- bounds = this.getShapePageBounds(shape);
4787
- shapePageBounds[shape.id] = bounds;
4788
- nextShapePageBounds[shape.id] = bounds.clone();
4789
- area += bounds.width * bounds.height;
4790
- }
4791
- const commonBounds = import_Box.Box.Common((0, import_utils.compact)(Object.values(shapePageBounds)));
4848
+ const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4849
+ const shapesToPackFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4850
+ const shapeClustersToPack = [];
4851
+ const allBounds = [];
4852
+ const visited = /* @__PURE__ */ new Set();
4853
+ for (const shape of shapesToPackFirstPass) {
4854
+ if (visited.has(shape.id)) continue;
4855
+ visited.add(shape.id);
4856
+ const shapePageBounds = this.getShapePageBounds(shape);
4857
+ if (!shapePageBounds) continue;
4858
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
4859
+ type: "pack",
4860
+ shapes: shapesToPackFirstPass
4861
+ })) {
4862
+ continue;
4863
+ }
4864
+ const shapesMovingTogether = [shape];
4865
+ const boundsOfShapesMovingTogether = [shapePageBounds];
4866
+ this.collectShapesViaArrowBindings({
4867
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
4868
+ initialShapes: shapesToPackFirstPass,
4869
+ resultShapes: shapesMovingTogether,
4870
+ resultBounds: boundsOfShapesMovingTogether,
4871
+ visited
4872
+ });
4873
+ const commonPageBounds = import_Box.Box.Common(boundsOfShapesMovingTogether);
4874
+ if (!commonPageBounds) continue;
4875
+ shapeClustersToPack.push({
4876
+ shapes: shapesMovingTogether,
4877
+ pageBounds: commonPageBounds,
4878
+ nextPageBounds: commonPageBounds.clone()
4879
+ });
4880
+ allBounds.push(commonPageBounds);
4881
+ }
4882
+ if (shapeClustersToPack.length < 2) return this;
4883
+ let area = 0;
4884
+ for (const { pageBounds } of shapeClustersToPack) {
4885
+ area += pageBounds.width * pageBounds.height;
4886
+ }
4887
+ const commonBounds = import_Box.Box.Common(allBounds);
4792
4888
  const maxWidth = commonBounds.width;
4793
- shapesToPack.sort((a, b) => shapePageBounds[b.id].height - shapePageBounds[a.id].height);
4889
+ shapeClustersToPack.sort((a, b) => a.pageBounds.width - b.pageBounds.width).sort((a, b) => a.pageBounds.height - b.pageBounds.height);
4794
4890
  const startWidth = Math.max(Math.ceil(Math.sqrt(area / 0.95)), maxWidth);
4795
4891
  const spaces = [new import_Box.Box(commonBounds.x, commonBounds.y, startWidth, Infinity)];
4796
4892
  let width = 0;
4797
4893
  let height = 0;
4798
4894
  let space;
4799
4895
  let last2;
4800
- for (let i = 0; i < shapesToPack.length; i++) {
4801
- shape = shapesToPack[i];
4802
- bounds = nextShapePageBounds[shape.id];
4803
- for (let i2 = spaces.length - 1; i2 >= 0; i2--) {
4804
- space = spaces[i2];
4805
- if (bounds.width > space.width || bounds.height > space.height) continue;
4806
- bounds.x = space.x;
4807
- bounds.y = space.y;
4808
- height = Math.max(height, bounds.maxY);
4809
- width = Math.max(width, bounds.maxX);
4810
- if (bounds.width === space.width && bounds.height === space.height) {
4896
+ for (const { nextPageBounds } of shapeClustersToPack) {
4897
+ for (let i = spaces.length - 1; i >= 0; i--) {
4898
+ space = spaces[i];
4899
+ if (nextPageBounds.width > space.width || nextPageBounds.height > space.height) continue;
4900
+ nextPageBounds.x = space.x;
4901
+ nextPageBounds.y = space.y;
4902
+ height = Math.max(height, nextPageBounds.maxY);
4903
+ width = Math.max(width, nextPageBounds.maxX);
4904
+ if (nextPageBounds.width === space.width && nextPageBounds.height === space.height) {
4811
4905
  last2 = spaces.pop();
4812
- if (i2 < spaces.length) spaces[i2] = last2;
4813
- } else if (bounds.height === space.height) {
4814
- space.x += bounds.width + gap;
4815
- space.width -= bounds.width + gap;
4816
- } else if (bounds.width === space.width) {
4817
- space.y += bounds.height + gap;
4818
- space.height -= bounds.height + gap;
4906
+ if (i < spaces.length) spaces[i] = last2;
4907
+ } else if (nextPageBounds.height === space.height) {
4908
+ space.x += nextPageBounds.width + gap;
4909
+ space.width -= nextPageBounds.width + gap;
4910
+ } else if (nextPageBounds.width === space.width) {
4911
+ space.y += nextPageBounds.height + gap;
4912
+ space.height -= nextPageBounds.height + gap;
4819
4913
  } else {
4820
4914
  spaces.push(
4821
4915
  new import_Box.Box(
4822
- space.x + (bounds.width + gap),
4916
+ space.x + (nextPageBounds.width + gap),
4823
4917
  space.y,
4824
- space.width - (bounds.width + gap),
4825
- bounds.height
4918
+ space.width - (nextPageBounds.width + gap),
4919
+ nextPageBounds.height
4826
4920
  )
4827
4921
  );
4828
- space.y += bounds.height + gap;
4829
- space.height -= bounds.height + gap;
4922
+ space.y += nextPageBounds.height + gap;
4923
+ space.height -= nextPageBounds.height + gap;
4830
4924
  }
4831
4925
  break;
4832
4926
  }
4833
4927
  }
4834
- const commonAfter = import_Box.Box.Common(Object.values(nextShapePageBounds));
4928
+ const commonAfter = import_Box.Box.Common(shapeClustersToPack.map((s) => s.nextPageBounds));
4835
4929
  const centerDelta = import_Vec.Vec.Sub(commonBounds.center, commonAfter.center);
4836
- let nextBounds;
4837
4930
  const changes = [];
4838
- for (let i = 0; i < shapesToPack.length; i++) {
4839
- shape = shapesToPack[i];
4840
- bounds = shapePageBounds[shape.id];
4841
- nextBounds = nextShapePageBounds[shape.id];
4842
- const delta = import_Vec.Vec.Sub(nextBounds.point, bounds.point).add(centerDelta);
4843
- const parentTransform = this.getShapeParentTransform(shape);
4844
- if (parentTransform) delta.rot(-parentTransform.rotation());
4845
- const change = {
4846
- id: shape.id,
4847
- type: shape.type,
4848
- x: shape.x + delta.x,
4849
- y: shape.y + delta.y
4850
- };
4851
- const translateStartChange = this.getShapeUtil(shape).onTranslateStart?.({
4852
- ...shape,
4853
- ...change
4854
- });
4855
- if (translateStartChange) {
4856
- changes.push({ ...change, ...translateStartChange });
4857
- } else {
4858
- changes.push(change);
4931
+ for (const { shapes: shapes2, pageBounds, nextPageBounds } of shapeClustersToPack) {
4932
+ const delta = import_Vec.Vec.Sub(nextPageBounds.point, pageBounds.point).add(centerDelta);
4933
+ for (const shape of shapes2) {
4934
+ const shapeDelta = delta.clone();
4935
+ const parent = this.getShapeParent(shape);
4936
+ if (parent) {
4937
+ const parentTransform = this.getShapeParentTransform(shape);
4938
+ if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
4939
+ }
4940
+ shapeDelta.add(shape);
4941
+ changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
4859
4942
  }
4860
4943
  }
4861
4944
  if (changes.length) {
@@ -4878,19 +4961,45 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4878
4961
  * @public
4879
4962
  */
4880
4963
  alignShapes(shapes, operation) {
4881
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4882
4964
  if (this.getIsReadonly()) return this;
4883
- if (ids.length < 2) return this;
4884
- const shapesToAlign = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4885
- const shapePageBounds = Object.fromEntries(
4886
- shapesToAlign.map((shape) => [shape.id, this.getShapePageBounds(shape)])
4887
- );
4888
- const commonBounds = import_Box.Box.Common((0, import_utils.compact)(Object.values(shapePageBounds)));
4965
+ const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4966
+ const shapesToAlignFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4967
+ const shapeClustersToAlign = [];
4968
+ const allBounds = [];
4969
+ const visited = /* @__PURE__ */ new Set();
4970
+ for (const shape of shapesToAlignFirstPass) {
4971
+ if (visited.has(shape.id)) continue;
4972
+ visited.add(shape.id);
4973
+ const shapePageBounds = this.getShapePageBounds(shape);
4974
+ if (!shapePageBounds) continue;
4975
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
4976
+ type: "align",
4977
+ shapes: shapesToAlignFirstPass
4978
+ })) {
4979
+ continue;
4980
+ }
4981
+ const shapesMovingTogether = [shape];
4982
+ const boundsOfShapesMovingTogether = [shapePageBounds];
4983
+ this.collectShapesViaArrowBindings({
4984
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
4985
+ initialShapes: shapesToAlignFirstPass,
4986
+ resultShapes: shapesMovingTogether,
4987
+ resultBounds: boundsOfShapesMovingTogether,
4988
+ visited
4989
+ });
4990
+ const commonPageBounds = import_Box.Box.Common(boundsOfShapesMovingTogether);
4991
+ if (!commonPageBounds) continue;
4992
+ shapeClustersToAlign.push({
4993
+ shapes: shapesMovingTogether,
4994
+ pageBounds: commonPageBounds
4995
+ });
4996
+ allBounds.push(commonPageBounds);
4997
+ }
4998
+ if (shapeClustersToAlign.length < 2) return this;
4999
+ const commonBounds = import_Box.Box.Common(allBounds);
4889
5000
  const changes = [];
4890
- shapesToAlign.forEach((shape) => {
4891
- const pageBounds = shapePageBounds[shape.id];
4892
- if (!pageBounds) return;
4893
- const delta = { x: 0, y: 0 };
5001
+ shapeClustersToAlign.forEach(({ shapes: shapes2, pageBounds }) => {
5002
+ const delta = new import_Vec.Vec();
4894
5003
  switch (operation) {
4895
5004
  case "top": {
4896
5005
  delta.y = commonBounds.minY - pageBounds.minY;
@@ -4917,9 +5026,16 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4917
5026
  break;
4918
5027
  }
4919
5028
  }
4920
- const parent = this.getShapeParent(shape);
4921
- const localDelta = parent ? import_Vec.Vec.Rot(delta, -this.getShapePageTransform(parent).decompose().rotation) : delta;
4922
- changes.push(this.getChangesToTranslateShape(shape, import_Vec.Vec.Add(shape, localDelta)));
5029
+ for (const shape of shapes2) {
5030
+ const shapeDelta = delta.clone();
5031
+ const parent = this.getShapeParent(shape);
5032
+ if (parent) {
5033
+ const parentTransform = this.getShapePageTransform(parent);
5034
+ if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5035
+ }
5036
+ shapeDelta.add(shape);
5037
+ changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
5038
+ }
4923
5039
  });
4924
5040
  this.updateShapes(changes);
4925
5041
  return this;
@@ -4939,47 +5055,95 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4939
5055
  * @public
4940
5056
  */
4941
5057
  distributeShapes(shapes, operation) {
4942
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4943
5058
  if (this.getIsReadonly()) return this;
4944
- if (ids.length < 3) return this;
4945
- const len = ids.length;
4946
- const shapesToDistribute = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4947
- const pageBounds = Object.fromEntries(
4948
- shapesToDistribute.map((shape) => [shape.id, this.getShapePageBounds(shape)])
4949
- );
5059
+ const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5060
+ const shapesToDistributeFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
5061
+ const shapeClustersToDistribute = [];
5062
+ const allBounds = [];
5063
+ const visited = /* @__PURE__ */ new Set();
5064
+ for (const shape of shapesToDistributeFirstPass) {
5065
+ if (visited.has(shape.id)) continue;
5066
+ visited.add(shape.id);
5067
+ const shapePageBounds = this.getShapePageBounds(shape);
5068
+ if (!shapePageBounds) continue;
5069
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
5070
+ type: "distribute",
5071
+ shapes: shapesToDistributeFirstPass
5072
+ })) {
5073
+ continue;
5074
+ }
5075
+ const shapesMovingTogether = [shape];
5076
+ const boundsOfShapesMovingTogether = [shapePageBounds];
5077
+ this.collectShapesViaArrowBindings({
5078
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
5079
+ initialShapes: shapesToDistributeFirstPass,
5080
+ resultShapes: shapesMovingTogether,
5081
+ resultBounds: boundsOfShapesMovingTogether,
5082
+ visited
5083
+ });
5084
+ const commonPageBounds = import_Box.Box.Common(boundsOfShapesMovingTogether);
5085
+ if (!commonPageBounds) continue;
5086
+ shapeClustersToDistribute.push({
5087
+ shapes: shapesMovingTogether,
5088
+ pageBounds: commonPageBounds
5089
+ });
5090
+ allBounds.push(commonPageBounds);
5091
+ }
5092
+ if (shapeClustersToDistribute.length < 3) return this;
4950
5093
  let val;
4951
5094
  let min;
4952
5095
  let max;
4953
- let mid;
4954
5096
  let dim;
4955
5097
  if (operation === "horizontal") {
4956
5098
  val = "x";
4957
5099
  min = "minX";
4958
5100
  max = "maxX";
4959
- mid = "midX";
4960
5101
  dim = "width";
4961
5102
  } else {
4962
5103
  val = "y";
4963
5104
  min = "minY";
4964
5105
  max = "maxY";
4965
- mid = "midY";
4966
5106
  dim = "height";
4967
5107
  }
4968
5108
  const changes = [];
4969
- const first = shapesToDistribute.sort(
4970
- (a, b) => pageBounds[a.id][min] - pageBounds[b.id][min]
4971
- )[0];
4972
- const last2 = shapesToDistribute.sort((a, b) => pageBounds[b.id][max] - pageBounds[a.id][max])[0];
4973
- const midFirst = pageBounds[first.id][mid];
4974
- const step = (pageBounds[last2.id][mid] - midFirst) / (len - 1);
4975
- const v = midFirst + step;
4976
- shapesToDistribute.filter((shape) => shape !== first && shape !== last2).sort((a, b) => pageBounds[a.id][mid] - pageBounds[b.id][mid]).forEach((shape, i) => {
4977
- const delta = { x: 0, y: 0 };
4978
- delta[val] = v + step * i - pageBounds[shape.id][dim] / 2 - pageBounds[shape.id][val];
4979
- const parent = this.getShapeParent(shape);
4980
- const localDelta = parent ? import_Vec.Vec.Rot(delta, -this.getShapePageTransform(parent).rotation()) : delta;
4981
- changes.push(this.getChangesToTranslateShape(shape, import_Vec.Vec.Add(shape, localDelta)));
5109
+ const first = shapeClustersToDistribute.sort((a, b) => a.pageBounds[min] - b.pageBounds[min])[0];
5110
+ const last2 = shapeClustersToDistribute.sort((a, b) => b.pageBounds[max] - a.pageBounds[max])[0];
5111
+ if (first === last2) {
5112
+ const excludedShapeIds = new Set(first.shapes.map((s) => s.id));
5113
+ return this.distributeShapes(
5114
+ ids.filter((id) => !excludedShapeIds.has(id)),
5115
+ operation
5116
+ );
5117
+ }
5118
+ const shapeClustersToMove = shapeClustersToDistribute.filter((shape) => shape !== first && shape !== last2).sort((a, b) => {
5119
+ if (a.pageBounds[min] === b.pageBounds[min]) {
5120
+ return a.shapes[0].id < b.shapes[0].id ? -1 : 1;
5121
+ }
5122
+ return a.pageBounds[min] - b.pageBounds[min];
4982
5123
  });
5124
+ const maxFirst = first.pageBounds[max];
5125
+ const range = last2.pageBounds[min] - maxFirst;
5126
+ const summedShapeDimensions = shapeClustersToMove.reduce((acc, s) => acc + s.pageBounds[dim], 0);
5127
+ const gap = (range - summedShapeDimensions) / (shapeClustersToMove.length + 1);
5128
+ for (let v = maxFirst + gap, i = 0; i < shapeClustersToMove.length; i++) {
5129
+ const { shapes: shapes2, pageBounds } = shapeClustersToMove[i];
5130
+ const delta = new import_Vec.Vec();
5131
+ delta[val] = v - pageBounds[val];
5132
+ if (v + pageBounds[dim] > last2.pageBounds[max] - 1) {
5133
+ delta[val] = last2.pageBounds[max] - pageBounds[max] - 1;
5134
+ }
5135
+ for (const shape of shapes2) {
5136
+ const shapeDelta = delta.clone();
5137
+ const parent = this.getShapeParent(shape);
5138
+ if (parent) {
5139
+ const parentTransform = this.getShapePageTransform(parent);
5140
+ if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5141
+ }
5142
+ shapeDelta.add(shape);
5143
+ changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
5144
+ }
5145
+ v += pageBounds[dim] + gap;
5146
+ }
4983
5147
  this.updateShapes(changes);
4984
5148
  return this;
4985
5149
  }
@@ -5000,59 +5164,78 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
5000
5164
  stretchShapes(shapes, operation) {
5001
5165
  const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5002
5166
  if (this.getIsReadonly()) return this;
5003
- if (ids.length < 2) return this;
5004
- const shapesToStretch = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
5005
- const shapeBounds = Object.fromEntries(ids.map((id) => [id, this.getShapeGeometry(id).bounds]));
5006
- const shapePageBounds = Object.fromEntries(ids.map((id) => [id, this.getShapePageBounds(id)]));
5007
- const commonBounds = import_Box.Box.Common((0, import_utils.compact)(Object.values(shapePageBounds)));
5008
- switch (operation) {
5009
- case "vertical": {
5010
- this.run(() => {
5011
- for (const shape of shapesToStretch) {
5012
- const pageRotation = this.getShapePageTransform(shape).rotation();
5013
- if (pageRotation % import_utils2.PI2) continue;
5014
- const bounds = shapeBounds[shape.id];
5015
- const pageBounds = shapePageBounds[shape.id];
5016
- const localOffset = new import_Vec.Vec(0, commonBounds.minY - pageBounds.minY);
5017
- const parentTransform = this.getShapeParentTransform(shape);
5018
- if (parentTransform) localOffset.rot(-parentTransform.rotation());
5019
- const { x, y } = import_Vec.Vec.Add(localOffset, shape);
5020
- this.updateShapes([{ id: shape.id, type: shape.type, x, y }]);
5021
- const scale = new import_Vec.Vec(1, commonBounds.height / pageBounds.height);
5022
- this.resizeShape(shape.id, scale, {
5023
- initialBounds: bounds,
5024
- scaleOrigin: new import_Vec.Vec(pageBounds.center.x, commonBounds.minY),
5025
- isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
5026
- scaleAxisRotation: 0
5027
- });
5028
- }
5029
- });
5030
- break;
5031
- }
5032
- case "horizontal": {
5033
- this.run(() => {
5034
- for (const shape of shapesToStretch) {
5035
- const bounds = shapeBounds[shape.id];
5036
- const pageBounds = shapePageBounds[shape.id];
5037
- const pageRotation = this.getShapePageTransform(shape).rotation();
5038
- if (pageRotation % import_utils2.PI2) continue;
5039
- const localOffset = new import_Vec.Vec(commonBounds.minX - pageBounds.minX, 0);
5040
- const parentTransform = this.getShapeParentTransform(shape);
5041
- if (parentTransform) localOffset.rot(-parentTransform.rotation());
5042
- const { x, y } = import_Vec.Vec.Add(localOffset, shape);
5043
- this.updateShapes([{ id: shape.id, type: shape.type, x, y }]);
5044
- const scale = new import_Vec.Vec(commonBounds.width / pageBounds.width, 1);
5045
- this.resizeShape(shape.id, scale, {
5046
- initialBounds: bounds,
5047
- scaleOrigin: new import_Vec.Vec(commonBounds.minX, pageBounds.center.y),
5048
- isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
5049
- scaleAxisRotation: 0
5050
- });
5051
- }
5052
- });
5053
- break;
5167
+ const shapesToStretchFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id))).filter(
5168
+ (s) => this.getShapePageTransform(s)?.rotation() % (import_utils2.PI / 2) === 0
5169
+ );
5170
+ const shapeClustersToStretch = [];
5171
+ const allBounds = [];
5172
+ const visited = /* @__PURE__ */ new Set();
5173
+ for (const shape of shapesToStretchFirstPass) {
5174
+ if (visited.has(shape.id)) continue;
5175
+ visited.add(shape.id);
5176
+ const shapePageBounds = this.getShapePageBounds(shape);
5177
+ if (!shapePageBounds) continue;
5178
+ const shapesMovingTogether = [shape];
5179
+ const boundsOfShapesMovingTogether = [shapePageBounds];
5180
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
5181
+ type: "stretch",
5182
+ shapes: shapesToStretchFirstPass
5183
+ })) {
5184
+ continue;
5054
5185
  }
5186
+ this.collectShapesViaArrowBindings({
5187
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
5188
+ initialShapes: shapesToStretchFirstPass,
5189
+ resultShapes: shapesMovingTogether,
5190
+ resultBounds: boundsOfShapesMovingTogether,
5191
+ visited
5192
+ });
5193
+ const commonPageBounds = import_Box.Box.Common(boundsOfShapesMovingTogether);
5194
+ if (!commonPageBounds) continue;
5195
+ shapeClustersToStretch.push({
5196
+ shapes: shapesMovingTogether,
5197
+ pageBounds: commonPageBounds
5198
+ });
5199
+ allBounds.push(commonPageBounds);
5200
+ }
5201
+ if (shapeClustersToStretch.length < 2) return this;
5202
+ const commonBounds = import_Box.Box.Common(allBounds);
5203
+ let val;
5204
+ let min;
5205
+ let dim;
5206
+ if (operation === "horizontal") {
5207
+ val = "x";
5208
+ min = "minX";
5209
+ dim = "width";
5210
+ } else {
5211
+ val = "y";
5212
+ min = "minY";
5213
+ dim = "height";
5055
5214
  }
5215
+ this.run(() => {
5216
+ shapeClustersToStretch.forEach(({ shapes: shapes2, pageBounds }) => {
5217
+ const localOffset = new import_Vec.Vec();
5218
+ localOffset[val] = commonBounds[min] - pageBounds[min];
5219
+ const scaleOrigin = pageBounds.center.clone();
5220
+ scaleOrigin[val] = commonBounds[min];
5221
+ const scale = new import_Vec.Vec(1, 1);
5222
+ scale[val] = commonBounds[dim] / pageBounds[dim];
5223
+ for (const shape of shapes2) {
5224
+ const shapeLocalOffset = localOffset.clone();
5225
+ const parentTransform = this.getShapeParentTransform(shape);
5226
+ if (parentTransform) localOffset.rot(-parentTransform.rotation());
5227
+ shapeLocalOffset.add(shape);
5228
+ const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset);
5229
+ this.updateShape(changes);
5230
+ this.resizeShape(shape.id, scale, {
5231
+ initialBounds: this.getShapeGeometry(shape).bounds,
5232
+ scaleOrigin,
5233
+ isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
5234
+ scaleAxisRotation: 0
5235
+ });
5236
+ }
5237
+ });
5238
+ });
5056
5239
  return this;
5057
5240
  }
5058
5241
  /**
@@ -5067,8 +5250,8 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
5067
5250
  resizeShape(shape, scale, opts = {}) {
5068
5251
  const id = typeof shape === "string" ? shape : shape.id;
5069
5252
  if (this.getIsReadonly()) return this;
5070
- if (!Number.isFinite(scale.x)) scale = new import_Vec.Vec(1, scale.y);
5071
- if (!Number.isFinite(scale.y)) scale = new import_Vec.Vec(scale.x, 1);
5253
+ if (!import_core_js.Number.isFinite(scale.x)) scale = new import_Vec.Vec(1, scale.y);
5254
+ if (!import_core_js.Number.isFinite(scale.y)) scale = new import_Vec.Vec(scale.x, 1);
5072
5255
  const initialShape = opts.initialShape ?? this.getShape(id);
5073
5256
  if (!initialShape) return this;
5074
5257
  const scaleOrigin = opts.scaleOrigin ?? this.getShapePageBounds(id)?.center;
@@ -7301,7 +7484,6 @@ __decorateElement(_init, 1, "getPages", _getPages_dec, Editor);
7301
7484
  __decorateElement(_init, 1, "getCurrentPageId", _getCurrentPageId_dec, Editor);
7302
7485
  __decorateElement(_init, 1, "getCurrentPageShapeIdsSorted", _getCurrentPageShapeIdsSorted_dec, Editor);
7303
7486
  __decorateElement(_init, 1, "_getAllAssetsQuery", __getAllAssetsQuery_dec, Editor);
7304
- __decorateElement(_init, 1, "_getShapeGeometryCache", __getShapeGeometryCache_dec, Editor);
7305
7487
  __decorateElement(_init, 1, "_getShapeHandlesCache", __getShapeHandlesCache_dec, Editor);
7306
7488
  __decorateElement(_init, 1, "_getShapePageTransformCache", __getShapePageTransformCache_dec, Editor);
7307
7489
  __decorateElement(_init, 1, "_getShapePageBoundsCache", __getShapePageBoundsCache_dec, Editor);